@cohortapp/agent-sdk 2.17.0 → 2.18.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/settings.json +18 -0
- package/.env.example +18 -5
- package/README.md +1 -0
- package/bin/maestro.mjs +62 -0
- package/docs/guides/billing-console-keys.md +60 -0
- package/docs/guides/front-door-session.md +54 -9
- package/docs/guides/mac-mini.md +20 -25
- package/docs/guides/setup-wizard.md +1 -1
- package/docs/runbooks/fleet-rollout.md +156 -0
- package/docs/runbooks/mac-mini-bootstrap.md +12 -14
- package/lib/action-executor.js +19 -3
- package/lib/budget-guard.mjs +279 -3
- package/lib/channels/base-adapter.mjs +3 -1
- package/lib/channels/contract.mjs +2 -1
- package/lib/channels/inbox-item.mjs +8 -0
- package/lib/claude-bin.mjs +5 -6
- package/lib/cli/doctor-checks.mjs +141 -10
- package/lib/cli/global-setup-extras.mjs +5 -1
- package/lib/cli/inbox.mjs +100 -15
- package/lib/cli/seat-auth.mjs +463 -0
- package/lib/cli/session.mjs +80 -12
- package/lib/collective/capture.mjs +8 -6
- package/lib/collective/global-config.mjs +63 -1
- package/lib/collective/presence.mjs +142 -5
- package/lib/comms/send-gate.mjs +559 -1
- package/lib/diagnostics/alerts.mjs +49 -0
- package/lib/diagnostics/cadence-output-freshness.mjs +288 -0
- package/lib/engine/agents/definitions.mjs +343 -0
- package/lib/engine/agents/persist.mjs +275 -0
- package/lib/engine/agents/runtime.mjs +748 -0
- package/lib/engine/agents/usage.mjs +95 -0
- package/lib/engine/auth-status.mjs +139 -0
- package/lib/engine/budget.mjs +194 -0
- package/lib/engine/cli.mjs +1204 -0
- package/lib/engine/commands/index.mjs +269 -0
- package/lib/engine/context/budget.mjs +219 -0
- package/lib/engine/context/cache.mjs +125 -0
- package/lib/engine/context/child-env.mjs +215 -0
- package/lib/engine/context/compaction.mjs +342 -0
- package/lib/engine/context/images.mjs +90 -0
- package/lib/engine/context/instructions.mjs +327 -0
- package/lib/engine/context/lazy-instructions.mjs +169 -0
- package/lib/engine/context/manager.mjs +182 -0
- package/lib/engine/context/real-path.mjs +91 -0
- package/lib/engine/context/secret-values.mjs +163 -0
- package/lib/engine/context/settings.mjs +274 -0
- package/lib/engine/context/stream-input.mjs +159 -0
- package/lib/engine/guard.mjs +152 -0
- package/lib/engine/hooks.mjs +713 -0
- package/lib/engine/loop.mjs +560 -0
- package/lib/engine/mcp/client.mjs +254 -0
- package/lib/engine/mcp/config.mjs +301 -0
- package/lib/engine/mcp/http.mjs +201 -0
- package/lib/engine/mcp/index.mjs +146 -0
- package/lib/engine/mcp/jsonrpc.mjs +147 -0
- package/lib/engine/mcp/naming.mjs +66 -0
- package/lib/engine/mcp/resources.mjs +89 -0
- package/lib/engine/mcp/results.mjs +133 -0
- package/lib/engine/mcp/stdio.mjs +137 -0
- package/lib/engine/mcp/supervisor.mjs +116 -0
- package/lib/engine/messages.mjs +104 -0
- package/lib/engine/output/json.mjs +164 -0
- package/lib/engine/output/stream-json.mjs +266 -0
- package/lib/engine/permissions.mjs +845 -0
- package/lib/engine/process-identity.mjs +164 -0
- package/lib/engine/process-tree.mjs +551 -0
- package/lib/engine/prompt.mjs +60 -0
- package/lib/engine/session/store.mjs +299 -0
- package/lib/engine/session-runtime/args.mjs +97 -0
- package/lib/engine/session-runtime/host.mjs +143 -0
- package/lib/engine/session-runtime/inbox.mjs +122 -0
- package/lib/engine/session-runtime/notifications.mjs +129 -0
- package/lib/engine/session-runtime/registry.mjs +328 -0
- package/lib/engine/session-runtime/runner.mjs +344 -0
- package/lib/engine/session-runtime/socket.mjs +212 -0
- package/lib/engine/session-runtime/wakeup.mjs +115 -0
- package/lib/engine/skills/index.mjs +321 -0
- package/lib/engine/tools/bash-background.mjs +533 -0
- package/lib/engine/tools/bash.mjs +216 -0
- package/lib/engine/tools/edit.mjs +97 -0
- package/lib/engine/tools/glob.mjs +81 -0
- package/lib/engine/tools/grep.mjs +224 -0
- package/lib/engine/tools/index.mjs +84 -0
- package/lib/engine/tools/list-agents.mjs +32 -0
- package/lib/engine/tools/ls.mjs +127 -0
- package/lib/engine/tools/monitor.mjs +82 -0
- package/lib/engine/tools/notebook-edit.mjs +218 -0
- package/lib/engine/tools/read.mjs +103 -0
- package/lib/engine/tools/schedule-wakeup.mjs +45 -0
- package/lib/engine/tools/schema.mjs +144 -0
- package/lib/engine/tools/send-message.mjs +77 -0
- package/lib/engine/tools/session.mjs +70 -0
- package/lib/engine/tools/todo.mjs +144 -0
- package/lib/engine/tools/toolsearch.mjs +217 -0
- package/lib/engine/tools/walk.mjs +193 -0
- package/lib/engine/tools/web-switch.mjs +31 -0
- package/lib/engine/tools/webfetch-html.mjs +387 -0
- package/lib/engine/tools/webfetch-net.mjs +340 -0
- package/lib/engine/tools/webfetch.mjs +198 -0
- package/lib/engine/tools/websearch.mjs +91 -0
- package/lib/engine/tools/workflow.mjs +95 -0
- package/lib/engine/tools/write.mjs +76 -0
- package/lib/engine/tui/line-editor.mjs +137 -0
- package/lib/engine/tui/render.mjs +86 -0
- package/lib/engine/tui/tui.mjs +274 -0
- package/lib/engine/wire/anthropic-messages.mjs +263 -0
- package/lib/engine/wire/effort.mjs +36 -0
- package/lib/engine/wire/errors.mjs +496 -0
- package/lib/engine/wire/http.mjs +441 -0
- package/lib/engine/wire/index.mjs +76 -0
- package/lib/engine/wire/openai-chat.mjs +332 -0
- package/lib/engine/wire/prompt-cache.mjs +79 -0
- package/lib/engine/wire/search.mjs +140 -0
- package/lib/engine/wire/sse.mjs +114 -0
- package/lib/engine/wire/stall.mjs +349 -0
- package/lib/engine/wire/token-provider.mjs +175 -0
- package/lib/engine/wire/usage.mjs +192 -0
- package/lib/engine/workflow/host.mjs +524 -0
- package/lib/engine/workflow/journal.mjs +188 -0
- package/lib/engine/workflow/json-schema.mjs +171 -0
- package/lib/engine/workflow/meta.mjs +329 -0
- package/lib/engine/workflow/notifications.mjs +52 -0
- package/lib/engine/workflow/runtime.mjs +447 -0
- package/lib/engine/workflow/sandbox.mjs +534 -0
- package/lib/engine/workflow/worker.mjs +141 -0
- package/lib/engine/workflow/worktree.mjs +74 -0
- package/lib/execution/disposition.mjs +1 -1
- package/lib/execution/intake.mjs +10 -0
- package/lib/execution/surface-policy.mjs +15 -0
- package/lib/learning/curator.mjs +8 -6
- package/lib/learning/reflect.mjs +8 -6
- package/lib/model-router/catalog/cohort.yaml +137 -0
- package/lib/model-router/catalog.mjs +118 -1
- package/lib/model-router/failover.mjs +67 -16
- package/lib/model-router/llm-task.mjs +39 -3
- package/lib/model-router/resolve.mjs +89 -3
- package/lib/model-router/spawn.mjs +46 -47
- package/lib/model-router/taxonomy.mjs +126 -4
- package/lib/org/cost-sync.mjs +141 -11
- package/lib/org/inbound/broadcast.mjs +289 -0
- package/lib/org/inbound/collective.mjs +375 -0
- package/lib/org/inbound/directedness.mjs +96 -8
- package/lib/org/inbound/facts.mjs +78 -2
- package/lib/org/inbound/project.mjs +22 -0
- package/lib/org/inbound/surfaces.mjs +14 -0
- package/lib/org/llm-token.mjs +879 -0
- package/lib/org/mesh.mjs +61 -0
- package/lib/org/messaging.mjs +3 -1
- package/lib/org/protocol.checksum +1 -1
- package/lib/org/protocol.mjs +15 -0
- package/lib/org/quota.mjs +520 -0
- package/lib/org/tool-surface.mjs +104 -16
- package/lib/org/ui-parity.mjs +16 -1
- package/lib/org/work-ledger.mjs +37 -6
- package/lib/rate-guard.mjs +114 -1
- package/lib/resource-governor.mjs +41 -6
- package/lib/runtime/adapter.mjs +823 -0
- package/lib/runtime/child-env.mjs +191 -0
- package/lib/runtime/legacy-shell-guard.mjs +97 -0
- package/lib/runtime/seat-engine.mjs +162 -0
- package/lib/session/ask-ledger.mjs +271 -0
- package/lib/session/current-work.mjs +676 -0
- package/lib/session/feed-core.mjs +40 -3
- package/lib/session/launch-args.mjs +56 -4
- package/lib/session/status-summary.mjs +26 -9
- package/lib/session/upgrade-notice.mjs +42 -0
- package/lib/setup/claude-probe.mjs +117 -13
- package/lib/setup/enrich.mjs +13 -10
- package/lib/setup/sections/model.mjs +39 -13
- package/lib/telemetry/collect.mjs +208 -9
- package/lib/upgrade/ignored-drift.mjs +105 -0
- package/lib/voice/post-call-brief.mjs +30 -17
- package/package.json +13 -3
- package/plugins/maestro-skills/skills/board-work.md +5 -0
- package/plugins/maestro-skills/skills/inbound-triage.md +56 -15
- package/plugins/maestro-skills/skills/main-session.md +18 -7
- package/scripts/ci/check-tarball-fidelity.mjs +126 -2
- package/scripts/ci/run-tests.mjs +47 -19
- package/scripts/cohort-llm/api-key-helper.mjs +92 -0
- package/scripts/collective/hook-runner.mjs +29 -2
- package/scripts/continuous-monitor.sh +13 -0
- package/scripts/cost/track-claude-usage.mjs +15 -0
- package/scripts/daemon/agent-daemon.mjs +408 -20
- package/scripts/daemon/assurance.mjs +48 -12
- package/scripts/daemon/cadence-consumer.mjs +218 -68
- package/scripts/daemon/cadence-handlers.mjs +73 -4
- package/scripts/daemon/classifier.mjs +75 -26
- package/scripts/daemon/context-compiler.mjs +51 -37
- package/scripts/daemon/deliver.mjs +30 -1
- package/scripts/daemon/dispatcher.mjs +595 -149
- package/scripts/daemon/health.mjs +14 -1
- package/scripts/daemon/maestro-daemon.mjs +11 -0
- package/scripts/daemon/prompt-builder.mjs +24 -0
- package/scripts/daemon/responder.mjs +246 -79
- package/scripts/daemon/sdk-version.mjs +98 -16
- package/scripts/eval/probe-gateway.mjs +635 -0
- package/scripts/eval/replay/extract.mjs +270 -0
- package/scripts/eval/replay/grade.mjs +260 -0
- package/scripts/eval/replay/lib/config.mjs +50 -0
- package/scripts/eval/replay/lib/effects.mjs +65 -0
- package/scripts/eval/replay/lib/fixture.mjs +188 -0
- package/scripts/eval/replay/lib/judge.mjs +72 -0
- package/scripts/eval/replay/lib/redact.mjs +136 -0
- package/scripts/eval/replay/lib/sandbox.mjs +170 -0
- package/scripts/eval/replay/lib/schema-check.mjs +63 -0
- package/scripts/eval/replay/lib/transcript.mjs +76 -0
- package/scripts/eval/replay/mcp-replay-stub.mjs +101 -0
- package/scripts/eval/replay/report.mjs +185 -0
- package/scripts/eval/replay/run.mjs +404 -0
- package/scripts/fleet/rollout.mjs +1094 -0
- package/scripts/hooks/pre-send-audit.sh +36 -245
- package/scripts/hooks/pre-write-yaml-validate.mjs +275 -0
- package/scripts/hooks/validate-state-yaml.sh +190 -0
- package/scripts/huddle/huddle-llm.mjs +361 -0
- package/scripts/huddle/huddle-server.mjs +46 -121
- package/scripts/local-triggers/autoupdate.sh +448 -78
- package/scripts/local-triggers/run-trigger.sh +13 -0
- package/scripts/maintenance/pin-integrity.mjs +364 -0
- package/scripts/poll-slack-events.sh +41 -9
- package/scripts/poller/slack-socket-mode.mjs +28 -3
- package/scripts/session/supervisor.mjs +80 -13
- package/scripts/spawn-session.sh +13 -0
- package/bin/maestro.test.mjs +0 -1574
- package/lib/action-executor.test.mjs +0 -871
- package/lib/archetype.test.mjs +0 -132
- package/lib/assurance/plan-note.test.mjs +0 -234
- package/lib/assurance/room-budget.test.mjs +0 -486
- package/lib/assurance/tier.test.mjs +0 -174
- package/lib/autonomy.test.mjs +0 -66
- package/lib/backlog.test.mjs +0 -302
- package/lib/backup/policy.test.mjs +0 -305
- package/lib/budget-escalate.test.mjs +0 -232
- package/lib/budget-guard.envelope.test.mjs +0 -476
- package/lib/budget-guard.test.mjs +0 -427
- package/lib/cadence-bus-requeue.test.mjs +0 -83
- package/lib/cadence-bus-schedule.test.mjs +0 -194
- package/lib/cadence-bus.test.mjs +0 -720
- package/lib/cadences.test.mjs +0 -230
- package/lib/capability/inventory.test.mjs +0 -232
- package/lib/capability.test.mjs +0 -78
- package/lib/channels/base-adapter.test.mjs +0 -590
- package/lib/channels/channels.test.mjs +0 -371
- package/lib/channels/contract.test.mjs +0 -162
- package/lib/channels/inbox-item.test.mjs +0 -368
- package/lib/channels/orgmail/adapter.test.mjs +0 -448
- package/lib/channels/pairing.test.mjs +0 -270
- package/lib/channels/repeat-suppressor.test.mjs +0 -134
- package/lib/channels/slack-adapter.test.mjs +0 -212
- package/lib/channels/telegram-adapter.test.mjs +0 -306
- package/lib/channels/voice/adapter.test.mjs +0 -278
- package/lib/channels/whatsapp/adapter-baileys.test.mjs +0 -359
- package/lib/channels/whatsapp/baileys-typing.test.mjs +0 -154
- package/lib/charter.test.mjs +0 -89
- package/lib/claude-bin.test.mjs +0 -131
- package/lib/cli/board.test.mjs +0 -227
- package/lib/cli/design.test.mjs +0 -270
- package/lib/cli/doctor-checks.test.mjs +0 -336
- package/lib/cli/global-setup-extras.test.mjs +0 -462
- package/lib/cli/inbox.test.mjs +0 -230
- package/lib/cli/session-ack.test.mjs +0 -63
- package/lib/cli/session.test.mjs +0 -613
- package/lib/collective/capture.test.mjs +0 -121
- package/lib/collective/cards.test.mjs +0 -114
- package/lib/collective/config.test.mjs +0 -123
- package/lib/collective/global-config.test.mjs +0 -220
- package/lib/collective/global-skills.test.mjs +0 -126
- package/lib/collective/presence.test.mjs +0 -95
- package/lib/collective/recall.test.mjs +0 -116
- package/lib/collective/vendor-skills.test.mjs +0 -306
- package/lib/comms/send-gate.test.mjs +0 -770
- package/lib/comms.test.mjs +0 -41
- package/lib/context/budget.test.mjs +0 -252
- package/lib/context/history-scope.test.mjs +0 -79
- package/lib/cost/ledger-row.test.mjs +0 -183
- package/lib/design/design-md.test.mjs +0 -318
- package/lib/design/fixtures/DESIGN.golden.md +0 -238
- package/lib/design/fixtures/PRODUCT.golden.md +0 -67
- package/lib/design/fixtures/foundation.json +0 -133
- package/lib/design/refresh-gate.test.mjs +0 -144
- package/lib/design/write.test.mjs +0 -241
- package/lib/diagnostics/alerts.test.mjs +0 -318
- package/lib/diagnostics/backup-freshness.test.mjs +0 -185
- package/lib/diagnostics/counters.test.mjs +0 -206
- package/lib/diagnostics/events.test.mjs +0 -290
- package/lib/diagnostics/otel.test.mjs +0 -196
- package/lib/diagnostics/trace.test.mjs +0 -251
- package/lib/env-compat.test.mjs +0 -104
- package/lib/execution/disposition.test.mjs +0 -553
- package/lib/execution/drive.test.mjs +0 -270
- package/lib/execution/effects.test.mjs +0 -344
- package/lib/execution/intake.test.mjs +0 -389
- package/lib/execution/journal.test.mjs +0 -261
- package/lib/execution/match.test.mjs +0 -235
- package/lib/execution/pipeline.test.mjs +0 -392
- package/lib/execution/route.test.mjs +0 -186
- package/lib/execution/surface-policy.test.mjs +0 -162
- package/lib/fs-atomic.test.mjs +0 -72
- package/lib/fs-ownership.test.mjs +0 -158
- package/lib/goals/admission.test.mjs +0 -164
- package/lib/goals/classify.test.mjs +0 -167
- package/lib/goals/collaborate.test.mjs +0 -336
- package/lib/goals/gaps.test.mjs +0 -284
- package/lib/goals/loop.test.mjs +0 -845
- package/lib/hooks/bus.test.mjs +0 -387
- package/lib/identity/persona.test.mjs +0 -142
- package/lib/kpi-sensors.test.mjs +0 -278
- package/lib/kpi.test.mjs +0 -244
- package/lib/learning/config.test.mjs +0 -75
- package/lib/learning/counters.test.mjs +0 -69
- package/lib/learning/curator-consolidate.test.mjs +0 -238
- package/lib/learning/curator.test.mjs +0 -106
- package/lib/learning/reflect.test.mjs +0 -0
- package/lib/learning/session-index.test.mjs +0 -125
- package/lib/learning/skill-writer.test.mjs +0 -210
- package/lib/mandate/audit.test.mjs +0 -195
- package/lib/mandate/contract.test.mjs +0 -185
- package/lib/mandate/derive.test.mjs +0 -274
- package/lib/mandate/model.test.mjs +0 -164
- package/lib/mandate/refresh.test.mjs +0 -389
- package/lib/mcp/server.test.mjs +0 -426
- package/lib/model-router/auth-profiles.test.mjs +0 -580
- package/lib/model-router/catalog.test.mjs +0 -385
- package/lib/model-router/economics.test.mjs +0 -438
- package/lib/model-router/failover.test.mjs +0 -439
- package/lib/model-router/health.test.mjs +0 -338
- package/lib/model-router/integration-coverage.test.mjs +0 -831
- package/lib/model-router/integration.test.mjs +0 -564
- package/lib/model-router/ledger.test.mjs +0 -415
- package/lib/model-router/llm-task.test.mjs +0 -392
- package/lib/model-router/org-credentials.test.mjs +0 -265
- package/lib/model-router/pricing-refresh.test.mjs +0 -286
- package/lib/model-router/reconcile.test.mjs +0 -316
- package/lib/model-router/repair.test.mjs +0 -180
- package/lib/model-router/spawn.test.mjs +0 -446
- package/lib/model-router/taxonomy.test.mjs +0 -410
- package/lib/model-router.test.mjs +0 -1207
- package/lib/org/activity.test.mjs +0 -134
- package/lib/org/approvals.test.mjs +0 -216
- package/lib/org/awareness.test.mjs +0 -159
- package/lib/org/board-mine-cache.test.mjs +0 -53
- package/lib/org/board.test.mjs +0 -187
- package/lib/org/bootstrap-context.test.mjs +0 -153
- package/lib/org/client.test.mjs +0 -1206
- package/lib/org/cohort-client.test.mjs +0 -126
- package/lib/org/cost-sync.test.mjs +0 -153
- package/lib/org/doctor.test.mjs +0 -346
- package/lib/org/engagement-ledger.test.mjs +0 -112
- package/lib/org/engagement.test.mjs +0 -739
- package/lib/org/handoff.test.mjs +0 -269
- package/lib/org/inbound/directedness.test.mjs +0 -668
- package/lib/org/inbound/facts.test.mjs +0 -471
- package/lib/org/inbound/hydrate.test.mjs +0 -908
- package/lib/org/inbound/index.test.mjs +0 -429
- package/lib/org/inbound/project.test.mjs +0 -287
- package/lib/org/integration-tools.test.mjs +0 -160
- package/lib/org/keys.test.mjs +0 -92
- package/lib/org/knowledge.test.mjs +0 -326
- package/lib/org/leases.test.mjs +0 -235
- package/lib/org/mesh-directives.test.mjs +0 -110
- package/lib/org/mesh-integration.test.mjs +0 -127
- package/lib/org/mesh.test.mjs +0 -400
- package/lib/org/messaging.test.mjs +0 -471
- package/lib/org/param-contract.test.mjs +0 -477
- package/lib/org/policy.test.mjs +0 -237
- package/lib/org/protocol.checksum.test.mjs +0 -90
- package/lib/org/protocol.test.mjs +0 -323
- package/lib/org/push.test.mjs +0 -792
- package/lib/org/registry.test.mjs +0 -100
- package/lib/org/resource-tools.test.mjs +0 -361
- package/lib/org/tool-access.test.mjs +0 -144
- package/lib/org/tool-surface-integration.test.mjs +0 -120
- package/lib/org/tool-surface.test.mjs +0 -1268
- package/lib/org/typing.test.mjs +0 -291
- package/lib/org/ui-parity.test.mjs +0 -560
- package/lib/org/verify.test.mjs +0 -194
- package/lib/org/work-ledger.test.mjs +0 -273
- package/lib/plan/adoption-e2e.test.mjs +0 -366
- package/lib/plan/budget-enforcement.test.mjs +0 -400
- package/lib/plan/compile.test.mjs +0 -382
- package/lib/plan/emit.test.mjs +0 -269
- package/lib/plan/explain.test.mjs +0 -188
- package/lib/prompts/parallelism.test.mjs +0 -177
- package/lib/rag/rag.test.mjs +0 -505
- package/lib/rate-guard.test.mjs +0 -272
- package/lib/reactive-gate.test.mjs +0 -57
- package/lib/render.test.mjs +0 -68
- package/lib/resource-governor.test.mjs +0 -488
- package/lib/scheduling/dynamic-jobs.test.mjs +0 -344
- package/lib/scheduling/jitter.test.mjs +0 -140
- package/lib/secrets/broker.test.mjs +0 -280
- package/lib/secrets/providers.test.mjs +0 -274
- package/lib/security/audit-engine.test.mjs +0 -424
- package/lib/security/coerce-args.test.mjs +0 -281
- package/lib/security/dangerous-tools.test.mjs +0 -68
- package/lib/security/external-content.test.mjs +0 -84
- package/lib/security/redact.test.mjs +0 -441
- package/lib/security/secret-equal.test.mjs +0 -55
- package/lib/session/config.test.mjs +0 -92
- package/lib/session/feed-core.test.mjs +0 -198
- package/lib/session/first-run.test.mjs +0 -121
- package/lib/session/frontdoor.test.mjs +0 -205
- package/lib/session/handoffs.test.mjs +0 -183
- package/lib/session/identity.test.mjs +0 -180
- package/lib/session/inbox-claims.test.mjs +0 -286
- package/lib/session/launch-args.test.mjs +0 -157
- package/lib/session/liveness.test.mjs +0 -100
- package/lib/session/status-summary.test.mjs +0 -118
- package/lib/session-permissions.test.mjs +0 -120
- package/lib/setup/claude-probe.test.mjs +0 -187
- package/lib/setup/completeness.test.mjs +0 -110
- package/lib/setup/context-pack.test.mjs +0 -89
- package/lib/setup/enrich.test.mjs +0 -115
- package/lib/setup/enroll-from-cohort.test.mjs +0 -300
- package/lib/setup/integration.test.mjs +0 -162
- package/lib/setup/io.test.mjs +0 -77
- package/lib/setup/runner.test.mjs +0 -132
- package/lib/setup/sections/identity.test.mjs +0 -234
- package/lib/setup/sections/inventory.test.mjs +0 -198
- package/lib/setup/sections/learning.test.mjs +0 -81
- package/lib/setup/sections/mandate.test.mjs +0 -388
- package/lib/setup/sections/messaging.test.mjs +0 -127
- package/lib/setup/sections/model.test.mjs +0 -240
- package/lib/setup/sections/org.test.mjs +0 -346
- package/lib/setup/sections/orgmail.test.mjs +0 -118
- package/lib/setup/sections/recovery.test.mjs +0 -98
- package/lib/setup/sections/subagents.test.mjs +0 -429
- package/lib/setup/sections/verify.test.mjs +0 -175
- package/lib/setup/sot.test.mjs +0 -81
- package/lib/setup/state.test.mjs +0 -115
- package/lib/singleton.test.mjs +0 -151
- package/lib/subagents/cli.test.mjs +0 -389
- package/lib/subagents/client.test.mjs +0 -309
- package/lib/subagents/gap.test.mjs +0 -234
- package/lib/subagents/lock.test.mjs +0 -248
- package/lib/subagents/manifest.test.mjs +0 -175
- package/lib/subagents/refs.test.mjs +0 -204
- package/lib/subagents/resolve.test.mjs +0 -422
- package/lib/subagents/schema.test.mjs +0 -328
- package/lib/telemetry/alerts.test.mjs +0 -109
- package/lib/telemetry/collect.test.mjs +0 -1274
- package/lib/tool-definitions-integration.test.mjs +0 -83
- package/lib/tool-definitions.test.mjs +0 -437
- package/lib/upgrade/global-refresh.test.mjs +0 -65
- package/lib/upgrade/launchd-reconcile.test.mjs +0 -272
- package/lib/upgrade/post-steps.test.mjs +0 -200
- package/lib/upgrade/verify.test.mjs +0 -164
- package/lib/util/fetch-timeout.test.mjs +0 -202
- package/lib/util/reconnect.test.mjs +0 -369
- package/lib/util/unhandled.test.mjs +0 -216
- package/lib/voice/outbound.test.mjs +0 -69
- package/lib/voice/session-rotation.test.mjs +0 -114
- package/lib/voice/stt.test.mjs +0 -226
- package/lib/voice/voice.test.mjs +0 -990
- package/scripts/cadence/enqueue-cadence-tick.test.mjs +0 -187
- package/scripts/ci/check-docs-accuracy.test.mjs +0 -409
- package/scripts/ci/check-durable-write-seam.test.mjs +0 -90
- package/scripts/ci/check-no-build-artifacts.test.mjs +0 -71
- package/scripts/ci/check-no-residual-identity.test.mjs +0 -202
- package/scripts/ci/check-skill-packs.test.mjs +0 -495
- package/scripts/ci/check-subagent-frontmatter.test.mjs +0 -124
- package/scripts/ci/check.test.mjs +0 -194
- package/scripts/ci/conformance-org-api.test.mjs +0 -425
- package/scripts/cloud-relay/voice/relay-identity.test.mjs +0 -96
- package/scripts/collective/hook-runner.test.mjs +0 -173
- package/scripts/cost/fleet-digest.test.mjs +0 -207
- package/scripts/cost/track-claude-usage-pricing.test.mjs +0 -183
- package/scripts/cost/track-claude-usage.test.mjs +0 -148
- package/scripts/daemon/agent-daemon-board-mine.test.mjs +0 -96
- package/scripts/daemon/agent-daemon-design.test.mjs +0 -238
- package/scripts/daemon/agent-daemon-frontdoor.test.mjs +0 -60
- package/scripts/daemon/agent-daemon.test.mjs +0 -995
- package/scripts/daemon/assurance-e2e.test.mjs +0 -613
- package/scripts/daemon/assurance.test.mjs +0 -1791
- package/scripts/daemon/board-mirror.test.mjs +0 -165
- package/scripts/daemon/cadence-consumer-frontdoor.test.mjs +0 -393
- package/scripts/daemon/cadence-consumer-governance.test.mjs +0 -276
- package/scripts/daemon/cadence-consumer.test.mjs +0 -776
- package/scripts/daemon/cadence-handlers.test.mjs +0 -837
- package/scripts/daemon/classifier-identity.test.mjs +0 -137
- package/scripts/daemon/classifier.test.mjs +0 -266
- package/scripts/daemon/classify-kind.test.mjs +0 -40
- package/scripts/daemon/context-compiler.test.mjs +0 -406
- package/scripts/daemon/deliver.test.mjs +0 -564
- package/scripts/daemon/dispatcher-cooldown.test.mjs +0 -122
- package/scripts/daemon/dispatcher-governance.test.mjs +0 -1013
- package/scripts/daemon/dispatcher-resume.test.mjs +0 -166
- package/scripts/daemon/dispatcher-session-continuity.test.mjs +0 -365
- package/scripts/daemon/execution-ladder.test.mjs +0 -470
- package/scripts/daemon/goal-steward-cadence.test.mjs +0 -312
- package/scripts/daemon/inbox-deferral-session.test.mjs +0 -49
- package/scripts/daemon/inbox-deferral.test.mjs +0 -336
- package/scripts/daemon/inbox-wake.test.mjs +0 -199
- package/scripts/daemon/integration.test.mjs +0 -149
- package/scripts/daemon/lib/self-echo.test.mjs +0 -153
- package/scripts/daemon/lib/session-router.test.mjs +0 -554
- package/scripts/daemon/prompt-builder-preamble.test.mjs +0 -210
- package/scripts/daemon/prompt-builder.test.mjs +0 -556
- package/scripts/daemon/responder-cost.test.mjs +0 -68
- package/scripts/daemon/responder-history.test.mjs +0 -221
- package/scripts/daemon/sdk-version.test.mjs +0 -31
- package/scripts/daemon/session-lock.test.mjs +0 -252
- package/scripts/daemon/session-outcomes.test.mjs +0 -533
- package/scripts/daemon/typing-registry.test.mjs +0 -102
- package/scripts/hooks/pre-send-audit.test.mjs +0 -354
- package/scripts/huddle/huddle-prompt.test.mjs +0 -176
- package/scripts/local-triggers/autoupdate.test.mjs +0 -518
- package/scripts/local-triggers/generate-plists.test.mjs +0 -456
- package/scripts/media-generation/brand-clause.test.mjs +0 -135
- package/scripts/org/send-orgmail.first-contact.test.mjs +0 -102
- package/scripts/poller/inbox-privilege-injection.test.mjs +0 -167
- package/scripts/poller/inbox-scan-poller.test.mjs +0 -295
- package/scripts/poller/lib/cloud-relay-dedup.test.mjs +0 -133
- package/scripts/poller/slack-socket-mode.test.mjs +0 -805
- package/scripts/poller-launchd/install.test.mjs +0 -243
- package/scripts/restore-from-backup.test.mjs +0 -181
- package/scripts/session/feed.test.mjs +0 -196
- package/scripts/session/supervisor-sh.test.mjs +0 -218
- package/scripts/session/supervisor.test.mjs +0 -482
- package/scripts/setup/configure-macos.test.mjs +0 -306
- package/scripts/setup/gen-subagent-manifest.test.mjs +0 -124
- package/scripts/setup/generate-agent-package-json.test.mjs +0 -143
- package/scripts/setup/generate-capability.test.mjs +0 -134
- package/scripts/setup/init-agent.test.mjs +0 -370
- package/scripts/setup/init-skill-marketplace.test.mjs +0 -193
- package/scripts/vendor/sync-skill-packs.test.mjs +0 -103
- package/scripts/watchdog/memory-watchdog.test.mjs +0 -64
|
@@ -1,438 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tests for lib/model-router/economics.mjs — the router economics levers:
|
|
3
|
-
* - budgetLadder transitions at 100 / 125 / 150 (a ladder, not a cliff) —
|
|
4
|
-
* the SAME rungs lib/budget-guard bands the seat envelope on
|
|
5
|
-
* - degradeChain tightens a candidate chain per band
|
|
6
|
-
* - batchLaneFor classifies deferrable vs realtime work
|
|
7
|
-
* - cacheAffinity pin / lookupPin / resolvePin override (+ pin_overridden audit)
|
|
8
|
-
* - spawnKnobsFor task-class defaults incl. the cheap-subagent map
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
import { test } from "node:test";
|
|
12
|
-
import assert from "node:assert/strict";
|
|
13
|
-
|
|
14
|
-
import {
|
|
15
|
-
budgetLadder,
|
|
16
|
-
degradeChain,
|
|
17
|
-
batchLaneFor,
|
|
18
|
-
pin,
|
|
19
|
-
lookupPin,
|
|
20
|
-
resolvePin,
|
|
21
|
-
spawnKnobsFor,
|
|
22
|
-
ttlMs,
|
|
23
|
-
hashSessionKey,
|
|
24
|
-
_internals,
|
|
25
|
-
} from "./economics.mjs";
|
|
26
|
-
|
|
27
|
-
// ---------------------------------------------------------------------------
|
|
28
|
-
// budgetLadder — transitions at 100/125/150 (the governance ladder's rungs)
|
|
29
|
-
// ---------------------------------------------------------------------------
|
|
30
|
-
|
|
31
|
-
test("budgetLadder: below 100% is band 0, no degradation", () => {
|
|
32
|
-
for (const spent of [0, 10, 37, 80, 99.9]) {
|
|
33
|
-
const r = budgetLadder(spent, 100);
|
|
34
|
-
assert.equal(r.band, 0, `spent ${spent} → band 0`);
|
|
35
|
-
assert.equal(r.degrade.preferCheaper, false);
|
|
36
|
-
assert.equal(r.degrade.cheapOnly, false);
|
|
37
|
-
assert.equal(r.degrade.essentialOnly, false);
|
|
38
|
-
}
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
test("budgetLadder: the 80% supervisor notice does NOT degrade routing (band 0)", () => {
|
|
42
|
-
// budget-guard notifies the SUPERVISOR at 80%, but a notice is not a routing
|
|
43
|
-
// change — the first degradation is at 100%, where the seat has spent what the
|
|
44
|
-
// org actually funded.
|
|
45
|
-
const r = budgetLadder(80, 100);
|
|
46
|
-
assert.equal(r.band, 0);
|
|
47
|
-
assert.equal(r.degrade.downgradeTiers, 0);
|
|
48
|
-
assert.equal(r.degrade.selfDirected, true);
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
test("budgetLadder: 100% → cheapest class, fan-out 1, no self-directed work", () => {
|
|
52
|
-
const r = budgetLadder(100, 100);
|
|
53
|
-
assert.equal(r.band, 100);
|
|
54
|
-
assert.equal(r.degrade.preferCheaper, true);
|
|
55
|
-
assert.equal(r.degrade.cheapOnly, true);
|
|
56
|
-
assert.equal(r.degrade.batchDeferrable, true);
|
|
57
|
-
assert.equal(r.degrade.maxFanout, 1);
|
|
58
|
-
assert.equal(r.degrade.selfDirected, false, "D3 stops here");
|
|
59
|
-
assert.equal(r.degrade.essentialOnly, false, "inbox + obligations still run — this rung is not a cliff");
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
test("budgetLadder: 125% → essential-only on top of the degrade rung", () => {
|
|
63
|
-
const r = budgetLadder(125, 100);
|
|
64
|
-
assert.equal(r.band, 125);
|
|
65
|
-
assert.equal(r.degrade.essentialOnly, true);
|
|
66
|
-
assert.equal(r.degrade.cheapOnly, true);
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
test("budgetLadder: past 150% stays band 150", () => {
|
|
70
|
-
assert.equal(budgetLadder(150, 100).band, 150);
|
|
71
|
-
assert.equal(budgetLadder(250, 100).band, 150);
|
|
72
|
-
assert.equal(budgetLadder(250, 100).degrade.essentialOnly, true);
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
test("budgetLadder: a cap of ZERO with money spent is EXHAUSTED, not uncapped", () => {
|
|
76
|
-
// This test used to assert band 0 for every cap<=0, and it was correct when
|
|
77
|
-
// capUSD could only be a local config number > 0. `dailyAllowance` now returns
|
|
78
|
-
// 0 for a seat whose MONTHLY envelope is spent out — so cap===0 became a
|
|
79
|
-
// reachable production value meaning "there is no room at all", and collapsing
|
|
80
|
-
// it to band 0 inverted the ladder: the further past its envelope a seat was,
|
|
81
|
-
// the less the router degraded.
|
|
82
|
-
assert.equal(budgetLadder(500, 0).band, 150, "$500 spent against a $0 allowance is fully over");
|
|
83
|
-
assert.equal(budgetLadder(500, -1).band, 150);
|
|
84
|
-
assert.equal(budgetLadder(500, NaN).band, 150);
|
|
85
|
-
// Nothing spent and no cap is genuinely unknown — do not degrade on it.
|
|
86
|
-
assert.equal(budgetLadder(0, 0).band, 0);
|
|
87
|
-
assert.equal(budgetLadder(0, NaN).band, 0);
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
test("budgetLadder: the governor's band wins over any re-derivation", () => {
|
|
91
|
-
// The dispatcher and the cadence consumer pass `dailyStatus().band`, which has
|
|
92
|
-
// already reconciled day vs month vs blindness. Re-deriving from (spent, cap)
|
|
93
|
-
// is what let a seat at 109% of its MONTH route the full frontier chain
|
|
94
|
-
// because today happened to look quiet.
|
|
95
|
-
const r = budgetLadder(3, 30, { band: 125 });
|
|
96
|
-
assert.equal(r.band, 125);
|
|
97
|
-
assert.equal(r.degrade.cheapOnly, true);
|
|
98
|
-
assert.equal(r.degrade.essentialOnly, true);
|
|
99
|
-
assert.equal(budgetLadder(3, 30).band, 0, "and without it, the local reading still applies");
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
test("budgetLadder: pct is rounded and exposed", () => {
|
|
103
|
-
const r = budgetLadder(33.333, 100);
|
|
104
|
-
assert.equal(r.pct, 33.33);
|
|
105
|
-
assert.equal(r.capUSD, 100);
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
// ---------------------------------------------------------------------------
|
|
109
|
-
// degradeChain — tighten per band
|
|
110
|
-
// ---------------------------------------------------------------------------
|
|
111
|
-
|
|
112
|
-
const ALIASES = {
|
|
113
|
-
frontier: "anthropic/claude-opus-4-8",
|
|
114
|
-
default: "anthropic/claude-sonnet-4-6",
|
|
115
|
-
fast: "anthropic/claude-haiku-4-5",
|
|
116
|
-
cheap: "deepseek/deepseek-v4-flash",
|
|
117
|
-
};
|
|
118
|
-
|
|
119
|
-
function aliasChain(...names) {
|
|
120
|
-
return names.map((n) => ({ ref: ALIASES[n] || n, alias: ALIASES[n] ? n : null }));
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
test("degradeChain: band < 100 returns the chain unchanged", () => {
|
|
124
|
-
const chain = aliasChain("frontier", "default");
|
|
125
|
-
const out = degradeChain(chain, 0, { aliases: ALIASES });
|
|
126
|
-
assert.deepEqual(out, chain);
|
|
127
|
-
assert.notEqual(out, chain, "returns a copy, not the same reference");
|
|
128
|
-
});
|
|
129
|
-
|
|
130
|
-
test("degradeChain: the 80% notice rung changes no routing at all", () => {
|
|
131
|
-
// There is deliberately no one-rung "prefer cheaper" step any more: the band
|
|
132
|
-
// below the drop rung is a supervisor NOTICE, and a notice must not silently
|
|
133
|
-
// re-route the seat onto a worse model.
|
|
134
|
-
const chain = aliasChain("frontier", "default");
|
|
135
|
-
assert.deepEqual(degradeChain(chain, 80, { aliases: ALIASES }), chain);
|
|
136
|
-
});
|
|
137
|
-
|
|
138
|
-
test("degradeChain: band 100 drops frontier/default head, keeps cheap/fast", () => {
|
|
139
|
-
const chain = aliasChain("frontier", "default", "fast", "cheap");
|
|
140
|
-
const out = degradeChain(chain, 100, { aliases: ALIASES });
|
|
141
|
-
const aliasesOut = out.map((m) => m.alias);
|
|
142
|
-
assert.ok(!aliasesOut.includes("frontier"));
|
|
143
|
-
assert.ok(!aliasesOut.includes("default"));
|
|
144
|
-
assert.deepEqual(aliasesOut, ["fast", "cheap"]);
|
|
145
|
-
});
|
|
146
|
-
|
|
147
|
-
test("degradeChain: band 100 with an all-frontier chain keeps the single cheapest as a safety net", () => {
|
|
148
|
-
const chain = aliasChain("frontier", "default");
|
|
149
|
-
const out = degradeChain(chain, 100, { aliases: ALIASES });
|
|
150
|
-
assert.equal(out.length, 1);
|
|
151
|
-
// 'default' is cheaper than 'frontier', so it's the safety-net survivor.
|
|
152
|
-
assert.equal(out[0].alias, "default");
|
|
153
|
-
});
|
|
154
|
-
|
|
155
|
-
test("degradeChain: exempt (pin/critical) bypasses degradation", () => {
|
|
156
|
-
const chain = aliasChain("frontier", "default");
|
|
157
|
-
const out = degradeChain(chain, 100, { aliases: ALIASES, exempt: true });
|
|
158
|
-
assert.deepEqual(out.map((m) => m.alias), ["frontier", "default"]);
|
|
159
|
-
});
|
|
160
|
-
|
|
161
|
-
test("degradeChain: preserves a rules sentinel as a tail at any band", () => {
|
|
162
|
-
const chain = [...aliasChain("frontier"), { ref: "rules", isRules: true }];
|
|
163
|
-
const out90 = degradeChain(chain, 100, { aliases: ALIASES });
|
|
164
|
-
assert.ok(out90.some((m) => m.ref === "rules"));
|
|
165
|
-
});
|
|
166
|
-
|
|
167
|
-
test("degradeChain: infers tier from literal refs without alias tags", () => {
|
|
168
|
-
const chain = [{ ref: "anthropic/claude-opus-4-8" }, { ref: "anthropic/claude-haiku-4-5" }];
|
|
169
|
-
const out = degradeChain(chain, 100, { aliases: ALIASES });
|
|
170
|
-
// opus (frontier) dropped; haiku (fast) kept.
|
|
171
|
-
assert.equal(out.length, 1);
|
|
172
|
-
assert.match(out[0].ref, /haiku/);
|
|
173
|
-
});
|
|
174
|
-
|
|
175
|
-
test("degradeChain: de-dupes refs that collapse onto the same model", () => {
|
|
176
|
-
const chain = aliasChain("fast", "fast");
|
|
177
|
-
const out = degradeChain(chain, 100, { aliases: ALIASES });
|
|
178
|
-
assert.equal(out.length, 1);
|
|
179
|
-
});
|
|
180
|
-
|
|
181
|
-
// ---------------------------------------------------------------------------
|
|
182
|
-
// inferTier — the untagged-ref heuristic behind degradeChain
|
|
183
|
-
// ---------------------------------------------------------------------------
|
|
184
|
-
|
|
185
|
-
const { inferTier } = _internals;
|
|
186
|
-
|
|
187
|
-
test("inferTier: a cheap PROVIDER beats a 'flash'-shaped model name", () => {
|
|
188
|
-
// The regression this locks: the fast check (haiku|flash|mini) used to run
|
|
189
|
-
// BEFORE the cheap check, so the two cheapest rows in the bundled catalog were
|
|
190
|
-
// filed on the Haiku rung — deepseek-v4-flash is $0.14/$0.28 against Haiku's
|
|
191
|
-
// $1/$5, a ~7x mis-read of the ladder's own bottom rung.
|
|
192
|
-
assert.equal(inferTier({ ref: "deepseek/deepseek-v4-flash" }), "cheap");
|
|
193
|
-
assert.equal(inferTier({ ref: "qwen/qwen3.5-flash" }), "cheap");
|
|
194
|
-
// …while a genuinely fast-tier Anthropic row is untouched.
|
|
195
|
-
assert.equal(inferTier({ ref: "anthropic/claude-haiku-4-5" }), "fast");
|
|
196
|
-
assert.equal(inferTier({ ref: "anthropic/claude-opus-5" }), "frontier");
|
|
197
|
-
assert.equal(inferTier({ ref: "anthropic/claude-sonnet-5" }), "default");
|
|
198
|
-
});
|
|
199
|
-
|
|
200
|
-
test("inferTier: every cheap provider the economics layer NAMES is classified", () => {
|
|
201
|
-
// economics.mjs has always claimed deepseek/kimi/glm/qwen as the cheap tier;
|
|
202
|
-
// xai/grok is added so the classifier is ready the moment a Grok row lands
|
|
203
|
-
// (an agent can add one today via catalog_overrides — see model-router.test.mjs).
|
|
204
|
-
for (const ref of [
|
|
205
|
-
"deepseek/deepseek-v4-flash",
|
|
206
|
-
"moonshot/kimi-k2.6",
|
|
207
|
-
"qwen/qwen3-coder-plus",
|
|
208
|
-
"xai/grok-4-fast",
|
|
209
|
-
"zhipu/glm-5",
|
|
210
|
-
"kimi-k2.6", // bare, no provider segment
|
|
211
|
-
"grok-4-fast", // bare, no provider segment
|
|
212
|
-
]) {
|
|
213
|
-
assert.equal(inferTier({ ref }), "cheap", `${ref} should classify as cheap-tier`);
|
|
214
|
-
}
|
|
215
|
-
});
|
|
216
|
-
|
|
217
|
-
test("inferTier: a cheap provider's WHOLE line is cheap, not just its famous model", () => {
|
|
218
|
-
// The model-name hints ("kimi", "grok", "glm") would carry the refs above on
|
|
219
|
-
// their own, which makes them a trap: the next model each of these providers
|
|
220
|
-
// ships will not be called "grok-4" or "kimi-k2". The provider segment is the
|
|
221
|
-
// durable signal, so these refs — none of which contain a hint word — are the
|
|
222
|
-
// ones that actually hold CHEAP_PROVIDERS in place.
|
|
223
|
-
assert.equal(inferTier({ ref: "xai/sonic-2" }), "cheap");
|
|
224
|
-
assert.equal(inferTier({ ref: "moonshot/k3-preview" }), "cheap");
|
|
225
|
-
assert.equal(inferTier({ ref: "deepseek/v5" }), "cheap");
|
|
226
|
-
});
|
|
227
|
-
|
|
228
|
-
test("inferTier: an unrecognised ref stays null so degradeChain fails OPEN", () => {
|
|
229
|
-
// A model we cannot classify must be KEPT, never dropped: a budget heuristic
|
|
230
|
-
// that silently deletes candidates it doesn't recognise strands the caller
|
|
231
|
-
// with an empty chain. Asserted against a MIXED chain on purpose — with an
|
|
232
|
-
// unknown row alone, the "keep the single cheapest" safety net returns it
|
|
233
|
-
// either way, so a fail-closed filter would slip through unnoticed.
|
|
234
|
-
assert.equal(inferTier({ ref: "acme/some-new-model" }), null);
|
|
235
|
-
const out = degradeChain(
|
|
236
|
-
[{ ref: "anthropic/claude-opus-5" }, { ref: "acme/some-new-model" }],
|
|
237
|
-
150,
|
|
238
|
-
{}
|
|
239
|
-
);
|
|
240
|
-
assert.deepEqual(
|
|
241
|
-
out.map((m) => m.ref),
|
|
242
|
-
["acme/some-new-model"],
|
|
243
|
-
"frontier dropped, the unclassifiable row survives"
|
|
244
|
-
);
|
|
245
|
-
});
|
|
246
|
-
|
|
247
|
-
test("degradeChain: a cheap third-party row survives band 150 alongside fast rows", () => {
|
|
248
|
-
const chain = [
|
|
249
|
-
{ ref: "anthropic/claude-opus-5" },
|
|
250
|
-
{ ref: "anthropic/claude-haiku-4-5" },
|
|
251
|
-
{ ref: "xai/grok-4-fast" },
|
|
252
|
-
];
|
|
253
|
-
const out = degradeChain(chain, 150, {}).map((m) => m.ref);
|
|
254
|
-
assert.deepEqual(out, ["anthropic/claude-haiku-4-5", "xai/grok-4-fast"]);
|
|
255
|
-
});
|
|
256
|
-
|
|
257
|
-
// ---------------------------------------------------------------------------
|
|
258
|
-
// batchLaneFor — classify deferrable vs realtime
|
|
259
|
-
// ---------------------------------------------------------------------------
|
|
260
|
-
|
|
261
|
-
test("batchLaneFor: explicit lane wins", () => {
|
|
262
|
-
assert.equal(batchLaneFor({ lane: "batch", task_class: "classify.inbox" }), true);
|
|
263
|
-
assert.equal(batchLaneFor({ lane: "realtime", task_class: "learning.reflect" }), false);
|
|
264
|
-
});
|
|
265
|
-
|
|
266
|
-
test("batchLaneFor: deferrable task classes default to batch", () => {
|
|
267
|
-
assert.equal(batchLaneFor({ task_class: "learning.reflect" }), true);
|
|
268
|
-
assert.equal(batchLaneFor({ task_class: "learning.curate" }), true);
|
|
269
|
-
assert.equal(batchLaneFor({ task_class: "digest.daily" }), true);
|
|
270
|
-
assert.equal(batchLaneFor({ task_class: "index.sessions" }), true);
|
|
271
|
-
assert.equal(batchLaneFor({ task_class: "curate.skills" }), true);
|
|
272
|
-
assert.equal(batchLaneFor({ task_class: "enrich" }), true);
|
|
273
|
-
});
|
|
274
|
-
|
|
275
|
-
test("batchLaneFor: realtime classes never defer", () => {
|
|
276
|
-
assert.equal(batchLaneFor({ task_class: "classify.inbox" }), false);
|
|
277
|
-
assert.equal(batchLaneFor({ task_class: "session.responder" }), false);
|
|
278
|
-
assert.equal(batchLaneFor({ task_class: "huddle" }), false);
|
|
279
|
-
assert.equal(batchLaneFor({ task_class: "voice.brief" }), false);
|
|
280
|
-
});
|
|
281
|
-
|
|
282
|
-
test("batchLaneFor: high/critical priority never defers even a learning task", () => {
|
|
283
|
-
assert.equal(batchLaneFor({ task_class: "learning.reflect", priority: "critical" }), false);
|
|
284
|
-
assert.equal(batchLaneFor({ task_class: "learning.reflect", priority: "high" }), false);
|
|
285
|
-
});
|
|
286
|
-
|
|
287
|
-
test("batchLaneFor: unknown class defaults to realtime (never silently defers user work)", () => {
|
|
288
|
-
assert.equal(batchLaneFor({ task_class: "lookup.gmail" }), false);
|
|
289
|
-
assert.equal(batchLaneFor({}), false);
|
|
290
|
-
});
|
|
291
|
-
|
|
292
|
-
// ---------------------------------------------------------------------------
|
|
293
|
-
// cacheAffinity — pin / lookupPin / resolvePin override
|
|
294
|
-
// ---------------------------------------------------------------------------
|
|
295
|
-
|
|
296
|
-
test("ttlMs: parses lane cache_ttl strings", () => {
|
|
297
|
-
assert.equal(ttlMs("1h"), 3600_000);
|
|
298
|
-
assert.equal(ttlMs("5m"), 300_000);
|
|
299
|
-
assert.equal(ttlMs("30s"), 30_000);
|
|
300
|
-
assert.equal(ttlMs("0"), 0);
|
|
301
|
-
assert.equal(ttlMs("none"), 0);
|
|
302
|
-
assert.equal(ttlMs(60_000), 60_000);
|
|
303
|
-
assert.equal(ttlMs(undefined), 3600_000);
|
|
304
|
-
});
|
|
305
|
-
|
|
306
|
-
test("cacheAffinity: pin then lookupPin within TTL", () => {
|
|
307
|
-
const store = {};
|
|
308
|
-
let clock = 1_000_000;
|
|
309
|
-
const deps = { store, now: () => clock };
|
|
310
|
-
const r = pin("slack:C1:thread", "anthropic/claude-sonnet-4-6", "1h", { ...deps, decisionId: "d1" });
|
|
311
|
-
assert.equal(r.pinned, true);
|
|
312
|
-
assert.equal(r.expires_at, 1_000_000 + 3600_000);
|
|
313
|
-
|
|
314
|
-
const live = lookupPin("slack:C1:thread", deps);
|
|
315
|
-
assert.ok(live);
|
|
316
|
-
assert.equal(live.ref, "anthropic/claude-sonnet-4-6");
|
|
317
|
-
assert.equal(live.decision_id, "d1");
|
|
318
|
-
});
|
|
319
|
-
|
|
320
|
-
test("cacheAffinity: pin expires after TTL", () => {
|
|
321
|
-
const store = {};
|
|
322
|
-
let clock = 0;
|
|
323
|
-
const deps = { store, now: () => clock };
|
|
324
|
-
pin("k", "anthropic/claude-haiku-4-5", "5m", deps);
|
|
325
|
-
clock = 4 * 60_000;
|
|
326
|
-
assert.ok(lookupPin("k", deps), "live at 4m");
|
|
327
|
-
clock = 6 * 60_000;
|
|
328
|
-
assert.equal(lookupPin("k", deps), null, "expired at 6m");
|
|
329
|
-
});
|
|
330
|
-
|
|
331
|
-
test("cacheAffinity: ttl 0 does not pin", () => {
|
|
332
|
-
const store = {};
|
|
333
|
-
const r = pin("k", "anthropic/x", "0", { store, now: () => 0 });
|
|
334
|
-
assert.equal(r.pinned, false);
|
|
335
|
-
assert.equal(lookupPin("k", { store, now: () => 0 }), null);
|
|
336
|
-
});
|
|
337
|
-
|
|
338
|
-
test("cacheAffinity: on-disk store hashes the session_key (no raw PII)", () => {
|
|
339
|
-
const store = {};
|
|
340
|
-
pin("dm:+15551234567", "anthropic/x", "1h", { store, now: () => 0 });
|
|
341
|
-
const keys = Object.keys(store);
|
|
342
|
-
assert.equal(keys.length, 1);
|
|
343
|
-
assert.equal(keys[0], hashSessionKey("dm:+15551234567"));
|
|
344
|
-
assert.ok(!keys[0].includes("5551234567"));
|
|
345
|
-
});
|
|
346
|
-
|
|
347
|
-
test("resolvePin: honors a live pin that passes the gate", () => {
|
|
348
|
-
const store = {};
|
|
349
|
-
const deps = { store, now: () => 0 };
|
|
350
|
-
pin("k", "anthropic/claude-sonnet-4-6", "1h", deps);
|
|
351
|
-
const r = resolvePin("k", () => ({ ok: true }), deps);
|
|
352
|
-
assert.equal(r.ref, "anthropic/claude-sonnet-4-6");
|
|
353
|
-
assert.equal(r.overridden, false);
|
|
354
|
-
});
|
|
355
|
-
|
|
356
|
-
test("resolvePin: drops a pin the gate rejects and emits pin_overridden", () => {
|
|
357
|
-
const store = {};
|
|
358
|
-
const audit = [];
|
|
359
|
-
const deps = { store, now: () => 0, audit: (row) => audit.push(row), decisionId: "d9" };
|
|
360
|
-
pin("k", "moonshot/kimi-k2.6", "1h", deps);
|
|
361
|
-
|
|
362
|
-
const r = resolvePin("k", () => ({ ok: false, reason: "breaker_open" }), deps);
|
|
363
|
-
assert.equal(r.ref, null);
|
|
364
|
-
assert.equal(r.overridden, true);
|
|
365
|
-
assert.equal(r.reason, "breaker_open");
|
|
366
|
-
|
|
367
|
-
assert.equal(audit.length, 1);
|
|
368
|
-
assert.equal(audit[0].kind, "pin_overridden");
|
|
369
|
-
assert.equal(audit[0].ref, "moonshot/kimi-k2.6");
|
|
370
|
-
assert.equal(audit[0].reason, "breaker_open");
|
|
371
|
-
// The raw session_key is hashed in the audit row, not stored verbatim.
|
|
372
|
-
assert.equal(audit[0].session_key_hash, hashSessionKey("k"));
|
|
373
|
-
});
|
|
374
|
-
|
|
375
|
-
test("resolvePin: no live pin returns a clean null result", () => {
|
|
376
|
-
const r = resolvePin("absent", () => ({ ok: true }), { store: {}, now: () => 0 });
|
|
377
|
-
assert.equal(r.ref, null);
|
|
378
|
-
assert.equal(r.overridden, false);
|
|
379
|
-
});
|
|
380
|
-
|
|
381
|
-
test("resolvePin: a throwing gate is treated as a rejection (fail-closed for the pin)", () => {
|
|
382
|
-
const store = {};
|
|
383
|
-
const audit = [];
|
|
384
|
-
const deps = { store, now: () => 0, audit: (row) => audit.push(row) };
|
|
385
|
-
pin("k", "anthropic/x", "1h", deps);
|
|
386
|
-
const r = resolvePin("k", () => { throw new Error("boom"); }, deps);
|
|
387
|
-
assert.equal(r.overridden, true);
|
|
388
|
-
assert.equal(r.reason, "gate_error");
|
|
389
|
-
assert.equal(audit.length, 1);
|
|
390
|
-
});
|
|
391
|
-
|
|
392
|
-
// ---------------------------------------------------------------------------
|
|
393
|
-
// spawnKnobsFor — per-task-class defaults incl. cheap-subagent map
|
|
394
|
-
// ---------------------------------------------------------------------------
|
|
395
|
-
|
|
396
|
-
test("spawnKnobsFor: heavy session classes get the Haiku-Explore subagent map", () => {
|
|
397
|
-
const k = spawnKnobsFor("session.responder");
|
|
398
|
-
assert.equal(k.maxTurns, 40);
|
|
399
|
-
assert.ok(k.agentsJson);
|
|
400
|
-
assert.ok(k.agentsJson["Haiku-Explore"]);
|
|
401
|
-
assert.match(k.agentsJson["Haiku-Explore"].model, /haiku/);
|
|
402
|
-
});
|
|
403
|
-
|
|
404
|
-
test("spawnKnobsFor: cheap one-shot classes get no subagent map", () => {
|
|
405
|
-
assert.equal(spawnKnobsFor("classify.inbox").agentsJson, null);
|
|
406
|
-
assert.equal(spawnKnobsFor("classify.inbox").maxTurns, 1);
|
|
407
|
-
assert.equal(spawnKnobsFor("lookup.gmail").maxTurns, 2); // prefix match
|
|
408
|
-
assert.equal(spawnKnobsFor("lookup.gmail").agentsJson, null);
|
|
409
|
-
});
|
|
410
|
-
|
|
411
|
-
test("spawnKnobsFor: learning.reflect uses medium effort", () => {
|
|
412
|
-
assert.equal(spawnKnobsFor("learning.reflect").effort, "medium");
|
|
413
|
-
});
|
|
414
|
-
|
|
415
|
-
test("spawnKnobsFor: prefix match for cadence.* ", () => {
|
|
416
|
-
const k = spawnKnobsFor("cadence.daily-digest");
|
|
417
|
-
assert.equal(k.maxTurns, 20);
|
|
418
|
-
assert.ok(k.agentsJson);
|
|
419
|
-
});
|
|
420
|
-
|
|
421
|
-
test("spawnKnobsFor: unknown class falls back to the generic ceiling", () => {
|
|
422
|
-
const k = spawnKnobsFor("totally.unknown");
|
|
423
|
-
assert.equal(k.maxTurns, 40);
|
|
424
|
-
assert.equal(k.agentsJson, null);
|
|
425
|
-
});
|
|
426
|
-
|
|
427
|
-
test("spawnKnobsFor: returns a fresh agentsJson clone (safe to mutate)", () => {
|
|
428
|
-
const a = spawnKnobsFor("session.responder");
|
|
429
|
-
const b = spawnKnobsFor("session.responder");
|
|
430
|
-
assert.notEqual(a.agentsJson, b.agentsJson);
|
|
431
|
-
a.agentsJson["Haiku-Explore"].model = "tampered";
|
|
432
|
-
assert.notEqual(b.agentsJson["Haiku-Explore"].model, "tampered");
|
|
433
|
-
});
|
|
434
|
-
|
|
435
|
-
test("internals exposed for cross-layer integration", () => {
|
|
436
|
-
assert.ok(Array.isArray(_internals.DEFERRABLE_PREFIXES));
|
|
437
|
-
assert.ok(_internals.TIER_RANK.frontier < _internals.TIER_RANK.cheap);
|
|
438
|
-
});
|