@cohortapp/agent-sdk 2.16.0 → 2.18.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/settings.json +18 -0
- package/.env.example +23 -7
- package/README.md +1 -0
- package/bin/maestro.mjs +62 -0
- package/docs/guides/billing-console-keys.md +60 -0
- package/docs/guides/front-door-session.md +54 -9
- package/docs/guides/mac-mini.md +20 -25
- package/docs/guides/poller-daemon-setup.md +4 -1
- package/docs/guides/setup-wizard.md +1 -1
- package/docs/runbooks/fleet-rollout.md +156 -0
- package/docs/runbooks/mac-mini-bootstrap.md +12 -14
- package/lib/action-executor.js +19 -3
- package/lib/budget-guard.mjs +279 -3
- package/lib/channels/base-adapter.mjs +3 -1
- package/lib/channels/contract.mjs +2 -1
- package/lib/channels/inbox-item.mjs +8 -0
- package/lib/claude-bin.mjs +5 -6
- package/lib/cli/doctor-checks.mjs +141 -10
- package/lib/cli/global-setup-extras.mjs +5 -1
- package/lib/cli/inbox.mjs +100 -15
- package/lib/cli/seat-auth.mjs +463 -0
- package/lib/cli/session.mjs +80 -12
- package/lib/collective/capture.mjs +8 -6
- package/lib/collective/global-config.mjs +63 -1
- package/lib/collective/presence.mjs +142 -5
- package/lib/comms/send-gate.mjs +559 -1
- package/lib/context/budget.mjs +327 -0
- package/lib/context/history-scope.mjs +138 -0
- package/lib/diagnostics/alerts.mjs +49 -0
- package/lib/diagnostics/cadence-output-freshness.mjs +288 -0
- package/lib/engine/agents/definitions.mjs +343 -0
- package/lib/engine/agents/persist.mjs +275 -0
- package/lib/engine/agents/runtime.mjs +748 -0
- package/lib/engine/agents/usage.mjs +95 -0
- package/lib/engine/auth-status.mjs +139 -0
- package/lib/engine/budget.mjs +194 -0
- package/lib/engine/cli.mjs +1204 -0
- package/lib/engine/commands/index.mjs +269 -0
- package/lib/engine/context/budget.mjs +219 -0
- package/lib/engine/context/cache.mjs +125 -0
- package/lib/engine/context/child-env.mjs +215 -0
- package/lib/engine/context/compaction.mjs +342 -0
- package/lib/engine/context/images.mjs +90 -0
- package/lib/engine/context/instructions.mjs +327 -0
- package/lib/engine/context/lazy-instructions.mjs +169 -0
- package/lib/engine/context/manager.mjs +182 -0
- package/lib/engine/context/real-path.mjs +91 -0
- package/lib/engine/context/secret-values.mjs +163 -0
- package/lib/engine/context/settings.mjs +274 -0
- package/lib/engine/context/stream-input.mjs +159 -0
- package/lib/engine/guard.mjs +152 -0
- package/lib/engine/hooks.mjs +713 -0
- package/lib/engine/loop.mjs +560 -0
- package/lib/engine/mcp/client.mjs +254 -0
- package/lib/engine/mcp/config.mjs +301 -0
- package/lib/engine/mcp/http.mjs +201 -0
- package/lib/engine/mcp/index.mjs +146 -0
- package/lib/engine/mcp/jsonrpc.mjs +147 -0
- package/lib/engine/mcp/naming.mjs +66 -0
- package/lib/engine/mcp/resources.mjs +89 -0
- package/lib/engine/mcp/results.mjs +133 -0
- package/lib/engine/mcp/stdio.mjs +137 -0
- package/lib/engine/mcp/supervisor.mjs +116 -0
- package/lib/engine/messages.mjs +104 -0
- package/lib/engine/output/json.mjs +164 -0
- package/lib/engine/output/stream-json.mjs +266 -0
- package/lib/engine/permissions.mjs +845 -0
- package/lib/engine/process-identity.mjs +164 -0
- package/lib/engine/process-tree.mjs +551 -0
- package/lib/engine/prompt.mjs +60 -0
- package/lib/engine/session/store.mjs +299 -0
- package/lib/engine/session-runtime/args.mjs +97 -0
- package/lib/engine/session-runtime/host.mjs +143 -0
- package/lib/engine/session-runtime/inbox.mjs +122 -0
- package/lib/engine/session-runtime/notifications.mjs +129 -0
- package/lib/engine/session-runtime/registry.mjs +328 -0
- package/lib/engine/session-runtime/runner.mjs +344 -0
- package/lib/engine/session-runtime/socket.mjs +212 -0
- package/lib/engine/session-runtime/wakeup.mjs +115 -0
- package/lib/engine/skills/index.mjs +321 -0
- package/lib/engine/tools/bash-background.mjs +533 -0
- package/lib/engine/tools/bash.mjs +216 -0
- package/lib/engine/tools/edit.mjs +97 -0
- package/lib/engine/tools/glob.mjs +81 -0
- package/lib/engine/tools/grep.mjs +224 -0
- package/lib/engine/tools/index.mjs +84 -0
- package/lib/engine/tools/list-agents.mjs +32 -0
- package/lib/engine/tools/ls.mjs +127 -0
- package/lib/engine/tools/monitor.mjs +82 -0
- package/lib/engine/tools/notebook-edit.mjs +218 -0
- package/lib/engine/tools/read.mjs +103 -0
- package/lib/engine/tools/schedule-wakeup.mjs +45 -0
- package/lib/engine/tools/schema.mjs +144 -0
- package/lib/engine/tools/send-message.mjs +77 -0
- package/lib/engine/tools/session.mjs +70 -0
- package/lib/engine/tools/todo.mjs +144 -0
- package/lib/engine/tools/toolsearch.mjs +217 -0
- package/lib/engine/tools/walk.mjs +193 -0
- package/lib/engine/tools/web-switch.mjs +31 -0
- package/lib/engine/tools/webfetch-html.mjs +387 -0
- package/lib/engine/tools/webfetch-net.mjs +340 -0
- package/lib/engine/tools/webfetch.mjs +198 -0
- package/lib/engine/tools/websearch.mjs +91 -0
- package/lib/engine/tools/workflow.mjs +95 -0
- package/lib/engine/tools/write.mjs +76 -0
- package/lib/engine/tui/line-editor.mjs +137 -0
- package/lib/engine/tui/render.mjs +86 -0
- package/lib/engine/tui/tui.mjs +274 -0
- package/lib/engine/wire/anthropic-messages.mjs +263 -0
- package/lib/engine/wire/effort.mjs +36 -0
- package/lib/engine/wire/errors.mjs +496 -0
- package/lib/engine/wire/http.mjs +441 -0
- package/lib/engine/wire/index.mjs +76 -0
- package/lib/engine/wire/openai-chat.mjs +332 -0
- package/lib/engine/wire/prompt-cache.mjs +79 -0
- package/lib/engine/wire/search.mjs +140 -0
- package/lib/engine/wire/sse.mjs +114 -0
- package/lib/engine/wire/stall.mjs +349 -0
- package/lib/engine/wire/token-provider.mjs +175 -0
- package/lib/engine/wire/usage.mjs +192 -0
- package/lib/engine/workflow/host.mjs +524 -0
- package/lib/engine/workflow/journal.mjs +188 -0
- package/lib/engine/workflow/json-schema.mjs +171 -0
- package/lib/engine/workflow/meta.mjs +329 -0
- package/lib/engine/workflow/notifications.mjs +52 -0
- package/lib/engine/workflow/runtime.mjs +447 -0
- package/lib/engine/workflow/sandbox.mjs +534 -0
- package/lib/engine/workflow/worker.mjs +141 -0
- package/lib/engine/workflow/worktree.mjs +74 -0
- package/lib/execution/disposition.mjs +1 -1
- package/lib/execution/intake.mjs +10 -0
- package/lib/execution/surface-policy.mjs +15 -0
- package/lib/learning/curator.mjs +8 -6
- package/lib/learning/reflect.mjs +8 -6
- package/lib/model-router/catalog/cohort.yaml +137 -0
- package/lib/model-router/catalog.mjs +118 -1
- package/lib/model-router/economics.mjs +9 -0
- package/lib/model-router/failover.mjs +67 -16
- package/lib/model-router/llm-task.mjs +39 -3
- package/lib/model-router/resolve.mjs +95 -3
- package/lib/model-router/spawn.mjs +46 -47
- package/lib/model-router/taxonomy.mjs +126 -4
- package/lib/org/cost-sync.mjs +141 -11
- package/lib/org/inbound/broadcast.mjs +289 -0
- package/lib/org/inbound/collective.mjs +375 -0
- package/lib/org/inbound/directedness.mjs +96 -8
- package/lib/org/inbound/facts.mjs +82 -4
- package/lib/org/inbound/hydrate.mjs +555 -51
- package/lib/org/inbound/project.mjs +22 -0
- package/lib/org/inbound/surfaces.mjs +14 -0
- package/lib/org/llm-token.mjs +879 -0
- package/lib/org/mesh.mjs +61 -0
- package/lib/org/messaging.mjs +3 -1
- package/lib/org/protocol.checksum +1 -1
- package/lib/org/protocol.mjs +15 -0
- package/lib/org/quota.mjs +520 -0
- package/lib/org/tool-surface.mjs +104 -16
- package/lib/org/ui-parity.mjs +16 -1
- package/lib/org/work-ledger.mjs +37 -6
- package/lib/rate-guard.mjs +114 -1
- package/lib/resource-governor.mjs +41 -6
- package/lib/runtime/adapter.mjs +823 -0
- package/lib/runtime/child-env.mjs +191 -0
- package/lib/runtime/legacy-shell-guard.mjs +97 -0
- package/lib/runtime/seat-engine.mjs +162 -0
- package/lib/session/ask-ledger.mjs +271 -0
- package/lib/session/current-work.mjs +676 -0
- package/lib/session/feed-core.mjs +40 -3
- package/lib/session/launch-args.mjs +56 -4
- package/lib/session/status-summary.mjs +26 -9
- package/lib/session/upgrade-notice.mjs +42 -0
- package/lib/setup/claude-probe.mjs +117 -13
- package/lib/setup/enrich.mjs +13 -10
- package/lib/setup/sections/model.mjs +39 -13
- package/lib/telemetry/collect.mjs +208 -9
- package/lib/upgrade/ignored-drift.mjs +105 -0
- package/lib/voice/post-call-brief.mjs +30 -17
- package/package.json +15 -3
- package/plugins/maestro-skills/skills/board-work.md +5 -0
- package/plugins/maestro-skills/skills/inbound-triage.md +56 -15
- package/plugins/maestro-skills/skills/main-session.md +18 -7
- package/scripts/ci/check-tarball-fidelity.mjs +126 -2
- package/scripts/ci/run-tests.mjs +47 -19
- package/scripts/cohort-llm/api-key-helper.mjs +92 -0
- package/scripts/collective/hook-runner.mjs +29 -2
- package/scripts/continuous-monitor.sh +13 -0
- package/scripts/cost/track-claude-usage.mjs +15 -0
- package/scripts/daemon/agent-daemon.mjs +408 -20
- package/scripts/daemon/assurance.mjs +48 -12
- package/scripts/daemon/cadence-consumer.mjs +218 -68
- package/scripts/daemon/cadence-handlers.mjs +73 -4
- package/scripts/daemon/classifier.mjs +75 -26
- package/scripts/daemon/context-compiler.mjs +104 -59
- package/scripts/daemon/deliver.mjs +30 -1
- package/scripts/daemon/dispatcher.mjs +804 -157
- package/scripts/daemon/health.mjs +14 -1
- package/scripts/daemon/lib/session-router.mjs +310 -42
- package/scripts/daemon/maestro-daemon.mjs +11 -0
- package/scripts/daemon/prompt-builder.mjs +121 -12
- package/scripts/daemon/responder.mjs +315 -146
- package/scripts/daemon/sdk-version.mjs +98 -16
- package/scripts/eval/probe-gateway.mjs +635 -0
- package/scripts/eval/replay/extract.mjs +270 -0
- package/scripts/eval/replay/grade.mjs +260 -0
- package/scripts/eval/replay/lib/config.mjs +50 -0
- package/scripts/eval/replay/lib/effects.mjs +65 -0
- package/scripts/eval/replay/lib/fixture.mjs +188 -0
- package/scripts/eval/replay/lib/judge.mjs +72 -0
- package/scripts/eval/replay/lib/redact.mjs +136 -0
- package/scripts/eval/replay/lib/sandbox.mjs +170 -0
- package/scripts/eval/replay/lib/schema-check.mjs +63 -0
- package/scripts/eval/replay/lib/transcript.mjs +76 -0
- package/scripts/eval/replay/mcp-replay-stub.mjs +101 -0
- package/scripts/eval/replay/report.mjs +185 -0
- package/scripts/eval/replay/run.mjs +404 -0
- package/scripts/fleet/rollout.mjs +1094 -0
- package/scripts/hooks/pre-send-audit.sh +36 -245
- package/scripts/hooks/pre-write-yaml-validate.mjs +275 -0
- package/scripts/hooks/validate-state-yaml.sh +190 -0
- package/scripts/huddle/huddle-llm.mjs +361 -0
- package/scripts/huddle/huddle-server.mjs +46 -121
- package/scripts/local-triggers/autoupdate.sh +448 -78
- package/scripts/local-triggers/run-trigger.sh +13 -0
- package/scripts/maintenance/pin-integrity.mjs +364 -0
- package/scripts/poll-slack-events.sh +41 -9
- package/scripts/poller/slack-socket-mode.mjs +28 -3
- package/scripts/session/supervisor.mjs +80 -13
- package/scripts/spawn-session.sh +13 -0
- package/bin/maestro.test.mjs +0 -1574
- package/lib/action-executor.test.mjs +0 -871
- package/lib/archetype.test.mjs +0 -132
- package/lib/assurance/plan-note.test.mjs +0 -234
- package/lib/assurance/room-budget.test.mjs +0 -486
- package/lib/assurance/tier.test.mjs +0 -174
- package/lib/autonomy.test.mjs +0 -66
- package/lib/backlog.test.mjs +0 -302
- package/lib/backup/policy.test.mjs +0 -305
- package/lib/budget-escalate.test.mjs +0 -232
- package/lib/budget-guard.envelope.test.mjs +0 -476
- package/lib/budget-guard.test.mjs +0 -427
- package/lib/cadence-bus-requeue.test.mjs +0 -83
- package/lib/cadence-bus-schedule.test.mjs +0 -194
- package/lib/cadence-bus.test.mjs +0 -720
- package/lib/cadences.test.mjs +0 -230
- package/lib/capability/inventory.test.mjs +0 -232
- package/lib/capability.test.mjs +0 -78
- package/lib/channels/base-adapter.test.mjs +0 -590
- package/lib/channels/channels.test.mjs +0 -371
- package/lib/channels/contract.test.mjs +0 -162
- package/lib/channels/inbox-item.test.mjs +0 -368
- package/lib/channels/orgmail/adapter.test.mjs +0 -448
- package/lib/channels/pairing.test.mjs +0 -270
- package/lib/channels/repeat-suppressor.test.mjs +0 -134
- package/lib/channels/slack-adapter.test.mjs +0 -212
- package/lib/channels/telegram-adapter.test.mjs +0 -306
- package/lib/channels/voice/adapter.test.mjs +0 -278
- package/lib/channels/whatsapp/adapter-baileys.test.mjs +0 -359
- package/lib/channels/whatsapp/baileys-typing.test.mjs +0 -154
- package/lib/charter.test.mjs +0 -89
- package/lib/claude-bin.test.mjs +0 -131
- package/lib/cli/board.test.mjs +0 -227
- package/lib/cli/design.test.mjs +0 -270
- package/lib/cli/doctor-checks.test.mjs +0 -336
- package/lib/cli/global-setup-extras.test.mjs +0 -462
- package/lib/cli/inbox.test.mjs +0 -230
- package/lib/cli/session-ack.test.mjs +0 -63
- package/lib/cli/session.test.mjs +0 -613
- package/lib/collective/capture.test.mjs +0 -121
- package/lib/collective/cards.test.mjs +0 -114
- package/lib/collective/config.test.mjs +0 -123
- package/lib/collective/global-config.test.mjs +0 -220
- package/lib/collective/global-skills.test.mjs +0 -126
- package/lib/collective/presence.test.mjs +0 -95
- package/lib/collective/recall.test.mjs +0 -116
- package/lib/collective/vendor-skills.test.mjs +0 -306
- package/lib/comms/send-gate.test.mjs +0 -770
- package/lib/comms.test.mjs +0 -41
- package/lib/cost/ledger-row.test.mjs +0 -183
- package/lib/design/design-md.test.mjs +0 -318
- package/lib/design/fixtures/DESIGN.golden.md +0 -238
- package/lib/design/fixtures/PRODUCT.golden.md +0 -67
- package/lib/design/fixtures/foundation.json +0 -133
- package/lib/design/refresh-gate.test.mjs +0 -144
- package/lib/design/write.test.mjs +0 -241
- package/lib/diagnostics/alerts.test.mjs +0 -318
- package/lib/diagnostics/backup-freshness.test.mjs +0 -185
- package/lib/diagnostics/counters.test.mjs +0 -206
- package/lib/diagnostics/events.test.mjs +0 -290
- package/lib/diagnostics/otel.test.mjs +0 -196
- package/lib/diagnostics/trace.test.mjs +0 -251
- package/lib/env-compat.test.mjs +0 -104
- package/lib/execution/disposition.test.mjs +0 -553
- package/lib/execution/drive.test.mjs +0 -270
- package/lib/execution/effects.test.mjs +0 -344
- package/lib/execution/intake.test.mjs +0 -389
- package/lib/execution/journal.test.mjs +0 -261
- package/lib/execution/match.test.mjs +0 -235
- package/lib/execution/pipeline.test.mjs +0 -392
- package/lib/execution/route.test.mjs +0 -186
- package/lib/execution/surface-policy.test.mjs +0 -162
- package/lib/fs-atomic.test.mjs +0 -72
- package/lib/fs-ownership.test.mjs +0 -158
- package/lib/goals/admission.test.mjs +0 -164
- package/lib/goals/classify.test.mjs +0 -167
- package/lib/goals/collaborate.test.mjs +0 -336
- package/lib/goals/gaps.test.mjs +0 -284
- package/lib/goals/loop.test.mjs +0 -845
- package/lib/hooks/bus.test.mjs +0 -387
- package/lib/identity/persona.test.mjs +0 -142
- package/lib/kpi-sensors.test.mjs +0 -278
- package/lib/kpi.test.mjs +0 -244
- package/lib/learning/config.test.mjs +0 -75
- package/lib/learning/counters.test.mjs +0 -69
- package/lib/learning/curator-consolidate.test.mjs +0 -238
- package/lib/learning/curator.test.mjs +0 -106
- package/lib/learning/reflect.test.mjs +0 -0
- package/lib/learning/session-index.test.mjs +0 -125
- package/lib/learning/skill-writer.test.mjs +0 -210
- package/lib/mandate/audit.test.mjs +0 -195
- package/lib/mandate/contract.test.mjs +0 -185
- package/lib/mandate/derive.test.mjs +0 -274
- package/lib/mandate/model.test.mjs +0 -164
- package/lib/mandate/refresh.test.mjs +0 -389
- package/lib/mcp/server.test.mjs +0 -426
- package/lib/model-router/auth-profiles.test.mjs +0 -580
- package/lib/model-router/catalog.test.mjs +0 -385
- package/lib/model-router/economics.test.mjs +0 -438
- package/lib/model-router/failover.test.mjs +0 -439
- package/lib/model-router/health.test.mjs +0 -338
- package/lib/model-router/integration-coverage.test.mjs +0 -831
- package/lib/model-router/integration.test.mjs +0 -564
- package/lib/model-router/ledger.test.mjs +0 -415
- package/lib/model-router/llm-task.test.mjs +0 -392
- package/lib/model-router/org-credentials.test.mjs +0 -265
- package/lib/model-router/pricing-refresh.test.mjs +0 -286
- package/lib/model-router/reconcile.test.mjs +0 -316
- package/lib/model-router/repair.test.mjs +0 -180
- package/lib/model-router/spawn.test.mjs +0 -446
- package/lib/model-router/taxonomy.test.mjs +0 -410
- package/lib/model-router.test.mjs +0 -1207
- package/lib/org/activity.test.mjs +0 -134
- package/lib/org/approvals.test.mjs +0 -216
- package/lib/org/awareness.test.mjs +0 -159
- package/lib/org/board-mine-cache.test.mjs +0 -53
- package/lib/org/board.test.mjs +0 -187
- package/lib/org/bootstrap-context.test.mjs +0 -153
- package/lib/org/client.test.mjs +0 -1206
- package/lib/org/cohort-client.test.mjs +0 -126
- package/lib/org/cost-sync.test.mjs +0 -153
- package/lib/org/doctor.test.mjs +0 -346
- package/lib/org/engagement-ledger.test.mjs +0 -112
- package/lib/org/engagement.test.mjs +0 -739
- package/lib/org/handoff.test.mjs +0 -269
- package/lib/org/inbound/directedness.test.mjs +0 -668
- package/lib/org/inbound/facts.test.mjs +0 -471
- package/lib/org/inbound/hydrate.test.mjs +0 -453
- package/lib/org/inbound/index.test.mjs +0 -429
- package/lib/org/inbound/project.test.mjs +0 -287
- package/lib/org/integration-tools.test.mjs +0 -160
- package/lib/org/keys.test.mjs +0 -92
- package/lib/org/knowledge.test.mjs +0 -326
- package/lib/org/leases.test.mjs +0 -235
- package/lib/org/mesh-directives.test.mjs +0 -110
- package/lib/org/mesh-integration.test.mjs +0 -127
- package/lib/org/mesh.test.mjs +0 -400
- package/lib/org/messaging.test.mjs +0 -471
- package/lib/org/param-contract.test.mjs +0 -477
- package/lib/org/policy.test.mjs +0 -237
- package/lib/org/protocol.checksum.test.mjs +0 -90
- package/lib/org/protocol.test.mjs +0 -323
- package/lib/org/push.test.mjs +0 -792
- package/lib/org/registry.test.mjs +0 -100
- package/lib/org/resource-tools.test.mjs +0 -361
- package/lib/org/tool-access.test.mjs +0 -144
- package/lib/org/tool-surface-integration.test.mjs +0 -120
- package/lib/org/tool-surface.test.mjs +0 -1268
- package/lib/org/typing.test.mjs +0 -291
- package/lib/org/ui-parity.test.mjs +0 -560
- package/lib/org/verify.test.mjs +0 -194
- package/lib/org/work-ledger.test.mjs +0 -273
- package/lib/plan/adoption-e2e.test.mjs +0 -366
- package/lib/plan/budget-enforcement.test.mjs +0 -400
- package/lib/plan/compile.test.mjs +0 -382
- package/lib/plan/emit.test.mjs +0 -269
- package/lib/plan/explain.test.mjs +0 -188
- package/lib/prompts/parallelism.test.mjs +0 -177
- package/lib/rag/rag.test.mjs +0 -505
- package/lib/rate-guard.test.mjs +0 -272
- package/lib/reactive-gate.test.mjs +0 -57
- package/lib/render.test.mjs +0 -68
- package/lib/resource-governor.test.mjs +0 -488
- package/lib/scheduling/dynamic-jobs.test.mjs +0 -344
- package/lib/scheduling/jitter.test.mjs +0 -140
- package/lib/secrets/broker.test.mjs +0 -280
- package/lib/secrets/providers.test.mjs +0 -274
- package/lib/security/audit-engine.test.mjs +0 -424
- package/lib/security/coerce-args.test.mjs +0 -281
- package/lib/security/dangerous-tools.test.mjs +0 -68
- package/lib/security/external-content.test.mjs +0 -84
- package/lib/security/redact.test.mjs +0 -441
- package/lib/security/secret-equal.test.mjs +0 -55
- package/lib/session/config.test.mjs +0 -92
- package/lib/session/feed-core.test.mjs +0 -198
- package/lib/session/first-run.test.mjs +0 -121
- package/lib/session/frontdoor.test.mjs +0 -205
- package/lib/session/handoffs.test.mjs +0 -183
- package/lib/session/identity.test.mjs +0 -180
- package/lib/session/inbox-claims.test.mjs +0 -286
- package/lib/session/launch-args.test.mjs +0 -157
- package/lib/session/liveness.test.mjs +0 -100
- package/lib/session/status-summary.test.mjs +0 -118
- package/lib/session-permissions.test.mjs +0 -120
- package/lib/setup/claude-probe.test.mjs +0 -187
- package/lib/setup/completeness.test.mjs +0 -110
- package/lib/setup/context-pack.test.mjs +0 -89
- package/lib/setup/enrich.test.mjs +0 -115
- package/lib/setup/enroll-from-cohort.test.mjs +0 -300
- package/lib/setup/integration.test.mjs +0 -162
- package/lib/setup/io.test.mjs +0 -77
- package/lib/setup/runner.test.mjs +0 -132
- package/lib/setup/sections/identity.test.mjs +0 -234
- package/lib/setup/sections/inventory.test.mjs +0 -198
- package/lib/setup/sections/learning.test.mjs +0 -81
- package/lib/setup/sections/mandate.test.mjs +0 -388
- package/lib/setup/sections/messaging.test.mjs +0 -127
- package/lib/setup/sections/model.test.mjs +0 -240
- package/lib/setup/sections/org.test.mjs +0 -346
- package/lib/setup/sections/orgmail.test.mjs +0 -118
- package/lib/setup/sections/recovery.test.mjs +0 -98
- package/lib/setup/sections/subagents.test.mjs +0 -429
- package/lib/setup/sections/verify.test.mjs +0 -175
- package/lib/setup/sot.test.mjs +0 -81
- package/lib/setup/state.test.mjs +0 -115
- package/lib/singleton.test.mjs +0 -151
- package/lib/subagents/cli.test.mjs +0 -389
- package/lib/subagents/client.test.mjs +0 -309
- package/lib/subagents/gap.test.mjs +0 -234
- package/lib/subagents/lock.test.mjs +0 -248
- package/lib/subagents/manifest.test.mjs +0 -175
- package/lib/subagents/refs.test.mjs +0 -204
- package/lib/subagents/resolve.test.mjs +0 -422
- package/lib/subagents/schema.test.mjs +0 -328
- package/lib/telemetry/alerts.test.mjs +0 -109
- package/lib/telemetry/collect.test.mjs +0 -1274
- package/lib/tool-definitions-integration.test.mjs +0 -83
- package/lib/tool-definitions.test.mjs +0 -437
- package/lib/upgrade/global-refresh.test.mjs +0 -65
- package/lib/upgrade/launchd-reconcile.test.mjs +0 -272
- package/lib/upgrade/post-steps.test.mjs +0 -200
- package/lib/upgrade/verify.test.mjs +0 -164
- package/lib/util/fetch-timeout.test.mjs +0 -202
- package/lib/util/reconnect.test.mjs +0 -369
- package/lib/util/unhandled.test.mjs +0 -216
- package/lib/voice/outbound.test.mjs +0 -69
- package/lib/voice/session-rotation.test.mjs +0 -114
- package/lib/voice/stt.test.mjs +0 -226
- package/lib/voice/voice.test.mjs +0 -990
- package/scripts/cadence/enqueue-cadence-tick.test.mjs +0 -187
- package/scripts/ci/check-docs-accuracy.test.mjs +0 -409
- package/scripts/ci/check-durable-write-seam.test.mjs +0 -90
- package/scripts/ci/check-no-build-artifacts.test.mjs +0 -71
- package/scripts/ci/check-no-residual-identity.test.mjs +0 -202
- package/scripts/ci/check-skill-packs.test.mjs +0 -495
- package/scripts/ci/check-subagent-frontmatter.test.mjs +0 -124
- package/scripts/ci/check.test.mjs +0 -194
- package/scripts/ci/conformance-org-api.test.mjs +0 -425
- package/scripts/cloud-relay/voice/relay-identity.test.mjs +0 -96
- package/scripts/collective/hook-runner.test.mjs +0 -173
- package/scripts/cost/fleet-digest.test.mjs +0 -207
- package/scripts/cost/track-claude-usage-pricing.test.mjs +0 -183
- package/scripts/cost/track-claude-usage.test.mjs +0 -148
- package/scripts/daemon/agent-daemon-board-mine.test.mjs +0 -96
- package/scripts/daemon/agent-daemon-design.test.mjs +0 -238
- package/scripts/daemon/agent-daemon-frontdoor.test.mjs +0 -60
- package/scripts/daemon/agent-daemon.test.mjs +0 -995
- package/scripts/daemon/assurance-e2e.test.mjs +0 -613
- package/scripts/daemon/assurance.test.mjs +0 -1791
- package/scripts/daemon/board-mirror.test.mjs +0 -165
- package/scripts/daemon/cadence-consumer-frontdoor.test.mjs +0 -393
- package/scripts/daemon/cadence-consumer-governance.test.mjs +0 -276
- package/scripts/daemon/cadence-consumer.test.mjs +0 -776
- package/scripts/daemon/cadence-handlers.test.mjs +0 -837
- package/scripts/daemon/classifier-identity.test.mjs +0 -137
- package/scripts/daemon/classifier.test.mjs +0 -266
- package/scripts/daemon/classify-kind.test.mjs +0 -40
- package/scripts/daemon/context-compiler.test.mjs +0 -300
- package/scripts/daemon/deliver.test.mjs +0 -564
- package/scripts/daemon/dispatcher-cooldown.test.mjs +0 -122
- package/scripts/daemon/dispatcher-governance.test.mjs +0 -1013
- package/scripts/daemon/dispatcher-resume.test.mjs +0 -166
- package/scripts/daemon/execution-ladder.test.mjs +0 -470
- package/scripts/daemon/goal-steward-cadence.test.mjs +0 -312
- package/scripts/daemon/inbox-deferral-session.test.mjs +0 -49
- package/scripts/daemon/inbox-deferral.test.mjs +0 -336
- package/scripts/daemon/inbox-wake.test.mjs +0 -199
- package/scripts/daemon/integration.test.mjs +0 -149
- package/scripts/daemon/lib/self-echo.test.mjs +0 -153
- package/scripts/daemon/lib/session-router.test.mjs +0 -295
- package/scripts/daemon/prompt-builder-preamble.test.mjs +0 -210
- package/scripts/daemon/prompt-builder.test.mjs +0 -344
- package/scripts/daemon/responder-cost.test.mjs +0 -68
- package/scripts/daemon/responder-history.test.mjs +0 -185
- package/scripts/daemon/sdk-version.test.mjs +0 -31
- package/scripts/daemon/session-lock.test.mjs +0 -252
- package/scripts/daemon/session-outcomes.test.mjs +0 -533
- package/scripts/daemon/typing-registry.test.mjs +0 -102
- package/scripts/hooks/pre-send-audit.test.mjs +0 -354
- package/scripts/huddle/huddle-prompt.test.mjs +0 -176
- package/scripts/local-triggers/autoupdate.test.mjs +0 -518
- package/scripts/local-triggers/generate-plists.test.mjs +0 -456
- package/scripts/media-generation/brand-clause.test.mjs +0 -135
- package/scripts/org/send-orgmail.first-contact.test.mjs +0 -102
- package/scripts/poller/inbox-privilege-injection.test.mjs +0 -167
- package/scripts/poller/inbox-scan-poller.test.mjs +0 -295
- package/scripts/poller/lib/cloud-relay-dedup.test.mjs +0 -133
- package/scripts/poller/slack-socket-mode.test.mjs +0 -805
- package/scripts/poller-launchd/install.test.mjs +0 -243
- package/scripts/restore-from-backup.test.mjs +0 -181
- package/scripts/session/feed.test.mjs +0 -196
- package/scripts/session/supervisor-sh.test.mjs +0 -218
- package/scripts/session/supervisor.test.mjs +0 -482
- package/scripts/setup/configure-macos.test.mjs +0 -306
- package/scripts/setup/gen-subagent-manifest.test.mjs +0 -124
- package/scripts/setup/generate-agent-package-json.test.mjs +0 -143
- package/scripts/setup/generate-capability.test.mjs +0 -134
- package/scripts/setup/init-agent.test.mjs +0 -370
- package/scripts/setup/init-skill-marketplace.test.mjs +0 -193
- package/scripts/vendor/sync-skill-packs.test.mjs +0 -103
- package/scripts/watchdog/memory-watchdog.test.mjs +0 -64
|
@@ -1,477 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* param-contract.test.mjs — the wire param contract (lib/org/param-contract.mjs).
|
|
3
|
-
*
|
|
4
|
-
* Three layers, all hermetic (no network, no disk):
|
|
5
|
-
*
|
|
6
|
-
* 1. NORMALISER MECHANICS — alias/mint/fold/strict/enum/transform, purity,
|
|
7
|
-
* idempotence, and the fail-open guarantees.
|
|
8
|
-
* 2. ONE REGRESSION PER DRIFT ROW — every row the 2026-08 wire audit found gets
|
|
9
|
-
* a test that asserts the canonical name hq validates is on the wire. These
|
|
10
|
-
* are the tests that would have gone red on the day each bug shipped.
|
|
11
|
-
* 3. SURFACE GUARD — a static sweep of the curated LLM tool table asserting no
|
|
12
|
-
* tool advertises a param name hq does not read. This is the CI half of
|
|
13
|
-
* "impossible to ship again": it needs no credentials and no server.
|
|
14
|
-
*
|
|
15
|
-
* Run: node --test lib/org/param-contract.test.mjs
|
|
16
|
-
*/
|
|
17
|
-
|
|
18
|
-
"use strict";
|
|
19
|
-
|
|
20
|
-
import { test } from "node:test";
|
|
21
|
-
import assert from "node:assert/strict";
|
|
22
|
-
|
|
23
|
-
import {
|
|
24
|
-
PARAM_CONTRACT,
|
|
25
|
-
BOARD_COLS,
|
|
26
|
-
TASK_PRIORITIES,
|
|
27
|
-
MEMORY_CLASSES,
|
|
28
|
-
ESCALATION_SEVERITIES,
|
|
29
|
-
hasContract,
|
|
30
|
-
contractFor,
|
|
31
|
-
normalizeParams,
|
|
32
|
-
missingRequired,
|
|
33
|
-
formatReport,
|
|
34
|
-
reportIsInteresting,
|
|
35
|
-
logReportOnce,
|
|
36
|
-
_resetLogOnce,
|
|
37
|
-
toRegisterParams,
|
|
38
|
-
} from "./param-contract.mjs";
|
|
39
|
-
import { ORG_TOOLS } from "./tool-surface.mjs";
|
|
40
|
-
import { methodDef } from "./protocol.mjs";
|
|
41
|
-
|
|
42
|
-
/** Normalise with a deterministic id so wire bodies are comparable. */
|
|
43
|
-
const norm = (method, params) =>
|
|
44
|
-
normalizeParams(method, params, { mintId: () => "MINTED" });
|
|
45
|
-
/** Just the wire body. */
|
|
46
|
-
const wire = (method, params) => norm(method, params).params;
|
|
47
|
-
|
|
48
|
-
// ═══════════════════════════════════════════════════════════════════════════
|
|
49
|
-
// 1. normaliser mechanics
|
|
50
|
-
// ═══════════════════════════════════════════════════════════════════════════
|
|
51
|
-
|
|
52
|
-
test("a method with no contract entry passes through byte-for-byte", () => {
|
|
53
|
-
const input = { anything: 1, nested: { a: [1, 2] } };
|
|
54
|
-
const r = norm("presence.beat", input);
|
|
55
|
-
assert.deepEqual(r.params, input);
|
|
56
|
-
assert.deepEqual(r.renamed, []);
|
|
57
|
-
assert.equal(hasContract("presence.beat"), false);
|
|
58
|
-
assert.equal(contractFor("presence.beat"), null);
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
test("PURE: the caller's params object is never mutated", () => {
|
|
62
|
-
const input = { channelId: "C1", body: "hi" };
|
|
63
|
-
const snapshot = JSON.parse(JSON.stringify(input));
|
|
64
|
-
norm("messaging.send", input);
|
|
65
|
-
assert.deepEqual(input, snapshot, "input untouched");
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
test("TOTAL: null / non-object / unknown method never throw", () => {
|
|
69
|
-
assert.deepEqual(norm("messaging.send", null).params.body, undefined);
|
|
70
|
-
assert.doesNotThrow(() => norm(undefined, undefined));
|
|
71
|
-
assert.doesNotThrow(() => norm("no.such.method", { a: 1 }));
|
|
72
|
-
assert.deepEqual(norm("no.such.method", { a: 1 }).params, { a: 1 });
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
test("alias: an EXPLICIT canonical value always beats a legacy one", () => {
|
|
76
|
-
const w = wire("decision.comment", { body: "legacy", text: "canonical" });
|
|
77
|
-
assert.equal(w.text, "canonical");
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
test("alias: the legacy key rides along by default (an older server may read it)", () => {
|
|
81
|
-
const w = wire("member.get", { memberId: "m-1" });
|
|
82
|
-
assert.equal(w.slug, "m-1", "canonical name added");
|
|
83
|
-
assert.equal(w.memberId, "m-1", "legacy name kept — hq's schema is not strict");
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
test("strict: hq's .strict() schemas drop every unknown key, legacy names included", () => {
|
|
87
|
-
const r = norm("board.complete", { itemId: "i-1", note: "shipped", stray: 1 });
|
|
88
|
-
assert.deepEqual(Object.keys(r.params).sort(), ["itemId", "proof"]);
|
|
89
|
-
assert.deepEqual(r.params.proof, { note: "shipped" });
|
|
90
|
-
assert.ok(r.dropped.includes("note") && r.dropped.includes("stray"));
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
test("mint: a required business-dedup id is auto-minted only when absent", () => {
|
|
94
|
-
assert.equal(wire("messaging.send", { channelId: "C", body: "x" }).idempotencyId, "MINTED");
|
|
95
|
-
assert.equal(
|
|
96
|
-
wire("messaging.send", { channelId: "C", body: "x", idempotencyId: "keep" }).idempotencyId,
|
|
97
|
-
"keep",
|
|
98
|
-
);
|
|
99
|
-
// An empty string is not an id — mint over it.
|
|
100
|
-
assert.equal(wire("messaging.send", { channelId: "C", body: "x", idempotencyId: "" }).idempotencyId, "MINTED");
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
test("fold: enrichment hq only persists inside a container is moved there", () => {
|
|
104
|
-
const r = norm("knowledge.append", {
|
|
105
|
-
body: "note",
|
|
106
|
-
kind: "call",
|
|
107
|
-
participants: ["a"],
|
|
108
|
-
links: ["u"],
|
|
109
|
-
refs: ["r"],
|
|
110
|
-
});
|
|
111
|
-
assert.deepEqual(r.params.metadata, { kind: "call", participants: ["a"], links: ["u"], refs: ["r"] });
|
|
112
|
-
assert.deepEqual(r.folded.sort(), ["kind", "links", "participants", "refs"]);
|
|
113
|
-
// A pre-existing metadata bag is merged into, never clobbered.
|
|
114
|
-
const merged = wire("knowledge.append", { body: "n", metadata: { keep: 1 }, kind: "call" });
|
|
115
|
-
assert.deepEqual(merged.metadata, { keep: 1, kind: "call" });
|
|
116
|
-
});
|
|
117
|
-
|
|
118
|
-
test("enums: a case-variant value is coerced; an unmappable one is reported, not dropped", () => {
|
|
119
|
-
const ok = norm("board.createTask", { title: "t", status: "TODO" });
|
|
120
|
-
assert.equal(ok.params.col, "todo");
|
|
121
|
-
const bad = norm("board.createTask", { title: "t", status: "in-progress" });
|
|
122
|
-
assert.equal(bad.params.col, "in-progress", "left for the server to reject — we do not invent values");
|
|
123
|
-
assert.ok(bad.notes.some((n) => n.includes("in-progress")));
|
|
124
|
-
});
|
|
125
|
-
|
|
126
|
-
test("fail-open: a throwing transform loses nothing", () => {
|
|
127
|
-
const saved = PARAM_CONTRACT["member.get"].transform;
|
|
128
|
-
PARAM_CONTRACT["member.get"].transform = () => {
|
|
129
|
-
throw new Error("boom");
|
|
130
|
-
};
|
|
131
|
-
try {
|
|
132
|
-
const w = wire("member.get", { memberId: "m-1" });
|
|
133
|
-
assert.equal(w.slug, "m-1", "alias still applied after the transform faulted");
|
|
134
|
-
} finally {
|
|
135
|
-
if (saved === undefined) delete PARAM_CONTRACT["member.get"].transform;
|
|
136
|
-
else PARAM_CONTRACT["member.get"].transform = saved;
|
|
137
|
-
}
|
|
138
|
-
});
|
|
139
|
-
|
|
140
|
-
test("IDEMPOTENT: normalising twice is the same as normalising once", () => {
|
|
141
|
-
for (const method of Object.keys(PARAM_CONTRACT)) {
|
|
142
|
-
const seed = {
|
|
143
|
-
itemId: "i", taskId: "t", decisionId: "d", channelId: "c", messageId: "msg",
|
|
144
|
-
callId: "call", memberId: "m", scope: "work-claim", resourceId: "r",
|
|
145
|
-
id: "x", title: "T", body: "B", text: "X", emoji: "+1", assignee: "a",
|
|
146
|
-
actionClass: "external_comms", payload: { a: 1 }, decision: "approve",
|
|
147
|
-
displayName: "D",
|
|
148
|
-
};
|
|
149
|
-
const once = normalizeParams(method, seed, { mintId: () => "M" }).params;
|
|
150
|
-
const twice = normalizeParams(method, once, { mintId: () => "M" }).params;
|
|
151
|
-
assert.deepEqual(twice, once, `${method} is not idempotent`);
|
|
152
|
-
}
|
|
153
|
-
});
|
|
154
|
-
|
|
155
|
-
test("missingRequired: reports absent required names and unmet one-of groups", () => {
|
|
156
|
-
assert.deepEqual(missingRequired("decision.comment", { decisionId: "d" }), ["text"]);
|
|
157
|
-
assert.deepEqual(missingRequired("escalation.create", { title: "t" }), ["taskId|channelId"]);
|
|
158
|
-
assert.deepEqual(missingRequired("escalation.create", { title: "t", channelId: "c" }), []);
|
|
159
|
-
// An empty string / empty array is not "present".
|
|
160
|
-
assert.deepEqual(missingRequired("calling.start", { participantIds: [] }), ["channelId|participantIds"]);
|
|
161
|
-
assert.deepEqual(missingRequired("no.such.method", {}), []);
|
|
162
|
-
});
|
|
163
|
-
|
|
164
|
-
test("reporting: a rewrite is never silent, and never logged twice", () => {
|
|
165
|
-
_resetLogOnce();
|
|
166
|
-
const lines = [];
|
|
167
|
-
const sink = (l) => lines.push(l);
|
|
168
|
-
const r = norm("member.get", { memberId: "m-1" });
|
|
169
|
-
assert.equal(reportIsInteresting(r), true);
|
|
170
|
-
assert.match(formatReport("member.get", r), /renamed memberId→slug/);
|
|
171
|
-
assert.equal(logReportOnce("member.get", r, sink), true);
|
|
172
|
-
assert.equal(logReportOnce("member.get", r, sink), false, "one shot per distinct rewrite");
|
|
173
|
-
assert.equal(lines.length, 1);
|
|
174
|
-
// A clean call says nothing.
|
|
175
|
-
const clean = norm("member.get", { slug: "agent-one" });
|
|
176
|
-
assert.equal(reportIsInteresting(clean), false);
|
|
177
|
-
assert.equal(formatReport("member.get", clean), "");
|
|
178
|
-
assert.equal(logReportOnce("member.get", clean, sink), false);
|
|
179
|
-
});
|
|
180
|
-
|
|
181
|
-
// ═══════════════════════════════════════════════════════════════════════════
|
|
182
|
-
// 2. one regression per DRIFT row (2026-08 wire audit)
|
|
183
|
-
// ═══════════════════════════════════════════════════════════════════════════
|
|
184
|
-
|
|
185
|
-
// ── P0: the feature was 100 % dead — every call returned BAD_REQUEST ────────
|
|
186
|
-
|
|
187
|
-
test("P0/1 messaging.send: hq requires idempotencyId, NOT clientMsgId", () => {
|
|
188
|
-
const w = wire("messaging.send", { channelId: "C1", body: "hello", clientMsgId: "cm-1" });
|
|
189
|
-
assert.equal(w.idempotencyId, "cm-1", "the minted dedup id lands on the name hq reads");
|
|
190
|
-
assert.deepEqual(missingRequired("messaging.send", w), []);
|
|
191
|
-
});
|
|
192
|
-
|
|
193
|
-
test("P0/2 registry.register: the raw self-entry is projected onto the .strict() schema", () => {
|
|
194
|
-
// A persona-agnostic fixture: the framework must carry no instance identity.
|
|
195
|
-
const entry = {
|
|
196
|
-
id: "A001", name: "agent-one", fullName: "Agent One", title: "Engineer",
|
|
197
|
-
email: "agent-one@example.test", repo: "example-org/agent-one",
|
|
198
|
-
archetype: { function: "engineering", altitude: "specialist", label: "Specialist Engineer" },
|
|
199
|
-
towers: ["eng"], focus: "wire contracts", principal: { fullName: "Sponsor Name" },
|
|
200
|
-
presence: "online", status: "ACTIVE",
|
|
201
|
-
};
|
|
202
|
-
const r = norm("registry.register", entry);
|
|
203
|
-
assert.deepEqual(Object.keys(r.params).sort(), ["archetype", "card", "displayName", "humanSponsor"]);
|
|
204
|
-
assert.equal(r.params.displayName, "Agent One");
|
|
205
|
-
assert.equal(r.params.archetype, "Specialist Engineer", "the archetype OBJECT is flattened to a string");
|
|
206
|
-
assert.equal(r.params.humanSponsor, "Sponsor Name");
|
|
207
|
-
assert.deepEqual(r.params.card, entry, "everything hq does not model is preserved under card");
|
|
208
|
-
// …and every one of those raw top-level keys is gone from the wire.
|
|
209
|
-
for (const k of ["id", "name", "fullName", "email", "repo", "towers", "focus", "presence", "status"]) {
|
|
210
|
-
assert.equal(r.params[k], undefined, `${k} would have 400'd the strict schema`);
|
|
211
|
-
}
|
|
212
|
-
});
|
|
213
|
-
|
|
214
|
-
test("P0/2b toRegisterParams is idempotent — an already-projected object is untouched", () => {
|
|
215
|
-
const projected = { displayName: "D", archetype: "A", humanSponsor: "H", card: { x: 1 } };
|
|
216
|
-
assert.deepEqual(toRegisterParams(projected), projected);
|
|
217
|
-
assert.deepEqual(toRegisterParams(null), { card: {} });
|
|
218
|
-
});
|
|
219
|
-
|
|
220
|
-
test("P0/3 memory.author: hq requires {memoryClass (enum), title}", () => {
|
|
221
|
-
const w = wire("memory.author", { content: "Q3 pricing moved to usage-based.\nDetail follows.", kind: "STRATEGY" });
|
|
222
|
-
assert.equal(w.memoryClass, "strategy", "kind→memoryClass, case-folded into hq's enum");
|
|
223
|
-
assert.equal(w.title, "Q3 pricing moved to usage-based.", "a title is derived from the first line");
|
|
224
|
-
assert.equal(w.summary, "Q3 pricing moved to usage-based.\nDetail follows.");
|
|
225
|
-
assert.deepEqual(missingRequired("memory.author", w), []);
|
|
226
|
-
assert.ok(MEMORY_CLASSES.includes(w.memoryClass));
|
|
227
|
-
});
|
|
228
|
-
|
|
229
|
-
test("P0/3b memory.author: `tags` are NOT scopes — they are reported, never smuggled into the ACL", () => {
|
|
230
|
-
const r = norm("memory.author", { memoryClass: "ledger", title: "t", tags: ["private", "finance"] });
|
|
231
|
-
assert.equal(r.params.scopes, undefined, "tags must never become ACL scopes");
|
|
232
|
-
assert.ok(r.unsupported.includes("tags"));
|
|
233
|
-
});
|
|
234
|
-
|
|
235
|
-
test("P0/4 escalation.create: hq requires a title AND a target", () => {
|
|
236
|
-
const w = wire("escalation.create", { severity: "high", subject: "Deploy blocked", body: "CI is red" });
|
|
237
|
-
assert.equal(w.title, "Deploy blocked");
|
|
238
|
-
assert.equal(w.detail, "CI is red");
|
|
239
|
-
assert.deepEqual(missingRequired("escalation.create", w), ["taskId|channelId"]);
|
|
240
|
-
assert.deepEqual(missingRequired("escalation.create", { ...w, taskId: "t-1" }), []);
|
|
241
|
-
assert.ok(ESCALATION_SEVERITIES.includes(w.severity));
|
|
242
|
-
});
|
|
243
|
-
|
|
244
|
-
test("P0/5 member.get: hq resolves by slug, not memberId", () => {
|
|
245
|
-
assert.equal(wire("member.get", { memberId: "m-1" }).slug, "m-1");
|
|
246
|
-
assert.equal(wire("member.get", { id: "m-2" }).slug, "m-2");
|
|
247
|
-
});
|
|
248
|
-
|
|
249
|
-
test("P0/6 decision.comment: hq reads `text`, and threw on `body`", () => {
|
|
250
|
-
const w = wire("decision.comment", { decisionId: "d-1", body: "agreed" });
|
|
251
|
-
assert.equal(w.text, "agreed");
|
|
252
|
-
assert.deepEqual(missingRequired("decision.comment", w), []);
|
|
253
|
-
});
|
|
254
|
-
|
|
255
|
-
test("P0/7 approval.request: actionClass is REQUIRED; the hash and expiry are renamed", () => {
|
|
256
|
-
const w = wire("approval.request", {
|
|
257
|
-
id: "ap-1",
|
|
258
|
-
kind: "external_comms",
|
|
259
|
-
subject: "email to a new contact",
|
|
260
|
-
payload_hash: "a".repeat(64),
|
|
261
|
-
requester: "acme",
|
|
262
|
-
expiresInMs: 3_600_000,
|
|
263
|
-
});
|
|
264
|
-
assert.equal(w.actionClass, "external_comms", 'hq threw "actionClass is required" on every call');
|
|
265
|
-
assert.equal(w.payloadHash, "a".repeat(64));
|
|
266
|
-
assert.ok(Date.parse(w.expiresAt) > Date.now(), "expiresInMs → an absolute expiresAt");
|
|
267
|
-
assert.deepEqual(missingRequired("approval.request", w), []);
|
|
268
|
-
});
|
|
269
|
-
|
|
270
|
-
test("P0/7b approval.request: `requester` is server-derived and is reported as not-read", () => {
|
|
271
|
-
const r = norm("approval.request", { actionClass: "x", payload: {}, requester: "acme" });
|
|
272
|
-
assert.ok(r.unsupported.includes("requester"));
|
|
273
|
-
});
|
|
274
|
-
|
|
275
|
-
test("P0/8 calling.start: hq's refine demands channelId or a non-empty participantIds", () => {
|
|
276
|
-
const w = wire("calling.start", { channel: "C1", invitees: ["m1", "m2"], topic: "standup", id: "i-1" });
|
|
277
|
-
assert.equal(w.channelId, "C1");
|
|
278
|
-
assert.deepEqual(w.participantIds, ["m1", "m2"]);
|
|
279
|
-
assert.deepEqual(missingRequired("calling.start", w), []);
|
|
280
|
-
// The topic has nowhere to go on hq's schema — say so rather than pretend.
|
|
281
|
-
assert.ok(norm("calling.start", { channel: "C1", topic: "standup" }).unsupported.includes("topic"));
|
|
282
|
-
});
|
|
283
|
-
|
|
284
|
-
test("P0/9 board.complete: `note` 400'd the .strict() schema — it is wrapped as proof.note", () => {
|
|
285
|
-
assert.deepEqual(wire("board.complete", { itemId: "i-1", note: "PR #42 merged" }), {
|
|
286
|
-
itemId: "i-1",
|
|
287
|
-
proof: { note: "PR #42 merged" },
|
|
288
|
-
});
|
|
289
|
-
// A caller that already speaks `proof` is left alone.
|
|
290
|
-
assert.deepEqual(wire("board.complete", { itemId: "i-1", proof: { url: "u" } }), {
|
|
291
|
-
itemId: "i-1",
|
|
292
|
-
proof: { url: "u" },
|
|
293
|
-
});
|
|
294
|
-
});
|
|
295
|
-
|
|
296
|
-
// ── P1: the call SUCCEEDED but silently discarded the caller's intent ───────
|
|
297
|
-
|
|
298
|
-
test("P1/10 board.createTask: description/assignee/status were stripped by zod", () => {
|
|
299
|
-
const r = norm("board.createTask", {
|
|
300
|
-
title: "Ship the probe",
|
|
301
|
-
description: "wire conformance",
|
|
302
|
-
assignee: "m-1",
|
|
303
|
-
status: "todo",
|
|
304
|
-
priority: "P1",
|
|
305
|
-
dueAt: "2026-09-01",
|
|
306
|
-
});
|
|
307
|
-
assert.equal(r.params.detail, "wire conformance");
|
|
308
|
-
assert.equal(r.params.assigneeId, "m-1");
|
|
309
|
-
assert.equal(r.params.col, "todo");
|
|
310
|
-
assert.equal(r.params.priority, "P1");
|
|
311
|
-
// createTaskSchema models no dueAt at all — the loss is reported, not hidden.
|
|
312
|
-
assert.ok(r.unsupported.includes("dueAt"));
|
|
313
|
-
assert.ok(r.notes.some((n) => n.includes("board.updateTask")));
|
|
314
|
-
});
|
|
315
|
-
|
|
316
|
-
test("P1/11 board.updateTask: editTaskSchema has NO assignee — reassignment needs board.assignTask", () => {
|
|
317
|
-
const r = norm("board.updateTask", { taskId: "t-1", status: "review", description: "d", assignee: "m-9" });
|
|
318
|
-
assert.equal(r.params.col, "review");
|
|
319
|
-
assert.equal(r.params.detail, "d");
|
|
320
|
-
assert.ok(r.unsupported.includes("assignee"), "an agent that thought it reassigned a task did not");
|
|
321
|
-
// The verb that DOES work.
|
|
322
|
-
assert.deepEqual(missingRequired("board.assignTask", wire("board.assignTask", { taskId: "t-1", assignee: "m-9" })), []);
|
|
323
|
-
assert.equal(wire("board.assignTask", { taskId: "t-1", assignee: "m-9" }).assigneeId, "m-9");
|
|
324
|
-
});
|
|
325
|
-
|
|
326
|
-
test("P1/12 decision.propose: the rationale lives at why.reason, and `scope` is hq's `tag`", () => {
|
|
327
|
-
const w = wire("decision.propose", { title: "Adopt usage pricing", rationale: "margin", scope: "pricing" });
|
|
328
|
-
assert.deepEqual(w.why, { reason: "margin" });
|
|
329
|
-
assert.equal(w.tag, "pricing");
|
|
330
|
-
// An explicit why block is preserved and merged, not overwritten.
|
|
331
|
-
const merged = wire("decision.propose", { title: "t", why: { options: ["a", "b"] }, rationale: "r" });
|
|
332
|
-
assert.deepEqual(merged.why, { options: ["a", "b"], reason: "r" });
|
|
333
|
-
});
|
|
334
|
-
|
|
335
|
-
test("P1/13 messaging.history: hq paginates on before/after — since/cursor were dropped", () => {
|
|
336
|
-
const w = wire("messaging.history", { channel: "C1", cursor: "m-100", since: "m-1", limit: 50 });
|
|
337
|
-
assert.equal(w.channelId, "C1");
|
|
338
|
-
assert.equal(w.before, "m-100");
|
|
339
|
-
assert.equal(w.after, "m-1");
|
|
340
|
-
assert.equal(w.limit, 50);
|
|
341
|
-
});
|
|
342
|
-
|
|
343
|
-
test("P1/14 approval_request: a hash-only request keeps the hash on the name hq binds", () => {
|
|
344
|
-
const w = wire("approval.request", { actionClass: "external_comms", payload_hash: "b".repeat(64) });
|
|
345
|
-
assert.equal(w.payloadHash, "b".repeat(64));
|
|
346
|
-
assert.deepEqual(missingRequired("approval.request", w), [], "payload OR payloadHash satisfies the bind");
|
|
347
|
-
});
|
|
348
|
-
|
|
349
|
-
// ── P2: dropped enrichment ─────────────────────────────────────────────────
|
|
350
|
-
|
|
351
|
-
test("P2/15 knowledge.append: kind/participants/links/refs now persist under metadata", () => {
|
|
352
|
-
const r = norm("knowledge.append", { text: "call notes", kind: "note", participants: ["a", "b"] });
|
|
353
|
-
assert.equal(r.params.body, "call notes", "hq reads body (text is its accepted alias)");
|
|
354
|
-
assert.deepEqual(r.params.metadata, { kind: "note", participants: ["a", "b"] });
|
|
355
|
-
});
|
|
356
|
-
|
|
357
|
-
test("P2/16 knowledge.search: kind/includeInvalid/lexical are not read by hq — reported", () => {
|
|
358
|
-
const r = norm("knowledge.search", { query: "pricing", kind: "note", includeInvalid: true, lexical: true });
|
|
359
|
-
assert.equal(r.params.q, "pricing");
|
|
360
|
-
assert.deepEqual(r.unsupported.sort(), ["includeInvalid", "kind", "lexical"]);
|
|
361
|
-
});
|
|
362
|
-
|
|
363
|
-
test("P2/17 contacts.upsert / meetings.record: the ACL `group` hint is not read", () => {
|
|
364
|
-
assert.ok(norm("contacts.upsert", { displayName: "X", group: "org" }).unsupported.includes("group"));
|
|
365
|
-
assert.ok(norm("meetings.record", { title: "T", group: "org" }).unsupported.includes("group"));
|
|
366
|
-
});
|
|
367
|
-
|
|
368
|
-
test("P2/18 leases: `holder` and `leaseToken` never round-trip — ownership is the actor", () => {
|
|
369
|
-
assert.ok(norm("lease.claim", { scope: "work-claim", resourceId: "r", holder: "me" }).unsupported.includes("holder"));
|
|
370
|
-
assert.ok(norm("lease.release", { scope: "work-claim", resourceId: "r", leaseToken: "t" }).unsupported.includes("leaseToken"));
|
|
371
|
-
assert.equal(wire("lease.claim", { scope: "s", resourceId: "r", token: "hash" }).tokenHash, "hash");
|
|
372
|
-
});
|
|
373
|
-
|
|
374
|
-
// ═══════════════════════════════════════════════════════════════════════════
|
|
375
|
-
// 3. surface guard — the offline CI half
|
|
376
|
-
// ═══════════════════════════════════════════════════════════════════════════
|
|
377
|
-
|
|
378
|
-
test("GUARD: every contracted method is a real protocol method", () => {
|
|
379
|
-
for (const method of Object.keys(PARAM_CONTRACT)) {
|
|
380
|
-
assert.ok(methodDef(method), `${method} is not in the vendored protocol table`);
|
|
381
|
-
}
|
|
382
|
-
});
|
|
383
|
-
|
|
384
|
-
test("GUARD: no curated tool advertises a param name hq does not read", () => {
|
|
385
|
-
const offenders = [];
|
|
386
|
-
for (const t of ORG_TOOLS) {
|
|
387
|
-
if (!t.binding || t.binding.kind !== "rpc") continue;
|
|
388
|
-
const c = contractFor(t.binding.method);
|
|
389
|
-
if (!c || !c.alias) continue;
|
|
390
|
-
const stillRead = new Set(c.serverAccepts || []);
|
|
391
|
-
const props = Object.keys((t.input_schema && t.input_schema.properties) || {});
|
|
392
|
-
for (const p of props) {
|
|
393
|
-
// A legacy alias key the server does NOT also accept is a name the model
|
|
394
|
-
// would fill in and hq would ignore (or reject). That is the whole bug.
|
|
395
|
-
if (Object.prototype.hasOwnProperty.call(c.alias, p) && !stillRead.has(p)) {
|
|
396
|
-
offenders.push(`${t.name} (${t.binding.method}) advertises "${p}" — hq reads "${c.alias[p]}"`);
|
|
397
|
-
}
|
|
398
|
-
}
|
|
399
|
-
}
|
|
400
|
-
assert.deepEqual(offenders, [], `curated tools drifted from hq:\n ${offenders.join("\n ")}`);
|
|
401
|
-
});
|
|
402
|
-
|
|
403
|
-
test("GUARD: no curated tool advertises a param hq accepts but never reads", () => {
|
|
404
|
-
const offenders = [];
|
|
405
|
-
for (const t of ORG_TOOLS) {
|
|
406
|
-
if (!t.binding || t.binding.kind !== "rpc") continue;
|
|
407
|
-
const c = contractFor(t.binding.method);
|
|
408
|
-
if (!c || !c.unsupported) continue;
|
|
409
|
-
const props = Object.keys((t.input_schema && t.input_schema.properties) || {});
|
|
410
|
-
for (const p of props) {
|
|
411
|
-
if (c.unsupported.includes(p)) {
|
|
412
|
-
offenders.push(`${t.name} (${t.binding.method}) offers "${p}" — hq discards it`);
|
|
413
|
-
}
|
|
414
|
-
}
|
|
415
|
-
}
|
|
416
|
-
assert.deepEqual(offenders, [], `tools promise the model things hq drops:\n ${offenders.join("\n ")}`);
|
|
417
|
-
});
|
|
418
|
-
|
|
419
|
-
test("GUARD: a curated tool's REQUIRED params cover everything hq requires", () => {
|
|
420
|
-
const offenders = [];
|
|
421
|
-
for (const t of ORG_TOOLS) {
|
|
422
|
-
if (!t.binding || t.binding.kind !== "rpc") continue;
|
|
423
|
-
const c = contractFor(t.binding.method);
|
|
424
|
-
if (!c || !c.required) continue;
|
|
425
|
-
const props = new Set(Object.keys((t.input_schema && t.input_schema.properties) || {}));
|
|
426
|
-
for (const need of c.required) {
|
|
427
|
-
// Minted ids are supplied by the executor, not the model.
|
|
428
|
-
if ((c.mint || []).includes(need)) continue;
|
|
429
|
-
if (!props.has(need)) {
|
|
430
|
-
offenders.push(`${t.name} (${t.binding.method}) cannot supply required "${need}"`);
|
|
431
|
-
}
|
|
432
|
-
}
|
|
433
|
-
}
|
|
434
|
-
assert.deepEqual(offenders, [], `tools cannot satisfy hq:\n ${offenders.join("\n ")}`);
|
|
435
|
-
});
|
|
436
|
-
|
|
437
|
-
test("GUARD: enum-valued tool params list exactly hq's vocabulary", () => {
|
|
438
|
-
const byTool = {
|
|
439
|
-
task_create: { col: BOARD_COLS, priority: TASK_PRIORITIES },
|
|
440
|
-
task_update: { col: BOARD_COLS, priority: TASK_PRIORITIES },
|
|
441
|
-
memory_author: { memoryClass: MEMORY_CLASSES },
|
|
442
|
-
escalation_raise: { severity: ESCALATION_SEVERITIES },
|
|
443
|
-
};
|
|
444
|
-
for (const [toolName, expect] of Object.entries(byTool)) {
|
|
445
|
-
const t = ORG_TOOLS.find((x) => x.name === toolName);
|
|
446
|
-
assert.ok(t, `${toolName} is in the table`);
|
|
447
|
-
for (const [param, values] of Object.entries(expect)) {
|
|
448
|
-
assert.deepEqual(t.input_schema.properties[param].enum, values, `${toolName}.${param}`);
|
|
449
|
-
}
|
|
450
|
-
}
|
|
451
|
-
});
|
|
452
|
-
|
|
453
|
-
test("messaging.send/edit: mentions are coerced to hq's object shape at the ONE chokepoint", () => {
|
|
454
|
-
const sent = normalizeParams("messaging.send", {
|
|
455
|
-
channelId: "C-1",
|
|
456
|
-
body: "b",
|
|
457
|
-
idempotencyId: "i",
|
|
458
|
-
mentions: ["M-2", { memberId: "M-2" }, { id: "M-3" }, { memberId: "M-4", offset: 3 }, "", null],
|
|
459
|
-
});
|
|
460
|
-
assert.deepEqual(sent.params.mentions, [
|
|
461
|
-
{ memberId: "M-2" },
|
|
462
|
-
{ memberId: "M-3" },
|
|
463
|
-
{ memberId: "M-4", offset: 3 },
|
|
464
|
-
]);
|
|
465
|
-
|
|
466
|
-
// Idempotent: applying the contract twice must not re-wrap.
|
|
467
|
-
const twice = normalizeParams("messaging.send", sent.params);
|
|
468
|
-
assert.deepEqual(twice.params.mentions, sent.params.mentions);
|
|
469
|
-
|
|
470
|
-
// An empty list states nothing rather than asserting an empty relation.
|
|
471
|
-
const none = normalizeParams("messaging.send", { channelId: "C", body: "b", idempotencyId: "i", mentions: [] });
|
|
472
|
-
assert.equal("mentions" in none.params, false);
|
|
473
|
-
|
|
474
|
-
// messaging.edit reuses hq's mentionSchema, so it gets the same coercion.
|
|
475
|
-
const edited = normalizeParams("messaging.edit", { messageId: "m1", body: "b", mentions: ["M-9"] });
|
|
476
|
-
assert.deepEqual(edited.params.mentions, [{ memberId: "M-9" }]);
|
|
477
|
-
});
|