@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,156 @@
|
|
|
1
|
+
# Fleet rollout — publish one version, then prove the fleet took it
|
|
2
|
+
|
|
3
|
+
`scripts/fleet/rollout.mjs` is the whole propagation path in one command.
|
|
4
|
+
|
|
5
|
+
```
|
|
6
|
+
node scripts/fleet/rollout.mjs [--dry-run] [--verify-only]
|
|
7
|
+
[--deadline <min>] [--interval <s>]
|
|
8
|
+
[--seat-window <min>] [--agent-root <path>] [--json]
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
There is exactly one way an SDK change reaches the fleet: it is published to
|
|
12
|
+
npm, and each seat's hourly `scripts/local-triggers/autoupdate.sh` installs
|
|
13
|
+
`@latest`. Nothing is pushed to a machine. **This tool never ssh's anywhere**,
|
|
14
|
+
and it takes **no credential as an argument or environment variable** — npm auth
|
|
15
|
+
comes from the operator's own `npm login`, org auth from the agent config the
|
|
16
|
+
rest of `lib/org` reads.
|
|
17
|
+
|
|
18
|
+
Both promises are enforced **twice**, because a source-reading test only catches
|
|
19
|
+
the shapes it was taught — `execSync("ssh …")` and `deps.exec(cmd, [])` both
|
|
20
|
+
slipped past the first version of it. `runCommand()` refuses any command but
|
|
21
|
+
`npm` and `git` at runtime, on the value; `parseArgs()` drops everything after
|
|
22
|
+
the first `=` before an error string is built, so no branch can echo a
|
|
23
|
+
credential back into your scrollback. The structural tests in
|
|
24
|
+
`scripts/fleet/rollout.test.mjs` sit on top, pinning the import surface of
|
|
25
|
+
`node:child_process` so nothing can reach a process without that guarded seam.
|
|
26
|
+
|
|
27
|
+
## The three stages
|
|
28
|
+
|
|
29
|
+
**1 — pre-flight.** Refuses the publish unless all of:
|
|
30
|
+
|
|
31
|
+
| gate | why it is here |
|
|
32
|
+
| --- | --- |
|
|
33
|
+
| `npm whoami` succeeds | a publish with no auth fails halfway and leaves you guessing |
|
|
34
|
+
| local version > registry `@latest` | the registry, not a changelog, is the authority on what is published |
|
|
35
|
+
| working tree clean | you can only publish what is committed |
|
|
36
|
+
| `v<version>` exists locally **and** on the remote | published bytes stay findable |
|
|
37
|
+
| `npm run check` passes | … |
|
|
38
|
+
| `npm test` passes | … on the bytes about to be published |
|
|
39
|
+
|
|
40
|
+
The last two are **re-run**, never read from a log: a green run from an hour ago
|
|
41
|
+
is a fact about a different tree. They are skipped only when a cheaper gate has
|
|
42
|
+
already blocked the publish — and the run says so in that case, so "pre-flight
|
|
43
|
+
passed" can never quietly mean "was not run".
|
|
44
|
+
|
|
45
|
+
**2 — publish.** `npm publish`, then poll the registry until `@latest` **is** the
|
|
46
|
+
new version. A 404 or a stale answer in the first seconds is **read lag, not
|
|
47
|
+
failure** — that has been misread as a failed publish before, so the poll
|
|
48
|
+
retries and only fails when its attempts run out.
|
|
49
|
+
|
|
50
|
+
**3 — verify propagation.** Poll the org directory — hq's server-stamped
|
|
51
|
+
presence-beat derivation, the same signal the human fleet page paints — and
|
|
52
|
+
print a per-seat table every round until every seat reports the new version or
|
|
53
|
+
the deadline passes. The run exits non-zero naming exactly which seats are
|
|
54
|
+
behind.
|
|
55
|
+
|
|
56
|
+
A **seat** is an AI colleague with a server-stamped beat inside `--seat-window`
|
|
57
|
+
(default 30 min). An agent with no machine (responder-only) takes no SDK version
|
|
58
|
+
and is counted separately, never as "behind".
|
|
59
|
+
|
|
60
|
+
## Exit codes
|
|
61
|
+
|
|
62
|
+
| code | meaning |
|
|
63
|
+
| --- | --- |
|
|
64
|
+
| 0 | published (or already published) **and** every seat reports the version |
|
|
65
|
+
| 1 | pre-flight refusal — nothing was published, nothing changed |
|
|
66
|
+
| 2 | publish failed, or the registry never served the new version |
|
|
67
|
+
| 3 | deadline passed with seats behind or unverifiable |
|
|
68
|
+
|
|
69
|
+
### `3` is three different things — do not gate on the exit code yet
|
|
70
|
+
|
|
71
|
+
**`rollout` cannot be used as a success gate today.** Every seat classifies as
|
|
72
|
+
`unverifiable` (see below), an unverified seat is deliberately not counted as
|
|
73
|
+
done, so a flawless publish onto a fully propagated fleet **still exits 3**.
|
|
74
|
+
A cron, a CI step or a wrapper script that treats `0` as "shipped" will never
|
|
75
|
+
fire; one that treats `3` as "seats are behind" will page you for nothing.
|
|
76
|
+
|
|
77
|
+
Branch on `--json` instead. It prints one object — `{code, reason, note,
|
|
78
|
+
summary}` — whose `reason` separates the cases the exit code merges:
|
|
79
|
+
|
|
80
|
+
| `reason` | meaning |
|
|
81
|
+
| --- | --- |
|
|
82
|
+
| `verified` | every seat reports the target version (`code` 0) |
|
|
83
|
+
| `behind` | at least one seat is **provably** on an older version |
|
|
84
|
+
| `unverifiable` | no seat is known to be behind; nothing available can prove any seat is current |
|
|
85
|
+
| `empty-fleet` | no beating seats were found — never treated as done |
|
|
86
|
+
| `no-fleet-read` | the org read never succeeded; says nothing about the fleet |
|
|
87
|
+
|
|
88
|
+
`unverifiable` becomes `verified` the moment the one-line hq change below lands,
|
|
89
|
+
with no change to this script. `propagationOutcome()` is pure and exported, so
|
|
90
|
+
the mapping is pinned by test rather than by this table.
|
|
91
|
+
|
|
92
|
+
Idempotent: when `@latest` already equals the local version the run prints
|
|
93
|
+
`already published, verifying propagation` and goes straight to stage 3.
|
|
94
|
+
Re-running it is the supported way to watch a rollout land. `--verify-only`
|
|
95
|
+
does stage 3 alone, against any version, without touching npm.
|
|
96
|
+
|
|
97
|
+
## Today's blocker, and the one honest gap
|
|
98
|
+
|
|
99
|
+
**npm auth.** `npm whoami` is a 401 on this machine, so stage 1 refuses. Only a
|
|
100
|
+
human can clear it (`npm login` as the publisher). The moment it is cleared,
|
|
101
|
+
this command is the entire rollout.
|
|
102
|
+
|
|
103
|
+
**`unverifiable` seats.** Stage 3 wants each seat's *running* version. Every seat
|
|
104
|
+
already reports it — the presence beat carries `status.machine.sdkVersion` and
|
|
105
|
+
hq persists it to `AgentStatus.machine` — but **no read an agent can make gives
|
|
106
|
+
it back**: hq's `directory` read (`src/server/rpc/reads.ts#directory`) selects
|
|
107
|
+
five `AgentStatus` columns and `machine` is not among them, and `member.get`
|
|
108
|
+
does not read `AgentStatus` at all. So every seat currently reads `unknown` and
|
|
109
|
+
the run exits 3.
|
|
110
|
+
|
|
111
|
+
That is deliberate. The tool does **not** count a beating seat as up to date:
|
|
112
|
+
treating "running" as "healthy" is exactly what let one seat sit six days behind
|
|
113
|
+
while its own log said "up to date" every hour. It proves liveness, says plainly
|
|
114
|
+
that it cannot prove the version, and fails.
|
|
115
|
+
|
|
116
|
+
**To close it (hq side, one lane):** add `machine: true` to the `agentStatus`
|
|
117
|
+
select in `directory()` and echo `sdkVersion` (only that key — `machine` is an
|
|
118
|
+
open record and the rest is seat telemetry) into the per-member `status` object,
|
|
119
|
+
with a test pinning the field. `seatVersion()` in the script already reads
|
|
120
|
+
`status.machine.sdkVersion` first and will light up with no change here.
|
|
121
|
+
|
|
122
|
+
Until it does, the run says so in words on every all-unverifiable finish — that
|
|
123
|
+
exit 3 does **not** mean a seat is behind — rather than leaving the reader to
|
|
124
|
+
infer it from a count.
|
|
125
|
+
|
|
126
|
+
## One wrinkle: the gates dirty the tree
|
|
127
|
+
|
|
128
|
+
`npm test` appends to a tracked runtime ledger (`.claude-flow/policy/state.json`),
|
|
129
|
+
so a second run in a row would refuse on a file the *first* run wrote. The
|
|
130
|
+
clean-tree gate is not relaxed for it — the run names those paths and tells you
|
|
131
|
+
to `git checkout --` them. The gate order also means a first run is unaffected:
|
|
132
|
+
cleanliness is read before the gates run.
|
|
133
|
+
|
|
134
|
+
## Reading a round
|
|
135
|
+
|
|
136
|
+
```
|
|
137
|
+
Fleet vs 2.18.1 (round 2 · 46s elapsed)
|
|
138
|
+
SEAT ID BEAT REPORTS VERDICT
|
|
139
|
+
-------------- ---- ---- ------- ------------
|
|
140
|
+
<name> A0xx 6s 2.18.1 current
|
|
141
|
+
<name> A0xx 15s 2.17.0 stale
|
|
142
|
+
<name> A0xx 20s unknown unverifiable
|
|
143
|
+
15 seat(s): 1 current · 1 stale · 13 unverifiable (+32 agent(s) with no beating machine — not rollout targets)
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
`BEAT` is the age of the seat's last **server-stamped** beat — the only honest
|
|
147
|
+
liveness signal there is. `REPORTS` is what that seat says it is running.
|
|
148
|
+
|
|
149
|
+
## What the suite covers
|
|
150
|
+
|
|
151
|
+
`npm test` discovers every `*.test.mjs` under `lib/`, `scripts/`, `bin/`,
|
|
152
|
+
`services/` and `test/` — there is no list to append to. `npm run test:list`
|
|
153
|
+
asks the same runner what it found and prints it without running anything; it
|
|
154
|
+
used to be a hand-typed enumeration that had drifted to 46 of 82 files, and
|
|
155
|
+
`scripts/ci/run-tests.test.mjs` now fails the build if any `package.json` script
|
|
156
|
+
starts enumerating the suite again.
|
|
@@ -22,9 +22,9 @@ This runbook covers the complete setup of a Mac mini as the perpetual operations
|
|
|
22
22
|
|
|
23
23
|
Before starting, ensure you have credentials for:
|
|
24
24
|
|
|
25
|
-
- Claude
|
|
26
|
-
|
|
27
|
-
|
|
25
|
+
- A per-agent Claude Console API key in a workspace with a spend cap — the
|
|
26
|
+
fleet posture (see Step 6 and `docs/guides/billing-console-keys.md`). Do not
|
|
27
|
+
provision a seat on Max-subscription OAuth (`claude setup-token`)
|
|
28
28
|
- GitHub (access to maestro repo)
|
|
29
29
|
- Google Workspace (the-agent@the-company)
|
|
30
30
|
- Slack (the company workspace)
|
|
@@ -114,14 +114,12 @@ cat > ~/.maestro-env << 'EOF'
|
|
|
114
114
|
# the agent operating system Environment Variables
|
|
115
115
|
# Source this file in your shell profile
|
|
116
116
|
|
|
117
|
-
# Required — Claude auth. THE FLEET
|
|
118
|
-
#
|
|
119
|
-
#
|
|
120
|
-
#
|
|
121
|
-
#
|
|
122
|
-
|
|
123
|
-
export CLAUDE_CODE_OAUTH_TOKEN="paste-the-output-of-claude-setup-token"
|
|
124
|
-
export MAESTRO_PREFER_SUBSCRIPTION_AUTH=1
|
|
117
|
+
# Required — Claude auth. THE FLEET POSTURE is a per-agent Console API key in a
|
|
118
|
+
# workspace with a hard spend cap (docs/guides/billing-console-keys.md). Do not
|
|
119
|
+
# set MAESTRO_PREFER_SUBSCRIPTION_AUTH or CLAUDE_CODE_OAUTH_TOKEN. `maestro
|
|
120
|
+
# setup` writes the key into the agent's .env; `maestro doctor` verifies it with
|
|
121
|
+
# a real API call.
|
|
122
|
+
export ANTHROPIC_API_KEY="paste-this-agent's-console-api-key"
|
|
125
123
|
|
|
126
124
|
# Optional but recommended
|
|
127
125
|
export SLACK_TOKEN="your-slack-token-here"
|
|
@@ -145,8 +143,8 @@ source ~/.maestro-env
|
|
|
145
143
|
Verify:
|
|
146
144
|
|
|
147
145
|
```bash
|
|
148
|
-
echo $
|
|
149
|
-
# Should print the first 10 characters of the
|
|
146
|
+
echo $ANTHROPIC_API_KEY | head -c 10
|
|
147
|
+
# Should print the first 10 characters of the key
|
|
150
148
|
```
|
|
151
149
|
|
|
152
150
|
**Security note**: Set file permissions to owner-only:
|
|
@@ -356,7 +354,7 @@ echo "=============================="
|
|
|
356
354
|
- [ ] Git, Node.js, Python, jq, yq installed
|
|
357
355
|
- [ ] Claude Code CLI installed and verified
|
|
358
356
|
- [ ] maestro repo cloned to ~/maestro
|
|
359
|
-
- [ ] Claude auth:
|
|
357
|
+
- [ ] Claude auth: a per-agent Console API key (ANTHROPIC_API_KEY) in the agent's .env; `maestro doctor` shows it verified
|
|
360
358
|
- [ ] Environment variables configured in ~/.maestro-env
|
|
361
359
|
- [ ] File permissions set (chmod 600 on secrets)
|
|
362
360
|
- [ ] MCP servers installed and configured
|
package/lib/action-executor.js
CHANGED
|
@@ -21,7 +21,9 @@ import {
|
|
|
21
21
|
normalizeAccessLevel,
|
|
22
22
|
} from "./tool-definitions.js";
|
|
23
23
|
import { isOrgTool, executeOrgTool } from "./org/tool-surface.mjs";
|
|
24
|
-
import {
|
|
24
|
+
import { buildSpawn, DEFAULT_COHORT_TIER_MAP } from "./runtime/adapter.mjs";
|
|
25
|
+
import { resolveSeatSpawn } from "./runtime/seat-engine.mjs";
|
|
26
|
+
import { cohortTierFor } from "./model-router/catalog.mjs";
|
|
25
27
|
|
|
26
28
|
const AGENT_ROOT = process.env.AGENT_ROOT || process.cwd();
|
|
27
29
|
|
|
@@ -524,12 +526,26 @@ export async function executeLookup(toolName, input, agentRoot, opts = {}) {
|
|
|
524
526
|
// unchanged. Fail-open: routing errors yield the historical `haiku` default.
|
|
525
527
|
const routed = await resolveLookupModel(toolName, agentRoot, opts);
|
|
526
528
|
|
|
529
|
+
// The seat's engine, read per lookup (conformance row 36: a huddle's or a
|
|
530
|
+
// voice call's tools land here). A claude seat's buildSpawn input is exactly
|
|
531
|
+
// what it was; an engine-cohort seat runs `cli.mjs run` with its seat token
|
|
532
|
+
// and never falls back to claude. The engine is never handed a third-party
|
|
533
|
+
// model id the router chose for claude: the lane's own slot is used instead.
|
|
534
|
+
const seat = await (opts.resolveSeatSpawn || resolveSeatSpawn)(agentRoot);
|
|
535
|
+
const model = seat.engine === "cohort" && !cohortTierFor(routed.modelFlag, DEFAULT_COHORT_TIER_MAP)
|
|
536
|
+
? LOOKUP_DEFAULT_MODEL
|
|
537
|
+
: routed.modelFlag;
|
|
538
|
+
|
|
527
539
|
try {
|
|
528
540
|
const { stdout } = await new Promise((resolve, reject) => {
|
|
529
|
-
|
|
541
|
+
// argv + env from the runtime adapter (lane "lookup"):
|
|
542
|
+
// --print --model <m> -p <prompt>, TERM=dumb, PATH augmented.
|
|
543
|
+
const spec = (opts.buildSpawn || buildSpawn)({ lane: "lookup", model, prompt, env: process.env, ...seat.fields });
|
|
544
|
+
if (!spec.ok) return reject(new Error(spec.error.message));
|
|
545
|
+
execFile(spec.bin, spec.argv, {
|
|
530
546
|
timeout: LOOKUP_TIMEOUT_MS,
|
|
531
547
|
maxBuffer: LOOKUP_MAX_BUFFER,
|
|
532
|
-
env:
|
|
548
|
+
env: spec.env,
|
|
533
549
|
cwd: agentRoot,
|
|
534
550
|
}, (err, stdout, stderr) => {
|
|
535
551
|
if (err) reject(err);
|
package/lib/budget-guard.mjs
CHANGED
|
@@ -57,6 +57,144 @@ import { join, resolve, dirname } from "node:path";
|
|
|
57
57
|
import { randomBytes } from "node:crypto";
|
|
58
58
|
|
|
59
59
|
import { summariseRows, imputeUnmeasured } from "./cost/ledger-row.mjs";
|
|
60
|
+
import { quotaBand, quotaStatePath, readQuotaSnapshot } from "./org/quota.mjs";
|
|
61
|
+
import { load as yamlLoad } from "js-yaml";
|
|
62
|
+
|
|
63
|
+
// ---------------------------------------------------------------------------
|
|
64
|
+
// cohort-metered seats (Cohort Compute, design §8.3)
|
|
65
|
+
// ---------------------------------------------------------------------------
|
|
66
|
+
//
|
|
67
|
+
// A seat whose model routing uses the cohort provider (or whose runtime engine
|
|
68
|
+
// is cohort) is metered by cohort-llm, not by this ledger. hq owns its limits —
|
|
69
|
+
// the seat band's 5h/7d/30d windows, credits and spend limits — and publishes
|
|
70
|
+
// them at GET /cohort/v1/quota. For such a seat the band comes from THAT
|
|
71
|
+
// reading (lib/org/quota.mjs, persisted by the quota client at
|
|
72
|
+
// state/cohort-llm/quota.json), never from summing local JSONL: the gateway's
|
|
73
|
+
// number is the one that refuses, so it is the one that must pace.
|
|
74
|
+
//
|
|
75
|
+
// utilization ≥ 75 % → band 80 (warn the supervisor)
|
|
76
|
+
// utilization ≥ 100 % → band 100 (degrade: self-directed work stops)
|
|
77
|
+
// canAdmit refused → band 100
|
|
78
|
+
// Spawn-level refusal is the pre-spawn gate's job (lib/runtime/adapter.mjs
|
|
79
|
+
// admitSpawn), which reads the same endpoint.
|
|
80
|
+
|
|
81
|
+
/** A persisted quota reading older than this is not trusted for banding. */
|
|
82
|
+
export const COHORT_QUOTA_MAX_AGE_MS = 10 * 60 * 1000;
|
|
83
|
+
|
|
84
|
+
function agentRootFor(deps) {
|
|
85
|
+
return resolve((deps && deps.agentRoot) || process.env.AGENT_ROOT || process.env.AGENT_DIR || process.cwd());
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/** The catalog provider name cohort-llm rows carry (`cohort/<tier>`). */
|
|
89
|
+
const COHORT_PROVIDER = "cohort";
|
|
90
|
+
|
|
91
|
+
/** A ledger row billed by cohort-llm is metered by the quota, never summed here. */
|
|
92
|
+
export function isCohortBilledRow(r) {
|
|
93
|
+
return !!(r && typeof r === "object" && r.billed_by === "cohort-llm");
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* How much of a routing config's traffic cohort-llm serves. PURE.
|
|
98
|
+
* "none" no routed ref names the cohort provider
|
|
99
|
+
* "whole" every routed ref is cohort AND the Anthropic safety net is off
|
|
100
|
+
* "mixed" anything else that routes at least one ref to cohort
|
|
101
|
+
* Routed refs are every routing_policy chain member (aliases resolved) plus
|
|
102
|
+
* every alias value; the fallback is Anthropic unless fallback_to_anthropic is
|
|
103
|
+
* explicitly false (the router's own rule). An unresolvable member counts as
|
|
104
|
+
* non-cohort — the conservative direction, because "mixed" takes the worse of
|
|
105
|
+
* both bands. Parsed, never regex-scanned: a commented-out ref routes nothing.
|
|
106
|
+
* @param {object|null} cfg a parsed config/model-routing document
|
|
107
|
+
* @returns {"none"|"mixed"|"whole"}
|
|
108
|
+
*/
|
|
109
|
+
export function routingCohortMode(cfg) {
|
|
110
|
+
if (!cfg || typeof cfg !== "object") return "none";
|
|
111
|
+
const aliases = cfg.aliases && typeof cfg.aliases === "object" && !Array.isArray(cfg.aliases) ? cfg.aliases : {};
|
|
112
|
+
const refs = [];
|
|
113
|
+
const add = (member, depth = 0) => {
|
|
114
|
+
if (Array.isArray(member)) { for (const m of member) add(m, depth); return; }
|
|
115
|
+
if (typeof member !== "string") return;
|
|
116
|
+
const s = member.trim();
|
|
117
|
+
if (!s || s === "rules") return;
|
|
118
|
+
if (s.includes("/")) { refs.push(s); return; }
|
|
119
|
+
if (depth < 8 && Object.prototype.hasOwnProperty.call(aliases, s)) { add(aliases[s], depth + 1); return; }
|
|
120
|
+
refs.push(`unresolved:${s}`);
|
|
121
|
+
};
|
|
122
|
+
for (const v of Object.values(aliases)) add(v);
|
|
123
|
+
for (const rule of Array.isArray(cfg.routing_policy) ? cfg.routing_policy : []) {
|
|
124
|
+
if (rule && typeof rule === "object") add(rule.chain);
|
|
125
|
+
}
|
|
126
|
+
const isCohort = (ref) => ref.split("/")[0].trim().toLowerCase() === COHORT_PROVIDER;
|
|
127
|
+
const cohortRefs = refs.filter(isCohort).length;
|
|
128
|
+
if (cohortRefs === 0) return "none";
|
|
129
|
+
const everyRefCohort = cohortRefs === refs.length;
|
|
130
|
+
return everyRefCohort && cfg.fallback_to_anthropic === false ? "whole" : "mixed";
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/** Read and parse the seat's routing config the way lib/model-router.mjs finds it. null on absence or a parse error. */
|
|
134
|
+
function readRoutingConfigDoc(root, deps) {
|
|
135
|
+
if (deps && deps.routingConfig !== undefined) return deps.routingConfig;
|
|
136
|
+
const candidates = [
|
|
137
|
+
deps && deps.routingConfigPath,
|
|
138
|
+
process.env.MAESTRO_ROUTING_CONFIG,
|
|
139
|
+
join(root, "config", "model-routing.yaml"),
|
|
140
|
+
join(root, "config", "model-routing.json"),
|
|
141
|
+
].filter(Boolean);
|
|
142
|
+
for (const p of candidates) {
|
|
143
|
+
try {
|
|
144
|
+
if (!existsSync(p)) continue;
|
|
145
|
+
const raw = readFileSync(p, "utf-8");
|
|
146
|
+
return p.endsWith(".json") ? JSON.parse(raw) : yamlLoad(raw);
|
|
147
|
+
} catch {
|
|
148
|
+
// The daemon falls back to the stock Anthropic CLI on an invalid config
|
|
149
|
+
// (dispatcher getRoutingConfig), so nothing routes to cohort: "none".
|
|
150
|
+
return null;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
return null;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* How this seat is metered by cohort-llm: {mode, source}. `deps.cohortMetered`
|
|
158
|
+
* (boolean → whole/none) or `deps.cohortMetering` ("none"|"mixed"|"whole")
|
|
159
|
+
* override. Otherwise config/agent.json runtime.engine === "cohort" → whole;
|
|
160
|
+
* else the parsed routing config ({@link routingCohortMode}). Never throws.
|
|
161
|
+
*/
|
|
162
|
+
export function cohortMetering(deps) {
|
|
163
|
+
if (deps && typeof deps.cohortMetering === "string" && ["none", "mixed", "whole"].includes(deps.cohortMetering)) {
|
|
164
|
+
return { mode: deps.cohortMetering, source: "override" };
|
|
165
|
+
}
|
|
166
|
+
if (deps && typeof deps.cohortMetered === "boolean") return { mode: deps.cohortMetered ? "whole" : "none", source: "override" };
|
|
167
|
+
const root = agentRootFor(deps);
|
|
168
|
+
try {
|
|
169
|
+
const agentJson = join(root, "config", "agent.json");
|
|
170
|
+
if (existsSync(agentJson)) {
|
|
171
|
+
const cfg = JSON.parse(readFileSync(agentJson, "utf-8"));
|
|
172
|
+
const engine = cfg && cfg.runtime && typeof cfg.runtime.engine === "string" ? cfg.runtime.engine.trim().toLowerCase() : "";
|
|
173
|
+
if (engine === "cohort") return { mode: "whole", source: "runtime.engine" };
|
|
174
|
+
}
|
|
175
|
+
} catch { /* unreadable agent.json → not cohort by this signal */ }
|
|
176
|
+
try {
|
|
177
|
+
return { mode: routingCohortMode(readRoutingConfigDoc(root, deps)), source: "model-routing" };
|
|
178
|
+
} catch {
|
|
179
|
+
return { mode: "none", source: "model-routing" };
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/** Does cohort-llm serve ANY of this seat's traffic? (whole or mixed) */
|
|
184
|
+
export function isCohortMetered(deps) {
|
|
185
|
+
return cohortMetering(deps).mode !== "none";
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/** The seat's latest trusted quota reading, or null. `deps.cohortQuota` overrides. */
|
|
189
|
+
function cohortQuotaFor(deps) {
|
|
190
|
+
if (deps && deps.cohortQuota !== undefined) return deps.cohortQuota;
|
|
191
|
+
return readQuotaSnapshot(quotaStatePath(agentRootFor(deps)), clock(deps)(), COHORT_QUOTA_MAX_AGE_MS);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
const microsToUsd = (m) => {
|
|
195
|
+
const n = Number(m);
|
|
196
|
+
return Number.isFinite(n) ? +(n / 1e6).toFixed(6) : null;
|
|
197
|
+
};
|
|
60
198
|
|
|
61
199
|
// ---------------------------------------------------------------------------
|
|
62
200
|
// Config
|
|
@@ -305,7 +443,7 @@ function sumToday(deps) {
|
|
|
305
443
|
let malformed = 0;
|
|
306
444
|
for (const line of body.split("\n")) {
|
|
307
445
|
if (!line.trim()) continue;
|
|
308
|
-
try {
|
|
446
|
+
try { const r = JSON.parse(line); if (!isCohortBilledRow(r)) rows.push(r); } catch { malformed += 1; }
|
|
309
447
|
}
|
|
310
448
|
|
|
311
449
|
const summary = summariseRows(rows);
|
|
@@ -369,7 +507,7 @@ function monthMeanSessionUsd(deps) {
|
|
|
369
507
|
const rows = [];
|
|
370
508
|
for (const line of body.split("\n")) {
|
|
371
509
|
if (!line.trim()) continue;
|
|
372
|
-
try {
|
|
510
|
+
try { const r = JSON.parse(line); if (!isCohortBilledRow(r)) rows.push(r); } catch { /* skip */ }
|
|
373
511
|
}
|
|
374
512
|
const s = summariseRows(rows);
|
|
375
513
|
usd += s.measuredUsd;
|
|
@@ -408,7 +546,7 @@ export function monthToDate(deps) {
|
|
|
408
546
|
const rows = [];
|
|
409
547
|
for (const line of body.split("\n")) {
|
|
410
548
|
if (!line.trim()) continue;
|
|
411
|
-
try {
|
|
549
|
+
try { const r = JSON.parse(line); if (!isCohortBilledRow(r)) rows.push(r); } catch { /* skip */ }
|
|
412
550
|
}
|
|
413
551
|
const summary = summariseRows(rows);
|
|
414
552
|
spentUSD += summary.measuredUsd + imputeUnmeasured(summary, { fallbackPerSessionUsd }).imputedUsd;
|
|
@@ -672,11 +810,47 @@ export function dailyAllowance(deps) {
|
|
|
672
810
|
* the log every tick. `deps.subscriptionAuth` overrides for tests.
|
|
673
811
|
*/
|
|
674
812
|
export function isSubscriptionAuth(deps) {
|
|
813
|
+
// A wholly cohort-served seat HAS a meter (cohort-llm), whatever its Claude
|
|
814
|
+
// auth says: it must never be waved through as a flat-fee subscription. On a
|
|
815
|
+
// MIXED seat this answers for the Anthropic half only (the local ledger); the
|
|
816
|
+
// cohort half is banded from the quota regardless (see dailyStatus).
|
|
817
|
+
if (cohortMetering(deps).mode === "whole") return false;
|
|
675
818
|
if (deps && typeof deps.subscriptionAuth === "boolean") return deps.subscriptionAuth;
|
|
676
819
|
return process.env.MAESTRO_PREFER_SUBSCRIPTION_AUTH === "1" && !process.env.ANTHROPIC_API_KEY;
|
|
677
820
|
}
|
|
678
821
|
|
|
822
|
+
/**
|
|
823
|
+
* The seat's budget status. A wholly cohort-served seat is banded from its
|
|
824
|
+
* /cohort/v1/quota reading alone. A MIXED seat (some routes cohort, some — or
|
|
825
|
+
* the safety net — Anthropic) is banded on the WORSE of the local ledger (its
|
|
826
|
+
* Anthropic traffic; cohort-billed rows are excluded) and the cohort quota, so
|
|
827
|
+
* neither meter can silently stop pacing the traffic it owns.
|
|
828
|
+
*/
|
|
679
829
|
export function dailyStatus(deps) {
|
|
830
|
+
const metering = cohortMetering(deps);
|
|
831
|
+
if (metering.mode === "whole") return cohortDailyStatus(deps);
|
|
832
|
+
const local = ledgerDailyStatus(deps);
|
|
833
|
+
if (metering.mode === "mixed") return worseOfLedgerAndCohort(local, cohortDailyStatus(deps));
|
|
834
|
+
return local;
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
/** Merge a mixed seat's two readings: the ledger status, with the cohort band when it is worse. */
|
|
838
|
+
function worseOfLedgerAndCohort(local, cohort) {
|
|
839
|
+
const out = { ...local, cohortMetering: "mixed", cohort: cohort.cohort, bandSource: "ledger" };
|
|
840
|
+
out.degradations = [...(local.degradations || []), ...(cohort.degradations || [])];
|
|
841
|
+
if (cohort.band > local.band) {
|
|
842
|
+
const posture = postureForBand(cohort.band);
|
|
843
|
+
out.band = cohort.band;
|
|
844
|
+
out.mode = posture.mode;
|
|
845
|
+
out.posture = posture;
|
|
846
|
+
out.essentialOnly = cohort.band >= 125;
|
|
847
|
+
out.pct = Math.max(Number(local.pct) || 0, Number(cohort.pct) || 0);
|
|
848
|
+
out.bandSource = "cohort-quota";
|
|
849
|
+
}
|
|
850
|
+
return out;
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
function ledgerDailyStatus(deps) {
|
|
680
854
|
const subscriptionAuth = isSubscriptionAuth(deps);
|
|
681
855
|
const iterationCap = cfgIterationCap(deps);
|
|
682
856
|
const today = sumToday(deps);
|
|
@@ -803,6 +977,77 @@ export function dailyStatus(deps) {
|
|
|
803
977
|
};
|
|
804
978
|
}
|
|
805
979
|
|
|
980
|
+
/**
|
|
981
|
+
* dailyStatus for a cohort-metered seat: the same shape, banded from the
|
|
982
|
+
* seat's /cohort/v1/quota reading. Fail-open (band 0) with a loud degradation
|
|
983
|
+
* when no fresh reading exists — the pre-spawn gate still refuses a spawn the
|
|
984
|
+
* gateway would refuse.
|
|
985
|
+
*/
|
|
986
|
+
function cohortDailyStatus(deps) {
|
|
987
|
+
const quota = cohortQuotaFor(deps);
|
|
988
|
+
const q = quotaBand(quota);
|
|
989
|
+
const now = clock(deps)();
|
|
990
|
+
const degradations = [];
|
|
991
|
+
let band = 0;
|
|
992
|
+
if (q.band === "unknown") {
|
|
993
|
+
degradations.push(
|
|
994
|
+
"COHORT QUOTA UNKNOWN: this seat is metered by cohort-llm but has no fresh /cohort/v1/quota reading " +
|
|
995
|
+
`(state/cohort-llm/quota.json missing or older than ${COHORT_QUOTA_MAX_AGE_MS / 60000} min) — band 0; ` +
|
|
996
|
+
"the pre-spawn quota gate is the barrier until a reading lands."
|
|
997
|
+
);
|
|
998
|
+
} else if (q.band === "at") {
|
|
999
|
+
band = 100;
|
|
1000
|
+
} else if (q.band === "approaching") {
|
|
1001
|
+
band = 80;
|
|
1002
|
+
}
|
|
1003
|
+
for (const d of degradations) {
|
|
1004
|
+
try { console.warn(`[budget-guard] degraded: ${d}`); } catch { /* never throw */ }
|
|
1005
|
+
}
|
|
1006
|
+
const pct = q.utilization === null ? (q.canAdmit === false ? 100 : 0) : q.utilization * 100;
|
|
1007
|
+
const month = quota && Array.isArray(quota.windows) ? quota.windows.find((w) => w.kind === "30d") : null;
|
|
1008
|
+
const posture = postureForBand(band);
|
|
1009
|
+
const capUSD = microsToUsd(q.limitMicros) ?? 0;
|
|
1010
|
+
const env = readSeatEnvelope(deps);
|
|
1011
|
+
return {
|
|
1012
|
+
spentUSD: microsToUsd(q.consumedMicros) ?? 0,
|
|
1013
|
+
capUSD,
|
|
1014
|
+
bandingCapUSD: capUSD,
|
|
1015
|
+
pct: +pct.toFixed(2),
|
|
1016
|
+
dayPct: null,
|
|
1017
|
+
monthPct: month && month.utilization !== null ? +(month.utilization * 100).toFixed(2) : 0,
|
|
1018
|
+
band,
|
|
1019
|
+
mode: posture.mode,
|
|
1020
|
+
posture,
|
|
1021
|
+
essentialOnly: band >= 125,
|
|
1022
|
+
sessions: 0,
|
|
1023
|
+
iterationCap: cfgIterationCap(deps),
|
|
1024
|
+
date: todayUtc(deps),
|
|
1025
|
+
funded: q.band !== "unknown",
|
|
1026
|
+
capSource: "cohort-quota",
|
|
1027
|
+
configuredCapUSD: null,
|
|
1028
|
+
envelope: {
|
|
1029
|
+
seatBudgetCents: null,
|
|
1030
|
+
monthUSD: month ? microsToUsd(month.limitMicros) : null,
|
|
1031
|
+
source: "cohort-quota",
|
|
1032
|
+
supervisorMemberId: env.supervisorMemberId,
|
|
1033
|
+
},
|
|
1034
|
+
month: {
|
|
1035
|
+
spentUSD: month ? (microsToUsd(month.consumedMicros) ?? 0) : 0,
|
|
1036
|
+
pct: month && month.utilization !== null ? +(month.utilization * 100).toFixed(2) : null,
|
|
1037
|
+
daysInMonth: daysInUtcMonth(now),
|
|
1038
|
+
dayOfMonth: new Date(now).getUTCDate(),
|
|
1039
|
+
},
|
|
1040
|
+
supervisorMemberId: env.supervisorMemberId || null,
|
|
1041
|
+
measuredUSD: 0,
|
|
1042
|
+
imputedUSD: 0,
|
|
1043
|
+
unmeasuredSessions: 0,
|
|
1044
|
+
nonLlmRows: 0,
|
|
1045
|
+
blind: false,
|
|
1046
|
+
degradations,
|
|
1047
|
+
cohort: { band: q.band, utilization: q.utilization, window: q.window, resetsAt: q.resetsAt, canAdmit: q.canAdmit, asOf: quota ? quota.asOf || null : null },
|
|
1048
|
+
};
|
|
1049
|
+
}
|
|
1050
|
+
|
|
806
1051
|
/**
|
|
807
1052
|
* The lowest binding cap this period has run under, and the envelope it belongs
|
|
808
1053
|
* to. See the LATCH comment in {@link dailyStatus} for why banding may not use a
|
|
@@ -1140,6 +1385,7 @@ export function rollingWindowUsage(deps) {
|
|
|
1140
1385
|
if (!line.trim()) continue;
|
|
1141
1386
|
let r;
|
|
1142
1387
|
try { r = JSON.parse(line); } catch { continue; }
|
|
1388
|
+
if (isCohortBilledRow(r)) continue; // metered by the cohort quota, not these windows
|
|
1143
1389
|
const at = r && r.ts ? Date.parse(r.ts) : NaN;
|
|
1144
1390
|
if (!Number.isFinite(at)) continue;
|
|
1145
1391
|
const dt = now - at;
|
|
@@ -1159,6 +1405,36 @@ export function rollingWindowUsage(deps) {
|
|
|
1159
1405
|
* band is the WORSE of the 5h and weekly horizons.
|
|
1160
1406
|
*/
|
|
1161
1407
|
export function windowBand(deps) {
|
|
1408
|
+
const metering = cohortMetering(deps);
|
|
1409
|
+
if (metering.mode === "none") return ledgerWindowBand(deps);
|
|
1410
|
+
// cohort-llm's own windows (5h/7d/30d) are the pacing signal for its traffic.
|
|
1411
|
+
const q = quotaBand(cohortQuotaFor(deps));
|
|
1412
|
+
const cohort = {
|
|
1413
|
+
band: q.band === "unknown" ? "ok" : q.band,
|
|
1414
|
+
source: "cohort-quota",
|
|
1415
|
+
active: q.band !== "unknown",
|
|
1416
|
+
utilization: q.utilization,
|
|
1417
|
+
window: q.window,
|
|
1418
|
+
resetsAt: q.resetsAt,
|
|
1419
|
+
canAdmit: q.canAdmit,
|
|
1420
|
+
window5hTokens: 0, window5hSessions: 0, week7dTokens: 0, week7dSessions: 0,
|
|
1421
|
+
cap5h: null, capWeek: null,
|
|
1422
|
+
};
|
|
1423
|
+
if (metering.mode === "whole") return cohort;
|
|
1424
|
+
// Mixed: the worse of the local token windows (Anthropic traffic) and the quota.
|
|
1425
|
+
const local = ledgerWindowBand(deps);
|
|
1426
|
+
const rank = { ok: 0, approaching: 1, at: 2 };
|
|
1427
|
+
const worse = rank[cohort.band] > rank[local.band] ? cohort.band : local.band;
|
|
1428
|
+
return {
|
|
1429
|
+
...local,
|
|
1430
|
+
band: worse,
|
|
1431
|
+
source: rank[cohort.band] > rank[local.band] ? "cohort-quota" : "ledger",
|
|
1432
|
+
active: local.active || cohort.active,
|
|
1433
|
+
cohort: { band: cohort.band, active: cohort.active, utilization: q.utilization, window: q.window, resetsAt: q.resetsAt, canAdmit: q.canAdmit },
|
|
1434
|
+
};
|
|
1435
|
+
}
|
|
1436
|
+
|
|
1437
|
+
function ledgerWindowBand(deps) {
|
|
1162
1438
|
const u = rollingWindowUsage(deps);
|
|
1163
1439
|
// Caps come from deps first (tests / a per-seat override), else the env consts.
|
|
1164
1440
|
const cap5 = deps && deps.cap5h != null ? deps.cap5h : WINDOW_5H_TOKEN_CAP;
|
|
@@ -448,7 +448,9 @@ export class BaseAdapter {
|
|
|
448
448
|
* Order (gaps-product-quality P0-3/P0-5):
|
|
449
449
|
* 1. send-gate — the hook-bus `beforeSend` cancellable chain AND the shared
|
|
450
450
|
* lib/comms/send-gate.screenOutbound. EITHER vetoing blocks the send.
|
|
451
|
-
* Now EVERY adapter send passes the gate, not just Gmail.
|
|
451
|
+
* Now EVERY adapter send passes the gate, not just Gmail. screenOutbound
|
|
452
|
+
* runs screenScaffold first, so a raw model turn is caught here too —
|
|
453
|
+
* coverage is real, just not visible at this call site.
|
|
452
454
|
* 2. pair guard — a per-conversation sliding window so two agents in one
|
|
453
455
|
* channel can't reply-storm.
|
|
454
456
|
* 3. _send — the adapter transport.
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
|
|
34
34
|
/**
|
|
35
35
|
* @typedef {"message"|"reaction"|"channel_cc"|"voice_note"|"call"
|
|
36
|
-
* |"mention"|"thread_reply"|"task_assigned"|"task_comment"
|
|
36
|
+
* |"mention"|"broadcast"|"thread_reply"|"task_assigned"|"task_comment"
|
|
37
37
|
* |"file_comment"|"approval"|"decision"|"escalation"|"handoff"
|
|
38
38
|
* |"email"} EventKind
|
|
39
39
|
*
|
|
@@ -73,6 +73,7 @@ export const EVENT_KINDS = Object.freeze([
|
|
|
73
73
|
"call",
|
|
74
74
|
// Work surfaces (org inbound).
|
|
75
75
|
"mention",
|
|
76
|
+
"broadcast",
|
|
76
77
|
"thread_reply",
|
|
77
78
|
"task_assigned",
|
|
78
79
|
"task_comment",
|
|
@@ -310,6 +310,14 @@ export function eventToInboxItem(ev) {
|
|
|
310
310
|
item.scope_id = sanitizeYamlField(scopeId);
|
|
311
311
|
}
|
|
312
312
|
|
|
313
|
+
// A roll-call's respondent bound: how many AI members the org has, so the
|
|
314
|
+
// composer can be told it is "one of up to N" answering the same message.
|
|
315
|
+
// A number, never interpolated; absent for every other surface.
|
|
316
|
+
const respondents = cohort ? cohort.respondents : null;
|
|
317
|
+
if (Number.isFinite(respondents) && respondents > 0) {
|
|
318
|
+
item.respondents = Math.floor(respondents);
|
|
319
|
+
}
|
|
320
|
+
|
|
313
321
|
// Carry attachments through when present (voice notes, media). The legacy
|
|
314
322
|
// YAML writer (scripts/poller/utils.mjs) renders these when shaped as
|
|
315
323
|
// { id, name, mimetype, size }; adapters that produce contract-style
|
package/lib/claude-bin.mjs
CHANGED
|
@@ -7,12 +7,11 @@
|
|
|
7
7
|
* fails with ENOENT — even though the operator's interactive shell finds
|
|
8
8
|
* it without trouble.
|
|
9
9
|
*
|
|
10
|
-
* Caller
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
* - any other module that spawns claude from a non-interactive context
|
|
10
|
+
* Caller: lib/runtime/adapter.mjs, the runtime adapter seam, which owns the
|
|
11
|
+
* binary, argv and env of every JS lane that starts `claude` (it lists the 17
|
|
12
|
+
* lanes). Spawn sites ask the adapter, not this module. The remaining direct
|
|
13
|
+
* readers only resolve the binary without spawning it (the daemon's
|
|
14
|
+
* `claudeAvailable()` presence check, `global-setup`'s CLI probe).
|
|
16
15
|
*/
|
|
17
16
|
|
|
18
17
|
import { existsSync, statSync } from "node:fs";
|