@cohortapp/agent-sdk 2.17.0 → 2.18.4
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/settings.json +18 -0
- package/.env.example +18 -5
- package/README.md +1 -0
- package/bin/maestro.mjs +62 -0
- package/docs/guides/billing-console-keys.md +60 -0
- package/docs/guides/front-door-session.md +54 -9
- package/docs/guides/mac-mini.md +20 -25
- package/docs/guides/setup-wizard.md +1 -1
- package/docs/runbooks/fleet-rollout.md +156 -0
- package/docs/runbooks/mac-mini-bootstrap.md +12 -14
- package/lib/action-executor.js +19 -3
- package/lib/budget-guard.mjs +279 -3
- package/lib/channels/base-adapter.mjs +3 -1
- package/lib/channels/contract.mjs +2 -1
- package/lib/channels/inbox-item.mjs +8 -0
- package/lib/claude-bin.mjs +5 -6
- package/lib/cli/doctor-checks.mjs +141 -10
- package/lib/cli/global-setup-extras.mjs +5 -1
- package/lib/cli/inbox.mjs +100 -15
- package/lib/cli/seat-auth.mjs +463 -0
- package/lib/cli/session.mjs +80 -12
- package/lib/collective/capture.mjs +8 -6
- package/lib/collective/global-config.mjs +63 -1
- package/lib/collective/presence.mjs +142 -5
- package/lib/comms/send-gate.mjs +559 -1
- package/lib/diagnostics/alerts.mjs +49 -0
- package/lib/diagnostics/cadence-output-freshness.mjs +288 -0
- package/lib/engine/agents/definitions.mjs +343 -0
- package/lib/engine/agents/persist.mjs +275 -0
- package/lib/engine/agents/runtime.mjs +748 -0
- package/lib/engine/agents/usage.mjs +95 -0
- package/lib/engine/auth-status.mjs +139 -0
- package/lib/engine/budget.mjs +194 -0
- package/lib/engine/cli.mjs +1204 -0
- package/lib/engine/commands/index.mjs +269 -0
- package/lib/engine/context/budget.mjs +219 -0
- package/lib/engine/context/cache.mjs +125 -0
- package/lib/engine/context/child-env.mjs +215 -0
- package/lib/engine/context/compaction.mjs +342 -0
- package/lib/engine/context/images.mjs +90 -0
- package/lib/engine/context/instructions.mjs +327 -0
- package/lib/engine/context/lazy-instructions.mjs +169 -0
- package/lib/engine/context/manager.mjs +182 -0
- package/lib/engine/context/real-path.mjs +91 -0
- package/lib/engine/context/secret-values.mjs +163 -0
- package/lib/engine/context/settings.mjs +274 -0
- package/lib/engine/context/stream-input.mjs +159 -0
- package/lib/engine/guard.mjs +152 -0
- package/lib/engine/hooks.mjs +713 -0
- package/lib/engine/loop.mjs +560 -0
- package/lib/engine/mcp/client.mjs +254 -0
- package/lib/engine/mcp/config.mjs +301 -0
- package/lib/engine/mcp/http.mjs +201 -0
- package/lib/engine/mcp/index.mjs +146 -0
- package/lib/engine/mcp/jsonrpc.mjs +147 -0
- package/lib/engine/mcp/naming.mjs +66 -0
- package/lib/engine/mcp/resources.mjs +89 -0
- package/lib/engine/mcp/results.mjs +133 -0
- package/lib/engine/mcp/stdio.mjs +137 -0
- package/lib/engine/mcp/supervisor.mjs +116 -0
- package/lib/engine/messages.mjs +104 -0
- package/lib/engine/output/json.mjs +164 -0
- package/lib/engine/output/stream-json.mjs +266 -0
- package/lib/engine/permissions.mjs +845 -0
- package/lib/engine/process-identity.mjs +164 -0
- package/lib/engine/process-tree.mjs +551 -0
- package/lib/engine/prompt.mjs +60 -0
- package/lib/engine/session/store.mjs +299 -0
- package/lib/engine/session-runtime/args.mjs +97 -0
- package/lib/engine/session-runtime/host.mjs +143 -0
- package/lib/engine/session-runtime/inbox.mjs +122 -0
- package/lib/engine/session-runtime/notifications.mjs +129 -0
- package/lib/engine/session-runtime/registry.mjs +328 -0
- package/lib/engine/session-runtime/runner.mjs +344 -0
- package/lib/engine/session-runtime/socket.mjs +212 -0
- package/lib/engine/session-runtime/wakeup.mjs +115 -0
- package/lib/engine/skills/index.mjs +321 -0
- package/lib/engine/tools/bash-background.mjs +533 -0
- package/lib/engine/tools/bash.mjs +216 -0
- package/lib/engine/tools/edit.mjs +97 -0
- package/lib/engine/tools/glob.mjs +81 -0
- package/lib/engine/tools/grep.mjs +224 -0
- package/lib/engine/tools/index.mjs +84 -0
- package/lib/engine/tools/list-agents.mjs +32 -0
- package/lib/engine/tools/ls.mjs +127 -0
- package/lib/engine/tools/monitor.mjs +82 -0
- package/lib/engine/tools/notebook-edit.mjs +218 -0
- package/lib/engine/tools/read.mjs +103 -0
- package/lib/engine/tools/schedule-wakeup.mjs +45 -0
- package/lib/engine/tools/schema.mjs +144 -0
- package/lib/engine/tools/send-message.mjs +77 -0
- package/lib/engine/tools/session.mjs +70 -0
- package/lib/engine/tools/todo.mjs +144 -0
- package/lib/engine/tools/toolsearch.mjs +217 -0
- package/lib/engine/tools/walk.mjs +193 -0
- package/lib/engine/tools/web-switch.mjs +31 -0
- package/lib/engine/tools/webfetch-html.mjs +387 -0
- package/lib/engine/tools/webfetch-net.mjs +340 -0
- package/lib/engine/tools/webfetch.mjs +198 -0
- package/lib/engine/tools/websearch.mjs +91 -0
- package/lib/engine/tools/workflow.mjs +95 -0
- package/lib/engine/tools/write.mjs +76 -0
- package/lib/engine/tui/line-editor.mjs +137 -0
- package/lib/engine/tui/render.mjs +86 -0
- package/lib/engine/tui/tui.mjs +274 -0
- package/lib/engine/wire/anthropic-messages.mjs +263 -0
- package/lib/engine/wire/effort.mjs +36 -0
- package/lib/engine/wire/errors.mjs +496 -0
- package/lib/engine/wire/http.mjs +441 -0
- package/lib/engine/wire/index.mjs +76 -0
- package/lib/engine/wire/openai-chat.mjs +332 -0
- package/lib/engine/wire/prompt-cache.mjs +79 -0
- package/lib/engine/wire/search.mjs +140 -0
- package/lib/engine/wire/sse.mjs +114 -0
- package/lib/engine/wire/stall.mjs +349 -0
- package/lib/engine/wire/token-provider.mjs +175 -0
- package/lib/engine/wire/usage.mjs +192 -0
- package/lib/engine/workflow/host.mjs +524 -0
- package/lib/engine/workflow/journal.mjs +188 -0
- package/lib/engine/workflow/json-schema.mjs +171 -0
- package/lib/engine/workflow/meta.mjs +329 -0
- package/lib/engine/workflow/notifications.mjs +52 -0
- package/lib/engine/workflow/runtime.mjs +447 -0
- package/lib/engine/workflow/sandbox.mjs +534 -0
- package/lib/engine/workflow/worker.mjs +141 -0
- package/lib/engine/workflow/worktree.mjs +74 -0
- package/lib/execution/disposition.mjs +1 -1
- package/lib/execution/intake.mjs +10 -0
- package/lib/execution/surface-policy.mjs +15 -0
- package/lib/learning/curator.mjs +8 -6
- package/lib/learning/reflect.mjs +8 -6
- package/lib/model-router/catalog/cohort.yaml +137 -0
- package/lib/model-router/catalog.mjs +118 -1
- package/lib/model-router/failover.mjs +67 -16
- package/lib/model-router/llm-task.mjs +39 -3
- package/lib/model-router/resolve.mjs +89 -3
- package/lib/model-router/spawn.mjs +46 -47
- package/lib/model-router/taxonomy.mjs +126 -4
- package/lib/org/cost-sync.mjs +141 -11
- package/lib/org/inbound/broadcast.mjs +289 -0
- package/lib/org/inbound/collective.mjs +375 -0
- package/lib/org/inbound/directedness.mjs +96 -8
- package/lib/org/inbound/facts.mjs +78 -2
- package/lib/org/inbound/project.mjs +22 -0
- package/lib/org/inbound/surfaces.mjs +14 -0
- package/lib/org/llm-token.mjs +879 -0
- package/lib/org/mesh.mjs +61 -0
- package/lib/org/messaging.mjs +3 -1
- package/lib/org/protocol.checksum +1 -1
- package/lib/org/protocol.mjs +15 -0
- package/lib/org/quota.mjs +520 -0
- package/lib/org/tool-surface.mjs +104 -16
- package/lib/org/ui-parity.mjs +16 -1
- package/lib/org/work-ledger.mjs +37 -6
- package/lib/rate-guard.mjs +114 -1
- package/lib/resource-governor.mjs +41 -6
- package/lib/runtime/adapter.mjs +823 -0
- package/lib/runtime/child-env.mjs +191 -0
- package/lib/runtime/legacy-shell-guard.mjs +97 -0
- package/lib/runtime/seat-engine.mjs +162 -0
- package/lib/session/ask-ledger.mjs +271 -0
- package/lib/session/current-work.mjs +676 -0
- package/lib/session/feed-core.mjs +40 -3
- package/lib/session/launch-args.mjs +56 -4
- package/lib/session/status-summary.mjs +26 -9
- package/lib/session/upgrade-notice.mjs +42 -0
- package/lib/setup/claude-probe.mjs +117 -13
- package/lib/setup/enrich.mjs +13 -10
- package/lib/setup/sections/model.mjs +39 -13
- package/lib/telemetry/collect.mjs +208 -9
- package/lib/upgrade/ignored-drift.mjs +105 -0
- package/lib/voice/post-call-brief.mjs +30 -17
- package/package.json +13 -3
- package/plugins/maestro-skills/skills/board-work.md +5 -0
- package/plugins/maestro-skills/skills/inbound-triage.md +56 -15
- package/plugins/maestro-skills/skills/main-session.md +18 -7
- package/scripts/ci/check-tarball-fidelity.mjs +126 -2
- package/scripts/ci/run-tests.mjs +47 -19
- package/scripts/cohort-llm/api-key-helper.mjs +92 -0
- package/scripts/collective/hook-runner.mjs +29 -2
- package/scripts/continuous-monitor.sh +13 -0
- package/scripts/cost/track-claude-usage.mjs +15 -0
- package/scripts/daemon/agent-daemon.mjs +408 -20
- package/scripts/daemon/assurance.mjs +48 -12
- package/scripts/daemon/cadence-consumer.mjs +218 -68
- package/scripts/daemon/cadence-handlers.mjs +73 -4
- package/scripts/daemon/classifier.mjs +75 -26
- package/scripts/daemon/context-compiler.mjs +51 -37
- package/scripts/daemon/deliver.mjs +30 -1
- package/scripts/daemon/dispatcher.mjs +595 -149
- package/scripts/daemon/health.mjs +14 -1
- package/scripts/daemon/maestro-daemon.mjs +11 -0
- package/scripts/daemon/prompt-builder.mjs +24 -0
- package/scripts/daemon/responder.mjs +246 -79
- package/scripts/daemon/sdk-version.mjs +98 -16
- package/scripts/eval/probe-gateway.mjs +635 -0
- package/scripts/eval/replay/extract.mjs +270 -0
- package/scripts/eval/replay/grade.mjs +260 -0
- package/scripts/eval/replay/lib/config.mjs +50 -0
- package/scripts/eval/replay/lib/effects.mjs +65 -0
- package/scripts/eval/replay/lib/fixture.mjs +188 -0
- package/scripts/eval/replay/lib/judge.mjs +72 -0
- package/scripts/eval/replay/lib/redact.mjs +136 -0
- package/scripts/eval/replay/lib/sandbox.mjs +170 -0
- package/scripts/eval/replay/lib/schema-check.mjs +63 -0
- package/scripts/eval/replay/lib/transcript.mjs +76 -0
- package/scripts/eval/replay/mcp-replay-stub.mjs +101 -0
- package/scripts/eval/replay/report.mjs +185 -0
- package/scripts/eval/replay/run.mjs +404 -0
- package/scripts/fleet/rollout.mjs +1094 -0
- package/scripts/hooks/pre-send-audit.sh +36 -245
- package/scripts/hooks/pre-write-yaml-validate.mjs +275 -0
- package/scripts/hooks/validate-state-yaml.sh +190 -0
- package/scripts/huddle/huddle-llm.mjs +361 -0
- package/scripts/huddle/huddle-server.mjs +46 -121
- package/scripts/local-triggers/autoupdate.sh +448 -78
- package/scripts/local-triggers/run-trigger.sh +13 -0
- package/scripts/maintenance/pin-integrity.mjs +364 -0
- package/scripts/poll-slack-events.sh +41 -9
- package/scripts/poller/slack-socket-mode.mjs +28 -3
- package/scripts/session/supervisor.mjs +80 -13
- package/scripts/spawn-session.sh +13 -0
- package/bin/maestro.test.mjs +0 -1574
- package/lib/action-executor.test.mjs +0 -871
- package/lib/archetype.test.mjs +0 -132
- package/lib/assurance/plan-note.test.mjs +0 -234
- package/lib/assurance/room-budget.test.mjs +0 -486
- package/lib/assurance/tier.test.mjs +0 -174
- package/lib/autonomy.test.mjs +0 -66
- package/lib/backlog.test.mjs +0 -302
- package/lib/backup/policy.test.mjs +0 -305
- package/lib/budget-escalate.test.mjs +0 -232
- package/lib/budget-guard.envelope.test.mjs +0 -476
- package/lib/budget-guard.test.mjs +0 -427
- package/lib/cadence-bus-requeue.test.mjs +0 -83
- package/lib/cadence-bus-schedule.test.mjs +0 -194
- package/lib/cadence-bus.test.mjs +0 -720
- package/lib/cadences.test.mjs +0 -230
- package/lib/capability/inventory.test.mjs +0 -232
- package/lib/capability.test.mjs +0 -78
- package/lib/channels/base-adapter.test.mjs +0 -590
- package/lib/channels/channels.test.mjs +0 -371
- package/lib/channels/contract.test.mjs +0 -162
- package/lib/channels/inbox-item.test.mjs +0 -368
- package/lib/channels/orgmail/adapter.test.mjs +0 -448
- package/lib/channels/pairing.test.mjs +0 -270
- package/lib/channels/repeat-suppressor.test.mjs +0 -134
- package/lib/channels/slack-adapter.test.mjs +0 -212
- package/lib/channels/telegram-adapter.test.mjs +0 -306
- package/lib/channels/voice/adapter.test.mjs +0 -278
- package/lib/channels/whatsapp/adapter-baileys.test.mjs +0 -359
- package/lib/channels/whatsapp/baileys-typing.test.mjs +0 -154
- package/lib/charter.test.mjs +0 -89
- package/lib/claude-bin.test.mjs +0 -131
- package/lib/cli/board.test.mjs +0 -227
- package/lib/cli/design.test.mjs +0 -270
- package/lib/cli/doctor-checks.test.mjs +0 -336
- package/lib/cli/global-setup-extras.test.mjs +0 -462
- package/lib/cli/inbox.test.mjs +0 -230
- package/lib/cli/session-ack.test.mjs +0 -63
- package/lib/cli/session.test.mjs +0 -613
- package/lib/collective/capture.test.mjs +0 -121
- package/lib/collective/cards.test.mjs +0 -114
- package/lib/collective/config.test.mjs +0 -123
- package/lib/collective/global-config.test.mjs +0 -220
- package/lib/collective/global-skills.test.mjs +0 -126
- package/lib/collective/presence.test.mjs +0 -95
- package/lib/collective/recall.test.mjs +0 -116
- package/lib/collective/vendor-skills.test.mjs +0 -306
- package/lib/comms/send-gate.test.mjs +0 -770
- package/lib/comms.test.mjs +0 -41
- package/lib/context/budget.test.mjs +0 -252
- package/lib/context/history-scope.test.mjs +0 -79
- package/lib/cost/ledger-row.test.mjs +0 -183
- package/lib/design/design-md.test.mjs +0 -318
- package/lib/design/fixtures/DESIGN.golden.md +0 -238
- package/lib/design/fixtures/PRODUCT.golden.md +0 -67
- package/lib/design/fixtures/foundation.json +0 -133
- package/lib/design/refresh-gate.test.mjs +0 -144
- package/lib/design/write.test.mjs +0 -241
- package/lib/diagnostics/alerts.test.mjs +0 -318
- package/lib/diagnostics/backup-freshness.test.mjs +0 -185
- package/lib/diagnostics/counters.test.mjs +0 -206
- package/lib/diagnostics/events.test.mjs +0 -290
- package/lib/diagnostics/otel.test.mjs +0 -196
- package/lib/diagnostics/trace.test.mjs +0 -251
- package/lib/env-compat.test.mjs +0 -104
- package/lib/execution/disposition.test.mjs +0 -553
- package/lib/execution/drive.test.mjs +0 -270
- package/lib/execution/effects.test.mjs +0 -344
- package/lib/execution/intake.test.mjs +0 -389
- package/lib/execution/journal.test.mjs +0 -261
- package/lib/execution/match.test.mjs +0 -235
- package/lib/execution/pipeline.test.mjs +0 -392
- package/lib/execution/route.test.mjs +0 -186
- package/lib/execution/surface-policy.test.mjs +0 -162
- package/lib/fs-atomic.test.mjs +0 -72
- package/lib/fs-ownership.test.mjs +0 -158
- package/lib/goals/admission.test.mjs +0 -164
- package/lib/goals/classify.test.mjs +0 -167
- package/lib/goals/collaborate.test.mjs +0 -336
- package/lib/goals/gaps.test.mjs +0 -284
- package/lib/goals/loop.test.mjs +0 -845
- package/lib/hooks/bus.test.mjs +0 -387
- package/lib/identity/persona.test.mjs +0 -142
- package/lib/kpi-sensors.test.mjs +0 -278
- package/lib/kpi.test.mjs +0 -244
- package/lib/learning/config.test.mjs +0 -75
- package/lib/learning/counters.test.mjs +0 -69
- package/lib/learning/curator-consolidate.test.mjs +0 -238
- package/lib/learning/curator.test.mjs +0 -106
- package/lib/learning/reflect.test.mjs +0 -0
- package/lib/learning/session-index.test.mjs +0 -125
- package/lib/learning/skill-writer.test.mjs +0 -210
- package/lib/mandate/audit.test.mjs +0 -195
- package/lib/mandate/contract.test.mjs +0 -185
- package/lib/mandate/derive.test.mjs +0 -274
- package/lib/mandate/model.test.mjs +0 -164
- package/lib/mandate/refresh.test.mjs +0 -389
- package/lib/mcp/server.test.mjs +0 -426
- package/lib/model-router/auth-profiles.test.mjs +0 -580
- package/lib/model-router/catalog.test.mjs +0 -385
- package/lib/model-router/economics.test.mjs +0 -438
- package/lib/model-router/failover.test.mjs +0 -439
- package/lib/model-router/health.test.mjs +0 -338
- package/lib/model-router/integration-coverage.test.mjs +0 -831
- package/lib/model-router/integration.test.mjs +0 -564
- package/lib/model-router/ledger.test.mjs +0 -415
- package/lib/model-router/llm-task.test.mjs +0 -392
- package/lib/model-router/org-credentials.test.mjs +0 -265
- package/lib/model-router/pricing-refresh.test.mjs +0 -286
- package/lib/model-router/reconcile.test.mjs +0 -316
- package/lib/model-router/repair.test.mjs +0 -180
- package/lib/model-router/spawn.test.mjs +0 -446
- package/lib/model-router/taxonomy.test.mjs +0 -410
- package/lib/model-router.test.mjs +0 -1207
- package/lib/org/activity.test.mjs +0 -134
- package/lib/org/approvals.test.mjs +0 -216
- package/lib/org/awareness.test.mjs +0 -159
- package/lib/org/board-mine-cache.test.mjs +0 -53
- package/lib/org/board.test.mjs +0 -187
- package/lib/org/bootstrap-context.test.mjs +0 -153
- package/lib/org/client.test.mjs +0 -1206
- package/lib/org/cohort-client.test.mjs +0 -126
- package/lib/org/cost-sync.test.mjs +0 -153
- package/lib/org/doctor.test.mjs +0 -346
- package/lib/org/engagement-ledger.test.mjs +0 -112
- package/lib/org/engagement.test.mjs +0 -739
- package/lib/org/handoff.test.mjs +0 -269
- package/lib/org/inbound/directedness.test.mjs +0 -668
- package/lib/org/inbound/facts.test.mjs +0 -471
- package/lib/org/inbound/hydrate.test.mjs +0 -908
- package/lib/org/inbound/index.test.mjs +0 -429
- package/lib/org/inbound/project.test.mjs +0 -287
- package/lib/org/integration-tools.test.mjs +0 -160
- package/lib/org/keys.test.mjs +0 -92
- package/lib/org/knowledge.test.mjs +0 -326
- package/lib/org/leases.test.mjs +0 -235
- package/lib/org/mesh-directives.test.mjs +0 -110
- package/lib/org/mesh-integration.test.mjs +0 -127
- package/lib/org/mesh.test.mjs +0 -400
- package/lib/org/messaging.test.mjs +0 -471
- package/lib/org/param-contract.test.mjs +0 -477
- package/lib/org/policy.test.mjs +0 -237
- package/lib/org/protocol.checksum.test.mjs +0 -90
- package/lib/org/protocol.test.mjs +0 -323
- package/lib/org/push.test.mjs +0 -792
- package/lib/org/registry.test.mjs +0 -100
- package/lib/org/resource-tools.test.mjs +0 -361
- package/lib/org/tool-access.test.mjs +0 -144
- package/lib/org/tool-surface-integration.test.mjs +0 -120
- package/lib/org/tool-surface.test.mjs +0 -1268
- package/lib/org/typing.test.mjs +0 -291
- package/lib/org/ui-parity.test.mjs +0 -560
- package/lib/org/verify.test.mjs +0 -194
- package/lib/org/work-ledger.test.mjs +0 -273
- package/lib/plan/adoption-e2e.test.mjs +0 -366
- package/lib/plan/budget-enforcement.test.mjs +0 -400
- package/lib/plan/compile.test.mjs +0 -382
- package/lib/plan/emit.test.mjs +0 -269
- package/lib/plan/explain.test.mjs +0 -188
- package/lib/prompts/parallelism.test.mjs +0 -177
- package/lib/rag/rag.test.mjs +0 -505
- package/lib/rate-guard.test.mjs +0 -272
- package/lib/reactive-gate.test.mjs +0 -57
- package/lib/render.test.mjs +0 -68
- package/lib/resource-governor.test.mjs +0 -488
- package/lib/scheduling/dynamic-jobs.test.mjs +0 -344
- package/lib/scheduling/jitter.test.mjs +0 -140
- package/lib/secrets/broker.test.mjs +0 -280
- package/lib/secrets/providers.test.mjs +0 -274
- package/lib/security/audit-engine.test.mjs +0 -424
- package/lib/security/coerce-args.test.mjs +0 -281
- package/lib/security/dangerous-tools.test.mjs +0 -68
- package/lib/security/external-content.test.mjs +0 -84
- package/lib/security/redact.test.mjs +0 -441
- package/lib/security/secret-equal.test.mjs +0 -55
- package/lib/session/config.test.mjs +0 -92
- package/lib/session/feed-core.test.mjs +0 -198
- package/lib/session/first-run.test.mjs +0 -121
- package/lib/session/frontdoor.test.mjs +0 -205
- package/lib/session/handoffs.test.mjs +0 -183
- package/lib/session/identity.test.mjs +0 -180
- package/lib/session/inbox-claims.test.mjs +0 -286
- package/lib/session/launch-args.test.mjs +0 -157
- package/lib/session/liveness.test.mjs +0 -100
- package/lib/session/status-summary.test.mjs +0 -118
- package/lib/session-permissions.test.mjs +0 -120
- package/lib/setup/claude-probe.test.mjs +0 -187
- package/lib/setup/completeness.test.mjs +0 -110
- package/lib/setup/context-pack.test.mjs +0 -89
- package/lib/setup/enrich.test.mjs +0 -115
- package/lib/setup/enroll-from-cohort.test.mjs +0 -300
- package/lib/setup/integration.test.mjs +0 -162
- package/lib/setup/io.test.mjs +0 -77
- package/lib/setup/runner.test.mjs +0 -132
- package/lib/setup/sections/identity.test.mjs +0 -234
- package/lib/setup/sections/inventory.test.mjs +0 -198
- package/lib/setup/sections/learning.test.mjs +0 -81
- package/lib/setup/sections/mandate.test.mjs +0 -388
- package/lib/setup/sections/messaging.test.mjs +0 -127
- package/lib/setup/sections/model.test.mjs +0 -240
- package/lib/setup/sections/org.test.mjs +0 -346
- package/lib/setup/sections/orgmail.test.mjs +0 -118
- package/lib/setup/sections/recovery.test.mjs +0 -98
- package/lib/setup/sections/subagents.test.mjs +0 -429
- package/lib/setup/sections/verify.test.mjs +0 -175
- package/lib/setup/sot.test.mjs +0 -81
- package/lib/setup/state.test.mjs +0 -115
- package/lib/singleton.test.mjs +0 -151
- package/lib/subagents/cli.test.mjs +0 -389
- package/lib/subagents/client.test.mjs +0 -309
- package/lib/subagents/gap.test.mjs +0 -234
- package/lib/subagents/lock.test.mjs +0 -248
- package/lib/subagents/manifest.test.mjs +0 -175
- package/lib/subagents/refs.test.mjs +0 -204
- package/lib/subagents/resolve.test.mjs +0 -422
- package/lib/subagents/schema.test.mjs +0 -328
- package/lib/telemetry/alerts.test.mjs +0 -109
- package/lib/telemetry/collect.test.mjs +0 -1274
- package/lib/tool-definitions-integration.test.mjs +0 -83
- package/lib/tool-definitions.test.mjs +0 -437
- package/lib/upgrade/global-refresh.test.mjs +0 -65
- package/lib/upgrade/launchd-reconcile.test.mjs +0 -272
- package/lib/upgrade/post-steps.test.mjs +0 -200
- package/lib/upgrade/verify.test.mjs +0 -164
- package/lib/util/fetch-timeout.test.mjs +0 -202
- package/lib/util/reconnect.test.mjs +0 -369
- package/lib/util/unhandled.test.mjs +0 -216
- package/lib/voice/outbound.test.mjs +0 -69
- package/lib/voice/session-rotation.test.mjs +0 -114
- package/lib/voice/stt.test.mjs +0 -226
- package/lib/voice/voice.test.mjs +0 -990
- package/scripts/cadence/enqueue-cadence-tick.test.mjs +0 -187
- package/scripts/ci/check-docs-accuracy.test.mjs +0 -409
- package/scripts/ci/check-durable-write-seam.test.mjs +0 -90
- package/scripts/ci/check-no-build-artifacts.test.mjs +0 -71
- package/scripts/ci/check-no-residual-identity.test.mjs +0 -202
- package/scripts/ci/check-skill-packs.test.mjs +0 -495
- package/scripts/ci/check-subagent-frontmatter.test.mjs +0 -124
- package/scripts/ci/check.test.mjs +0 -194
- package/scripts/ci/conformance-org-api.test.mjs +0 -425
- package/scripts/cloud-relay/voice/relay-identity.test.mjs +0 -96
- package/scripts/collective/hook-runner.test.mjs +0 -173
- package/scripts/cost/fleet-digest.test.mjs +0 -207
- package/scripts/cost/track-claude-usage-pricing.test.mjs +0 -183
- package/scripts/cost/track-claude-usage.test.mjs +0 -148
- package/scripts/daemon/agent-daemon-board-mine.test.mjs +0 -96
- package/scripts/daemon/agent-daemon-design.test.mjs +0 -238
- package/scripts/daemon/agent-daemon-frontdoor.test.mjs +0 -60
- package/scripts/daemon/agent-daemon.test.mjs +0 -995
- package/scripts/daemon/assurance-e2e.test.mjs +0 -613
- package/scripts/daemon/assurance.test.mjs +0 -1791
- package/scripts/daemon/board-mirror.test.mjs +0 -165
- package/scripts/daemon/cadence-consumer-frontdoor.test.mjs +0 -393
- package/scripts/daemon/cadence-consumer-governance.test.mjs +0 -276
- package/scripts/daemon/cadence-consumer.test.mjs +0 -776
- package/scripts/daemon/cadence-handlers.test.mjs +0 -837
- package/scripts/daemon/classifier-identity.test.mjs +0 -137
- package/scripts/daemon/classifier.test.mjs +0 -266
- package/scripts/daemon/classify-kind.test.mjs +0 -40
- package/scripts/daemon/context-compiler.test.mjs +0 -406
- package/scripts/daemon/deliver.test.mjs +0 -564
- package/scripts/daemon/dispatcher-cooldown.test.mjs +0 -122
- package/scripts/daemon/dispatcher-governance.test.mjs +0 -1013
- package/scripts/daemon/dispatcher-resume.test.mjs +0 -166
- package/scripts/daemon/dispatcher-session-continuity.test.mjs +0 -365
- package/scripts/daemon/execution-ladder.test.mjs +0 -470
- package/scripts/daemon/goal-steward-cadence.test.mjs +0 -312
- package/scripts/daemon/inbox-deferral-session.test.mjs +0 -49
- package/scripts/daemon/inbox-deferral.test.mjs +0 -336
- package/scripts/daemon/inbox-wake.test.mjs +0 -199
- package/scripts/daemon/integration.test.mjs +0 -149
- package/scripts/daemon/lib/self-echo.test.mjs +0 -153
- package/scripts/daemon/lib/session-router.test.mjs +0 -554
- package/scripts/daemon/prompt-builder-preamble.test.mjs +0 -210
- package/scripts/daemon/prompt-builder.test.mjs +0 -556
- package/scripts/daemon/responder-cost.test.mjs +0 -68
- package/scripts/daemon/responder-history.test.mjs +0 -221
- package/scripts/daemon/sdk-version.test.mjs +0 -31
- package/scripts/daemon/session-lock.test.mjs +0 -252
- package/scripts/daemon/session-outcomes.test.mjs +0 -533
- package/scripts/daemon/typing-registry.test.mjs +0 -102
- package/scripts/hooks/pre-send-audit.test.mjs +0 -354
- package/scripts/huddle/huddle-prompt.test.mjs +0 -176
- package/scripts/local-triggers/autoupdate.test.mjs +0 -518
- package/scripts/local-triggers/generate-plists.test.mjs +0 -456
- package/scripts/media-generation/brand-clause.test.mjs +0 -135
- package/scripts/org/send-orgmail.first-contact.test.mjs +0 -102
- package/scripts/poller/inbox-privilege-injection.test.mjs +0 -167
- package/scripts/poller/inbox-scan-poller.test.mjs +0 -295
- package/scripts/poller/lib/cloud-relay-dedup.test.mjs +0 -133
- package/scripts/poller/slack-socket-mode.test.mjs +0 -805
- package/scripts/poller-launchd/install.test.mjs +0 -243
- package/scripts/restore-from-backup.test.mjs +0 -181
- package/scripts/session/feed.test.mjs +0 -196
- package/scripts/session/supervisor-sh.test.mjs +0 -218
- package/scripts/session/supervisor.test.mjs +0 -482
- package/scripts/setup/configure-macos.test.mjs +0 -306
- package/scripts/setup/gen-subagent-manifest.test.mjs +0 -124
- package/scripts/setup/generate-agent-package-json.test.mjs +0 -143
- package/scripts/setup/generate-capability.test.mjs +0 -134
- package/scripts/setup/init-agent.test.mjs +0 -370
- package/scripts/setup/init-skill-marketplace.test.mjs +0 -193
- package/scripts/vendor/sync-skill-packs.test.mjs +0 -103
- package/scripts/watchdog/memory-watchdog.test.mjs +0 -64
|
@@ -22,9 +22,19 @@
|
|
|
22
22
|
*
|
|
23
23
|
* Lines emitted (one JSON object each — the session's whole vocabulary):
|
|
24
24
|
*
|
|
25
|
-
* {"type":"inbound","id","service","surface","topic","reason","from","channelId","channelName","threadId","preview","path","seq"}
|
|
25
|
+
* {"type":"inbound","id","service","surface","topic","reason","collective","from","channelId","channelName","threadId","preview","path","seq"}
|
|
26
26
|
* {"type":"handoff","id","cadence","promptPath","enqueuedAt","path","seq"}
|
|
27
|
-
* {"type":"directive","action":"restart"|"upgrade-available"|"daemon-stale","detail","seq"}
|
|
27
|
+
* {"type":"directive","action":"restart"|"upgrade-available"|"daemon-unhealthy"|"daemon-stale","detail","seq"}
|
|
28
|
+
*
|
|
29
|
+
* `upgrade-available` and `daemon-unhealthy` come from the SAME file,
|
|
30
|
+
* state/session/upgrade-notice.json, and are deliberately two actions: the
|
|
31
|
+
* first ({from,to,at}) is autoupdate reporting a healthy hop onto a new
|
|
32
|
+
* version — the session restarts itself onto it when idle; the second
|
|
33
|
+
* ({from,to,at,healthy:false,reason}) is the hourly gate reporting the daemon
|
|
34
|
+
* unhealthy on the version already installed — there is nothing to restart
|
|
35
|
+
* onto, and a restart would only lose the session's context. Telling them
|
|
36
|
+
* apart here, not in the skill prose, is what keeps a session from restarting
|
|
37
|
+
* on a health report.
|
|
28
38
|
*
|
|
29
39
|
* The seen-set is keyed by file, and a key whose file has GONE is dropped —
|
|
30
40
|
* so an item the daemon's assurance sweep re-opens (`.dispatched` → `.yaml`)
|
|
@@ -37,6 +47,8 @@
|
|
|
37
47
|
"use strict";
|
|
38
48
|
|
|
39
49
|
import { parseInboxItemYaml } from "../../scripts/poller/inbox-scan-poller.mjs";
|
|
50
|
+
import { isRollCallItem } from "../org/inbound/broadcast.mjs";
|
|
51
|
+
import { itemCollectiveIntent } from "../org/inbound/collective.mjs";
|
|
40
52
|
import { SURFACES } from "../org/inbound/surfaces.mjs";
|
|
41
53
|
import { maskTextString } from "../security/redact.mjs";
|
|
42
54
|
import { isDaemonStale } from "./liveness.mjs";
|
|
@@ -111,6 +123,28 @@ export function inboundEvent(a) {
|
|
|
111
123
|
surface,
|
|
112
124
|
topic: TOPIC_BY_KIND.get(surface) || "message",
|
|
113
125
|
reason: item.direct_reason || "",
|
|
126
|
+
// ── WHAT A MESSAGE TO THE WHOLE ROOM ASKED FOR ────────────────────────
|
|
127
|
+
//
|
|
128
|
+
// "" for anything that is not a collective address; otherwise one of
|
|
129
|
+
// `roll-call` / `work` / `ambient` (lib/org/inbound/collective.mjs).
|
|
130
|
+
//
|
|
131
|
+
// WHY IT IS ON THE FEED LINE AND NOT ONLY IN THE DAEMON. The daemon's
|
|
132
|
+
// `answerItem` — where a work-shaped broadcast opens its board row — is
|
|
133
|
+
// only reached through `pollService`, which returns 0 the moment
|
|
134
|
+
// `makeFrontDoorGate` says a live main session owns this service's inbox
|
|
135
|
+
// (scripts/daemon/agent-daemon.mjs). On a seat whose front door is up,
|
|
136
|
+
// which is the NORMAL state, the daemon never dispatches the item at all:
|
|
137
|
+
// it becomes an `inbound` line here and the session answers it. A verdict
|
|
138
|
+
// that lives only on the daemon's side of that fork is a verdict that
|
|
139
|
+
// never fires in production — the same dark-path shape as the 13-day board
|
|
140
|
+
// blackout. So the classifier's answer travels on the line that the live
|
|
141
|
+
// plane actually reads, and `inbound-triage` tells the session what to do
|
|
142
|
+
// with it: `work` is outcome 2 (claim, `board track --stage accepted`,
|
|
143
|
+
// then do it), `roll-call` and `ambient` are outcome 1.
|
|
144
|
+
//
|
|
145
|
+
// Best-effort by construction: a body this cannot parse yields "", which
|
|
146
|
+
// reads as "an ordinary message" and costs the session nothing.
|
|
147
|
+
collective: itemCollectiveIntent(item, isRollCallItem) || "",
|
|
114
148
|
from: item.sender || "",
|
|
115
149
|
channelId: item.channel_id || "",
|
|
116
150
|
channelName: item.channel || "",
|
|
@@ -198,6 +232,8 @@ export function computeFeedEvents(snapshot, seenIn, opts = {}) {
|
|
|
198
232
|
}
|
|
199
233
|
|
|
200
234
|
// upgrade-notice: once per notice (keyed by mtime), detail = parsed body.
|
|
235
|
+
// A body with healthy:false is a health REPORT, not an upgrade (see the
|
|
236
|
+
// header): it gets its own action so no consumer restarts on it.
|
|
201
237
|
const un = sig.upgradeNotice;
|
|
202
238
|
if (un && Number.isFinite(un.mtimeMs)) {
|
|
203
239
|
if (seen.signals.upgrade !== un.mtimeMs) {
|
|
@@ -205,7 +241,8 @@ export function computeFeedEvents(snapshot, seenIn, opts = {}) {
|
|
|
205
241
|
let notice = null;
|
|
206
242
|
try { notice = JSON.parse(un.body); } catch { notice = null; /* a hand-written marker still announces the upgrade */ }
|
|
207
243
|
const detail = notice && typeof notice === "object" ? { notice } : { notice: null, raw: String(un.body || "").slice(0, PREVIEW_MAX) };
|
|
208
|
-
|
|
244
|
+
const action = notice && typeof notice === "object" && notice.healthy === false ? "daemon-unhealthy" : "upgrade-available";
|
|
245
|
+
events.push({ type: "directive", action, detail, seq: next() });
|
|
209
246
|
}
|
|
210
247
|
} else {
|
|
211
248
|
delete seen.signals.upgrade;
|
|
@@ -41,6 +41,53 @@ export const BOOTSTRAP_PROMPT =
|
|
|
41
41
|
export const SESSION_EXIT_SCRIPT =
|
|
42
42
|
'exit_file="$1"; shift; "$@"; rc=$?; printf \'%s\\n\' "$rc" > "$exit_file" 2>/dev/null; exit "$rc"';
|
|
43
43
|
|
|
44
|
+
/**
|
|
45
|
+
* W4-E1: how an engine-cohort session gets its environment inside a
|
|
46
|
+
* multiplexer. tmux does NOT give a new session the env of the client that
|
|
47
|
+
* asked for it: a session created in an already-running server starts from the
|
|
48
|
+
* server's global environment — no seat token, and whatever Claude credentials
|
|
49
|
+
* the server holds. `tmux -e KEY=value` would put the token on argv (visible in
|
|
50
|
+
* `ps`). So the engine's env is written to a 0600 file under the seat's state
|
|
51
|
+
* dir, and the session's command is
|
|
52
|
+
*
|
|
53
|
+
* /usr/bin/env -i /bin/sh -c ENGINE_ENV_SCRIPT maestro-env <env-file> <bin> <args…>
|
|
54
|
+
*
|
|
55
|
+
* `env -i` drops everything the mux server (or screen) handed down; the script
|
|
56
|
+
* sources the file (exactly the engine's env), deletes it, and execs the
|
|
57
|
+
* engine. Positional only, as SESSION_EXIT_SCRIPT: nothing is interpolated.
|
|
58
|
+
*/
|
|
59
|
+
export const ENGINE_ENV_SCRIPT =
|
|
60
|
+
'maestro_env_file="$1"; shift; ' +
|
|
61
|
+
'if [ ! -r "$maestro_env_file" ]; then echo "maestro: engine env file $maestro_env_file is missing" >&2; exit 78; fi; ' +
|
|
62
|
+
'. "$maestro_env_file"; rm -f "$maestro_env_file"; unset maestro_env_file; exec "$@"';
|
|
63
|
+
|
|
64
|
+
const ENV_KEY_RE = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Pure: the env file's text — one `export KEY='value'` per variable, single
|
|
68
|
+
* quotes escaped, names a shell cannot export skipped.
|
|
69
|
+
* @param {Record<string, string|undefined|null>} env
|
|
70
|
+
* @returns {string}
|
|
71
|
+
*/
|
|
72
|
+
export function renderEnvFile(env) {
|
|
73
|
+
const lines = [];
|
|
74
|
+
for (const [k, v] of Object.entries(env || {})) {
|
|
75
|
+
if (v === undefined || v === null || !ENV_KEY_RE.test(k) || k === "maestro_env_file") continue;
|
|
76
|
+
lines.push(`export ${k}='${String(v).replace(/'/g, "'\\''")}'`);
|
|
77
|
+
}
|
|
78
|
+
return `${lines.join("\n")}\n`;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Pure: the argv that runs `bin args…` with exactly the env in `envFile`.
|
|
83
|
+
* @param {{envFile:string, bin:string, args:string[]}} a
|
|
84
|
+
* @returns {string[]}
|
|
85
|
+
*/
|
|
86
|
+
export function envFileCommand(a) {
|
|
87
|
+
if (!a || typeof a.envFile !== "string" || !a.envFile) throw new Error("envFileCommand: envFile is required");
|
|
88
|
+
return ["/usr/bin/env", "-i", "/bin/sh", "-c", ENGINE_ENV_SCRIPT, "maestro-env", a.envFile, a.bin, ...(a.args || [])];
|
|
89
|
+
}
|
|
90
|
+
|
|
44
91
|
const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
45
92
|
|
|
46
93
|
/** Where the multiplexers live on a Mac (nvm-free, launchd-safe, in order). */
|
|
@@ -105,10 +152,15 @@ export function chooseMux(pref, found) {
|
|
|
105
152
|
* stop ends the session on request (SIGTERM to the supervisor):
|
|
106
153
|
* `screen -S name -X quit` / `tmux kill-session -t =name`.
|
|
107
154
|
*
|
|
108
|
-
*
|
|
155
|
+
* `envFile` (W4-E1, engine cohort): the session's command runs through
|
|
156
|
+
* {@link envFileCommand}, so its env is the file's and never the mux server's.
|
|
157
|
+
* Absent (a claude seat): the argv is exactly as before.
|
|
158
|
+
*
|
|
159
|
+
* @param {{mux:{kind:string, bin:string}, muxName:string, cwd:string, claudeBin:string, claudeArgs:string[], exitFile:string, envFile?:string|null}} a
|
|
109
160
|
*/
|
|
110
161
|
export function buildMuxCommands(a) {
|
|
111
|
-
const
|
|
162
|
+
const command = a.envFile ? envFileCommand({ envFile: a.envFile, bin: a.claudeBin, args: a.claudeArgs }) : [a.claudeBin, ...a.claudeArgs];
|
|
163
|
+
const wrapped = ["/bin/sh", "-c", SESSION_EXIT_SCRIPT, "maestro-session", a.exitFile, ...command];
|
|
112
164
|
if (a.mux.kind === "screen") {
|
|
113
165
|
return {
|
|
114
166
|
blocking: true,
|
|
@@ -156,6 +208,6 @@ export function parseExitFile(text) {
|
|
|
156
208
|
}
|
|
157
209
|
|
|
158
210
|
export default {
|
|
159
|
-
BOOTSTRAP_PROMPT, SESSION_EXIT_SCRIPT, MUX_CANDIDATES,
|
|
160
|
-
buildClaudeArgs, detectMux, chooseMux, buildMuxCommands, parseScreenList, parseExitFile,
|
|
211
|
+
BOOTSTRAP_PROMPT, SESSION_EXIT_SCRIPT, ENGINE_ENV_SCRIPT, MUX_CANDIDATES,
|
|
212
|
+
renderEnvFile, envFileCommand, buildClaudeArgs, detectMux, chooseMux, buildMuxCommands, parseScreenList, parseExitFile,
|
|
161
213
|
};
|
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
import { existsSync, readFileSync, readdirSync } from "node:fs";
|
|
25
25
|
import { join } from "node:path";
|
|
26
26
|
import { buildIdentityClaudeMd, IDENTITY_BEGIN, IDENTITY_END } from "../collective/global-config.mjs";
|
|
27
|
+
import { sessionPaths } from "./config.mjs";
|
|
27
28
|
|
|
28
29
|
/** A heartbeat older than this is a dead or wedged session (feed beats every 15 s). */
|
|
29
30
|
export const DEFAULT_STALE_MS = 90_000;
|
|
@@ -128,6 +129,29 @@ function readJson(p) {
|
|
|
128
129
|
}
|
|
129
130
|
}
|
|
130
131
|
|
|
132
|
+
/**
|
|
133
|
+
* The open handoff files under `state/session/handoffs` — file NAMES only;
|
|
134
|
+
* a handoff is counted, never opened. THE one reader of that directory: the
|
|
135
|
+
* session status here and lib/session/current-work both count handoffs, and
|
|
136
|
+
* a second copy of this read would have to move in step with the cadence
|
|
137
|
+
* consumer's file naming (`<tickId>.json`, done ones under `handoffs/done/`,
|
|
138
|
+
* which is a directory and so never matches). The PARSING reader — the one
|
|
139
|
+
* that opens each file to act on it — is lib/session/handoffs.listHandoffs;
|
|
140
|
+
* this one exists so a status line never has to. Fail-open: an absent or
|
|
141
|
+
* vanished directory is an empty queue.
|
|
142
|
+
* @param {string} agentRoot
|
|
143
|
+
* @returns {string[]}
|
|
144
|
+
*/
|
|
145
|
+
export function listOpenHandoffs(agentRoot) {
|
|
146
|
+
try {
|
|
147
|
+
const dir = sessionPaths(String(agentRoot || "")).handoffsDir;
|
|
148
|
+
if (!existsSync(dir)) return [];
|
|
149
|
+
return readdirSync(dir).filter((f) => f.endsWith(".json"));
|
|
150
|
+
} catch {
|
|
151
|
+
return []; // a vanished directory mid-read is an empty queue
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
131
155
|
/**
|
|
132
156
|
* Read the four state files under an agent root and summarise. Fail-open:
|
|
133
157
|
* an unreadable file is the same as an absent one.
|
|
@@ -137,17 +161,10 @@ function readJson(p) {
|
|
|
137
161
|
export function readSessionStatus(agentRoot, o = {}) {
|
|
138
162
|
const root = String(agentRoot || "");
|
|
139
163
|
const sess = join(root, "state", "session");
|
|
140
|
-
let handoffs = [];
|
|
141
|
-
try {
|
|
142
|
-
const dir = join(sess, "handoffs");
|
|
143
|
-
if (existsSync(dir)) handoffs = readdirSync(dir).filter((f) => f.endsWith(".json"));
|
|
144
|
-
} catch {
|
|
145
|
-
handoffs = []; // a vanished directory mid-read is an empty queue
|
|
146
|
-
}
|
|
147
164
|
return summarizeSessionStatus({
|
|
148
165
|
heartbeat: readJson(join(sess, "heartbeat.json")),
|
|
149
166
|
peers: readJson(join(sess, "peers.json")),
|
|
150
|
-
handoffs,
|
|
167
|
+
handoffs: listOpenHandoffs(root),
|
|
151
168
|
boardMine: readJson(join(root, "state", "org", "board-mine.json")),
|
|
152
169
|
now: o.now,
|
|
153
170
|
staleMs: o.staleMs,
|
|
@@ -169,4 +186,4 @@ export function buildPrimerContext(o = {}) {
|
|
|
169
186
|
return [IDENTITY_BEGIN, block, IDENTITY_END, "", `Session status: ${statusLine(status)}.`].join("\n");
|
|
170
187
|
}
|
|
171
188
|
|
|
172
|
-
export default { summarizeSessionStatus, readSessionStatus, statusLine, buildPrimerContext, DEFAULT_STALE_MS };
|
|
189
|
+
export default { summarizeSessionStatus, readSessionStatus, listOpenHandoffs, statusLine, buildPrimerContext, DEFAULT_STALE_MS };
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* lib/session/upgrade-notice.mjs — is a pending upgrade notice HONOURED by
|
|
3
|
+
* the launch about to happen? Pure.
|
|
4
|
+
*
|
|
5
|
+
* autoupdate.sh writes state/session/upgrade-notice.json {from,to,at} after
|
|
6
|
+
* a healthy hop; the session's feed turns it into an `upgrade-available`
|
|
7
|
+
* directive and the session restarts itself onto the new code. Nothing then
|
|
8
|
+
* removed the file: `maestro session status` kept printing "upgrade available
|
|
9
|
+
* 2.16.0 → 2.17.0" nine days after the seat was on 2.17.0, and there was no
|
|
10
|
+
* way to tell a notice the session had acted on from one it had ignored
|
|
11
|
+
* (one seat ignored two — it was wedged on a dialog — and the flag read the
|
|
12
|
+
* same either way). The supervisor is the one place that KNOWS a relaunch
|
|
13
|
+
* happened and what version it launches on, so it retires the notice when
|
|
14
|
+
* the version it is launching onto is the one the notice asked for.
|
|
15
|
+
*
|
|
16
|
+
* A {healthy:false} body is a health REPORT, not a restart cue (feed-core):
|
|
17
|
+
* never retired here — autoupdate rewrites it on recovery.
|
|
18
|
+
*
|
|
19
|
+
* @module lib/session/upgrade-notice
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
"use strict";
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* @param {string|null|undefined} noticeText raw upgrade-notice.json, or null when absent
|
|
26
|
+
* @param {string|null|undefined} installedVersion the SDK version this launch runs
|
|
27
|
+
* @returns {{honoured:boolean, reason:string, from?:string, to?:string}}
|
|
28
|
+
*/
|
|
29
|
+
export function noticeHonouredBy(noticeText, installedVersion) {
|
|
30
|
+
if (typeof noticeText !== "string" || !noticeText.trim()) return { honoured: false, reason: "no-notice" };
|
|
31
|
+
let n;
|
|
32
|
+
try { n = JSON.parse(noticeText); } catch { return { honoured: false, reason: "unparseable" }; }
|
|
33
|
+
if (!n || typeof n !== "object") return { honoured: false, reason: "unparseable" };
|
|
34
|
+
if (n.healthy === false) return { honoured: false, reason: "health-report" };
|
|
35
|
+
const to = typeof n.to === "string" ? n.to.trim() : "";
|
|
36
|
+
const from = typeof n.from === "string" ? n.from.trim() : "";
|
|
37
|
+
if (!to) return { honoured: false, reason: "no-to" };
|
|
38
|
+
const v = typeof installedVersion === "string" ? installedVersion.trim() : "";
|
|
39
|
+
if (!v) return { honoured: false, reason: "installed-unknown", from, to };
|
|
40
|
+
if (v !== to) return { honoured: false, reason: `installed-${v}-is-not-${to}`, from, to };
|
|
41
|
+
return { honoured: true, reason: "launching-on-target", from, to };
|
|
42
|
+
}
|
|
@@ -7,8 +7,9 @@
|
|
|
7
7
|
*
|
|
8
8
|
* Auth modes, resolved from the agent's `.env`:
|
|
9
9
|
*
|
|
10
|
-
* subscription CLAUDE_CODE_OAUTH_TOKEN is set (
|
|
11
|
-
*
|
|
10
|
+
* subscription CLAUDE_CODE_OAUTH_TOKEN is set (a subscription OAuth
|
|
11
|
+
* token — still probed, but NOT the fleet posture: seats run
|
|
12
|
+
* on per-agent Console API keys, docs/guides/billing-console-keys.md) and/or
|
|
12
13
|
* MAESTRO_PREFER_SUBSCRIPTION_AUTH=1 (keychain login).
|
|
13
14
|
* The probe runs `claude auth status --json` NON-INTERACTIVELY
|
|
14
15
|
* with the token in the child env and maps the answer to
|
|
@@ -18,7 +19,7 @@
|
|
|
18
19
|
* is otherwise fine, and the beat reports it honestly.
|
|
19
20
|
* api-key ANTHROPIC_API_KEY only → a 5-token POST to api.anthropic.com
|
|
20
21
|
* (the historic curl ping). 401 is the one hard failure.
|
|
21
|
-
* none nothing configured → not ok, remedy names the
|
|
22
|
+
* none nothing configured → not ok, remedy names the Console API key path.
|
|
22
23
|
*
|
|
23
24
|
* WHAT THE PROBE CAN AND CANNOT PROVE (`verified` on the result):
|
|
24
25
|
* `claude auth status` reports the PRESENCE of a credential, not its
|
|
@@ -39,7 +40,16 @@
|
|
|
39
40
|
* launchd's environment, no .env). The two rows doctor prints can therefore
|
|
40
41
|
* not contradict each other.
|
|
41
42
|
*
|
|
42
|
-
*
|
|
43
|
+
* A seat whose config/agent.json names `runtime.engine: "cohort"` does not
|
|
44
|
+
* talk to Claude at all (W4 integration, conformance rows 1 and 25): its probe
|
|
45
|
+
* runs the engine's `auth status --json` with the seat's token helper and
|
|
46
|
+
* gateway URL, which proves the seat credential against GET /cohort/v1/quota
|
|
47
|
+
* (`mode:"cohort"`, `verified:true` on ok). A refused credential is
|
|
48
|
+
* `relogin_required`; an unreachable gateway or a broken engine is `unknown`
|
|
49
|
+
* and fail-open, as for the subscription probe. Claude seats are unchanged.
|
|
50
|
+
*
|
|
51
|
+
* Injectable (`opts.exec`, `opts.curl`, `opts.claudeBin`, `opts.env`,
|
|
52
|
+
* `opts.seatEngine`, `opts.cfg`, `opts.nodeBin`, `opts.engineCliPath`) so every
|
|
43
53
|
* outcome is unit-testable offline; never throws.
|
|
44
54
|
*
|
|
45
55
|
* @module lib/setup/claude-probe
|
|
@@ -50,7 +60,9 @@
|
|
|
50
60
|
import { existsSync, readFileSync } from "node:fs";
|
|
51
61
|
import { join } from "node:path";
|
|
52
62
|
import { spawnSync, execFileSync } from "node:child_process";
|
|
53
|
-
import {
|
|
63
|
+
import { buildSpawn, readSeatEngine, ENGINE_CLI_PATH } from "../runtime/adapter.mjs";
|
|
64
|
+
import { apiKeyHelperCommand, resolveLlmBaseUrl } from "../org/llm-token.mjs";
|
|
65
|
+
import { loadOrgConfig } from "../org/client.mjs";
|
|
54
66
|
|
|
55
67
|
/** Hard ceiling on the `claude auth status` fork — the beat must never wedge on it. */
|
|
56
68
|
export const AUTH_STATUS_TIMEOUT_MS = 8000;
|
|
@@ -111,11 +123,22 @@ function resolveValue(body, baseEnv, key) {
|
|
|
111
123
|
* @param {(cmd:string,args:string[],o:object)=>string} [opts.exec] injectable execFileSync (subscription mode; tests)
|
|
112
124
|
* @param {string} [opts.claudeBin] path of the claude CLI (default: resolveClaudeBin())
|
|
113
125
|
* @param {object} [opts.env] base env for the CLI fork (default: process.env)
|
|
114
|
-
* @returns {{ok:boolean, mode:'subscription'|'api-key'|'none', auth?:'ok'|'relogin_required'|'unknown', verified:boolean, code?:string, name:string, remedy?:string, detail?:string}}
|
|
126
|
+
* @returns {{ok:boolean, mode:'subscription'|'api-key'|'none'|'cohort', auth?:'ok'|'relogin_required'|'unknown', verified:boolean, code?:string, name:string, remedy?:string, detail?:string}}
|
|
115
127
|
* `verified` is true only when a real call proved the credential (api-key 200);
|
|
116
128
|
* a subscription `ok` is the CLI reporting a login it has not exercised.
|
|
117
129
|
*/
|
|
130
|
+
/**
|
|
131
|
+
* The remedy every probe failure gives (CF-13). Duplicated from
|
|
132
|
+
* lib/cli/doctor-checks.mjs CONSOLE_KEY_REMEDY rather than imported: the
|
|
133
|
+
* doctor module imports this one. claude-probe.test.mjs pins them equal.
|
|
134
|
+
*/
|
|
135
|
+
export const CONSOLE_KEY_REMEDY =
|
|
136
|
+
"put a per-agent Console API key (a workspace with a spend cap) in .env as ANTHROPIC_API_KEY and remove MAESTRO_PREFER_SUBSCRIPTION_AUTH / CLAUDE_CODE_OAUTH_TOKEN — see docs/guides/billing-console-keys.md";
|
|
137
|
+
|
|
118
138
|
export function probeClaude(agentRoot, opts = {}) {
|
|
139
|
+
let engine = "claude";
|
|
140
|
+
try { engine = (opts.seatEngine || readSeatEngine)(agentRoot); } catch { engine = "claude"; /* unreadable config: the default engine */ }
|
|
141
|
+
if (engine === "cohort") return probeCohortEngine(agentRoot, opts);
|
|
119
142
|
const envPath = join(agentRoot, ".env");
|
|
120
143
|
const body = existsSync(envPath) ? readFileSync(envPath, "utf-8") : "";
|
|
121
144
|
const baseEnv = opts.env || process.env;
|
|
@@ -128,7 +151,7 @@ export function probeClaude(agentRoot, opts = {}) {
|
|
|
128
151
|
if (!key) {
|
|
129
152
|
return {
|
|
130
153
|
ok: false, mode: "none", auth: "unknown", verified: false, name: "Claude auth",
|
|
131
|
-
remedy:
|
|
154
|
+
remedy: `nothing configured — ${CONSOLE_KEY_REMEDY}`,
|
|
132
155
|
};
|
|
133
156
|
}
|
|
134
157
|
return probeApiKey(key, opts);
|
|
@@ -136,13 +159,16 @@ export function probeClaude(agentRoot, opts = {}) {
|
|
|
136
159
|
|
|
137
160
|
function probeSubscription(body, opts, source) {
|
|
138
161
|
const name = source === "oauth-token" ? "Claude auth (subscription — OAuth token)" : "Claude auth (subscription — keychain login)";
|
|
139
|
-
const bin = opts.claudeBin || resolveClaudeBin();
|
|
140
162
|
const run = opts.exec || ((cmd, args, o) => execFileSync(cmd, args, o));
|
|
141
163
|
let stdout = "";
|
|
142
164
|
let failed = "";
|
|
143
165
|
try {
|
|
144
|
-
|
|
145
|
-
|
|
166
|
+
// Binary + argv from the runtime adapter (lane "auth-status"); the env is
|
|
167
|
+
// the probe's own, carrying the token it is probing.
|
|
168
|
+
const spec = buildSpawn({ lane: "auth-status", bin: opts.claudeBin || undefined, env: authStatusEnv(body, opts.env || process.env) });
|
|
169
|
+
if (!spec.ok) throw new Error(spec.error.message);
|
|
170
|
+
stdout = String(run(spec.bin, spec.argv, {
|
|
171
|
+
env: spec.env,
|
|
146
172
|
timeout: AUTH_STATUS_TIMEOUT_MS,
|
|
147
173
|
encoding: "utf-8",
|
|
148
174
|
stdio: ["ignore", "pipe", "ignore"],
|
|
@@ -168,8 +194,8 @@ function probeSubscription(body, opts, source) {
|
|
|
168
194
|
return {
|
|
169
195
|
ok: false, mode: "subscription", auth, verified: false, name,
|
|
170
196
|
remedy: source === "oauth-token"
|
|
171
|
-
?
|
|
172
|
-
:
|
|
197
|
+
? `the OAuth token was rejected — do not mint another: ${CONSOLE_KEY_REMEDY}`
|
|
198
|
+
: `not logged in (the keychain login expires headlessly) — ${CONSOLE_KEY_REMEDY}`,
|
|
173
199
|
};
|
|
174
200
|
}
|
|
175
201
|
// unknown: fail-OPEN. Say why, never silently.
|
|
@@ -201,7 +227,7 @@ function probeApiKey(key, opts) {
|
|
|
201
227
|
if (code === "401") {
|
|
202
228
|
return {
|
|
203
229
|
ok: false, mode: "api-key", auth: "relogin_required", verified: false, code, name,
|
|
204
|
-
remedy: "ANTHROPIC_API_KEY is INVALID (HTTP 401) —
|
|
230
|
+
remedy: "ANTHROPIC_API_KEY is INVALID (HTTP 401) — create a replacement Console API key in this agent's capped workspace and put it in .env (docs/guides/billing-console-keys.md)",
|
|
205
231
|
};
|
|
206
232
|
}
|
|
207
233
|
if (!code) {
|
|
@@ -209,3 +235,81 @@ function probeApiKey(key, opts) {
|
|
|
209
235
|
}
|
|
210
236
|
return { ok: false, mode: "api-key", auth: "unknown", verified: false, code, name, remedy: `unexpected HTTP ${code} (expected 200)` };
|
|
211
237
|
}
|
|
238
|
+
|
|
239
|
+
/** Hard ceiling on the engine's `auth status` fork (it mints a token and makes one gateway call). */
|
|
240
|
+
export const ENGINE_AUTH_STATUS_TIMEOUT_MS = 20000;
|
|
241
|
+
|
|
242
|
+
/** Engine error codes that are a hard answer about the credential, not an outage. */
|
|
243
|
+
const ENGINE_CREDENTIAL_REFUSED = new Set(["missing_token", "token_unavailable", "cohort_invalid_token", "cohort_invalid_key", "cohort_forbidden", "cohort_key_revoked", "http_401", "http_403"]);
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* Map the engine's `auth status --json` output to the probe vocabulary. Pure.
|
|
247
|
+
* @param {string|null|undefined} stdout
|
|
248
|
+
* @returns {{auth:"ok"|"relogin_required"|"unknown", result:any}}
|
|
249
|
+
*/
|
|
250
|
+
export function parseEngineAuthStatus(stdout) {
|
|
251
|
+
const line = String(stdout || "").trim().split(/\r?\n/).filter((l) => l.trim() !== "").pop() || "";
|
|
252
|
+
let result = null;
|
|
253
|
+
try { result = JSON.parse(line); } catch { return { auth: "unknown", result: null }; }
|
|
254
|
+
if (!result || typeof result !== "object" || typeof result.ok !== "boolean") return { auth: "unknown", result: null };
|
|
255
|
+
if (result.ok) return { auth: "ok", result };
|
|
256
|
+
const code = result.error && typeof result.error.code === "string" ? result.error.code : "";
|
|
257
|
+
return { auth: ENGINE_CREDENTIAL_REFUSED.has(code) ? "relogin_required" : "unknown", result };
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* The engine child's env for the probe: the base env without any inherited
|
|
262
|
+
* gateway credential or Anthropic key, plus the seat's helper and gateway URL. Pure.
|
|
263
|
+
*/
|
|
264
|
+
export function engineAuthStatusEnv(agentRoot, baseEnv, { baseUrl, nodeBin }) {
|
|
265
|
+
const out = {};
|
|
266
|
+
for (const [k, v] of Object.entries(baseEnv || {})) {
|
|
267
|
+
if (/^ANTHROPIC_/i.test(k) || /^COHORT_LLM_(TOKEN|TOKEN_HELPER|API_KEY_HELPER|BASE_URL)$/i.test(k)) continue;
|
|
268
|
+
out[k] = v;
|
|
269
|
+
}
|
|
270
|
+
out.AGENT_ROOT = agentRoot;
|
|
271
|
+
out.COHORT_LLM_BASE_URL = baseUrl;
|
|
272
|
+
out.COHORT_LLM_TOKEN_HELPER = apiKeyHelperCommand({ agentRoot, nodeBin });
|
|
273
|
+
return out;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
function probeCohortEngine(agentRoot, opts) {
|
|
277
|
+
const name = "Cohort engine auth (cohort-llm)";
|
|
278
|
+
const run = opts.exec || ((cmd, args, o) => execFileSync(cmd, args, o));
|
|
279
|
+
const baseEnv = opts.env || process.env;
|
|
280
|
+
let cfg = opts.cfg;
|
|
281
|
+
if (cfg === undefined) { try { cfg = loadOrgConfig(agentRoot); } catch { cfg = null; /* no org config: env or the production default */ } }
|
|
282
|
+
const baseUrl = resolveLlmBaseUrl({ cfg, env: baseEnv });
|
|
283
|
+
const nodeBin = opts.nodeBin || process.execPath;
|
|
284
|
+
let stdout = "";
|
|
285
|
+
let failed = "";
|
|
286
|
+
try {
|
|
287
|
+
stdout = String(run(nodeBin, [opts.engineCliPath || ENGINE_CLI_PATH, "auth", "status", "--json"], {
|
|
288
|
+
env: engineAuthStatusEnv(agentRoot, baseEnv, { baseUrl, nodeBin }),
|
|
289
|
+
timeout: ENGINE_AUTH_STATUS_TIMEOUT_MS,
|
|
290
|
+
encoding: "utf-8",
|
|
291
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
292
|
+
maxBuffer: 256 * 1024,
|
|
293
|
+
}) || "");
|
|
294
|
+
} catch (err) {
|
|
295
|
+
// Exit 1 still prints the JSON status; read it before giving up.
|
|
296
|
+
stdout = err && typeof err.stdout === "string" ? err.stdout : (err && err.stdout ? String(err.stdout) : "");
|
|
297
|
+
failed = err && (err.code || err.message) ? String(err.code || err.message) : "exec failed";
|
|
298
|
+
}
|
|
299
|
+
const { auth, result } = parseEngineAuthStatus(stdout);
|
|
300
|
+
if (auth === "ok") {
|
|
301
|
+
const who = result.principal ? ` as ${JSON.stringify(result.principal)}` : "";
|
|
302
|
+
return { ok: true, mode: "cohort", auth, verified: true, name, detail: `the seat credential is accepted by ${result.baseUrl || baseUrl}${who}` };
|
|
303
|
+
}
|
|
304
|
+
const err = result && result.error ? result.error : null;
|
|
305
|
+
if (auth === "relogin_required") {
|
|
306
|
+
return {
|
|
307
|
+
ok: false, mode: "cohort", auth, verified: false, code: err.code, name,
|
|
308
|
+
remedy: `the seat's cohort-llm credential was refused (${err.code}: ${err.message}) — check the seat's OrgApiKey in config/org.yaml or .env, then run \`node ${opts.engineCliPath || ENGINE_CLI_PATH} auth status --json\``,
|
|
309
|
+
};
|
|
310
|
+
}
|
|
311
|
+
return {
|
|
312
|
+
ok: true, mode: "cohort", auth: "unknown", verified: false, name,
|
|
313
|
+
detail: `could not verify (${err ? `${err.code}: ${err.message}` : failed ? `engine auth status failed: ${failed}` : "no usable answer"}) — run \`node ${opts.engineCliPath || ENGINE_CLI_PATH} auth status --json\` by hand`,
|
|
314
|
+
};
|
|
315
|
+
}
|
package/lib/setup/enrich.mjs
CHANGED
|
@@ -32,7 +32,7 @@ import { existsSync, readFileSync, writeFileSync, renameSync, mkdirSync } from "
|
|
|
32
32
|
import { join, dirname } from "node:path";
|
|
33
33
|
import { fileURLToPath } from "node:url";
|
|
34
34
|
import { execFile } from "node:child_process";
|
|
35
|
-
import {
|
|
35
|
+
import { buildSpawn } from "../runtime/adapter.mjs";
|
|
36
36
|
import { mergeAgentJson, readAgentJson } from "./sot.mjs";
|
|
37
37
|
import { readContextPack } from "./context-pack.mjs";
|
|
38
38
|
import { markEnrich } from "./state.mjs";
|
|
@@ -181,15 +181,18 @@ export function buildPrompt(unit, pack, skeleton) {
|
|
|
181
181
|
*/
|
|
182
182
|
function defaultSpawn(prompt, opts = {}) {
|
|
183
183
|
return new Promise((resolve, reject) => {
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
prompt.user,
|
|
191
|
-
|
|
192
|
-
|
|
184
|
+
// argv + env from the runtime adapter (lane "enrich"): --print <daemon MCP
|
|
185
|
+
// args> --model <m> --append-system-prompt <sys> <user>; env inherited.
|
|
186
|
+
const spec = buildSpawn({
|
|
187
|
+
lane: "enrich",
|
|
188
|
+
model: opts.model || "claude-sonnet-4-6",
|
|
189
|
+
systemPrompt: prompt.system,
|
|
190
|
+
prompt: prompt.user,
|
|
191
|
+
mcp: {},
|
|
192
|
+
env: process.env,
|
|
193
|
+
});
|
|
194
|
+
if (!spec.ok) return reject(new Error(spec.error.message));
|
|
195
|
+
execFile(spec.bin, spec.argv, { encoding: "utf-8", timeout: opts.timeoutMs || 60000, maxBuffer: 4 * 1024 * 1024, env: spec.env }, (err, stdout) => {
|
|
193
196
|
if (err) return reject(err);
|
|
194
197
|
resolve(stdout || "");
|
|
195
198
|
});
|
|
@@ -4,14 +4,17 @@
|
|
|
4
4
|
* Records how the agent authenticates to Claude. Three modes, in the order the
|
|
5
5
|
* wizard offers them:
|
|
6
6
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
7
|
+
* api-key DEFAULT. An Anthropic Console API key created for this agent,
|
|
8
|
+
* in a Console workspace with a spend limit
|
|
9
|
+
* (docs/guides/billing-console-keys.md). .env gets
|
|
10
|
+
* ANTHROPIC_API_KEY. Headless reads ANTHROPIC_API_KEY from the
|
|
11
|
+
* environment.
|
|
12
|
+
* oauth-token A Claude subscription token from `claude setup-token`, for a
|
|
13
|
+
* person's own development machine — not an agent seat.
|
|
9
14
|
* .env gets CLAUDE_CODE_OAUTH_TOKEN + MAESTRO_PREFER_SUBSCRIPTION_AUTH=1.
|
|
10
|
-
* Headless
|
|
11
|
-
*
|
|
12
|
-
* expires headlessly; the long-lived token does not.
|
|
15
|
+
* Headless pre-selects it only when CLAUDE_CODE_OAUTH_TOKEN is
|
|
16
|
+
* the one Claude credential in the environment (see defaultAuthMode).
|
|
13
17
|
* subscription Keychain login on this machine (MAESTRO_PREFER_SUBSCRIPTION_AUTH=1 only).
|
|
14
|
-
* api-key ANTHROPIC_API_KEY (pay-per-token).
|
|
15
18
|
*
|
|
16
19
|
* Every write goes through appendEnv (never clobbers an existing value) and
|
|
17
20
|
* .env is chmod 600 afterwards — it now carries a credential either way.
|
|
@@ -29,9 +32,25 @@ import * as defaultIo from "../io.mjs";
|
|
|
29
32
|
import { probeClaude as defaultProbeClaude } from "../claude-probe.mjs";
|
|
30
33
|
|
|
31
34
|
/** The three auth modes, in wizard order. The first is the default. */
|
|
32
|
-
export const AUTH_MODES = Object.freeze(["
|
|
35
|
+
export const AUTH_MODES = Object.freeze(["api-key", "oauth-token", "subscription"]);
|
|
33
36
|
export const DEFAULT_AUTH_MODE = AUTH_MODES[0];
|
|
34
37
|
|
|
38
|
+
/**
|
|
39
|
+
* Pure: the mode the wizard pre-selects for an environment. A Console key in
|
|
40
|
+
* the environment means api-key. A subscription token as the ONLY Claude
|
|
41
|
+
* credential is the operator stating intent (the pre-2.18 headless bootstrap,
|
|
42
|
+
* `CLAUDE_CODE_OAUTH_TOKEN=… maestro setup --headless`), so that seat keeps
|
|
43
|
+
* the mode it asked for. Otherwise the default.
|
|
44
|
+
* @param {object} [env]
|
|
45
|
+
* @returns {"api-key"|"oauth-token"|"subscription"}
|
|
46
|
+
*/
|
|
47
|
+
export function defaultAuthMode(env = {}) {
|
|
48
|
+
const has = (k) => !!env && typeof env[k] === "string" && env[k].trim() !== "";
|
|
49
|
+
if (has("ANTHROPIC_API_KEY")) return "api-key";
|
|
50
|
+
if (has("CLAUDE_CODE_OAUTH_TOKEN")) return "oauth-token";
|
|
51
|
+
return DEFAULT_AUTH_MODE;
|
|
52
|
+
}
|
|
53
|
+
|
|
35
54
|
function readEnv(agentRoot) {
|
|
36
55
|
const p = join(agentRoot, ".env");
|
|
37
56
|
return existsSync(p) ? readFileSync(p, "utf-8") : "";
|
|
@@ -88,11 +107,11 @@ export default {
|
|
|
88
107
|
message: "How should the agent authenticate to Claude?",
|
|
89
108
|
key: "provider",
|
|
90
109
|
choices: [
|
|
91
|
-
{ label: "
|
|
110
|
+
{ label: "Anthropic Console API key — one per agent, in a Console workspace with a spend limit (recommended; headless: ANTHROPIC_API_KEY)", value: "api-key" },
|
|
111
|
+
{ label: "Subscription token from `claude setup-token` — a person's own development machine only, not an agent seat", value: "oauth-token" },
|
|
92
112
|
{ label: "Subscription via this machine's keychain login (expires headlessly — not for a seat machine)", value: "subscription" },
|
|
93
|
-
{ label: "Anthropic API key (paste an sk-ant-… key, pay-per-token)", value: "api-key" },
|
|
94
113
|
],
|
|
95
|
-
default:
|
|
114
|
+
default: defaultAuthMode(process.env),
|
|
96
115
|
});
|
|
97
116
|
let oauthToken = "";
|
|
98
117
|
let apiKey = "";
|
|
@@ -103,7 +122,11 @@ export default {
|
|
|
103
122
|
env: "CLAUDE_CODE_OAUTH_TOKEN",
|
|
104
123
|
});
|
|
105
124
|
} else if (provider === "api-key") {
|
|
106
|
-
apiKey = await io.secret(ctx, {
|
|
125
|
+
apiKey = await io.secret(ctx, {
|
|
126
|
+
message: "Paste this agent's Anthropic Console API key (sk-ant-api…) — see docs/guides/billing-console-keys.md (headless: ANTHROPIC_API_KEY)",
|
|
127
|
+
key: "anthropicApiKey",
|
|
128
|
+
env: "ANTHROPIC_API_KEY",
|
|
129
|
+
});
|
|
107
130
|
}
|
|
108
131
|
const routing = await io.confirm(ctx, {
|
|
109
132
|
message: "Enable cost-optimised model routing (writes config/model-routing.yaml)?",
|
|
@@ -170,12 +193,15 @@ function pull(ctx) {
|
|
|
170
193
|
const m = ans.model && typeof ans.model === "object" ? ans.model : null;
|
|
171
194
|
const envToken = String(ans.claudeOauthToken || (m && m.oauthToken) || process.env.CLAUDE_CODE_OAUTH_TOKEN || "").trim();
|
|
172
195
|
const apiKey = String(ans.anthropicApiKey || (m && m.apiKey) || "");
|
|
196
|
+
// With no recorded provider: a key means api-key, a lone token means the
|
|
197
|
+
// operator asked for oauth-token, nothing means the default.
|
|
198
|
+
const inferred = apiKey ? "api-key" : envToken ? "oauth-token" : DEFAULT_AUTH_MODE;
|
|
173
199
|
if (m) {
|
|
174
|
-
const provider = m.provider ||
|
|
200
|
+
const provider = m.provider || inferred;
|
|
175
201
|
return { provider, oauthToken: provider === "oauth-token" ? envToken : "", apiKey, routing: !!m.routing };
|
|
176
202
|
}
|
|
177
203
|
if ("provider" in ans || "anthropicApiKey" in ans || envToken) {
|
|
178
|
-
const provider = ans.provider ||
|
|
204
|
+
const provider = ans.provider || inferred;
|
|
179
205
|
return { provider, oauthToken: provider === "oauth-token" ? envToken : "", apiKey, routing: !!ans.modelRouting };
|
|
180
206
|
}
|
|
181
207
|
return null;
|