@cohortapp/agent-sdk 2.16.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 +23 -7
- 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/poller-daemon-setup.md +4 -1
- 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/context/budget.mjs +327 -0
- package/lib/context/history-scope.mjs +138 -0
- 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/economics.mjs +9 -0
- package/lib/model-router/failover.mjs +67 -16
- package/lib/model-router/llm-task.mjs +39 -3
- package/lib/model-router/resolve.mjs +95 -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 +82 -4
- package/lib/org/inbound/hydrate.mjs +555 -51
- 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 +15 -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 +104 -59
- package/scripts/daemon/deliver.mjs +30 -1
- package/scripts/daemon/dispatcher.mjs +804 -157
- package/scripts/daemon/health.mjs +14 -1
- package/scripts/daemon/lib/session-router.mjs +310 -42
- package/scripts/daemon/maestro-daemon.mjs +11 -0
- package/scripts/daemon/prompt-builder.mjs +121 -12
- package/scripts/daemon/responder.mjs +315 -146
- 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/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 -453
- 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 -300
- 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/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 -295
- package/scripts/daemon/prompt-builder-preamble.test.mjs +0 -210
- package/scripts/daemon/prompt-builder.test.mjs +0 -344
- package/scripts/daemon/responder-cost.test.mjs +0 -68
- package/scripts/daemon/responder-history.test.mjs +0 -185
- 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
|
@@ -0,0 +1,463 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* lib/cli/seat-auth.mjs — G4: which Claude billing rail is this seat on, and
|
|
3
|
+
* is that the fleet posture?
|
|
4
|
+
*
|
|
5
|
+
* The fleet posture is a **per-agent Console API key in a workspace with a hard
|
|
6
|
+
* spend cap** (`docs/guides/billing-console-keys.md`). Subscription OAuth — the
|
|
7
|
+
* `MAESTRO_PREFER_SUBSCRIPTION_AUTH` flag, a `CLAUDE_CODE_OAUTH_TOKEN`, or the
|
|
8
|
+
* keychain login behind them — still RUNS, which is exactly why a seat can sit
|
|
9
|
+
* on it for months with nothing complaining. G4 is the gate that ends that, and
|
|
10
|
+
* this module is the tooling half: a verdict a seat can state about itself, an
|
|
11
|
+
* inventory row an operator can paste, and a fleet template to paste it into.
|
|
12
|
+
*
|
|
13
|
+
* Three deliberate constraints, each of which is a test:
|
|
14
|
+
*
|
|
15
|
+
* 1. **Names, never values.** Every verdict reports which variable NAMES are
|
|
16
|
+
* present. No value — masked, truncated or otherwise — ever reaches a
|
|
17
|
+
* message, a row or the JSON. A migration tool that prints a fragment of the
|
|
18
|
+
* credential it is migrating has created a second copy of the problem.
|
|
19
|
+
* 2. **Both-set is its own verdict.** A seat carrying a Console key AND a
|
|
20
|
+
* subscription credential is not "half migrated", it is ambiguous: the
|
|
21
|
+
* runtime adapter blanks `ANTHROPIC_API_KEY` on a seat that
|
|
22
|
+
* `subscriptionAuth()` calls true (`lib/runtime/adapter.mjs`), so the
|
|
23
|
+
* subscription rail wins the lanes that go through it while lanes that pass
|
|
24
|
+
* the environment straight through may use the key. Spend then lands in two
|
|
25
|
+
* places and neither is authoritative. It is called out loudly.
|
|
26
|
+
* 3. **Warn today, fail when told.** Seats are LIVE. A check that fails by
|
|
27
|
+
* default would turn every doctor run red the hour it ships and break the
|
|
28
|
+
* very seats it is trying to move. So `subscription` and `both` are `warn`
|
|
29
|
+
* until `MAESTRO_REQUIRE_CONSOLE_KEY=1` (documented in `.env.example` and the
|
|
30
|
+
* runbook) flips them to `fail`. `neither` is a failure regardless on a
|
|
31
|
+
* `claude` seat — a seat with no credential at all is broken, not lax — and
|
|
32
|
+
* is the EXPECTED state on an `engine=cohort` seat, which holds no Anthropic
|
|
33
|
+
* credential by design.
|
|
34
|
+
*
|
|
35
|
+
* Everything here is pure except `runSeatAuth`, whose every edge (the seat's
|
|
36
|
+
* `.env`, the hostname, the clock, the org directory) is injectable.
|
|
37
|
+
*
|
|
38
|
+
* What this module deliberately does NOT do: reach another machine. A seat's
|
|
39
|
+
* environment lives on the seat's own box; there is no remote inventory
|
|
40
|
+
* mechanism to invent. The fleet report is a TEMPLATE plus paste-able rows,
|
|
41
|
+
* which is the honest shape of the job.
|
|
42
|
+
*
|
|
43
|
+
* @module lib/cli/seat-auth
|
|
44
|
+
*/
|
|
45
|
+
|
|
46
|
+
"use strict";
|
|
47
|
+
|
|
48
|
+
import { hostname as osHostname } from "node:os";
|
|
49
|
+
|
|
50
|
+
/** The three variables that decide the rail. Order is the report order. */
|
|
51
|
+
export const SEAT_AUTH_VARS = Object.freeze([
|
|
52
|
+
"ANTHROPIC_API_KEY",
|
|
53
|
+
"MAESTRO_PREFER_SUBSCRIPTION_AUTH",
|
|
54
|
+
"CLAUDE_CODE_OAUTH_TOKEN",
|
|
55
|
+
]);
|
|
56
|
+
|
|
57
|
+
/** Set this to 1/true/yes to turn the G4 warning into a doctor failure. */
|
|
58
|
+
export const REQUIRE_CONSOLE_KEY_VAR = "MAESTRO_REQUIRE_CONSOLE_KEY";
|
|
59
|
+
|
|
60
|
+
/** The operator runbook every remedy points at. */
|
|
61
|
+
export const RUNBOOK = "docs/guides/billing-console-keys.md";
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* The one remedy every auth row gives (CF-13). Defined here — the G4 module —
|
|
65
|
+
* and re-exported by `lib/cli/doctor-checks.mjs` so the two surfaces cannot
|
|
66
|
+
* drift into saying different things.
|
|
67
|
+
*/
|
|
68
|
+
export const CONSOLE_KEY_REMEDY =
|
|
69
|
+
"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";
|
|
70
|
+
|
|
71
|
+
const TRUTHY = /^(1|true|yes)$/i;
|
|
72
|
+
|
|
73
|
+
/** Non-empty after trimming, for any value shape an env bag can hold. */
|
|
74
|
+
function nonEmpty(v) {
|
|
75
|
+
return v !== undefined && v !== null && String(v).trim() !== "";
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Is the G4 guard armed in this environment?
|
|
80
|
+
* @param {Record<string,string>} [env]
|
|
81
|
+
* @returns {boolean}
|
|
82
|
+
*/
|
|
83
|
+
export function enforcementEnabled(env = {}) {
|
|
84
|
+
return TRUTHY.test(String((env && env[REQUIRE_CONSOLE_KEY_VAR]) || "").trim());
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/** `ANTHROPIC_API_KEY (.env)` when the caller knows where it came from. */
|
|
88
|
+
function labelled(name, sources) {
|
|
89
|
+
const src = sources && sources[name];
|
|
90
|
+
return src ? `${name} (${src})` : name;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Which billing rail is this seat on?
|
|
95
|
+
*
|
|
96
|
+
* @param {Record<string,string>} [env] the seat's effective env (`.env` merged
|
|
97
|
+
* over the ambient env, the way every consumer sees it). VALUES ARE NEVER
|
|
98
|
+
* REPORTED — only whether each name is non-empty.
|
|
99
|
+
* @param {object} [opts]
|
|
100
|
+
* @param {"claude"|"cohort"|string} [opts.seatEngine="claude"] a `cohort` seat
|
|
101
|
+
* talks to cohort-llm and is SUPPOSED to hold no Anthropic credential.
|
|
102
|
+
* @param {Record<string,string>} [opts.sources] optional `{VAR: ".env"|"env"}`
|
|
103
|
+
* provenance, surfaced in the message (a token arriving through launchd's
|
|
104
|
+
* environment looks identical to one in `.env` until you say which).
|
|
105
|
+
* @param {boolean} [opts.enforce] override the env-derived guard state.
|
|
106
|
+
* @returns {{verdict:"api-key"|"subscription"|"both"|"neither", level:"ok"|"warn"|"fail",
|
|
107
|
+
* compliant:boolean, enforced:boolean, seatEngine:string, present:string[],
|
|
108
|
+
* absent:string[], msg:string, remedy:string}}
|
|
109
|
+
*/
|
|
110
|
+
export function seatAuthVerdict(env = {}, opts = {}) {
|
|
111
|
+
const e = env && typeof env === "object" ? env : {};
|
|
112
|
+
const sources = (opts && opts.sources) || {};
|
|
113
|
+
const seatEngine = String((opts && opts.seatEngine) || "claude");
|
|
114
|
+
const enforced = opts && opts.enforce !== undefined ? !!opts.enforce : enforcementEnabled(e);
|
|
115
|
+
|
|
116
|
+
const key = nonEmpty(e.ANTHROPIC_API_KEY);
|
|
117
|
+
const token = nonEmpty(e.CLAUDE_CODE_OAUTH_TOKEN);
|
|
118
|
+
const preferPresent = nonEmpty(e.MAESTRO_PREFER_SUBSCRIPTION_AUTH);
|
|
119
|
+
const preferOn = TRUTHY.test(String(e.MAESTRO_PREFER_SUBSCRIPTION_AUTH || "").trim());
|
|
120
|
+
// The same test the runtime adapter applies (lib/runtime/adapter.mjs
|
|
121
|
+
// subscriptionAuth): the prefer flag OR a token puts the seat on the
|
|
122
|
+
// subscription rail, and that is what decides whether a lane blanks the key.
|
|
123
|
+
const subscription = token || preferOn;
|
|
124
|
+
|
|
125
|
+
const present = SEAT_AUTH_VARS.filter((n) => nonEmpty(e[n]));
|
|
126
|
+
const absent = SEAT_AUTH_VARS.filter((n) => !nonEmpty(e[n]));
|
|
127
|
+
const names = present.length ? present.map((n) => labelled(n, sources)).join(", ") : "none";
|
|
128
|
+
const cohortSeat = seatEngine === "cohort";
|
|
129
|
+
|
|
130
|
+
const verdict = key && subscription ? "both" : key ? "api-key" : subscription ? "subscription" : "neither";
|
|
131
|
+
// A flag that is present but not truthy is ignored by every consumer, and
|
|
132
|
+
// "MAESTRO_PREFER_SUBSCRIPTION_AUTH is set" reads as the opposite. Say so.
|
|
133
|
+
const preferNote = preferPresent && !preferOn
|
|
134
|
+
? ` MAESTRO_PREFER_SUBSCRIPTION_AUTH is present but not 1/true/yes, so every consumer ignores it.`
|
|
135
|
+
: "";
|
|
136
|
+
|
|
137
|
+
const head = `Seat auth (G4): ${verdict} — names present: ${names}`;
|
|
138
|
+
const enforceNote = enforced
|
|
139
|
+
? ` ${REQUIRE_CONSOLE_KEY_VAR} is set, so this is a failure.`
|
|
140
|
+
: ` Warning only today; set ${REQUIRE_CONSOLE_KEY_VAR}=1 to make it a doctor failure once the fleet has migrated.`;
|
|
141
|
+
|
|
142
|
+
if (verdict === "api-key") {
|
|
143
|
+
const msg = cohortSeat
|
|
144
|
+
? `${head}. This engine-cohort seat needs no Anthropic credential; the key is harmless but unused by the engine lanes.${preferNote}`
|
|
145
|
+
: `${head}. This is the fleet posture: a Console API key in a capped workspace.${preferNote}`;
|
|
146
|
+
return { verdict, level: "ok", compliant: true, enforced, seatEngine, present, absent, msg, remedy: "" };
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
if (verdict === "both") {
|
|
150
|
+
const msg =
|
|
151
|
+
`${head}. AMBIGUOUS — this seat carries BOTH rails. The runtime adapter blanks ANTHROPIC_API_KEY on a ` +
|
|
152
|
+
`seat that is on subscription auth (lib/runtime/adapter.mjs subscriptionAuth), so the subscription rail ` +
|
|
153
|
+
`wins the lanes that go through it while a lane that passes the environment straight through may use the ` +
|
|
154
|
+
`key: spend lands in two places and neither is authoritative. Migrate, do not stack — ${CONSOLE_KEY_REMEDY}.` +
|
|
155
|
+
preferNote + enforceNote;
|
|
156
|
+
return { verdict, level: enforced ? "fail" : "warn", compliant: false, enforced, seatEngine, present, absent, msg, remedy: CONSOLE_KEY_REMEDY };
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
if (verdict === "subscription") {
|
|
160
|
+
const msg =
|
|
161
|
+
`${head}. Subscription OAuth is NOT the fleet posture${cohortSeat ? " and an engine-cohort seat should hold no Anthropic credential at all" : ""} — ` +
|
|
162
|
+
`it has no per-agent ceiling, no central spend visibility, and it expires headlessly. ${CONSOLE_KEY_REMEDY}.` +
|
|
163
|
+
preferNote + enforceNote;
|
|
164
|
+
return { verdict, level: enforced ? "fail" : "warn", compliant: false, enforced, seatEngine, present, absent, msg, remedy: CONSOLE_KEY_REMEDY };
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// neither
|
|
168
|
+
if (cohortSeat) {
|
|
169
|
+
return {
|
|
170
|
+
verdict, level: "ok", compliant: true, enforced, seatEngine, present, absent,
|
|
171
|
+
msg: `${head}. Expected on an engine-cohort seat: it reaches models through cohort-llm and holds no Anthropic credential.`,
|
|
172
|
+
remedy: "",
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
return {
|
|
176
|
+
verdict, level: "fail", compliant: false, enforced, seatEngine, present, absent,
|
|
177
|
+
msg: `${head}. This seat has NO Claude credential at all — every spawn will fail, not fall back. ${CONSOLE_KEY_REMEDY}.`,
|
|
178
|
+
remedy: CONSOLE_KEY_REMEDY,
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// ---------------------------------------------------------------------------
|
|
183
|
+
// The fleet inventory: a template, and rows that paste into it
|
|
184
|
+
// ---------------------------------------------------------------------------
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* The inventory columns, in order. The measured ones (Seat → Env names
|
|
188
|
+
* present) a seat can fill in about itself; the rest are Console-side facts
|
|
189
|
+
* only a human with Console access knows.
|
|
190
|
+
*/
|
|
191
|
+
export const INVENTORY_COLUMNS = Object.freeze([
|
|
192
|
+
"Seat",
|
|
193
|
+
"Agent id",
|
|
194
|
+
"Machine",
|
|
195
|
+
"Auth verdict",
|
|
196
|
+
"Env names present",
|
|
197
|
+
"Checked",
|
|
198
|
+
"Operator",
|
|
199
|
+
"Anthropic account backing it",
|
|
200
|
+
"Console workspace",
|
|
201
|
+
"Monthly cap",
|
|
202
|
+
"Key id (last 4)",
|
|
203
|
+
".env mode 600",
|
|
204
|
+
"Old token revoked",
|
|
205
|
+
"Verified (who / when)",
|
|
206
|
+
]);
|
|
207
|
+
|
|
208
|
+
/** Keys of the columns a seat measures about itself, in `inventoryRow` order. */
|
|
209
|
+
const MEASURED = Object.freeze(["seat", "agentId", "machine", "verdict", "names", "checked"]);
|
|
210
|
+
|
|
211
|
+
/** A markdown table cell: never break the table, never invent content. */
|
|
212
|
+
function cell(v) {
|
|
213
|
+
const s = v === undefined || v === null ? "" : String(v);
|
|
214
|
+
return s.replace(/\|/g, "\\|").replace(/\r?\n/g, " ").trim();
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* One inventory row. The measured fields are filled; every Console-side field
|
|
219
|
+
* is left blank for the operator — a tool that guesses them teaches people to
|
|
220
|
+
* trust a guess.
|
|
221
|
+
*
|
|
222
|
+
* @param {{seat?:string, agentId?:string, machine?:string, verdict?:string,
|
|
223
|
+
* names?:string[]|string, checked?:string}} [fields]
|
|
224
|
+
* @returns {string} a markdown table row with `INVENTORY_COLUMNS.length` cells
|
|
225
|
+
*/
|
|
226
|
+
export function inventoryRow(fields = {}) {
|
|
227
|
+
const f = fields || {};
|
|
228
|
+
const names = Array.isArray(f.names) ? f.names.join(" + ") : f.names || "";
|
|
229
|
+
const measured = { seat: f.seat, agentId: f.agentId, machine: f.machine, verdict: f.verdict, names, checked: f.checked };
|
|
230
|
+
const cells = INVENTORY_COLUMNS.map((_, i) => (i < MEASURED.length ? cell(measured[MEASURED[i]]) : ""));
|
|
231
|
+
return `| ${cells.join(" | ")} |`;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Turn the org directory into seat stubs for the template. Members that the
|
|
236
|
+
* directory marks human are dropped; anything ambiguous is KEPT, because a
|
|
237
|
+
* missing row is a seat nobody audits while a spurious row is one an operator
|
|
238
|
+
* deletes in a second.
|
|
239
|
+
*
|
|
240
|
+
* @param {object[]} [members]
|
|
241
|
+
* @returns {Array<{seat:string, agentId:string}>}
|
|
242
|
+
*/
|
|
243
|
+
export function seatsFromDirectory(members = []) {
|
|
244
|
+
const out = [];
|
|
245
|
+
const seen = new Set();
|
|
246
|
+
for (const m of Array.isArray(members) ? members : []) {
|
|
247
|
+
if (!m || typeof m !== "object") continue;
|
|
248
|
+
const human =
|
|
249
|
+
m.isHuman === true ||
|
|
250
|
+
/^human$/i.test(String(m.memberType || m.kind || m.type || "")) ||
|
|
251
|
+
/^(human|person)$/i.test(String(m.category || ""));
|
|
252
|
+
if (human) continue;
|
|
253
|
+
const agentId = String(m.agentId || m.slug || m.memberId || m.id || "").trim();
|
|
254
|
+
const seat = String(m.name || m.displayName || m.firstName || agentId || "").trim();
|
|
255
|
+
if (!seat && !agentId) continue;
|
|
256
|
+
const dedupe = `${seat.toLowerCase()}::${agentId.toLowerCase()}`;
|
|
257
|
+
if (seen.has(dedupe)) continue;
|
|
258
|
+
seen.add(dedupe);
|
|
259
|
+
out.push({ seat, agentId });
|
|
260
|
+
}
|
|
261
|
+
out.sort((a, b) => (a.seat || a.agentId).localeCompare(b.seat || b.agentId));
|
|
262
|
+
return out;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* The per-machine inventory an operator fills in, with one pre-filled row per
|
|
267
|
+
* known member. Pure: the caller supplies the seats and the timestamp.
|
|
268
|
+
*
|
|
269
|
+
* @param {{seats?:Array<{seat:string, agentId:string}>, generatedAt?:string,
|
|
270
|
+
* source?:string, note?:string}} [o]
|
|
271
|
+
* @returns {string} markdown
|
|
272
|
+
*/
|
|
273
|
+
export function fleetInventoryTemplate(o = {}) {
|
|
274
|
+
const seats = Array.isArray(o.seats) ? o.seats : [];
|
|
275
|
+
const generatedAt = o.generatedAt || "";
|
|
276
|
+
const source = o.source || "the org directory";
|
|
277
|
+
const lines = [];
|
|
278
|
+
lines.push("# G4 seat-auth inventory — off subscription OAuth, onto Console API keys");
|
|
279
|
+
lines.push("");
|
|
280
|
+
lines.push(`Generated by \`maestro seat-auth --fleet\`${generatedAt ? ` at ${generatedAt}` : ""} from ${source}.`);
|
|
281
|
+
lines.push("");
|
|
282
|
+
lines.push("**How to fill this in.** A seat's environment lives on the seat's own machine — there is no");
|
|
283
|
+
lines.push("remote inventory and this tool does not pretend otherwise. On EACH machine, in the agent's");
|
|
284
|
+
lines.push("directory, run:");
|
|
285
|
+
lines.push("");
|
|
286
|
+
lines.push("```sh");
|
|
287
|
+
lines.push("maestro seat-auth --row");
|
|
288
|
+
lines.push("```");
|
|
289
|
+
lines.push("");
|
|
290
|
+
lines.push("and paste the line it prints over that seat's row below. It fills the measured columns");
|
|
291
|
+
lines.push("(seat, agent id, machine, verdict, which variable NAMES are present, when it was checked)");
|
|
292
|
+
lines.push("and leaves the Console-side columns blank, because only a human with Console access knows them.");
|
|
293
|
+
lines.push("");
|
|
294
|
+
lines.push("**Never record a key value here** — or in a ticket, a commit, or a chat message. The last four");
|
|
295
|
+
lines.push("characters of a key id identify it; nothing else about a credential belongs in a document.");
|
|
296
|
+
lines.push("");
|
|
297
|
+
lines.push(`Migration steps, per seat: \`${RUNBOOK}\`. When every row reads \`api-key\`, set`);
|
|
298
|
+
lines.push(`\`${REQUIRE_CONSOLE_KEY_VAR}=1\` on the fleet so a seat that regresses fails \`maestro doctor\``);
|
|
299
|
+
lines.push("instead of warning quietly.");
|
|
300
|
+
if (o.note) { lines.push(""); lines.push(String(o.note)); }
|
|
301
|
+
lines.push("");
|
|
302
|
+
lines.push(`| ${INVENTORY_COLUMNS.join(" | ")} |`);
|
|
303
|
+
lines.push(`| ${INVENTORY_COLUMNS.map(() => "---").join(" | ")} |`);
|
|
304
|
+
if (seats.length === 0) {
|
|
305
|
+
lines.push(inventoryRow({}));
|
|
306
|
+
} else {
|
|
307
|
+
for (const s of seats) lines.push(inventoryRow({ seat: s.seat, agentId: s.agentId }));
|
|
308
|
+
}
|
|
309
|
+
lines.push("");
|
|
310
|
+
lines.push("## Record once, for the fleet");
|
|
311
|
+
lines.push("");
|
|
312
|
+
lines.push(`- Total live seats (rows above: **${seats.length}** from ${source} — confirm against the live fleet).`);
|
|
313
|
+
lines.push("- How many distinct Anthropic accounts backed the fleet before migration, and whose. A personal");
|
|
314
|
+
lines.push(" account powering several 24/7 seats is the most exposed shape.");
|
|
315
|
+
lines.push("- The date the last subscription token was revoked. That is the date this gate closes.");
|
|
316
|
+
lines.push(`- The date \`${REQUIRE_CONSOLE_KEY_VAR}=1\` was set fleet-wide, which is what stops it regressing.`);
|
|
317
|
+
lines.push("");
|
|
318
|
+
return lines.join("\n");
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
// ---------------------------------------------------------------------------
|
|
322
|
+
// The edge: `maestro seat-auth [--row] [--fleet] [--json]`
|
|
323
|
+
// ---------------------------------------------------------------------------
|
|
324
|
+
|
|
325
|
+
export const USAGE = `Usage:
|
|
326
|
+
maestro seat-auth This seat's billing rail: verdict, variable NAMES, remediation
|
|
327
|
+
maestro seat-auth --row One markdown row to paste into the G4 inventory
|
|
328
|
+
maestro seat-auth --fleet The per-machine inventory template, one row per org member
|
|
329
|
+
maestro seat-auth --json The same verdict as JSON
|
|
330
|
+
|
|
331
|
+
No value of any credential is ever printed. Exit 1 when the seat's verdict is a
|
|
332
|
+
failure (no credential at all, or a non-compliant rail with ${REQUIRE_CONSOLE_KEY_VAR}=1).
|
|
333
|
+
Runbook: ${RUNBOOK}
|
|
334
|
+
`;
|
|
335
|
+
|
|
336
|
+
/** config/agent.json, or {} — identity for the row, never a credential. */
|
|
337
|
+
function readAgentJson(agentRoot, deps) {
|
|
338
|
+
try {
|
|
339
|
+
const { readFileSync, existsSync } = deps.fs || {};
|
|
340
|
+
const p = `${agentRoot}/config/agent.json`;
|
|
341
|
+
if (existsSync && !existsSync(p)) return {};
|
|
342
|
+
return JSON.parse(String(readFileSync(p, "utf8")));
|
|
343
|
+
} catch {
|
|
344
|
+
return {};
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
/**
|
|
349
|
+
* Run `maestro seat-auth …`. Every edge is injectable; nothing is written.
|
|
350
|
+
*
|
|
351
|
+
* @param {string[]} [argv]
|
|
352
|
+
* @param {{env?:Record<string,string>, agentRoot?:string, hostname?:()=>string,
|
|
353
|
+
* now?:number, fs?:object, readDotEnv?:(root:string)=>Record<string,string>,
|
|
354
|
+
* fetchDirectory?:(o:object)=>Promise<object[]>, orgConfig?:object,
|
|
355
|
+
* seatEngine?:string, stdout?:(s:string)=>void}} [deps]
|
|
356
|
+
* @returns {Promise<number>} exit code
|
|
357
|
+
*/
|
|
358
|
+
export async function runSeatAuth(argv = [], deps = {}) {
|
|
359
|
+
const write = deps.stdout || ((s) => process.stdout.write(s));
|
|
360
|
+
const env = deps.env || process.env;
|
|
361
|
+
const agentRoot = deps.agentRoot || env.AGENT_ROOT || env.AGENT_DIR || process.cwd();
|
|
362
|
+
const now = Number.isFinite(deps.now) ? deps.now : Date.now();
|
|
363
|
+
const checked = new Date(now).toISOString().slice(0, 10);
|
|
364
|
+
|
|
365
|
+
let wantRow = false;
|
|
366
|
+
let wantFleet = false;
|
|
367
|
+
let wantJson = false;
|
|
368
|
+
for (const a of argv) {
|
|
369
|
+
if (a === "--row") wantRow = true;
|
|
370
|
+
else if (a === "--fleet") wantFleet = true;
|
|
371
|
+
else if (a === "--json") wantJson = true;
|
|
372
|
+
else if (a === "--help" || a === "-h") { write(USAGE); return 0; }
|
|
373
|
+
else if (a.startsWith("-")) { write(`seat-auth: unknown flag ${a}\n\n${USAGE}`); return 1; }
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
// The seat's effective env, exactly as every consumer sees it: .env wins over
|
|
377
|
+
// the ambient environment (dotenv never overrides an already-set variable is
|
|
378
|
+
// the LOADER's rule; the FILE is the seat's declared intent, which is what an
|
|
379
|
+
// audit is about — doctor merges the same way).
|
|
380
|
+
const readDotEnv = deps.readDotEnv || (async (root) => {
|
|
381
|
+
const { parseDotEnv } = await import("./doctor-checks.mjs");
|
|
382
|
+
const fs = deps.fs || (await import("node:fs"));
|
|
383
|
+
try { return parseDotEnv(fs.readFileSync(`${root}/.env`, "utf8")); } catch { return {}; }
|
|
384
|
+
});
|
|
385
|
+
const dotEnv = await readDotEnv(agentRoot);
|
|
386
|
+
const ambient = {};
|
|
387
|
+
for (const n of [...SEAT_AUTH_VARS, REQUIRE_CONSOLE_KEY_VAR]) if (nonEmpty(env[n])) ambient[n] = String(env[n]);
|
|
388
|
+
const effective = { ...ambient, ...dotEnv };
|
|
389
|
+
const sources = {};
|
|
390
|
+
for (const n of SEAT_AUTH_VARS) {
|
|
391
|
+
if (nonEmpty(dotEnv[n])) sources[n] = ".env";
|
|
392
|
+
else if (nonEmpty(ambient[n])) sources[n] = "env";
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
let seatEngine = deps.seatEngine;
|
|
396
|
+
if (!seatEngine) {
|
|
397
|
+
try {
|
|
398
|
+
const { readSeatEngine } = await import("../runtime/adapter.mjs");
|
|
399
|
+
seatEngine = readSeatEngine(agentRoot);
|
|
400
|
+
} catch { seatEngine = "claude"; }
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
const verdict = seatAuthVerdict(effective, { seatEngine, sources });
|
|
404
|
+
const agentJson = readAgentJson(agentRoot, { fs: deps.fs || (await import("node:fs")) });
|
|
405
|
+
const seat = String(agentJson.firstName || agentJson.name || "").split(/\s+/)[0] || String(agentRoot).split("/").pop();
|
|
406
|
+
const agentId = String(agentJson.agentId || agentJson.memberId || agentJson.slug || env.COHORT_AGENT_ID || "");
|
|
407
|
+
const hostname = (deps.hostname || (() => { try { return osHostname(); } catch { return ""; } }))();
|
|
408
|
+
|
|
409
|
+
if (wantFleet) {
|
|
410
|
+
let members = [];
|
|
411
|
+
let source = "the org directory";
|
|
412
|
+
try {
|
|
413
|
+
const fetchDirectory = deps.fetchDirectory || (await import("../org/client.mjs")).fetchDirectory;
|
|
414
|
+
const { loadOrgConfig } = await import("../org/client.mjs");
|
|
415
|
+
members = await fetchDirectory({ cfg: deps.orgConfig || loadOrgConfig(agentRoot) });
|
|
416
|
+
} catch {
|
|
417
|
+
members = [];
|
|
418
|
+
}
|
|
419
|
+
const seats = seatsFromDirectory(members);
|
|
420
|
+
if (seats.length === 0) source = "the org directory (EMPTY — this seat is not enrolled, or the directory was unreachable; add the rows by hand)";
|
|
421
|
+
const md = fleetInventoryTemplate({ seats, generatedAt: new Date(now).toISOString(), source });
|
|
422
|
+
if (wantJson) { write(JSON.stringify({ seats, generatedAt: new Date(now).toISOString() }, null, 2) + "\n"); return 0; }
|
|
423
|
+
write(md + "\n");
|
|
424
|
+
return 0;
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
if (wantRow) {
|
|
428
|
+
write(inventoryRow({ seat, agentId, machine: hostname, verdict: verdict.verdict, names: verdict.present, checked }) + "\n");
|
|
429
|
+
return verdict.level === "fail" ? 1 : 0;
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
if (wantJson) {
|
|
433
|
+
write(JSON.stringify({
|
|
434
|
+
seat, agentId, machine: hostname, checked,
|
|
435
|
+
verdict: verdict.verdict, level: verdict.level, compliant: verdict.compliant,
|
|
436
|
+
enforced: verdict.enforced, seatEngine: verdict.seatEngine,
|
|
437
|
+
present: verdict.present, absent: verdict.absent,
|
|
438
|
+
remedy: verdict.remedy, runbook: RUNBOOK,
|
|
439
|
+
}, null, 2) + "\n");
|
|
440
|
+
return verdict.level === "fail" ? 1 : 0;
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
const mark = verdict.level === "ok" ? "✓" : verdict.level === "warn" ? "!" : "✗";
|
|
444
|
+
write(`${mark} ${verdict.msg}\n`);
|
|
445
|
+
write(` seat ${seat}${agentId ? ` (${agentId})` : ""} on ${hostname || "this machine"}; engine ${verdict.seatEngine}\n`);
|
|
446
|
+
write(` absent: ${verdict.absent.length ? verdict.absent.join(", ") : "none"}\n`);
|
|
447
|
+
write(` paste into the G4 inventory: maestro seat-auth --row (template: maestro seat-auth --fleet)\n`);
|
|
448
|
+
return verdict.level === "fail" ? 1 : 0;
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
export default {
|
|
452
|
+
SEAT_AUTH_VARS,
|
|
453
|
+
REQUIRE_CONSOLE_KEY_VAR,
|
|
454
|
+
CONSOLE_KEY_REMEDY,
|
|
455
|
+
RUNBOOK,
|
|
456
|
+
INVENTORY_COLUMNS,
|
|
457
|
+
enforcementEnabled,
|
|
458
|
+
seatAuthVerdict,
|
|
459
|
+
inventoryRow,
|
|
460
|
+
seatsFromDirectory,
|
|
461
|
+
fleetInventoryTemplate,
|
|
462
|
+
runSeatAuth,
|
|
463
|
+
};
|
package/lib/cli/session.mjs
CHANGED
|
@@ -13,8 +13,10 @@
|
|
|
13
13
|
* Commands:
|
|
14
14
|
*
|
|
15
15
|
* status [--brief|--json] lock holder · heartbeat age · main session id ·
|
|
16
|
-
*
|
|
17
|
-
* open handoffs · restart/upgrade flags
|
|
16
|
+
* [--all] mux session · plist installed/loaded · peers ·
|
|
17
|
+
* open handoffs · restart/upgrade flags · the
|
|
18
|
+
* current-work block (lib/session/current-work;
|
|
19
|
+
* `--all` = the operator's unscoped view)
|
|
18
20
|
* attach print the exact `tmux attach` / `screen -r` line;
|
|
19
21
|
* exec it when stdin+stdout are a TTY
|
|
20
22
|
* start bootstrap the plist if not loaded, then kickstart
|
|
@@ -55,7 +57,8 @@
|
|
|
55
57
|
|
|
56
58
|
"use strict";
|
|
57
59
|
|
|
58
|
-
import { closeSync, existsSync, mkdirSync, openSync, readFileSync, readdirSync, statSync, unlinkSync, writeFileSync } from "node:fs";
|
|
60
|
+
import { chmodSync, closeSync, existsSync, mkdirSync, openSync, readFileSync, readdirSync, statSync, unlinkSync, writeFileSync } from "node:fs";
|
|
61
|
+
import { randomUUID } from "node:crypto";
|
|
59
62
|
import { basename, dirname, join } from "node:path";
|
|
60
63
|
import { homedir } from "node:os";
|
|
61
64
|
import { spawnSync } from "node:child_process";
|
|
@@ -63,9 +66,12 @@ import { parseArgs } from "node:util";
|
|
|
63
66
|
import { pathToFileURL } from "node:url";
|
|
64
67
|
import { writeJsonAtomic } from "../fs-atomic.mjs";
|
|
65
68
|
import { resolveAgentRoot } from "../agent-root.mjs";
|
|
66
|
-
import {
|
|
69
|
+
import { buildSpawn } from "../runtime/adapter.mjs";
|
|
70
|
+
import { resolveSeatSpawn } from "../runtime/seat-engine.mjs";
|
|
67
71
|
import { sessionPermissionArgs } from "../session-permissions.mjs";
|
|
68
72
|
import { withParallelism } from "../prompts/parallelism.mjs";
|
|
73
|
+
import { envFileCommand, renderEnvFile } from "../session/launch-args.mjs";
|
|
74
|
+
import { currentWorkBlock } from "../session/current-work.mjs";
|
|
69
75
|
|
|
70
76
|
/** The SDK's own package.json version — what "installed" means for an upgrade notice. */
|
|
71
77
|
function ownVersion() {
|
|
@@ -229,7 +235,32 @@ export function peerPermissionArgs({ env = process.env, allowedTools } = {}) {
|
|
|
229
235
|
* @returns {string[]}
|
|
230
236
|
*/
|
|
231
237
|
export function buildSpawnArgs({ first, slug, prompt, env, allowedTools }) {
|
|
232
|
-
|
|
238
|
+
const spec = peerSpawn({ first, slug, prompt, env, allowedTools, bin: "claude" });
|
|
239
|
+
// Only the claude engine is wired for peers, so a failure is a defect.
|
|
240
|
+
if (!spec.ok) throw new Error(`buildSpawnArgs: ${spec.error.message}`);
|
|
241
|
+
return spec.argv;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* The whole peer spawn — binary, argv, env — from the runtime adapter
|
|
246
|
+
* (lib/runtime/adapter.mjs, lane "peer"). `bin` is the caller's injected binary
|
|
247
|
+
* (deps.claudeBin); absent, the adapter resolves it.
|
|
248
|
+
*
|
|
249
|
+
* W4-E1 (row 5): `seat` is the seat's engine resolved once for this spawn
|
|
250
|
+
* (lib/runtime/seat-engine.mjs). A claude seat adds no fields (unchanged); an
|
|
251
|
+
* engine-cohort seat launches `cli.mjs session --name …` with the seat token.
|
|
252
|
+
* @param {{first:string, slug:string, prompt:string, env?:object, allowedTools?:string[], bin?:string, seat?:{fields:object}}} o
|
|
253
|
+
*/
|
|
254
|
+
export function peerSpawn({ first, slug, prompt, env, allowedTools, bin, seat = { fields: {} } }) {
|
|
255
|
+
return buildSpawn({
|
|
256
|
+
lane: "peer",
|
|
257
|
+
bin: bin || undefined,
|
|
258
|
+
name: `${first}-${slug}`,
|
|
259
|
+
permissions: peerPermissionArgs({ env, allowedTools }),
|
|
260
|
+
prompt: withParallelism(prompt),
|
|
261
|
+
env,
|
|
262
|
+
...seat.fields,
|
|
263
|
+
});
|
|
233
264
|
}
|
|
234
265
|
|
|
235
266
|
/**
|
|
@@ -280,6 +311,9 @@ function defaultDeps() {
|
|
|
280
311
|
},
|
|
281
312
|
/** The version the running SDK reports — an upgrade notice for it is already applied. */
|
|
282
313
|
installedVersion: ownVersion(),
|
|
314
|
+
/** W4-E1: a private (0600) file that must not exist yet — the engine env file. */
|
|
315
|
+
writePrivateFile(path, text) { writeFileSync(path, text, { mode: 0o600, flag: "wx" }); chmodSync(path, 0o600); },
|
|
316
|
+
envFileId: () => randomUUID(),
|
|
283
317
|
};
|
|
284
318
|
}
|
|
285
319
|
|
|
@@ -542,7 +576,7 @@ function ensureLoaded(ctx, say) {
|
|
|
542
576
|
export function usage() {
|
|
543
577
|
return `maestro session <command>
|
|
544
578
|
|
|
545
|
-
status [--brief|--json]
|
|
579
|
+
status [--brief|--json|--all] lock holder, heartbeat age, mux session, plist, peers, handoffs, current work (--all: unscoped)
|
|
546
580
|
attach print the attach command (tmux attach / screen -r); exec it on a TTY
|
|
547
581
|
start load the launchd job if needed, then kickstart it
|
|
548
582
|
stop launchctl bootout the session job
|
|
@@ -557,6 +591,7 @@ export function usage() {
|
|
|
557
591
|
const FLAGS = {
|
|
558
592
|
json: { type: "boolean", default: false },
|
|
559
593
|
brief: { type: "boolean", default: false },
|
|
594
|
+
all: { type: "boolean", default: false },
|
|
560
595
|
force: { type: "boolean", default: false },
|
|
561
596
|
name: { type: "string" },
|
|
562
597
|
cwd: { type: "string" },
|
|
@@ -596,9 +631,21 @@ export async function run(argv = [], opts = {}) {
|
|
|
596
631
|
|
|
597
632
|
case "status": {
|
|
598
633
|
const s = snapshot(ctx);
|
|
634
|
+
// The "what am I doing right now" block (lib/session/current-work) —
|
|
635
|
+
// the same fact `session_status` returns and the daemon's replies carry.
|
|
636
|
+
// Scoped like every other reader: by default a live session from any
|
|
637
|
+
// conversation is reported as existing only; `--all` is the operator's
|
|
638
|
+
// own view (the files under state/ are theirs to read anyway — the flag
|
|
639
|
+
// only says the printout may carry what they hold, so it is not pasted
|
|
640
|
+
// onward by reflex). Fail-open: an unreadable state dir is an empty
|
|
641
|
+
// block, not a failed status.
|
|
642
|
+
try { s.currentWork = await (d.currentWorkBlock || currentWorkBlock)(agentRoot, { now: d.now(), audience: flags.all ? null : {} }); } catch { s.currentWork = ""; }
|
|
599
643
|
if (flags.json) say(JSON.stringify(s, null, 2));
|
|
600
644
|
else if (flags.brief) say(briefLine(s));
|
|
601
|
-
else
|
|
645
|
+
else {
|
|
646
|
+
for (const l of humanStatus(s)) say(l);
|
|
647
|
+
if (s.currentWork) for (const l of s.currentWork.split("\n")) say(l);
|
|
648
|
+
}
|
|
602
649
|
return { ok: true, code: 0, lines, data: s };
|
|
603
650
|
}
|
|
604
651
|
|
|
@@ -679,12 +726,33 @@ export async function run(argv = [], opts = {}) {
|
|
|
679
726
|
|
|
680
727
|
const cfg = await loadCfg(ctx);
|
|
681
728
|
const kind = chooseMux(d, cfg);
|
|
682
|
-
|
|
683
|
-
|
|
729
|
+
// W4-E1: the seat's engine, once for this spawn; an engine-cohort peer never falls back to claude.
|
|
730
|
+
let seat = { engine: "claude", fields: {} };
|
|
731
|
+
try { seat = (await (d.seatSpawn || ((root) => resolveSeatSpawn(root)))(agentRoot)) || seat; } catch { seat = { engine: "claude", fields: {} }; }
|
|
732
|
+
const spec = peerSpawn({ first, slug, prompt, env: d.env, allowedTools: cfg.allowedTools, bin: d.claudeBin, seat });
|
|
733
|
+
if (!spec.ok) return fail(`session spawn: ${spec.error.message}`);
|
|
734
|
+
// W4-E1: an engine-cohort peer's env (seat token, Claude credentials
|
|
735
|
+
// scrubbed) rides a 0600 file the session sources and deletes before exec
|
|
736
|
+
// (launch-args ENGINE_ENV_SCRIPT). The mux cannot carry it: a tmux server
|
|
737
|
+
// already running gives a new session its own global env, not the client's.
|
|
738
|
+
let envFile = null;
|
|
739
|
+
if (seat.engine === "cohort") {
|
|
740
|
+
envFile = join(paths.dir, `engine-env-${muxName}-${d.envFileId()}.env`);
|
|
741
|
+
try {
|
|
742
|
+
mkdirSync(paths.dir, { recursive: true });
|
|
743
|
+
d.writePrivateFile(envFile, renderEnvFile(spec.env));
|
|
744
|
+
} catch (e) {
|
|
745
|
+
return fail(`session spawn: could not write the engine env file ${envFile}: ${e && e.message ? e.message : e}`);
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
const command = envFile ? envFileCommand({ envFile, bin: spec.bin, args: spec.argv }) : [spec.bin, ...spec.argv];
|
|
684
749
|
const r = kind === "tmux"
|
|
685
|
-
? d.exec("tmux", ["new-session", "-d", "-s", muxName, "-c", cwd, "--",
|
|
686
|
-
: d.exec("screen", ["-dmS", muxName,
|
|
687
|
-
if (r.status !== 0)
|
|
750
|
+
? d.exec("tmux", ["new-session", "-d", "-s", muxName, "-c", cwd, "--", ...command], { cwd, env: d.env })
|
|
751
|
+
: d.exec("screen", ["-dmS", muxName, ...command], { cwd, env: d.env });
|
|
752
|
+
if (r.status !== 0) {
|
|
753
|
+
if (envFile) { try { unlinkSync(envFile); } catch { /* never written */ } }
|
|
754
|
+
return fail(`session spawn: ${kind} returned ${r.status}${r.stderr ? `: ${r.stderr.trim()}` : ""}`);
|
|
755
|
+
}
|
|
688
756
|
|
|
689
757
|
const startedAt = new Date(d.now()).toISOString();
|
|
690
758
|
const reg = updatePeers(paths, (cur) => [
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
import { spawnSync } from "node:child_process";
|
|
23
23
|
import { normalizeCard, appendCards, repoLabel } from "./cards.mjs";
|
|
24
24
|
import { isRepoDenied } from "./config.mjs";
|
|
25
|
-
import {
|
|
25
|
+
import { buildSpawn } from "../runtime/adapter.mjs";
|
|
26
26
|
|
|
27
27
|
const DISTILL_TIMEOUT_MS = 60_000;
|
|
28
28
|
const MAX_TRANSCRIPT_CHARS = 16_000;
|
|
@@ -118,13 +118,15 @@ export function extractCardsFromModelOutput(text) {
|
|
|
118
118
|
/** Default distiller: spawn a cheap `claude --print` pass. Fail-open → "". */
|
|
119
119
|
export function defaultDistiller(prompt, opts = {}) {
|
|
120
120
|
try {
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
const
|
|
124
|
-
|
|
121
|
+
// argv + env from the runtime adapter (lane "capture"): --print --model <m>
|
|
122
|
+
// <daemon MCP args>, prompt on stdin, PATH augmented.
|
|
123
|
+
const spec = buildSpawn({ lane: "capture", model: opts.model || "haiku", prompt, mcp: {}, env: process.env });
|
|
124
|
+
if (!spec.ok) return "";
|
|
125
|
+
const res = spawnSync(spec.bin, spec.argv, {
|
|
126
|
+
input: spec.stdin,
|
|
125
127
|
encoding: "utf8",
|
|
126
128
|
timeout: DISTILL_TIMEOUT_MS,
|
|
127
|
-
env:
|
|
129
|
+
env: spec.env,
|
|
128
130
|
maxBuffer: 8 * 1024 * 1024,
|
|
129
131
|
});
|
|
130
132
|
if (res.status === 0 && res.stdout) return res.stdout;
|