@cohortapp/agent-sdk 2.17.0 → 2.18.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/settings.json +18 -0
- package/.env.example +18 -5
- package/README.md +1 -0
- package/bin/maestro.mjs +62 -0
- package/docs/guides/billing-console-keys.md +60 -0
- package/docs/guides/front-door-session.md +54 -9
- package/docs/guides/mac-mini.md +20 -25
- package/docs/guides/setup-wizard.md +1 -1
- package/docs/runbooks/fleet-rollout.md +156 -0
- package/docs/runbooks/mac-mini-bootstrap.md +12 -14
- package/lib/action-executor.js +19 -3
- package/lib/budget-guard.mjs +279 -3
- package/lib/channels/base-adapter.mjs +3 -1
- package/lib/channels/contract.mjs +2 -1
- package/lib/channels/inbox-item.mjs +8 -0
- package/lib/claude-bin.mjs +5 -6
- package/lib/cli/doctor-checks.mjs +141 -10
- package/lib/cli/global-setup-extras.mjs +5 -1
- package/lib/cli/inbox.mjs +100 -15
- package/lib/cli/seat-auth.mjs +463 -0
- package/lib/cli/session.mjs +80 -12
- package/lib/collective/capture-slots.mjs +234 -0
- package/lib/collective/capture.mjs +8 -6
- package/lib/collective/config.mjs +2 -0
- package/lib/collective/global-config.mjs +63 -1
- package/lib/collective/loop-guard.mjs +155 -0
- package/lib/collective/presence.mjs +142 -5
- package/lib/comms/send-gate.mjs +559 -1
- package/lib/diagnostics/alerts.mjs +49 -0
- package/lib/diagnostics/cadence-output-freshness.mjs +288 -0
- package/lib/engine/agents/definitions.mjs +343 -0
- package/lib/engine/agents/persist.mjs +275 -0
- package/lib/engine/agents/runtime.mjs +748 -0
- package/lib/engine/agents/usage.mjs +95 -0
- package/lib/engine/auth-status.mjs +139 -0
- package/lib/engine/budget.mjs +194 -0
- package/lib/engine/cli.mjs +1204 -0
- package/lib/engine/commands/index.mjs +269 -0
- package/lib/engine/context/budget.mjs +219 -0
- package/lib/engine/context/cache.mjs +125 -0
- package/lib/engine/context/child-env.mjs +215 -0
- package/lib/engine/context/compaction.mjs +342 -0
- package/lib/engine/context/images.mjs +90 -0
- package/lib/engine/context/instructions.mjs +327 -0
- package/lib/engine/context/lazy-instructions.mjs +169 -0
- package/lib/engine/context/manager.mjs +182 -0
- package/lib/engine/context/real-path.mjs +91 -0
- package/lib/engine/context/secret-values.mjs +163 -0
- package/lib/engine/context/settings.mjs +274 -0
- package/lib/engine/context/stream-input.mjs +159 -0
- package/lib/engine/guard.mjs +152 -0
- package/lib/engine/hooks.mjs +713 -0
- package/lib/engine/loop.mjs +560 -0
- package/lib/engine/mcp/client.mjs +254 -0
- package/lib/engine/mcp/config.mjs +301 -0
- package/lib/engine/mcp/http.mjs +201 -0
- package/lib/engine/mcp/index.mjs +146 -0
- package/lib/engine/mcp/jsonrpc.mjs +147 -0
- package/lib/engine/mcp/naming.mjs +66 -0
- package/lib/engine/mcp/resources.mjs +89 -0
- package/lib/engine/mcp/results.mjs +133 -0
- package/lib/engine/mcp/stdio.mjs +137 -0
- package/lib/engine/mcp/supervisor.mjs +116 -0
- package/lib/engine/messages.mjs +104 -0
- package/lib/engine/output/json.mjs +164 -0
- package/lib/engine/output/stream-json.mjs +266 -0
- package/lib/engine/permissions.mjs +845 -0
- package/lib/engine/process-identity.mjs +164 -0
- package/lib/engine/process-tree.mjs +551 -0
- package/lib/engine/prompt.mjs +60 -0
- package/lib/engine/session/store.mjs +299 -0
- package/lib/engine/session-runtime/args.mjs +97 -0
- package/lib/engine/session-runtime/host.mjs +143 -0
- package/lib/engine/session-runtime/inbox.mjs +122 -0
- package/lib/engine/session-runtime/notifications.mjs +129 -0
- package/lib/engine/session-runtime/registry.mjs +328 -0
- package/lib/engine/session-runtime/runner.mjs +344 -0
- package/lib/engine/session-runtime/socket.mjs +212 -0
- package/lib/engine/session-runtime/wakeup.mjs +115 -0
- package/lib/engine/skills/index.mjs +321 -0
- package/lib/engine/tools/bash-background.mjs +533 -0
- package/lib/engine/tools/bash.mjs +216 -0
- package/lib/engine/tools/edit.mjs +97 -0
- package/lib/engine/tools/glob.mjs +81 -0
- package/lib/engine/tools/grep.mjs +224 -0
- package/lib/engine/tools/index.mjs +84 -0
- package/lib/engine/tools/list-agents.mjs +32 -0
- package/lib/engine/tools/ls.mjs +127 -0
- package/lib/engine/tools/monitor.mjs +82 -0
- package/lib/engine/tools/notebook-edit.mjs +218 -0
- package/lib/engine/tools/read.mjs +103 -0
- package/lib/engine/tools/schedule-wakeup.mjs +45 -0
- package/lib/engine/tools/schema.mjs +144 -0
- package/lib/engine/tools/send-message.mjs +77 -0
- package/lib/engine/tools/session.mjs +70 -0
- package/lib/engine/tools/todo.mjs +144 -0
- package/lib/engine/tools/toolsearch.mjs +217 -0
- package/lib/engine/tools/walk.mjs +193 -0
- package/lib/engine/tools/web-switch.mjs +31 -0
- package/lib/engine/tools/webfetch-html.mjs +387 -0
- package/lib/engine/tools/webfetch-net.mjs +340 -0
- package/lib/engine/tools/webfetch.mjs +198 -0
- package/lib/engine/tools/websearch.mjs +91 -0
- package/lib/engine/tools/workflow.mjs +95 -0
- package/lib/engine/tools/write.mjs +76 -0
- package/lib/engine/tui/line-editor.mjs +137 -0
- package/lib/engine/tui/render.mjs +86 -0
- package/lib/engine/tui/tui.mjs +274 -0
- package/lib/engine/wire/anthropic-messages.mjs +263 -0
- package/lib/engine/wire/effort.mjs +36 -0
- package/lib/engine/wire/errors.mjs +496 -0
- package/lib/engine/wire/http.mjs +441 -0
- package/lib/engine/wire/index.mjs +76 -0
- package/lib/engine/wire/openai-chat.mjs +332 -0
- package/lib/engine/wire/prompt-cache.mjs +79 -0
- package/lib/engine/wire/search.mjs +140 -0
- package/lib/engine/wire/sse.mjs +114 -0
- package/lib/engine/wire/stall.mjs +349 -0
- package/lib/engine/wire/token-provider.mjs +175 -0
- package/lib/engine/wire/usage.mjs +192 -0
- package/lib/engine/workflow/host.mjs +524 -0
- package/lib/engine/workflow/journal.mjs +188 -0
- package/lib/engine/workflow/json-schema.mjs +171 -0
- package/lib/engine/workflow/meta.mjs +329 -0
- package/lib/engine/workflow/notifications.mjs +52 -0
- package/lib/engine/workflow/runtime.mjs +447 -0
- package/lib/engine/workflow/sandbox.mjs +534 -0
- package/lib/engine/workflow/worker.mjs +141 -0
- package/lib/engine/workflow/worktree.mjs +74 -0
- package/lib/execution/disposition.mjs +1 -1
- package/lib/execution/intake.mjs +10 -0
- package/lib/execution/surface-policy.mjs +15 -0
- package/lib/learning/curator.mjs +8 -6
- package/lib/learning/reflect.mjs +8 -6
- package/lib/model-router/catalog/cohort.yaml +137 -0
- package/lib/model-router/catalog.mjs +118 -1
- package/lib/model-router/failover.mjs +67 -16
- package/lib/model-router/llm-task.mjs +39 -3
- package/lib/model-router/resolve.mjs +89 -3
- package/lib/model-router/spawn.mjs +46 -47
- package/lib/model-router/taxonomy.mjs +126 -4
- package/lib/org/cost-sync.mjs +141 -11
- package/lib/org/inbound/broadcast.mjs +289 -0
- package/lib/org/inbound/collective.mjs +375 -0
- package/lib/org/inbound/directedness.mjs +96 -8
- package/lib/org/inbound/facts.mjs +78 -2
- package/lib/org/inbound/project.mjs +22 -0
- package/lib/org/inbound/surfaces.mjs +14 -0
- package/lib/org/llm-token.mjs +879 -0
- package/lib/org/mesh.mjs +61 -0
- package/lib/org/messaging.mjs +3 -1
- package/lib/org/protocol.checksum +1 -1
- package/lib/org/protocol.mjs +15 -0
- package/lib/org/quota.mjs +520 -0
- package/lib/org/tool-surface.mjs +104 -16
- package/lib/org/ui-parity.mjs +16 -1
- package/lib/org/work-ledger.mjs +37 -6
- package/lib/rate-guard.mjs +114 -1
- package/lib/resource-governor.mjs +41 -6
- package/lib/runtime/adapter.mjs +833 -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 +229 -11
- package/lib/upgrade/ignored-drift.mjs +105 -0
- package/lib/voice/post-call-brief.mjs +30 -17
- package/package.json +13 -3
- package/plugins/maestro-skills/skills/board-work.md +5 -0
- package/plugins/maestro-skills/skills/inbound-triage.md +56 -15
- package/plugins/maestro-skills/skills/main-session.md +18 -7
- package/scaffold/config/collective.yaml +7 -0
- package/scripts/ci/check-durable-write-seam.mjs +3 -1
- 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 +142 -19
- package/scripts/continuous-monitor.sh +13 -0
- package/scripts/cost/track-claude-usage.mjs +15 -0
- package/scripts/daemon/agent-daemon.mjs +408 -20
- package/scripts/daemon/assurance.mjs +48 -12
- package/scripts/daemon/cadence-consumer.mjs +218 -68
- package/scripts/daemon/cadence-handlers.mjs +73 -4
- package/scripts/daemon/classifier.mjs +75 -26
- package/scripts/daemon/context-compiler.mjs +51 -37
- package/scripts/daemon/deliver.mjs +30 -1
- package/scripts/daemon/dispatcher.mjs +595 -149
- package/scripts/daemon/health.mjs +14 -1
- package/scripts/daemon/maestro-daemon.mjs +11 -0
- package/scripts/daemon/prompt-builder.mjs +24 -0
- package/scripts/daemon/responder.mjs +246 -79
- package/scripts/daemon/sdk-version.mjs +98 -16
- package/scripts/eval/probe-gateway.mjs +635 -0
- package/scripts/eval/replay/extract.mjs +270 -0
- package/scripts/eval/replay/grade.mjs +260 -0
- package/scripts/eval/replay/lib/config.mjs +50 -0
- package/scripts/eval/replay/lib/effects.mjs +65 -0
- package/scripts/eval/replay/lib/fixture.mjs +188 -0
- package/scripts/eval/replay/lib/judge.mjs +72 -0
- package/scripts/eval/replay/lib/redact.mjs +136 -0
- package/scripts/eval/replay/lib/sandbox.mjs +170 -0
- package/scripts/eval/replay/lib/schema-check.mjs +63 -0
- package/scripts/eval/replay/lib/transcript.mjs +76 -0
- package/scripts/eval/replay/mcp-replay-stub.mjs +101 -0
- package/scripts/eval/replay/report.mjs +185 -0
- package/scripts/eval/replay/run.mjs +404 -0
- package/scripts/fleet/rollout.mjs +1151 -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 +465 -81
- package/scripts/local-triggers/run-trigger.sh +13 -0
- package/scripts/maintenance/pin-integrity.mjs +364 -0
- package/scripts/poll-slack-events.sh +41 -9
- package/scripts/poller/slack-socket-mode.mjs +28 -3
- package/scripts/session/supervisor.mjs +80 -13
- package/scripts/spawn-session.sh +13 -0
- package/bin/maestro.test.mjs +0 -1574
- package/lib/action-executor.test.mjs +0 -871
- package/lib/archetype.test.mjs +0 -132
- package/lib/assurance/plan-note.test.mjs +0 -234
- package/lib/assurance/room-budget.test.mjs +0 -486
- package/lib/assurance/tier.test.mjs +0 -174
- package/lib/autonomy.test.mjs +0 -66
- package/lib/backlog.test.mjs +0 -302
- package/lib/backup/policy.test.mjs +0 -305
- package/lib/budget-escalate.test.mjs +0 -232
- package/lib/budget-guard.envelope.test.mjs +0 -476
- package/lib/budget-guard.test.mjs +0 -427
- package/lib/cadence-bus-requeue.test.mjs +0 -83
- package/lib/cadence-bus-schedule.test.mjs +0 -194
- package/lib/cadence-bus.test.mjs +0 -720
- package/lib/cadences.test.mjs +0 -230
- package/lib/capability/inventory.test.mjs +0 -232
- package/lib/capability.test.mjs +0 -78
- package/lib/channels/base-adapter.test.mjs +0 -590
- package/lib/channels/channels.test.mjs +0 -371
- package/lib/channels/contract.test.mjs +0 -162
- package/lib/channels/inbox-item.test.mjs +0 -368
- package/lib/channels/orgmail/adapter.test.mjs +0 -448
- package/lib/channels/pairing.test.mjs +0 -270
- package/lib/channels/repeat-suppressor.test.mjs +0 -134
- package/lib/channels/slack-adapter.test.mjs +0 -212
- package/lib/channels/telegram-adapter.test.mjs +0 -306
- package/lib/channels/voice/adapter.test.mjs +0 -278
- package/lib/channels/whatsapp/adapter-baileys.test.mjs +0 -359
- package/lib/channels/whatsapp/baileys-typing.test.mjs +0 -154
- package/lib/charter.test.mjs +0 -89
- package/lib/claude-bin.test.mjs +0 -131
- package/lib/cli/board.test.mjs +0 -227
- package/lib/cli/design.test.mjs +0 -270
- package/lib/cli/doctor-checks.test.mjs +0 -336
- package/lib/cli/global-setup-extras.test.mjs +0 -462
- package/lib/cli/inbox.test.mjs +0 -230
- package/lib/cli/session-ack.test.mjs +0 -63
- package/lib/cli/session.test.mjs +0 -613
- package/lib/collective/capture.test.mjs +0 -121
- package/lib/collective/cards.test.mjs +0 -114
- package/lib/collective/config.test.mjs +0 -123
- package/lib/collective/global-config.test.mjs +0 -220
- package/lib/collective/global-skills.test.mjs +0 -126
- package/lib/collective/presence.test.mjs +0 -95
- package/lib/collective/recall.test.mjs +0 -116
- package/lib/collective/vendor-skills.test.mjs +0 -306
- package/lib/comms/send-gate.test.mjs +0 -770
- package/lib/comms.test.mjs +0 -41
- package/lib/context/budget.test.mjs +0 -252
- package/lib/context/history-scope.test.mjs +0 -79
- package/lib/cost/ledger-row.test.mjs +0 -183
- package/lib/design/design-md.test.mjs +0 -318
- package/lib/design/fixtures/DESIGN.golden.md +0 -238
- package/lib/design/fixtures/PRODUCT.golden.md +0 -67
- package/lib/design/fixtures/foundation.json +0 -133
- package/lib/design/refresh-gate.test.mjs +0 -144
- package/lib/design/write.test.mjs +0 -241
- package/lib/diagnostics/alerts.test.mjs +0 -318
- package/lib/diagnostics/backup-freshness.test.mjs +0 -185
- package/lib/diagnostics/counters.test.mjs +0 -206
- package/lib/diagnostics/events.test.mjs +0 -290
- package/lib/diagnostics/otel.test.mjs +0 -196
- package/lib/diagnostics/trace.test.mjs +0 -251
- package/lib/env-compat.test.mjs +0 -104
- package/lib/execution/disposition.test.mjs +0 -553
- package/lib/execution/drive.test.mjs +0 -270
- package/lib/execution/effects.test.mjs +0 -344
- package/lib/execution/intake.test.mjs +0 -389
- package/lib/execution/journal.test.mjs +0 -261
- package/lib/execution/match.test.mjs +0 -235
- package/lib/execution/pipeline.test.mjs +0 -392
- package/lib/execution/route.test.mjs +0 -186
- package/lib/execution/surface-policy.test.mjs +0 -162
- package/lib/fs-atomic.test.mjs +0 -72
- package/lib/fs-ownership.test.mjs +0 -158
- package/lib/goals/admission.test.mjs +0 -164
- package/lib/goals/classify.test.mjs +0 -167
- package/lib/goals/collaborate.test.mjs +0 -336
- package/lib/goals/gaps.test.mjs +0 -284
- package/lib/goals/loop.test.mjs +0 -845
- package/lib/hooks/bus.test.mjs +0 -387
- package/lib/identity/persona.test.mjs +0 -142
- package/lib/kpi-sensors.test.mjs +0 -278
- package/lib/kpi.test.mjs +0 -244
- package/lib/learning/config.test.mjs +0 -75
- package/lib/learning/counters.test.mjs +0 -69
- package/lib/learning/curator-consolidate.test.mjs +0 -238
- package/lib/learning/curator.test.mjs +0 -106
- package/lib/learning/reflect.test.mjs +0 -0
- package/lib/learning/session-index.test.mjs +0 -125
- package/lib/learning/skill-writer.test.mjs +0 -210
- package/lib/mandate/audit.test.mjs +0 -195
- package/lib/mandate/contract.test.mjs +0 -185
- package/lib/mandate/derive.test.mjs +0 -274
- package/lib/mandate/model.test.mjs +0 -164
- package/lib/mandate/refresh.test.mjs +0 -389
- package/lib/mcp/server.test.mjs +0 -426
- package/lib/model-router/auth-profiles.test.mjs +0 -580
- package/lib/model-router/catalog.test.mjs +0 -385
- package/lib/model-router/economics.test.mjs +0 -438
- package/lib/model-router/failover.test.mjs +0 -439
- package/lib/model-router/health.test.mjs +0 -338
- package/lib/model-router/integration-coverage.test.mjs +0 -831
- package/lib/model-router/integration.test.mjs +0 -564
- package/lib/model-router/ledger.test.mjs +0 -415
- package/lib/model-router/llm-task.test.mjs +0 -392
- package/lib/model-router/org-credentials.test.mjs +0 -265
- package/lib/model-router/pricing-refresh.test.mjs +0 -286
- package/lib/model-router/reconcile.test.mjs +0 -316
- package/lib/model-router/repair.test.mjs +0 -180
- package/lib/model-router/spawn.test.mjs +0 -446
- package/lib/model-router/taxonomy.test.mjs +0 -410
- package/lib/model-router.test.mjs +0 -1207
- package/lib/org/activity.test.mjs +0 -134
- package/lib/org/approvals.test.mjs +0 -216
- package/lib/org/awareness.test.mjs +0 -159
- package/lib/org/board-mine-cache.test.mjs +0 -53
- package/lib/org/board.test.mjs +0 -187
- package/lib/org/bootstrap-context.test.mjs +0 -153
- package/lib/org/client.test.mjs +0 -1206
- package/lib/org/cohort-client.test.mjs +0 -126
- package/lib/org/cost-sync.test.mjs +0 -153
- package/lib/org/doctor.test.mjs +0 -346
- package/lib/org/engagement-ledger.test.mjs +0 -112
- package/lib/org/engagement.test.mjs +0 -739
- package/lib/org/handoff.test.mjs +0 -269
- package/lib/org/inbound/directedness.test.mjs +0 -668
- package/lib/org/inbound/facts.test.mjs +0 -471
- package/lib/org/inbound/hydrate.test.mjs +0 -908
- package/lib/org/inbound/index.test.mjs +0 -429
- package/lib/org/inbound/project.test.mjs +0 -287
- package/lib/org/integration-tools.test.mjs +0 -160
- package/lib/org/keys.test.mjs +0 -92
- package/lib/org/knowledge.test.mjs +0 -326
- package/lib/org/leases.test.mjs +0 -235
- package/lib/org/mesh-directives.test.mjs +0 -110
- package/lib/org/mesh-integration.test.mjs +0 -127
- package/lib/org/mesh.test.mjs +0 -400
- package/lib/org/messaging.test.mjs +0 -471
- package/lib/org/param-contract.test.mjs +0 -477
- package/lib/org/policy.test.mjs +0 -237
- package/lib/org/protocol.checksum.test.mjs +0 -90
- package/lib/org/protocol.test.mjs +0 -323
- package/lib/org/push.test.mjs +0 -792
- package/lib/org/registry.test.mjs +0 -100
- package/lib/org/resource-tools.test.mjs +0 -361
- package/lib/org/tool-access.test.mjs +0 -144
- package/lib/org/tool-surface-integration.test.mjs +0 -120
- package/lib/org/tool-surface.test.mjs +0 -1268
- package/lib/org/typing.test.mjs +0 -291
- package/lib/org/ui-parity.test.mjs +0 -560
- package/lib/org/verify.test.mjs +0 -194
- package/lib/org/work-ledger.test.mjs +0 -273
- package/lib/plan/adoption-e2e.test.mjs +0 -366
- package/lib/plan/budget-enforcement.test.mjs +0 -400
- package/lib/plan/compile.test.mjs +0 -382
- package/lib/plan/emit.test.mjs +0 -269
- package/lib/plan/explain.test.mjs +0 -188
- package/lib/prompts/parallelism.test.mjs +0 -177
- package/lib/rag/rag.test.mjs +0 -505
- package/lib/rate-guard.test.mjs +0 -272
- package/lib/reactive-gate.test.mjs +0 -57
- package/lib/render.test.mjs +0 -68
- package/lib/resource-governor.test.mjs +0 -488
- package/lib/scheduling/dynamic-jobs.test.mjs +0 -344
- package/lib/scheduling/jitter.test.mjs +0 -140
- package/lib/secrets/broker.test.mjs +0 -280
- package/lib/secrets/providers.test.mjs +0 -274
- package/lib/security/audit-engine.test.mjs +0 -424
- package/lib/security/coerce-args.test.mjs +0 -281
- package/lib/security/dangerous-tools.test.mjs +0 -68
- package/lib/security/external-content.test.mjs +0 -84
- package/lib/security/redact.test.mjs +0 -441
- package/lib/security/secret-equal.test.mjs +0 -55
- package/lib/session/config.test.mjs +0 -92
- package/lib/session/feed-core.test.mjs +0 -198
- package/lib/session/first-run.test.mjs +0 -121
- package/lib/session/frontdoor.test.mjs +0 -205
- package/lib/session/handoffs.test.mjs +0 -183
- package/lib/session/identity.test.mjs +0 -180
- package/lib/session/inbox-claims.test.mjs +0 -286
- package/lib/session/launch-args.test.mjs +0 -157
- package/lib/session/liveness.test.mjs +0 -100
- package/lib/session/status-summary.test.mjs +0 -118
- package/lib/session-permissions.test.mjs +0 -120
- package/lib/setup/claude-probe.test.mjs +0 -187
- package/lib/setup/completeness.test.mjs +0 -110
- package/lib/setup/context-pack.test.mjs +0 -89
- package/lib/setup/enrich.test.mjs +0 -115
- package/lib/setup/enroll-from-cohort.test.mjs +0 -300
- package/lib/setup/integration.test.mjs +0 -162
- package/lib/setup/io.test.mjs +0 -77
- package/lib/setup/runner.test.mjs +0 -132
- package/lib/setup/sections/identity.test.mjs +0 -234
- package/lib/setup/sections/inventory.test.mjs +0 -198
- package/lib/setup/sections/learning.test.mjs +0 -81
- package/lib/setup/sections/mandate.test.mjs +0 -388
- package/lib/setup/sections/messaging.test.mjs +0 -127
- package/lib/setup/sections/model.test.mjs +0 -240
- package/lib/setup/sections/org.test.mjs +0 -346
- package/lib/setup/sections/orgmail.test.mjs +0 -118
- package/lib/setup/sections/recovery.test.mjs +0 -98
- package/lib/setup/sections/subagents.test.mjs +0 -429
- package/lib/setup/sections/verify.test.mjs +0 -175
- package/lib/setup/sot.test.mjs +0 -81
- package/lib/setup/state.test.mjs +0 -115
- package/lib/singleton.test.mjs +0 -151
- package/lib/subagents/cli.test.mjs +0 -389
- package/lib/subagents/client.test.mjs +0 -309
- package/lib/subagents/gap.test.mjs +0 -234
- package/lib/subagents/lock.test.mjs +0 -248
- package/lib/subagents/manifest.test.mjs +0 -175
- package/lib/subagents/refs.test.mjs +0 -204
- package/lib/subagents/resolve.test.mjs +0 -422
- package/lib/subagents/schema.test.mjs +0 -328
- package/lib/telemetry/alerts.test.mjs +0 -109
- package/lib/telemetry/collect.test.mjs +0 -1274
- package/lib/tool-definitions-integration.test.mjs +0 -83
- package/lib/tool-definitions.test.mjs +0 -437
- package/lib/upgrade/global-refresh.test.mjs +0 -65
- package/lib/upgrade/launchd-reconcile.test.mjs +0 -272
- package/lib/upgrade/post-steps.test.mjs +0 -200
- package/lib/upgrade/verify.test.mjs +0 -164
- package/lib/util/fetch-timeout.test.mjs +0 -202
- package/lib/util/reconnect.test.mjs +0 -369
- package/lib/util/unhandled.test.mjs +0 -216
- package/lib/voice/outbound.test.mjs +0 -69
- package/lib/voice/session-rotation.test.mjs +0 -114
- package/lib/voice/stt.test.mjs +0 -226
- package/lib/voice/voice.test.mjs +0 -990
- package/scripts/cadence/enqueue-cadence-tick.test.mjs +0 -187
- package/scripts/ci/check-docs-accuracy.test.mjs +0 -409
- package/scripts/ci/check-durable-write-seam.test.mjs +0 -90
- package/scripts/ci/check-no-build-artifacts.test.mjs +0 -71
- package/scripts/ci/check-no-residual-identity.test.mjs +0 -202
- package/scripts/ci/check-skill-packs.test.mjs +0 -495
- package/scripts/ci/check-subagent-frontmatter.test.mjs +0 -124
- package/scripts/ci/check.test.mjs +0 -194
- package/scripts/ci/conformance-org-api.test.mjs +0 -425
- package/scripts/cloud-relay/voice/relay-identity.test.mjs +0 -96
- package/scripts/collective/hook-runner.test.mjs +0 -173
- package/scripts/cost/fleet-digest.test.mjs +0 -207
- package/scripts/cost/track-claude-usage-pricing.test.mjs +0 -183
- package/scripts/cost/track-claude-usage.test.mjs +0 -148
- package/scripts/daemon/agent-daemon-board-mine.test.mjs +0 -96
- package/scripts/daemon/agent-daemon-design.test.mjs +0 -238
- package/scripts/daemon/agent-daemon-frontdoor.test.mjs +0 -60
- package/scripts/daemon/agent-daemon.test.mjs +0 -995
- package/scripts/daemon/assurance-e2e.test.mjs +0 -613
- package/scripts/daemon/assurance.test.mjs +0 -1791
- package/scripts/daemon/board-mirror.test.mjs +0 -165
- package/scripts/daemon/cadence-consumer-frontdoor.test.mjs +0 -393
- package/scripts/daemon/cadence-consumer-governance.test.mjs +0 -276
- package/scripts/daemon/cadence-consumer.test.mjs +0 -776
- package/scripts/daemon/cadence-handlers.test.mjs +0 -837
- package/scripts/daemon/classifier-identity.test.mjs +0 -137
- package/scripts/daemon/classifier.test.mjs +0 -266
- package/scripts/daemon/classify-kind.test.mjs +0 -40
- package/scripts/daemon/context-compiler.test.mjs +0 -406
- package/scripts/daemon/deliver.test.mjs +0 -564
- package/scripts/daemon/dispatcher-cooldown.test.mjs +0 -122
- package/scripts/daemon/dispatcher-governance.test.mjs +0 -1013
- package/scripts/daemon/dispatcher-resume.test.mjs +0 -166
- package/scripts/daemon/dispatcher-session-continuity.test.mjs +0 -365
- package/scripts/daemon/execution-ladder.test.mjs +0 -470
- package/scripts/daemon/goal-steward-cadence.test.mjs +0 -312
- package/scripts/daemon/inbox-deferral-session.test.mjs +0 -49
- package/scripts/daemon/inbox-deferral.test.mjs +0 -336
- package/scripts/daemon/inbox-wake.test.mjs +0 -199
- package/scripts/daemon/integration.test.mjs +0 -149
- package/scripts/daemon/lib/self-echo.test.mjs +0 -153
- package/scripts/daemon/lib/session-router.test.mjs +0 -554
- package/scripts/daemon/prompt-builder-preamble.test.mjs +0 -210
- package/scripts/daemon/prompt-builder.test.mjs +0 -556
- package/scripts/daemon/responder-cost.test.mjs +0 -68
- package/scripts/daemon/responder-history.test.mjs +0 -221
- package/scripts/daemon/sdk-version.test.mjs +0 -31
- package/scripts/daemon/session-lock.test.mjs +0 -252
- package/scripts/daemon/session-outcomes.test.mjs +0 -533
- package/scripts/daemon/typing-registry.test.mjs +0 -102
- package/scripts/hooks/pre-send-audit.test.mjs +0 -354
- package/scripts/huddle/huddle-prompt.test.mjs +0 -176
- package/scripts/local-triggers/autoupdate.test.mjs +0 -518
- package/scripts/local-triggers/generate-plists.test.mjs +0 -456
- package/scripts/media-generation/brand-clause.test.mjs +0 -135
- package/scripts/org/send-orgmail.first-contact.test.mjs +0 -102
- package/scripts/poller/inbox-privilege-injection.test.mjs +0 -167
- package/scripts/poller/inbox-scan-poller.test.mjs +0 -295
- package/scripts/poller/lib/cloud-relay-dedup.test.mjs +0 -133
- package/scripts/poller/slack-socket-mode.test.mjs +0 -805
- package/scripts/poller-launchd/install.test.mjs +0 -243
- package/scripts/restore-from-backup.test.mjs +0 -181
- package/scripts/session/feed.test.mjs +0 -196
- package/scripts/session/supervisor-sh.test.mjs +0 -218
- package/scripts/session/supervisor.test.mjs +0 -482
- package/scripts/setup/configure-macos.test.mjs +0 -306
- package/scripts/setup/gen-subagent-manifest.test.mjs +0 -124
- package/scripts/setup/generate-agent-package-json.test.mjs +0 -143
- package/scripts/setup/generate-capability.test.mjs +0 -134
- package/scripts/setup/init-agent.test.mjs +0 -370
- package/scripts/setup/init-skill-marketplace.test.mjs +0 -193
- package/scripts/vendor/sync-skill-packs.test.mjs +0 -103
- package/scripts/watchdog/memory-watchdog.test.mjs +0 -64
|
@@ -0,0 +1,496 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* lib/engine/wire/errors.mjs — typed wire errors and cohort response headers (pure).
|
|
3
|
+
*
|
|
4
|
+
* The gateway's error contract (hq docs/cohort-llm/design.md §4.6) separates a
|
|
5
|
+
* HARD REFUSAL — a seat window exhausted, credits gone, a spend limit hit, an
|
|
6
|
+
* unpriced model — from provider overload. A refusal must never be retried:
|
|
7
|
+
* retrying a 429 `cohort_limit_exceeded` ten times is exactly the failure the
|
|
8
|
+
* stock binary shows (design C6). So classification here is the one place that
|
|
9
|
+
* decides `retryable`, and the client obeys it.
|
|
10
|
+
*
|
|
11
|
+
* Precedence for `retryable`:
|
|
12
|
+
* 1. a known non-retryable cohort code → false (whatever the headers say)
|
|
13
|
+
* 2. `x-should-retry: false|true` → that
|
|
14
|
+
* 3. HTTP class: 408/409/429/5xx/529 retryable; other 4xx not
|
|
15
|
+
*
|
|
16
|
+
* Errors are VALUES: `{ kind, code, status, retryable, message, ... }`. Nothing
|
|
17
|
+
* here throws.
|
|
18
|
+
*
|
|
19
|
+
* @module lib/engine/wire/errors
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
import { readFrameUsage } from "./usage.mjs";
|
|
23
|
+
|
|
24
|
+
/** Codes the gateway emits that must never be retried. */
|
|
25
|
+
export const NON_RETRYABLE_CODES = new Set([
|
|
26
|
+
"cohort_limit_exceeded",
|
|
27
|
+
"cohort_credits_exhausted",
|
|
28
|
+
"cohort_spend_limit_reached",
|
|
29
|
+
"cohort_unpriced_model",
|
|
30
|
+
"cohort_seat_suspended",
|
|
31
|
+
]);
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* @typedef {Object} WireError
|
|
35
|
+
* @property {'refused'|'auth'|'invalid_request'|'rate_limited'|'overloaded'|'server'|'timeout'|'network'|'aborted'|'protocol'} kind
|
|
36
|
+
* @property {string} code stable machine code (cohort code when present)
|
|
37
|
+
* @property {number|null} status HTTP status, null for transport faults
|
|
38
|
+
* @property {boolean} retryable
|
|
39
|
+
* @property {string} message
|
|
40
|
+
* @property {number|null} retryAfterMs
|
|
41
|
+
* @property {string|null} [window] '5h' | '7d' | '30d' for cohort_limit_exceeded
|
|
42
|
+
* @property {string|null} [resetsAt] ISO timestamp when the refusal lifts
|
|
43
|
+
* @property {string|null} [scope] spend-limit scope
|
|
44
|
+
* @property {string|null} [requestId]
|
|
45
|
+
*/
|
|
46
|
+
|
|
47
|
+
/** @param {any} headers @param {string} name @returns {string|null} */
|
|
48
|
+
export function header(headers, name) {
|
|
49
|
+
if (!headers) return null;
|
|
50
|
+
if (typeof headers.get === "function") return headers.get(name);
|
|
51
|
+
const key = Object.keys(headers).find((k) => k.toLowerCase() === name);
|
|
52
|
+
if (key === undefined) return null;
|
|
53
|
+
const v = headers[key];
|
|
54
|
+
return Array.isArray(v) ? v.join(", ") : v == null ? null : String(v);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Parse `retry-after` (seconds or HTTP-date) to milliseconds.
|
|
59
|
+
* @param {string|null} v @param {number} nowMs @returns {number|null}
|
|
60
|
+
*/
|
|
61
|
+
export function parseRetryAfter(v, nowMs) {
|
|
62
|
+
if (v == null || v === "") return null;
|
|
63
|
+
const secs = Number(v);
|
|
64
|
+
if (Number.isFinite(secs)) return Math.max(0, Math.round(secs * 1000));
|
|
65
|
+
const at = Date.parse(v);
|
|
66
|
+
return Number.isFinite(at) ? Math.max(0, at - nowMs) : null;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Classify a non-2xx HTTP response.
|
|
71
|
+
*
|
|
72
|
+
* @param {number} status
|
|
73
|
+
* @param {any} headers WHATWG Headers or a plain object
|
|
74
|
+
* @param {string} bodyText
|
|
75
|
+
* @param {number} nowMs for HTTP-date retry-after
|
|
76
|
+
* @returns {WireError}
|
|
77
|
+
*/
|
|
78
|
+
export function classifyHttpError(status, headers, bodyText, nowMs) {
|
|
79
|
+
let body = null;
|
|
80
|
+
try {
|
|
81
|
+
body = bodyText ? JSON.parse(bodyText) : null;
|
|
82
|
+
} catch {
|
|
83
|
+
/* a non-JSON error body (an HTML proxy page) still classifies by status */
|
|
84
|
+
}
|
|
85
|
+
const err = body && typeof body === "object" ? body.error ?? body : {};
|
|
86
|
+
const cohort = body && typeof body === "object" && body.cohort && typeof body.cohort === "object" ? body.cohort : {};
|
|
87
|
+
const type = typeof err.type === "string" ? err.type : null;
|
|
88
|
+
const rawCode =
|
|
89
|
+
(typeof err.code === "string" && err.code) || (typeof cohort.code === "string" && cohort.code) || null;
|
|
90
|
+
const message =
|
|
91
|
+
(typeof err.message === "string" && err.message) ||
|
|
92
|
+
(typeof bodyText === "string" && bodyText.slice(0, 300)) ||
|
|
93
|
+
`HTTP ${status}`;
|
|
94
|
+
|
|
95
|
+
const shouldRetry = header(headers, "x-should-retry");
|
|
96
|
+
const retryAfterMs = parseRetryAfter(header(headers, "retry-after"), nowMs);
|
|
97
|
+
|
|
98
|
+
let kind;
|
|
99
|
+
if (rawCode && NON_RETRYABLE_CODES.has(rawCode)) kind = "refused";
|
|
100
|
+
else if (status === 401 || status === 403) kind = "auth";
|
|
101
|
+
else if (status === 429) kind = "rate_limited";
|
|
102
|
+
else if (status === 529 || type === "overloaded_error") kind = "overloaded";
|
|
103
|
+
else if (status >= 500) kind = "server";
|
|
104
|
+
else if (status === 402) kind = "refused";
|
|
105
|
+
else if (status === 408) kind = "timeout"; // the gateway's body idle timeout: transient, the body never arrived
|
|
106
|
+
else kind = "invalid_request";
|
|
107
|
+
|
|
108
|
+
let retryable;
|
|
109
|
+
if (rawCode && NON_RETRYABLE_CODES.has(rawCode)) retryable = false;
|
|
110
|
+
else if (shouldRetry === "false") retryable = false;
|
|
111
|
+
else if (shouldRetry === "true") retryable = true;
|
|
112
|
+
else retryable = status === 408 || status === 409 || status === 429 || status >= 500;
|
|
113
|
+
|
|
114
|
+
const code = rawCode || type || `http_${status}`;
|
|
115
|
+
return {
|
|
116
|
+
kind,
|
|
117
|
+
code,
|
|
118
|
+
status,
|
|
119
|
+
retryable,
|
|
120
|
+
message,
|
|
121
|
+
retryAfterMs,
|
|
122
|
+
window: pick(err.window, cohort.window),
|
|
123
|
+
// §4.8 names it resetsAt; §4.6 and the earliest gateway bodies say
|
|
124
|
+
// resets_at. Both are accepted; the error carries resetsAt.
|
|
125
|
+
resetsAt: pick(err.resetsAt, err.resets_at, cohort.resetsAt, cohort.resets_at),
|
|
126
|
+
scope: pick(err.scope, cohort.scope),
|
|
127
|
+
requestId: header(headers, "x-cohort-request-id") ?? pick(cohort.requestId, cohort.request_id),
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Transient codes the gateway uses for ITS OWN capacity or availability, not
|
|
133
|
+
* the tenant's credential or quota (hq apps/llm-gateway README, "Pre-admission
|
|
134
|
+
* limits and provider faults"; design §4.6):
|
|
135
|
+
* cohort_concurrency_exceeded 429, retry-after: 2 — the per-principal
|
|
136
|
+
* in-flight cap or the replica's body budget
|
|
137
|
+
* cohort_unavailable 503, x-should-retry: true — Cohort's own
|
|
138
|
+
* provider key was refused or unfunded upstream
|
|
139
|
+
* cohort_upstream_overloaded 529 — every leg saturated
|
|
140
|
+
*/
|
|
141
|
+
export const GATEWAY_TRANSIENT_CODES = new Set(["cohort_concurrency_exceeded", "cohort_unavailable", "cohort_upstream_overloaded"]);
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* @typedef {Object} GatewayDisposition
|
|
145
|
+
* @property {'retry'|'refresh_token'|'fail'} action
|
|
146
|
+
* retry back off (retry-after when given, else the backoff ladder) and send again
|
|
147
|
+
* refresh_token ask the token function for a replacement of the rejected token (a 401)
|
|
148
|
+
* fail hand the error back
|
|
149
|
+
* @property {'refusal'|'concurrency'|'unavailable'|'overloaded'|'timeout'|'auth'|'server'|'network'|'aborted'|'protocol'|'invalid'} class
|
|
150
|
+
* @property {boolean} honourRetryAfter sleep the server's retry-after before the retry
|
|
151
|
+
* @property {boolean} remint may mint a new seat token (a 401 only; never a 429/503)
|
|
152
|
+
* @property {boolean} coolsCredential always false: no gateway answer proves the seat's credential bad
|
|
153
|
+
* @property {boolean} failover always false: the gateway is the only route; nothing to fail over to
|
|
154
|
+
*/
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* What a gateway client does with a wire error. PURE and total: every
|
|
158
|
+
* WireError (and null) maps to exactly one row. The single decision point for
|
|
159
|
+
* wire/http.mjs, so a new gateway code is classified here or nowhere.
|
|
160
|
+
*
|
|
161
|
+
* The load-bearing rows:
|
|
162
|
+
* 429 cohort_concurrency_exceeded → retry, honouring retry-after; no mint, no cooling
|
|
163
|
+
* 503 cohort_unavailable (retryable) → retry with backoff; NOT an auth failure, no mint
|
|
164
|
+
* 408 → retry (transient: the request body stalled)
|
|
165
|
+
* 402/429 hard refusals → fail on the first response, whatever retry-after says
|
|
166
|
+
* 401 → refresh_token (the caller bounds it: once per request,
|
|
167
|
+
* and the token provider caps re-mints per window)
|
|
168
|
+
*
|
|
169
|
+
* @param {WireError|null|undefined} e
|
|
170
|
+
* @returns {GatewayDisposition}
|
|
171
|
+
*/
|
|
172
|
+
export function gatewayDisposition(e) {
|
|
173
|
+
const row = (action, cls) => ({
|
|
174
|
+
action,
|
|
175
|
+
class: cls,
|
|
176
|
+
honourRetryAfter: action === "retry",
|
|
177
|
+
remint: action === "refresh_token",
|
|
178
|
+
coolsCredential: false,
|
|
179
|
+
failover: false,
|
|
180
|
+
});
|
|
181
|
+
if (!e || typeof e !== "object") return row("fail", "invalid");
|
|
182
|
+
const retry = e.retryable === true ? "retry" : "fail";
|
|
183
|
+
if (e.kind === "refused" || (typeof e.code === "string" && NON_RETRYABLE_CODES.has(e.code))) return row("fail", "refusal");
|
|
184
|
+
if (e.kind === "aborted") return row("fail", "aborted");
|
|
185
|
+
if (e.kind === "protocol") return row("fail", "protocol");
|
|
186
|
+
if (e.kind === "network") return row(retry, "network");
|
|
187
|
+
if (e.status === 401) return row("refresh_token", "auth");
|
|
188
|
+
if (e.code === "cohort_concurrency_exceeded") return row(retry, "concurrency");
|
|
189
|
+
if (e.code === "cohort_unavailable") return row(retry, "unavailable");
|
|
190
|
+
if (e.kind === "overloaded" || e.code === "cohort_upstream_overloaded") return row(retry, "overloaded");
|
|
191
|
+
if (e.status === 408 || e.kind === "timeout") return row(retry, "timeout");
|
|
192
|
+
if (e.kind === "auth") return row("fail", "auth");
|
|
193
|
+
if (typeof e.status === "number" && e.status >= 500) return row(retry, "server");
|
|
194
|
+
if (e.kind === "rate_limited") return row(retry, "concurrency");
|
|
195
|
+
return row(retry, "invalid");
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* The §4.8 refusal object for a wire error that carries a cohort code, or null.
|
|
200
|
+
* This is what the headless result emits as `cohort.refusal`, so maestro's
|
|
201
|
+
* rate-guard (`classifyCohortRefusal`) reads one shape from the engine and
|
|
202
|
+
* from a gateway body alike.
|
|
203
|
+
* @param {WireError|null|undefined} e
|
|
204
|
+
* @returns {{code:string, window:string|null, scope:string|null, resetsAt:string|null, retryable:boolean, requestId:string|null}|null}
|
|
205
|
+
*/
|
|
206
|
+
export function refusalFromWireError(e) {
|
|
207
|
+
if (!e || typeof e !== "object" || typeof e.code !== "string" || !e.code.startsWith("cohort_")) return null;
|
|
208
|
+
return {
|
|
209
|
+
code: e.code,
|
|
210
|
+
window: e.window ?? null,
|
|
211
|
+
scope: e.scope ?? null,
|
|
212
|
+
resetsAt: e.resetsAt ?? null,
|
|
213
|
+
retryable: e.retryable === true,
|
|
214
|
+
requestId: e.requestId ?? null,
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
function pick(...vals) {
|
|
219
|
+
for (const v of vals) if (typeof v === "string" && v !== "") return v;
|
|
220
|
+
return null;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* A token function that could not produce a gateway token. PURE.
|
|
225
|
+
*
|
|
226
|
+
* Normally a configuration failure (no key, a refused key, the re-mint cap):
|
|
227
|
+
* `token_unavailable`, kind `auth`, not retryable. But when the token helper
|
|
228
|
+
* reports that the gateway's token endpoint was itself unavailable (maestro's
|
|
229
|
+
* api-key-helper prints `…: cohort_unavailable: …` after its own bounded
|
|
230
|
+
* retries, CF-143), the seat's credential is fine and the gateway is down: that
|
|
231
|
+
* is `cohort_unavailable`, kind `server`, retryable. The engine does not resend
|
|
232
|
+
* it (the helper already retried, and a resend would run the helper again); the
|
|
233
|
+
* run ends with a `cohort_unavailable` refusal that maestro holds briefly
|
|
234
|
+
* instead of reading it as a credential failure.
|
|
235
|
+
*
|
|
236
|
+
* @param {unknown} e @returns {WireError}
|
|
237
|
+
*/
|
|
238
|
+
export function tokenFailureError(e) {
|
|
239
|
+
const message = e instanceof Error ? e.message : String(e);
|
|
240
|
+
const outage = /(^|:\s*)cohort_unavailable:/.test(message);
|
|
241
|
+
return {
|
|
242
|
+
kind: outage ? "server" : "auth",
|
|
243
|
+
code: outage ? "cohort_unavailable" : "token_unavailable",
|
|
244
|
+
status: null,
|
|
245
|
+
retryable: outage,
|
|
246
|
+
message,
|
|
247
|
+
retryAfterMs: null,
|
|
248
|
+
window: null,
|
|
249
|
+
resetsAt: null,
|
|
250
|
+
scope: null,
|
|
251
|
+
requestId: null,
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* A transport fault (DNS, reset socket) or an abort.
|
|
257
|
+
* @param {unknown} e @param {AbortSignal} [signal] @returns {WireError}
|
|
258
|
+
*/
|
|
259
|
+
export function classifyTransportError(e, signal) {
|
|
260
|
+
const aborted = Boolean(signal?.aborted) || (e && typeof e === "object" && /** @type any */ (e).name === "AbortError");
|
|
261
|
+
const message = e instanceof Error ? e.message : String(e);
|
|
262
|
+
return {
|
|
263
|
+
kind: aborted ? "aborted" : "network",
|
|
264
|
+
code: aborted ? "aborted" : "network_error",
|
|
265
|
+
status: null,
|
|
266
|
+
retryable: !aborted,
|
|
267
|
+
message,
|
|
268
|
+
retryAfterMs: null,
|
|
269
|
+
window: null,
|
|
270
|
+
resetsAt: null,
|
|
271
|
+
scope: null,
|
|
272
|
+
requestId: null,
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/** Code for a stream the client gave up on because it went silent (CF-156). */
|
|
277
|
+
export const STREAM_IDLE_TIMEOUT_CODE = "stream_idle_timeout";
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* A gateway stream that was accepted and then went silent past the client's
|
|
281
|
+
* inter-event budget (`wire/stall.mjs`). W13 measured this twice in 25
|
|
282
|
+
* requests on `cohort-reason` and could not diagnose it; the engine now ends
|
|
283
|
+
* such a turn itself instead of spending a whole task budget on it (the
|
|
284
|
+
* tightest this repo owns is the replay eval's 180 s — NOT the dispatcher's,
|
|
285
|
+
* whose own timeouts run from 10 min to 12 h; see `wire/stall.mjs`).
|
|
286
|
+
*
|
|
287
|
+
* TRANSIENT, deliberately — kind `timeout`, so `gatewayDisposition` routes it
|
|
288
|
+
* to `retry`/`timeout`: no re-mint, no cooled credential, no failover. A
|
|
289
|
+
* stalled stream is not a credential problem and not a refusal.
|
|
290
|
+
*
|
|
291
|
+
* The code carries NO `cohort_` prefix, and that is load-bearing: this is the
|
|
292
|
+
* CLIENT's verdict, not an answer the gateway gave. `refusalFromWireError`
|
|
293
|
+
* therefore returns null for it and maestro's `classifyCohortRefusal` (which
|
|
294
|
+
* only looks at text containing `cohort_`) never reads it as a gateway refusal
|
|
295
|
+
* that would hold the seat.
|
|
296
|
+
*
|
|
297
|
+
* `mayHaveBilled` says WHICH phase went quiet. After a 2xx it is true — the
|
|
298
|
+
* request may already have been billed even though no completion was
|
|
299
|
+
* delivered — and the engine still never resends it, because after acceptance
|
|
300
|
+
* `postStream` has no retry path at all, which is what keeps a possibly-billed
|
|
301
|
+
* turn from being billed twice. Before any response headers it is false:
|
|
302
|
+
* nothing was accepted, so nothing was billed. Either way the flag is advice
|
|
303
|
+
* to a caller, not something acted on here.
|
|
304
|
+
*
|
|
305
|
+
* @param {{ idleMs:number, requestId?:string|null, message?:string, mayHaveBilled?:boolean }} p
|
|
306
|
+
* @returns {WireError & { mayHaveBilled: boolean }}
|
|
307
|
+
*/
|
|
308
|
+
export function streamIdleTimeoutError({ idleMs, requestId = null, message, mayHaveBilled = true }) {
|
|
309
|
+
return {
|
|
310
|
+
kind: "timeout",
|
|
311
|
+
code: STREAM_IDLE_TIMEOUT_CODE,
|
|
312
|
+
status: null,
|
|
313
|
+
retryable: true,
|
|
314
|
+
message: message ?? `the gateway stream sent no event for ${idleMs}ms`,
|
|
315
|
+
retryAfterMs: null,
|
|
316
|
+
window: null,
|
|
317
|
+
resetsAt: null,
|
|
318
|
+
scope: null,
|
|
319
|
+
requestId,
|
|
320
|
+
mayHaveBilled,
|
|
321
|
+
};
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* A malformed or truncated stream (the gateway spoke, but not the protocol).
|
|
326
|
+
* @param {string} message @param {string|null} [requestId] @returns {WireError}
|
|
327
|
+
*/
|
|
328
|
+
export function protocolError(message, requestId = null) {
|
|
329
|
+
return {
|
|
330
|
+
kind: "protocol",
|
|
331
|
+
code: "stream_protocol_error",
|
|
332
|
+
status: null,
|
|
333
|
+
retryable: false,
|
|
334
|
+
message,
|
|
335
|
+
retryAfterMs: null,
|
|
336
|
+
window: null,
|
|
337
|
+
resetsAt: null,
|
|
338
|
+
scope: null,
|
|
339
|
+
requestId,
|
|
340
|
+
};
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
/**
|
|
344
|
+
* An error event sent INSIDE a 200 stream (Anthropic `event: error`, or an
|
|
345
|
+
* OpenAI `{error:{…}}` data frame). Classified by its type/code like an HTTP one.
|
|
346
|
+
* @param {any} payload @param {string|null} requestId @param {number} nowMs @returns {WireError}
|
|
347
|
+
*/
|
|
348
|
+
export function classifyStreamError(payload, requestId, nowMs) {
|
|
349
|
+
const err = payload && typeof payload === "object" ? payload.error ?? payload : {};
|
|
350
|
+
const type = typeof err.type === "string" ? err.type : "";
|
|
351
|
+
const status = type === "overloaded_error" ? 529 : type === "rate_limit_error" ? 429 : 500;
|
|
352
|
+
const e = classifyHttpError(status, null, JSON.stringify(payload ?? {}), nowMs);
|
|
353
|
+
return { ...e, requestId };
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
/**
|
|
357
|
+
* Read the cohort-llm response headers (design §4.4/§4.6). Cost is kept as a
|
|
358
|
+
* decimal string of integer micro-USD so it survives JSON and never touches a
|
|
359
|
+
* float until the result is rendered.
|
|
360
|
+
*
|
|
361
|
+
* @param {any} headers
|
|
362
|
+
*/
|
|
363
|
+
export function readCohortHeaders(headers) {
|
|
364
|
+
const windows = {};
|
|
365
|
+
for (const w of ["5h", "7d", "30d"]) {
|
|
366
|
+
const utilization = header(headers, `x-cohort-quota-${w}-utilization`);
|
|
367
|
+
const reset = header(headers, `x-cohort-quota-${w}-reset`);
|
|
368
|
+
if (utilization != null || reset != null) {
|
|
369
|
+
const u = utilization == null ? null : Number(utilization);
|
|
370
|
+
windows[w] = { utilization: Number.isFinite(u) ? u : null, resetsAt: reset };
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
const costRaw = header(headers, "x-cohort-cost-micros");
|
|
374
|
+
const credits = header(headers, "x-cohort-credits-remaining-micros");
|
|
375
|
+
return {
|
|
376
|
+
requestId: header(headers, "x-cohort-request-id"),
|
|
377
|
+
modelTier: header(headers, "x-cohort-model-tier"),
|
|
378
|
+
costMicros: micros(costRaw, false),
|
|
379
|
+
// Headers never carry usage — only the terminal frame does (CF-158). The key
|
|
380
|
+
// is present so the merged shape is uniform whether a frame arrived or not.
|
|
381
|
+
usage: null,
|
|
382
|
+
quota: {
|
|
383
|
+
windows,
|
|
384
|
+
creditsRemainingMicros: micros(credits, true),
|
|
385
|
+
funding: header(headers, "x-cohort-funding"),
|
|
386
|
+
limitKind: header(headers, "x-cohort-limit-kind"),
|
|
387
|
+
},
|
|
388
|
+
};
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
/**
|
|
392
|
+
* Integer micro-USD as a decimal string, or null. Accepts a digit string or a
|
|
393
|
+
* safe integer number (a JSON frame may send either).
|
|
394
|
+
* @param {unknown} v @param {boolean} signed
|
|
395
|
+
*/
|
|
396
|
+
function micros(v, signed) {
|
|
397
|
+
if (typeof v === "number") return Number.isSafeInteger(v) && (signed || v >= 0) ? String(v) : null;
|
|
398
|
+
if (typeof v !== "string") return null;
|
|
399
|
+
const s = v.trim();
|
|
400
|
+
return (signed ? /^-?\d+$/ : /^\d+$/).test(s) ? s : null;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
/** SSE event name of the gateway's terminal cost/quota frame. */
|
|
404
|
+
export const COHORT_FRAME_EVENT = "cohort";
|
|
405
|
+
|
|
406
|
+
/**
|
|
407
|
+
* Parse the terminal `event: cohort` SSE frame. Response headers leave before a
|
|
408
|
+
* streamed completion exists, so they cannot carry its final cost; the gateway
|
|
409
|
+
* sends this frame after settling and before the wire's terminator
|
|
410
|
+
* (`data: [DONE]` / `message_stop`). The contract (design §4.8):
|
|
411
|
+
*
|
|
412
|
+
* event: cohort
|
|
413
|
+
* data: {"request_id":"…","model_tier":"…","cost_micros":"4321",
|
|
414
|
+
* "quota":{"5h":{"utilization":0.25,"resetsAt":"…"}},
|
|
415
|
+
* "credits_remaining_micros":"…","funding":"included","limit_kind":null}
|
|
416
|
+
*
|
|
417
|
+
* A window's reset is read from `resetsAt` or the older `resets_at`, and kept
|
|
418
|
+
* as `resetsAt`. Every field is optional. An unparseable frame is advisory and yields an
|
|
419
|
+
* empty patch — it must never fail a completion that streamed correctly.
|
|
420
|
+
*
|
|
421
|
+
* **CF-158 — the frame also carries `usage`, which design §4.8 does not list.**
|
|
422
|
+
* Measured live on 2026-09-22 (`req_bf94feaab6405214071532a5`): a `usage` object
|
|
423
|
+
* in the engine's own normalised shape rides every terminal frame. It used to be
|
|
424
|
+
* dropped here. It is now read through `readFrameUsage` (defensive: unknown
|
|
425
|
+
* fields ignored, a missing or malformed object yields `null`, never zeros) and
|
|
426
|
+
* carried on the wire result as `res.cohort.usage`, so a caller can tell an
|
|
427
|
+
* unreported turn from a free one.
|
|
428
|
+
*
|
|
429
|
+
* It reaches a run's JSON result under the DISTINCT key `cohort.gatewayUsage`
|
|
430
|
+
* — never as `cohort.usage`, which is the engine's own accumulated total for
|
|
431
|
+
* the whole run, a near-equal but different number. Promising the gateway's
|
|
432
|
+
* figure under the accumulator's name would reproduce CF-158's own failure
|
|
433
|
+
* (a real-but-different number read under the expected name), one layer up.
|
|
434
|
+
* Because it is undocumented upstream it is treated as advisory —
|
|
435
|
+
* nothing fails when it is absent. See docs/engine/eval.md §3; the fix for the
|
|
436
|
+
* contract itself belongs to hq, which this repo does not edit.
|
|
437
|
+
*
|
|
438
|
+
* @param {string} data
|
|
439
|
+
* @returns {Partial<ReturnType<typeof readCohortHeaders>> & { quota?: any }}
|
|
440
|
+
*/
|
|
441
|
+
export function readCohortFrame(data) {
|
|
442
|
+
let o;
|
|
443
|
+
try {
|
|
444
|
+
o = JSON.parse(data);
|
|
445
|
+
} catch {
|
|
446
|
+
return {};
|
|
447
|
+
}
|
|
448
|
+
if (!o || typeof o !== "object") return {};
|
|
449
|
+
/** @type {any} */
|
|
450
|
+
const patch = { quota: { windows: {} } };
|
|
451
|
+
if (typeof o.request_id === "string" && o.request_id) patch.requestId = o.request_id;
|
|
452
|
+
if (typeof o.model_tier === "string" && o.model_tier) patch.modelTier = o.model_tier;
|
|
453
|
+
const cost = micros(o.cost_micros, false);
|
|
454
|
+
if (cost !== null) patch.costMicros = cost;
|
|
455
|
+
const q = o.quota && typeof o.quota === "object" ? o.quota : {};
|
|
456
|
+
for (const w of ["5h", "7d", "30d"]) {
|
|
457
|
+
const win = q[w];
|
|
458
|
+
if (!win || typeof win !== "object") continue;
|
|
459
|
+
const u = Number(win.utilization);
|
|
460
|
+
patch.quota.windows[w] = {
|
|
461
|
+
utilization: win.utilization != null && Number.isFinite(u) ? u : null,
|
|
462
|
+
resetsAt: pick(win.resetsAt, win.resets_at),
|
|
463
|
+
};
|
|
464
|
+
}
|
|
465
|
+
const credits = micros(o.credits_remaining_micros, true);
|
|
466
|
+
if (credits !== null) patch.quota.creditsRemainingMicros = credits;
|
|
467
|
+
if (typeof o.funding === "string") patch.quota.funding = o.funding;
|
|
468
|
+
if (typeof o.limit_kind === "string") patch.quota.limitKind = o.limit_kind;
|
|
469
|
+
// CF-158: undocumented upstream, so absence is normal and never an error.
|
|
470
|
+
const usage = readFrameUsage(o.usage);
|
|
471
|
+
if (usage) patch.usage = usage;
|
|
472
|
+
return patch;
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
/**
|
|
476
|
+
* Overlay a frame patch on the header reading: a value the frame states wins,
|
|
477
|
+
* anything it omits keeps the header's value.
|
|
478
|
+
* @param {ReturnType<typeof readCohortHeaders>} base
|
|
479
|
+
* @param {ReturnType<typeof readCohortFrame>} patch
|
|
480
|
+
* @returns {ReturnType<typeof readCohortHeaders>}
|
|
481
|
+
*/
|
|
482
|
+
export function mergeCohort(base, patch) {
|
|
483
|
+
const pq = patch.quota || {};
|
|
484
|
+
return {
|
|
485
|
+
requestId: patch.requestId ?? base.requestId,
|
|
486
|
+
modelTier: patch.modelTier ?? base.modelTier,
|
|
487
|
+
costMicros: patch.costMicros ?? base.costMicros,
|
|
488
|
+
usage: patch.usage ?? base.usage ?? null,
|
|
489
|
+
quota: {
|
|
490
|
+
windows: { ...base.quota.windows, ...(pq.windows || {}) },
|
|
491
|
+
creditsRemainingMicros: pq.creditsRemainingMicros ?? base.quota.creditsRemainingMicros,
|
|
492
|
+
funding: pq.funding ?? base.quota.funding,
|
|
493
|
+
limitKind: pq.limitKind ?? base.quota.limitKind,
|
|
494
|
+
},
|
|
495
|
+
};
|
|
496
|
+
}
|