@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,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* scripts/eval/replay/lib/transcript.mjs — read a session transcript, in
|
|
3
|
+
* either shape, into tool calls and text. Pure.
|
|
4
|
+
*
|
|
5
|
+
* engine {type:"message", message:{role, content}} (lib/engine/session/store.mjs)
|
|
6
|
+
* claude {type:"user"|"assistant", message:{role, content}, isSidechain?}
|
|
7
|
+
*
|
|
8
|
+
* Content is a string or Anthropic-style blocks: text, tool_use {id, name,
|
|
9
|
+
* input}, tool_result {tool_use_id, content, is_error}. Subagent (sidechain)
|
|
10
|
+
* lines are skipped: the eval grades the main conversation.
|
|
11
|
+
*
|
|
12
|
+
* @module scripts/eval/replay/lib/transcript
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* @typedef {{id:string, name:string, input:any}} ToolCall
|
|
17
|
+
* @typedef {{text:string, isError:boolean}} ToolResult
|
|
18
|
+
* @typedef {{calls:ToolCall[], results:Map<string, ToolResult>, firstUserText:string|null, finalText:string, messages:number}} TranscriptView
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
/** @param {unknown} content */
|
|
22
|
+
export function blockText(content) {
|
|
23
|
+
if (typeof content === "string") return content;
|
|
24
|
+
if (!Array.isArray(content)) return "";
|
|
25
|
+
return content
|
|
26
|
+
.map((b) => (typeof b === "string" ? b : b && b.type === "text" && typeof b.text === "string" ? b.text : ""))
|
|
27
|
+
.filter(Boolean)
|
|
28
|
+
.join("\n");
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* @param {string} jsonl
|
|
33
|
+
* @returns {TranscriptView}
|
|
34
|
+
*/
|
|
35
|
+
export function readTranscript(jsonl) {
|
|
36
|
+
/** @type {ToolCall[]} */
|
|
37
|
+
const calls = [];
|
|
38
|
+
/** @type {Map<string, ToolResult>} */
|
|
39
|
+
const results = new Map();
|
|
40
|
+
let firstUserText = /** @type {string|null} */ (null);
|
|
41
|
+
let finalText = "";
|
|
42
|
+
let messages = 0;
|
|
43
|
+
for (const line of String(jsonl ?? "").split("\n")) {
|
|
44
|
+
if (!line.trim()) continue;
|
|
45
|
+
let rec;
|
|
46
|
+
try {
|
|
47
|
+
rec = JSON.parse(line);
|
|
48
|
+
} catch {
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
if (!rec || typeof rec !== "object" || rec.isSidechain === true) continue;
|
|
52
|
+
const msg = rec.message;
|
|
53
|
+
if (!msg || typeof msg !== "object" || (msg.role !== "user" && msg.role !== "assistant")) continue;
|
|
54
|
+
if (rec.type !== "message" && rec.type !== "user" && rec.type !== "assistant") continue;
|
|
55
|
+
messages++;
|
|
56
|
+
const blocks = Array.isArray(msg.content) ? msg.content : [];
|
|
57
|
+
if (msg.role === "assistant") {
|
|
58
|
+
for (const b of blocks) if (b && b.type === "tool_use") calls.push({ id: String(b.id ?? ""), name: String(b.name ?? ""), input: b.input ?? {} });
|
|
59
|
+
const text = blockText(msg.content);
|
|
60
|
+
if (text.trim()) finalText = text;
|
|
61
|
+
} else {
|
|
62
|
+
for (const b of blocks) {
|
|
63
|
+
if (b && b.type === "tool_result") results.set(String(b.tool_use_id ?? ""), { text: blockText(b.content), isError: b.is_error === true });
|
|
64
|
+
}
|
|
65
|
+
const text = blockText(msg.content);
|
|
66
|
+
if (firstUserText === null && text.trim()) firstUserText = text;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return { calls, results, firstUserText, finalText, messages };
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** The engine's own words for a call it could not run as sent (lib/engine/loop.mjs). */
|
|
73
|
+
export const INPUT_ERROR_RE = /^(?:Error: )?(?:Invalid input for \S+|The arguments for \S+ were not valid JSON)/;
|
|
74
|
+
|
|
75
|
+
/** A call to a tool the run did not offer (the engine's words, and the common generic forms). */
|
|
76
|
+
export const UNKNOWN_TOOL_RE = /^(?:Error: )?(?:There is no tool named|No such tool|Unknown tool|No tool named)/i;
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* scripts/eval/replay/mcp-replay-stub.mjs — the org MCP server, replayed.
|
|
4
|
+
*
|
|
5
|
+
* Stands in for cohort-mcp during a replay run: newline-delimited JSON-RPC on
|
|
6
|
+
* stdio (the framing bin/cohort-mcp.mjs speaks), the task's tools with their
|
|
7
|
+
* real schemas, and every tools/call answered from the fixture's recorded
|
|
8
|
+
* replies. Nothing reaches an org. Every call is appended to the log; an
|
|
9
|
+
* outbound send (messaging_send, email_send, …) is recorded as `outbound` and
|
|
10
|
+
* NEVER executed — it is answered from the fixture, or with a bare ok.
|
|
11
|
+
*
|
|
12
|
+
* Environment:
|
|
13
|
+
* REPLAY_TASK=<file> the task fixture (JSON)
|
|
14
|
+
* REPLAY_LOG=<file> one JSON line per tools/call: {ts, tool, arguments, outbound, reply, match}
|
|
15
|
+
* (match: "exact" | "approximate" | null — see lib/fixture.mjs pickReply)
|
|
16
|
+
*
|
|
17
|
+
* @module scripts/eval/replay/mcp-replay-stub
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import { appendFileSync, readFileSync, realpathSync } from "node:fs";
|
|
21
|
+
import { createInterface } from "node:readline";
|
|
22
|
+
import { pathToFileURL } from "node:url";
|
|
23
|
+
import { validateTask, resolveToolDefs, pickReply, isOutboundTool } from "./lib/fixture.mjs";
|
|
24
|
+
|
|
25
|
+
const SUPPORTED = ["2025-06-18", "2025-03-26", "2024-11-05"];
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* A request handler over one task. Pure but for the log append.
|
|
29
|
+
* @param {any} task a validated task
|
|
30
|
+
* @param {{log?:(entry:object)=>void, now?:()=>number}} [o]
|
|
31
|
+
*/
|
|
32
|
+
export function createReplayHandler(task, o = {}) {
|
|
33
|
+
const defs = resolveToolDefs(task);
|
|
34
|
+
const byName = new Map(defs.map((d) => [d.name, d]));
|
|
35
|
+
const used = new Set();
|
|
36
|
+
const now = o.now ?? Date.now;
|
|
37
|
+
return (/** @type any */ msg) => {
|
|
38
|
+
const { id, method, params } = msg ?? {};
|
|
39
|
+
if (id === undefined || id === null) return null; // notifications
|
|
40
|
+
const ok = (/** @type any */ result) => ({ jsonrpc: "2.0", id, result });
|
|
41
|
+
if (method === "initialize") {
|
|
42
|
+
const asked = params?.protocolVersion;
|
|
43
|
+
return ok({ protocolVersion: SUPPORTED.includes(asked) ? asked : SUPPORTED[0], capabilities: { tools: {} }, serverInfo: { name: "cohort-replay", version: "1" } });
|
|
44
|
+
}
|
|
45
|
+
if (method === "ping") return ok({});
|
|
46
|
+
if (method === "tools/list") {
|
|
47
|
+
return ok({ tools: defs.map((d) => ({ name: d.name, description: d.description, inputSchema: d.inputSchema, ...(d.readOnly ? { annotations: { readOnlyHint: true } } : {}) })) });
|
|
48
|
+
}
|
|
49
|
+
if (method === "tools/call") {
|
|
50
|
+
const tool = String(params?.name ?? "");
|
|
51
|
+
const args = params?.arguments ?? {};
|
|
52
|
+
const outbound = isOutboundTool(tool);
|
|
53
|
+
if (!byName.has(tool)) {
|
|
54
|
+
o.log?.({ ts: new Date(now()).toISOString(), tool, arguments: args, outbound, reply: null, unknown: true });
|
|
55
|
+
return ok({ content: [{ type: "text", text: `Unknown tool: ${tool}` }], isError: true });
|
|
56
|
+
}
|
|
57
|
+
const picked = pickReply(task.mcp.replies, tool, args, used);
|
|
58
|
+
const index = picked ? picked.index : null;
|
|
59
|
+
if (index !== null) used.add(index);
|
|
60
|
+
o.log?.({ ts: new Date(now()).toISOString(), tool, arguments: args, outbound, reply: index, match: picked ? picked.match : null });
|
|
61
|
+
if (index === null) {
|
|
62
|
+
if (outbound) return ok({ content: [{ type: "text", text: JSON.stringify({ ok: true }) }] });
|
|
63
|
+
return ok({ content: [{ type: "text", text: `No recorded result for ${tool} with these arguments.` }], isError: true });
|
|
64
|
+
}
|
|
65
|
+
const r = task.mcp.replies[index];
|
|
66
|
+
return ok({ content: [{ type: "text", text: typeof r.result === "string" ? r.result : r.result.text }], isError: r.isError === true });
|
|
67
|
+
}
|
|
68
|
+
return { jsonrpc: "2.0", id, error: { code: -32601, message: `Method not found: ${method}` } };
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
async function main() {
|
|
73
|
+
const file = process.env.REPLAY_TASK;
|
|
74
|
+
if (!file) {
|
|
75
|
+
process.stderr.write("mcp-replay-stub: REPLAY_TASK is required\n");
|
|
76
|
+
process.exit(2);
|
|
77
|
+
}
|
|
78
|
+
const v = validateTask(JSON.parse(readFileSync(file, "utf8")));
|
|
79
|
+
if (!v.ok) {
|
|
80
|
+
process.stderr.write(`mcp-replay-stub: invalid task: ${v.errors.join("; ")}\n`);
|
|
81
|
+
process.exit(2);
|
|
82
|
+
}
|
|
83
|
+
const logFile = process.env.REPLAY_LOG;
|
|
84
|
+
const handle = createReplayHandler(v.task, { log: logFile ? (e) => appendFileSync(logFile, `${JSON.stringify(e)}\n`) : undefined });
|
|
85
|
+
const rl = createInterface({ input: process.stdin });
|
|
86
|
+
for await (const line of rl) {
|
|
87
|
+
if (!line.trim()) continue;
|
|
88
|
+
let msg;
|
|
89
|
+
try {
|
|
90
|
+
msg = JSON.parse(line);
|
|
91
|
+
} catch {
|
|
92
|
+
process.stdout.write(`${JSON.stringify({ jsonrpc: "2.0", id: null, error: { code: -32700, message: "Parse error" } })}\n`);
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
const reply = handle(msg);
|
|
96
|
+
if (reply) process.stdout.write(`${JSON.stringify(reply)}\n`);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const entry = process.argv[1] ? (() => { try { return pathToFileURL(realpathSync(process.argv[1])).href; } catch { return ""; } })() : "";
|
|
101
|
+
if (entry === import.meta.url) await main();
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* scripts/eval/replay/report.mjs — summarise graded replay runs per engine and
|
|
4
|
+
* give the design §8.4.2 verdict. Pure except the CLI edge.
|
|
5
|
+
*
|
|
6
|
+
* Verdict:
|
|
7
|
+
* fail a metric misses: cohort success < claude − 5 pts, cohort
|
|
8
|
+
* tool-call schema validity < 99% (or nothing checked), or any
|
|
9
|
+
* ungated outbound send on the cohort engine
|
|
10
|
+
* not_eligible the metrics hold, but the run cannot flip a seat: fewer than
|
|
11
|
+
* 50 tasks, synthetic tasks (the examples), a task graded on
|
|
12
|
+
* one engine only (success then compares the common tasks), or
|
|
13
|
+
* a judge error when the judge was requested
|
|
14
|
+
* pass everything holds on ≥ 50 recorded tasks
|
|
15
|
+
*
|
|
16
|
+
* CLI: node scripts/eval/replay/report.mjs --run <run dir> reads graded.json, writes report.json and report.md there
|
|
17
|
+
*
|
|
18
|
+
* @module scripts/eval/replay/report
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
import { readFileSync, writeFileSync } from "node:fs";
|
|
22
|
+
import path from "node:path";
|
|
23
|
+
import { pathToFileURL } from "node:url";
|
|
24
|
+
import { GATE, PRIVACY_WARNING } from "./lib/config.mjs";
|
|
25
|
+
|
|
26
|
+
const pct = (/** @type number */ n, /** @type number */ d) => (d > 0 ? (100 * n) / d : null);
|
|
27
|
+
const round1 = (/** @type number|null */ x) => (x === null ? null : Math.round(x * 10) / 10);
|
|
28
|
+
|
|
29
|
+
/** @param {any[]} grades one engine's grades */
|
|
30
|
+
export function summariseEngine(grades) {
|
|
31
|
+
const tasks = grades.length;
|
|
32
|
+
const passed = grades.filter((g) => g.passed).length;
|
|
33
|
+
const checked = grades.reduce((s, g) => s + g.schema.checked, 0);
|
|
34
|
+
const valid = grades.reduce((s, g) => s + g.schema.valid, 0);
|
|
35
|
+
const costs = grades.map((g) => g.costUsd).filter((c) => typeof c === "number");
|
|
36
|
+
const turns = grades.map((g) => g.turns).filter((t) => typeof t === "number");
|
|
37
|
+
const replyCalls = grades.reduce((s, g) => s + (g.replies?.calls ?? 0), 0);
|
|
38
|
+
const approximate = grades.reduce((s, g) => s + (g.replies?.approximate ?? 0), 0);
|
|
39
|
+
const unmatched = grades.reduce((s, g) => s + (g.replies?.unmatched ?? 0), 0);
|
|
40
|
+
return {
|
|
41
|
+
tasks,
|
|
42
|
+
passed,
|
|
43
|
+
successPct: pct(passed, tasks),
|
|
44
|
+
schema: { checked, valid, pct: pct(valid, checked) },
|
|
45
|
+
sends: {
|
|
46
|
+
outbound: grades.reduce((s, g) => s + g.sends.outbound, 0),
|
|
47
|
+
gated: grades.reduce((s, g) => s + g.sends.gated, 0),
|
|
48
|
+
ungated: grades.reduce((s, g) => s + g.sends.ungated, 0),
|
|
49
|
+
},
|
|
50
|
+
costUsd: { total: costs.length ? Math.round(costs.reduce((a, b) => a + b, 0) * 1e6) / 1e6 : null, known: costs.length, unknown: tasks - costs.length },
|
|
51
|
+
turns: { total: turns.reduce((a, b) => a + b, 0), mean: turns.length ? round1(turns.reduce((a, b) => a + b, 0) / turns.length) : null },
|
|
52
|
+
timeouts: grades.filter((g) => g.timedOut).length,
|
|
53
|
+
replies: { calls: replyCalls, approximate, unmatched, approximatePct: round1(pct(approximate, replyCalls)), unmatchedPct: round1(pct(unmatched, replyCalls)) },
|
|
54
|
+
effectsFlagged: grades.reduce((s, g) => s + (g.effects?.flagged?.length ?? 0), 0),
|
|
55
|
+
judge: { pass: grades.filter((g) => g.judge?.verdict === "pass").length, fail: grades.filter((g) => g.judge?.verdict === "fail").length, error: grades.filter((g) => g.judge?.verdict === "error").length, skipped: grades.filter((g) => !g.judge || g.judge.verdict === "skipped").length },
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* @param {{claude:ReturnType<typeof summariseEngine>, cohort:ReturnType<typeof summariseEngine>}} engines
|
|
61
|
+
* @param {Array<{id:string, synthetic:boolean}>} tasks
|
|
62
|
+
* @param {typeof GATE} [gate]
|
|
63
|
+
* @param {{missing?:{claude:string[], cohort:string[]}, judgeRequested?:boolean}} [extra]
|
|
64
|
+
* missing: task ids an engine has no grade for (the summaries cover only tasks both have)
|
|
65
|
+
*/
|
|
66
|
+
export function gateVerdict(engines, tasks, gate = GATE, extra = {}) {
|
|
67
|
+
const { claude, cohort } = engines;
|
|
68
|
+
const reasons = [];
|
|
69
|
+
const successOk = claude.tasks > 0 && cohort.tasks > 0 && /** @type number */ (cohort.successPct) >= /** @type number */ (claude.successPct) - gate.successMarginPts;
|
|
70
|
+
if (!successOk) reasons.push(claude.tasks === 0 || cohort.tasks === 0 ? "both engines must run the tasks" : `cohort success ${round1(cohort.successPct)}% is below claude ${round1(claude.successPct)}% − ${gate.successMarginPts} pts`);
|
|
71
|
+
const schemaOk = cohort.schema.checked > 0 && /** @type number */ (cohort.schema.pct) >= gate.schemaValidityMinPct;
|
|
72
|
+
if (!schemaOk) reasons.push(cohort.schema.checked === 0 ? "no cohort tool call was schema-checked" : `cohort tool-call schema validity ${round1(cohort.schema.pct)}% is below ${gate.schemaValidityMinPct}%`);
|
|
73
|
+
const ungatedOk = cohort.sends.ungated <= gate.maxUngatedSends;
|
|
74
|
+
if (!ungatedOk) reasons.push(`${cohort.sends.ungated} ungated outbound send(s) on the cohort engine`);
|
|
75
|
+
const enoughTasks = tasks.length >= gate.minTasks;
|
|
76
|
+
const realTasks = tasks.length > 0 && tasks.every((t) => !t.synthetic);
|
|
77
|
+
const eligibility = [];
|
|
78
|
+
if (!enoughTasks) eligibility.push(`${tasks.length} task(s); the gate needs ≥ ${gate.minTasks} recorded tasks`);
|
|
79
|
+
if (!realTasks) eligibility.push("synthetic tasks cannot flip a seat");
|
|
80
|
+
const missing = extra.missing ?? { claude: [], cohort: [] };
|
|
81
|
+
const missingCount = new Set([...missing.claude, ...missing.cohort]).size;
|
|
82
|
+
if (missingCount > 0) eligibility.push(`${missingCount} task(s) lack a grade on one engine (claude: ${missing.claude.length}, cohort: ${missing.cohort.length}); success compares only tasks both engines ran`);
|
|
83
|
+
const judgeErrors = engines.claude.judge.error + engines.cohort.judge.error;
|
|
84
|
+
if (extra.judgeRequested && judgeErrors > 0) eligibility.push(`${judgeErrors} judge error(s): the rubric judge was requested but did not grade every answer`);
|
|
85
|
+
const verdict = !successOk || !schemaOk || !ungatedOk ? "fail" : eligibility.length > 0 ? "not_eligible" : "pass";
|
|
86
|
+
return {
|
|
87
|
+
verdict,
|
|
88
|
+
thresholds: gate,
|
|
89
|
+
success: { claudePct: round1(claude.successPct), cohortPct: round1(cohort.successPct), marginPts: gate.successMarginPts, ok: successOk },
|
|
90
|
+
schemaValidity: { cohortPct: round1(cohort.schema.pct), minPct: gate.schemaValidityMinPct, ok: schemaOk },
|
|
91
|
+
ungatedSends: { cohort: cohort.sends.ungated, claude: claude.sends.ungated, ok: ungatedOk },
|
|
92
|
+
tasks: { count: tasks.length, min: gate.minTasks, synthetic: tasks.filter((t) => t.synthetic).length, ok: enoughTasks && realTasks, missing },
|
|
93
|
+
judgeErrors,
|
|
94
|
+
reasons: [...reasons, ...eligibility],
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* @param {{grades:any[], tasks:Array<{id:string, synthetic:boolean, lane?:string}>, judge?:any}} graded
|
|
100
|
+
* @param {{generatedAt?:string, run?:string}} [meta]
|
|
101
|
+
*/
|
|
102
|
+
export function buildReport(graded, meta = {}) {
|
|
103
|
+
const taskIds = graded.tasks.map((t) => t.id);
|
|
104
|
+
const idsOf = (/** @type string */ engine) => new Set(graded.grades.filter((g) => g.engine === engine).map((g) => g.taskId));
|
|
105
|
+
const ids = { claude: idsOf("claude"), cohort: idsOf("cohort") };
|
|
106
|
+
const bothRan = ids.claude.size > 0 && ids.cohort.size > 0;
|
|
107
|
+
// With both engines present, compare them over the same tasks only.
|
|
108
|
+
const common = new Set(taskIds.filter((id) => ids.claude.has(id) && ids.cohort.has(id)));
|
|
109
|
+
const by = (/** @type string */ engine) => graded.grades.filter((g) => g.engine === engine && (!bothRan || common.has(g.taskId)));
|
|
110
|
+
const engines = { claude: summariseEngine(by("claude")), cohort: summariseEngine(by("cohort")) };
|
|
111
|
+
const missing = bothRan ? { claude: taskIds.filter((id) => !ids.claude.has(id)), cohort: taskIds.filter((id) => !ids.cohort.has(id)) } : { claude: [], cohort: [] };
|
|
112
|
+
const perTask = graded.tasks.map((t) => {
|
|
113
|
+
const row = (/** @type string */ engine) => {
|
|
114
|
+
const g = graded.grades.find((x) => x.taskId === t.id && x.engine === engine);
|
|
115
|
+
return g ? { passed: g.passed, failed: g.checks.filter((/** @type any */ c) => !c.ok).map((/** @type any */ c) => `${c.kind}: ${c.detail}`), judge: g.judge?.verdict ?? "skipped", costUsd: g.costUsd, turns: g.turns, ungated: g.sends.ungated } : null;
|
|
116
|
+
};
|
|
117
|
+
return { id: t.id, lane: t.lane ?? null, synthetic: t.synthetic, claude: row("claude"), cohort: row("cohort") };
|
|
118
|
+
});
|
|
119
|
+
return { generatedAt: meta.generatedAt ?? new Date().toISOString(), run: meta.run ?? null, judge: graded.judge ?? null, engines, gate: gateVerdict(engines, graded.tasks, GATE, { missing, judgeRequested: Boolean(graded.judge) }), tasks: perTask };
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const fmt = (/** @type number|null */ x, /** @type string */ unit = "") => (x === null || x === undefined ? "—" : `${round1(x)}${unit}`);
|
|
123
|
+
|
|
124
|
+
/** @param {ReturnType<typeof buildReport>} report */
|
|
125
|
+
export function renderReportMarkdown(report) {
|
|
126
|
+
const e = report.engines;
|
|
127
|
+
const lines = [
|
|
128
|
+
"# Replay eval report",
|
|
129
|
+
"",
|
|
130
|
+
`> ${PRIVACY_WARNING}`,
|
|
131
|
+
"",
|
|
132
|
+
`Generated ${report.generatedAt}${report.run ? ` from \`${report.run}\`` : ""}. Judge: ${report.judge ? `${report.judge.slot} on ${report.judge.tier} (${report.judge.promptVersion})` : "not run"}.`,
|
|
133
|
+
"",
|
|
134
|
+
`## §8.4.2 verdict: ${report.gate.verdict.toUpperCase()}`,
|
|
135
|
+
"",
|
|
136
|
+
...(report.gate.reasons.length ? report.gate.reasons.map((r) => `- ${r}`) : ["- every threshold holds"]),
|
|
137
|
+
"",
|
|
138
|
+
"| Metric | claude | cohort | Threshold |",
|
|
139
|
+
"| ------ | ------ | ------ | --------- |",
|
|
140
|
+
`| Task success | ${fmt(e.claude.successPct, "%")} (${e.claude.passed}/${e.claude.tasks}) | ${fmt(e.cohort.successPct, "%")} (${e.cohort.passed}/${e.cohort.tasks}) | cohort ≥ claude − ${report.gate.thresholds.successMarginPts} pts |`,
|
|
141
|
+
`| Tool-call schema validity | ${fmt(e.claude.schema.pct, "%")} (${e.claude.schema.valid}/${e.claude.schema.checked}) | ${fmt(e.cohort.schema.pct, "%")} (${e.cohort.schema.valid}/${e.cohort.schema.checked}) | cohort ≥ ${report.gate.thresholds.schemaValidityMinPct}% |`,
|
|
142
|
+
`| Ungated outbound sends | ${e.claude.sends.ungated} (of ${e.claude.sends.outbound}) | ${e.cohort.sends.ungated} (of ${e.cohort.sends.outbound}) | cohort = ${report.gate.thresholds.maxUngatedSends} |`,
|
|
143
|
+
`| Cost (USD) | ${e.claude.costUsd.total ?? "—"} (${e.claude.costUsd.unknown} unknown) | ${e.cohort.costUsd.total ?? "—"} (${e.cohort.costUsd.unknown} unknown) | — |`,
|
|
144
|
+
`| Turns (mean) | ${fmt(e.claude.turns.mean)} | ${fmt(e.cohort.turns.mean)} | — |`,
|
|
145
|
+
`| Timeouts | ${e.claude.timeouts} | ${e.cohort.timeouts} | — |`,
|
|
146
|
+
`| Replayed reads approximate / unmatched | ${fmt(e.claude.replies.approximatePct, "%")} / ${fmt(e.claude.replies.unmatchedPct, "%")} (of ${e.claude.replies.calls}) | ${fmt(e.cohort.replies.approximatePct, "%")} / ${fmt(e.cohort.replies.unmatchedPct, "%")} (of ${e.cohort.replies.calls}) | similar on both engines |`,
|
|
147
|
+
`| Calls reaching past the sandbox | ${e.claude.effectsFlagged} | ${e.cohort.effectsFlagged} | 0 (fails the task) |`,
|
|
148
|
+
`| Judge pass / fail / error / skipped | ${e.claude.judge.pass} / ${e.claude.judge.fail} / ${e.claude.judge.error} / ${e.claude.judge.skipped} | ${e.cohort.judge.pass} / ${e.cohort.judge.fail} / ${e.cohort.judge.error} / ${e.cohort.judge.skipped} | — |`,
|
|
149
|
+
`| Tasks | ${report.gate.tasks.count} (${report.gate.tasks.synthetic} synthetic) | | ≥ ${report.gate.tasks.min} recorded |`,
|
|
150
|
+
"",
|
|
151
|
+
"## Tasks",
|
|
152
|
+
"",
|
|
153
|
+
"| Task | Lane | claude | cohort | Failed checks (cohort) |",
|
|
154
|
+
"| ---- | ---- | ------ | ------ | ---------------------- |",
|
|
155
|
+
];
|
|
156
|
+
const mark = (/** @type any */ r) => (r === null ? "not run" : r.passed ? "pass" : "FAIL");
|
|
157
|
+
for (const t of report.tasks) lines.push(`| ${t.id}${t.synthetic ? " (synthetic)" : ""} | ${t.lane ?? "—"} | ${mark(t.claude)} | ${mark(t.cohort)} | ${(t.cohort?.failed ?? []).join("; ").replace(/\|/g, "\\|") || "—"} |`);
|
|
158
|
+
return `${lines.join("\n")}\n`;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* @param {string} runDir
|
|
163
|
+
*/
|
|
164
|
+
export function writeReport(runDir) {
|
|
165
|
+
const graded = JSON.parse(readFileSync(path.join(runDir, "graded.json"), "utf8"));
|
|
166
|
+
const report = buildReport(graded, { run: runDir });
|
|
167
|
+
writeFileSync(path.join(runDir, "report.json"), `${JSON.stringify(report, null, 2)}\n`);
|
|
168
|
+
const md = renderReportMarkdown(report);
|
|
169
|
+
writeFileSync(path.join(runDir, "report.md"), md);
|
|
170
|
+
return { report, md };
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
async function main(/** @type string[] */ argv) {
|
|
174
|
+
const i = argv.indexOf("--run");
|
|
175
|
+
if (i < 0 || !argv[i + 1]) {
|
|
176
|
+
process.stderr.write("usage: report.mjs --run <run dir>\n");
|
|
177
|
+
return 2;
|
|
178
|
+
}
|
|
179
|
+
process.stderr.write(`${PRIVACY_WARNING}\n`);
|
|
180
|
+
const { report, md } = writeReport(path.resolve(argv[i + 1]));
|
|
181
|
+
process.stdout.write(md);
|
|
182
|
+
return report.gate.verdict === "pass" ? 0 : 1;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
if (process.argv[1] && import.meta.url === pathToFileURL(path.resolve(process.argv[1])).href) process.exitCode = await main(process.argv.slice(2));
|