@cohortapp/agent-sdk 2.17.0 → 2.18.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/settings.json +18 -0
- package/.env.example +18 -5
- package/README.md +1 -0
- package/bin/maestro.mjs +62 -0
- package/docs/guides/billing-console-keys.md +60 -0
- package/docs/guides/front-door-session.md +54 -9
- package/docs/guides/mac-mini.md +20 -25
- package/docs/guides/setup-wizard.md +1 -1
- package/docs/runbooks/fleet-rollout.md +156 -0
- package/docs/runbooks/mac-mini-bootstrap.md +12 -14
- package/lib/action-executor.js +19 -3
- package/lib/budget-guard.mjs +279 -3
- package/lib/channels/base-adapter.mjs +3 -1
- package/lib/channels/contract.mjs +2 -1
- package/lib/channels/inbox-item.mjs +8 -0
- package/lib/claude-bin.mjs +5 -6
- package/lib/cli/doctor-checks.mjs +141 -10
- package/lib/cli/global-setup-extras.mjs +5 -1
- package/lib/cli/inbox.mjs +100 -15
- package/lib/cli/seat-auth.mjs +463 -0
- package/lib/cli/session.mjs +80 -12
- package/lib/collective/capture.mjs +8 -6
- package/lib/collective/global-config.mjs +63 -1
- package/lib/collective/presence.mjs +142 -5
- package/lib/comms/send-gate.mjs +559 -1
- package/lib/diagnostics/alerts.mjs +49 -0
- package/lib/diagnostics/cadence-output-freshness.mjs +288 -0
- package/lib/engine/agents/definitions.mjs +343 -0
- package/lib/engine/agents/persist.mjs +275 -0
- package/lib/engine/agents/runtime.mjs +748 -0
- package/lib/engine/agents/usage.mjs +95 -0
- package/lib/engine/auth-status.mjs +139 -0
- package/lib/engine/budget.mjs +194 -0
- package/lib/engine/cli.mjs +1204 -0
- package/lib/engine/commands/index.mjs +269 -0
- package/lib/engine/context/budget.mjs +219 -0
- package/lib/engine/context/cache.mjs +125 -0
- package/lib/engine/context/child-env.mjs +215 -0
- package/lib/engine/context/compaction.mjs +342 -0
- package/lib/engine/context/images.mjs +90 -0
- package/lib/engine/context/instructions.mjs +327 -0
- package/lib/engine/context/lazy-instructions.mjs +169 -0
- package/lib/engine/context/manager.mjs +182 -0
- package/lib/engine/context/real-path.mjs +91 -0
- package/lib/engine/context/secret-values.mjs +163 -0
- package/lib/engine/context/settings.mjs +274 -0
- package/lib/engine/context/stream-input.mjs +159 -0
- package/lib/engine/guard.mjs +152 -0
- package/lib/engine/hooks.mjs +713 -0
- package/lib/engine/loop.mjs +560 -0
- package/lib/engine/mcp/client.mjs +254 -0
- package/lib/engine/mcp/config.mjs +301 -0
- package/lib/engine/mcp/http.mjs +201 -0
- package/lib/engine/mcp/index.mjs +146 -0
- package/lib/engine/mcp/jsonrpc.mjs +147 -0
- package/lib/engine/mcp/naming.mjs +66 -0
- package/lib/engine/mcp/resources.mjs +89 -0
- package/lib/engine/mcp/results.mjs +133 -0
- package/lib/engine/mcp/stdio.mjs +137 -0
- package/lib/engine/mcp/supervisor.mjs +116 -0
- package/lib/engine/messages.mjs +104 -0
- package/lib/engine/output/json.mjs +164 -0
- package/lib/engine/output/stream-json.mjs +266 -0
- package/lib/engine/permissions.mjs +845 -0
- package/lib/engine/process-identity.mjs +164 -0
- package/lib/engine/process-tree.mjs +551 -0
- package/lib/engine/prompt.mjs +60 -0
- package/lib/engine/session/store.mjs +299 -0
- package/lib/engine/session-runtime/args.mjs +97 -0
- package/lib/engine/session-runtime/host.mjs +143 -0
- package/lib/engine/session-runtime/inbox.mjs +122 -0
- package/lib/engine/session-runtime/notifications.mjs +129 -0
- package/lib/engine/session-runtime/registry.mjs +328 -0
- package/lib/engine/session-runtime/runner.mjs +344 -0
- package/lib/engine/session-runtime/socket.mjs +212 -0
- package/lib/engine/session-runtime/wakeup.mjs +115 -0
- package/lib/engine/skills/index.mjs +321 -0
- package/lib/engine/tools/bash-background.mjs +533 -0
- package/lib/engine/tools/bash.mjs +216 -0
- package/lib/engine/tools/edit.mjs +97 -0
- package/lib/engine/tools/glob.mjs +81 -0
- package/lib/engine/tools/grep.mjs +224 -0
- package/lib/engine/tools/index.mjs +84 -0
- package/lib/engine/tools/list-agents.mjs +32 -0
- package/lib/engine/tools/ls.mjs +127 -0
- package/lib/engine/tools/monitor.mjs +82 -0
- package/lib/engine/tools/notebook-edit.mjs +218 -0
- package/lib/engine/tools/read.mjs +103 -0
- package/lib/engine/tools/schedule-wakeup.mjs +45 -0
- package/lib/engine/tools/schema.mjs +144 -0
- package/lib/engine/tools/send-message.mjs +77 -0
- package/lib/engine/tools/session.mjs +70 -0
- package/lib/engine/tools/todo.mjs +144 -0
- package/lib/engine/tools/toolsearch.mjs +217 -0
- package/lib/engine/tools/walk.mjs +193 -0
- package/lib/engine/tools/web-switch.mjs +31 -0
- package/lib/engine/tools/webfetch-html.mjs +387 -0
- package/lib/engine/tools/webfetch-net.mjs +340 -0
- package/lib/engine/tools/webfetch.mjs +198 -0
- package/lib/engine/tools/websearch.mjs +91 -0
- package/lib/engine/tools/workflow.mjs +95 -0
- package/lib/engine/tools/write.mjs +76 -0
- package/lib/engine/tui/line-editor.mjs +137 -0
- package/lib/engine/tui/render.mjs +86 -0
- package/lib/engine/tui/tui.mjs +274 -0
- package/lib/engine/wire/anthropic-messages.mjs +263 -0
- package/lib/engine/wire/effort.mjs +36 -0
- package/lib/engine/wire/errors.mjs +496 -0
- package/lib/engine/wire/http.mjs +441 -0
- package/lib/engine/wire/index.mjs +76 -0
- package/lib/engine/wire/openai-chat.mjs +332 -0
- package/lib/engine/wire/prompt-cache.mjs +79 -0
- package/lib/engine/wire/search.mjs +140 -0
- package/lib/engine/wire/sse.mjs +114 -0
- package/lib/engine/wire/stall.mjs +349 -0
- package/lib/engine/wire/token-provider.mjs +175 -0
- package/lib/engine/wire/usage.mjs +192 -0
- package/lib/engine/workflow/host.mjs +524 -0
- package/lib/engine/workflow/journal.mjs +188 -0
- package/lib/engine/workflow/json-schema.mjs +171 -0
- package/lib/engine/workflow/meta.mjs +329 -0
- package/lib/engine/workflow/notifications.mjs +52 -0
- package/lib/engine/workflow/runtime.mjs +447 -0
- package/lib/engine/workflow/sandbox.mjs +534 -0
- package/lib/engine/workflow/worker.mjs +141 -0
- package/lib/engine/workflow/worktree.mjs +74 -0
- package/lib/execution/disposition.mjs +1 -1
- package/lib/execution/intake.mjs +10 -0
- package/lib/execution/surface-policy.mjs +15 -0
- package/lib/learning/curator.mjs +8 -6
- package/lib/learning/reflect.mjs +8 -6
- package/lib/model-router/catalog/cohort.yaml +137 -0
- package/lib/model-router/catalog.mjs +118 -1
- package/lib/model-router/failover.mjs +67 -16
- package/lib/model-router/llm-task.mjs +39 -3
- package/lib/model-router/resolve.mjs +89 -3
- package/lib/model-router/spawn.mjs +46 -47
- package/lib/model-router/taxonomy.mjs +126 -4
- package/lib/org/cost-sync.mjs +141 -11
- package/lib/org/inbound/broadcast.mjs +289 -0
- package/lib/org/inbound/collective.mjs +375 -0
- package/lib/org/inbound/directedness.mjs +96 -8
- package/lib/org/inbound/facts.mjs +78 -2
- package/lib/org/inbound/project.mjs +22 -0
- package/lib/org/inbound/surfaces.mjs +14 -0
- package/lib/org/llm-token.mjs +879 -0
- package/lib/org/mesh.mjs +61 -0
- package/lib/org/messaging.mjs +3 -1
- package/lib/org/protocol.checksum +1 -1
- package/lib/org/protocol.mjs +15 -0
- package/lib/org/quota.mjs +520 -0
- package/lib/org/tool-surface.mjs +104 -16
- package/lib/org/ui-parity.mjs +16 -1
- package/lib/org/work-ledger.mjs +37 -6
- package/lib/rate-guard.mjs +114 -1
- package/lib/resource-governor.mjs +41 -6
- package/lib/runtime/adapter.mjs +823 -0
- package/lib/runtime/child-env.mjs +191 -0
- package/lib/runtime/legacy-shell-guard.mjs +97 -0
- package/lib/runtime/seat-engine.mjs +162 -0
- package/lib/session/ask-ledger.mjs +271 -0
- package/lib/session/current-work.mjs +676 -0
- package/lib/session/feed-core.mjs +40 -3
- package/lib/session/launch-args.mjs +56 -4
- package/lib/session/status-summary.mjs +26 -9
- package/lib/session/upgrade-notice.mjs +42 -0
- package/lib/setup/claude-probe.mjs +117 -13
- package/lib/setup/enrich.mjs +13 -10
- package/lib/setup/sections/model.mjs +39 -13
- package/lib/telemetry/collect.mjs +208 -9
- package/lib/upgrade/ignored-drift.mjs +105 -0
- package/lib/voice/post-call-brief.mjs +30 -17
- package/package.json +13 -3
- package/plugins/maestro-skills/skills/board-work.md +5 -0
- package/plugins/maestro-skills/skills/inbound-triage.md +56 -15
- package/plugins/maestro-skills/skills/main-session.md +18 -7
- package/scripts/ci/check-tarball-fidelity.mjs +126 -2
- package/scripts/ci/run-tests.mjs +47 -19
- package/scripts/cohort-llm/api-key-helper.mjs +92 -0
- package/scripts/collective/hook-runner.mjs +29 -2
- package/scripts/continuous-monitor.sh +13 -0
- package/scripts/cost/track-claude-usage.mjs +15 -0
- package/scripts/daemon/agent-daemon.mjs +408 -20
- package/scripts/daemon/assurance.mjs +48 -12
- package/scripts/daemon/cadence-consumer.mjs +218 -68
- package/scripts/daemon/cadence-handlers.mjs +73 -4
- package/scripts/daemon/classifier.mjs +75 -26
- package/scripts/daemon/context-compiler.mjs +51 -37
- package/scripts/daemon/deliver.mjs +30 -1
- package/scripts/daemon/dispatcher.mjs +595 -149
- package/scripts/daemon/health.mjs +14 -1
- package/scripts/daemon/maestro-daemon.mjs +11 -0
- package/scripts/daemon/prompt-builder.mjs +24 -0
- package/scripts/daemon/responder.mjs +246 -79
- package/scripts/daemon/sdk-version.mjs +98 -16
- package/scripts/eval/probe-gateway.mjs +635 -0
- package/scripts/eval/replay/extract.mjs +270 -0
- package/scripts/eval/replay/grade.mjs +260 -0
- package/scripts/eval/replay/lib/config.mjs +50 -0
- package/scripts/eval/replay/lib/effects.mjs +65 -0
- package/scripts/eval/replay/lib/fixture.mjs +188 -0
- package/scripts/eval/replay/lib/judge.mjs +72 -0
- package/scripts/eval/replay/lib/redact.mjs +136 -0
- package/scripts/eval/replay/lib/sandbox.mjs +170 -0
- package/scripts/eval/replay/lib/schema-check.mjs +63 -0
- package/scripts/eval/replay/lib/transcript.mjs +76 -0
- package/scripts/eval/replay/mcp-replay-stub.mjs +101 -0
- package/scripts/eval/replay/report.mjs +185 -0
- package/scripts/eval/replay/run.mjs +404 -0
- package/scripts/fleet/rollout.mjs +1094 -0
- package/scripts/hooks/pre-send-audit.sh +36 -245
- package/scripts/hooks/pre-write-yaml-validate.mjs +275 -0
- package/scripts/hooks/validate-state-yaml.sh +190 -0
- package/scripts/huddle/huddle-llm.mjs +361 -0
- package/scripts/huddle/huddle-server.mjs +46 -121
- package/scripts/local-triggers/autoupdate.sh +448 -78
- package/scripts/local-triggers/run-trigger.sh +13 -0
- package/scripts/maintenance/pin-integrity.mjs +364 -0
- package/scripts/poll-slack-events.sh +41 -9
- package/scripts/poller/slack-socket-mode.mjs +28 -3
- package/scripts/session/supervisor.mjs +80 -13
- package/scripts/spawn-session.sh +13 -0
- package/bin/maestro.test.mjs +0 -1574
- package/lib/action-executor.test.mjs +0 -871
- package/lib/archetype.test.mjs +0 -132
- package/lib/assurance/plan-note.test.mjs +0 -234
- package/lib/assurance/room-budget.test.mjs +0 -486
- package/lib/assurance/tier.test.mjs +0 -174
- package/lib/autonomy.test.mjs +0 -66
- package/lib/backlog.test.mjs +0 -302
- package/lib/backup/policy.test.mjs +0 -305
- package/lib/budget-escalate.test.mjs +0 -232
- package/lib/budget-guard.envelope.test.mjs +0 -476
- package/lib/budget-guard.test.mjs +0 -427
- package/lib/cadence-bus-requeue.test.mjs +0 -83
- package/lib/cadence-bus-schedule.test.mjs +0 -194
- package/lib/cadence-bus.test.mjs +0 -720
- package/lib/cadences.test.mjs +0 -230
- package/lib/capability/inventory.test.mjs +0 -232
- package/lib/capability.test.mjs +0 -78
- package/lib/channels/base-adapter.test.mjs +0 -590
- package/lib/channels/channels.test.mjs +0 -371
- package/lib/channels/contract.test.mjs +0 -162
- package/lib/channels/inbox-item.test.mjs +0 -368
- package/lib/channels/orgmail/adapter.test.mjs +0 -448
- package/lib/channels/pairing.test.mjs +0 -270
- package/lib/channels/repeat-suppressor.test.mjs +0 -134
- package/lib/channels/slack-adapter.test.mjs +0 -212
- package/lib/channels/telegram-adapter.test.mjs +0 -306
- package/lib/channels/voice/adapter.test.mjs +0 -278
- package/lib/channels/whatsapp/adapter-baileys.test.mjs +0 -359
- package/lib/channels/whatsapp/baileys-typing.test.mjs +0 -154
- package/lib/charter.test.mjs +0 -89
- package/lib/claude-bin.test.mjs +0 -131
- package/lib/cli/board.test.mjs +0 -227
- package/lib/cli/design.test.mjs +0 -270
- package/lib/cli/doctor-checks.test.mjs +0 -336
- package/lib/cli/global-setup-extras.test.mjs +0 -462
- package/lib/cli/inbox.test.mjs +0 -230
- package/lib/cli/session-ack.test.mjs +0 -63
- package/lib/cli/session.test.mjs +0 -613
- package/lib/collective/capture.test.mjs +0 -121
- package/lib/collective/cards.test.mjs +0 -114
- package/lib/collective/config.test.mjs +0 -123
- package/lib/collective/global-config.test.mjs +0 -220
- package/lib/collective/global-skills.test.mjs +0 -126
- package/lib/collective/presence.test.mjs +0 -95
- package/lib/collective/recall.test.mjs +0 -116
- package/lib/collective/vendor-skills.test.mjs +0 -306
- package/lib/comms/send-gate.test.mjs +0 -770
- package/lib/comms.test.mjs +0 -41
- package/lib/context/budget.test.mjs +0 -252
- package/lib/context/history-scope.test.mjs +0 -79
- package/lib/cost/ledger-row.test.mjs +0 -183
- package/lib/design/design-md.test.mjs +0 -318
- package/lib/design/fixtures/DESIGN.golden.md +0 -238
- package/lib/design/fixtures/PRODUCT.golden.md +0 -67
- package/lib/design/fixtures/foundation.json +0 -133
- package/lib/design/refresh-gate.test.mjs +0 -144
- package/lib/design/write.test.mjs +0 -241
- package/lib/diagnostics/alerts.test.mjs +0 -318
- package/lib/diagnostics/backup-freshness.test.mjs +0 -185
- package/lib/diagnostics/counters.test.mjs +0 -206
- package/lib/diagnostics/events.test.mjs +0 -290
- package/lib/diagnostics/otel.test.mjs +0 -196
- package/lib/diagnostics/trace.test.mjs +0 -251
- package/lib/env-compat.test.mjs +0 -104
- package/lib/execution/disposition.test.mjs +0 -553
- package/lib/execution/drive.test.mjs +0 -270
- package/lib/execution/effects.test.mjs +0 -344
- package/lib/execution/intake.test.mjs +0 -389
- package/lib/execution/journal.test.mjs +0 -261
- package/lib/execution/match.test.mjs +0 -235
- package/lib/execution/pipeline.test.mjs +0 -392
- package/lib/execution/route.test.mjs +0 -186
- package/lib/execution/surface-policy.test.mjs +0 -162
- package/lib/fs-atomic.test.mjs +0 -72
- package/lib/fs-ownership.test.mjs +0 -158
- package/lib/goals/admission.test.mjs +0 -164
- package/lib/goals/classify.test.mjs +0 -167
- package/lib/goals/collaborate.test.mjs +0 -336
- package/lib/goals/gaps.test.mjs +0 -284
- package/lib/goals/loop.test.mjs +0 -845
- package/lib/hooks/bus.test.mjs +0 -387
- package/lib/identity/persona.test.mjs +0 -142
- package/lib/kpi-sensors.test.mjs +0 -278
- package/lib/kpi.test.mjs +0 -244
- package/lib/learning/config.test.mjs +0 -75
- package/lib/learning/counters.test.mjs +0 -69
- package/lib/learning/curator-consolidate.test.mjs +0 -238
- package/lib/learning/curator.test.mjs +0 -106
- package/lib/learning/reflect.test.mjs +0 -0
- package/lib/learning/session-index.test.mjs +0 -125
- package/lib/learning/skill-writer.test.mjs +0 -210
- package/lib/mandate/audit.test.mjs +0 -195
- package/lib/mandate/contract.test.mjs +0 -185
- package/lib/mandate/derive.test.mjs +0 -274
- package/lib/mandate/model.test.mjs +0 -164
- package/lib/mandate/refresh.test.mjs +0 -389
- package/lib/mcp/server.test.mjs +0 -426
- package/lib/model-router/auth-profiles.test.mjs +0 -580
- package/lib/model-router/catalog.test.mjs +0 -385
- package/lib/model-router/economics.test.mjs +0 -438
- package/lib/model-router/failover.test.mjs +0 -439
- package/lib/model-router/health.test.mjs +0 -338
- package/lib/model-router/integration-coverage.test.mjs +0 -831
- package/lib/model-router/integration.test.mjs +0 -564
- package/lib/model-router/ledger.test.mjs +0 -415
- package/lib/model-router/llm-task.test.mjs +0 -392
- package/lib/model-router/org-credentials.test.mjs +0 -265
- package/lib/model-router/pricing-refresh.test.mjs +0 -286
- package/lib/model-router/reconcile.test.mjs +0 -316
- package/lib/model-router/repair.test.mjs +0 -180
- package/lib/model-router/spawn.test.mjs +0 -446
- package/lib/model-router/taxonomy.test.mjs +0 -410
- package/lib/model-router.test.mjs +0 -1207
- package/lib/org/activity.test.mjs +0 -134
- package/lib/org/approvals.test.mjs +0 -216
- package/lib/org/awareness.test.mjs +0 -159
- package/lib/org/board-mine-cache.test.mjs +0 -53
- package/lib/org/board.test.mjs +0 -187
- package/lib/org/bootstrap-context.test.mjs +0 -153
- package/lib/org/client.test.mjs +0 -1206
- package/lib/org/cohort-client.test.mjs +0 -126
- package/lib/org/cost-sync.test.mjs +0 -153
- package/lib/org/doctor.test.mjs +0 -346
- package/lib/org/engagement-ledger.test.mjs +0 -112
- package/lib/org/engagement.test.mjs +0 -739
- package/lib/org/handoff.test.mjs +0 -269
- package/lib/org/inbound/directedness.test.mjs +0 -668
- package/lib/org/inbound/facts.test.mjs +0 -471
- package/lib/org/inbound/hydrate.test.mjs +0 -908
- package/lib/org/inbound/index.test.mjs +0 -429
- package/lib/org/inbound/project.test.mjs +0 -287
- package/lib/org/integration-tools.test.mjs +0 -160
- package/lib/org/keys.test.mjs +0 -92
- package/lib/org/knowledge.test.mjs +0 -326
- package/lib/org/leases.test.mjs +0 -235
- package/lib/org/mesh-directives.test.mjs +0 -110
- package/lib/org/mesh-integration.test.mjs +0 -127
- package/lib/org/mesh.test.mjs +0 -400
- package/lib/org/messaging.test.mjs +0 -471
- package/lib/org/param-contract.test.mjs +0 -477
- package/lib/org/policy.test.mjs +0 -237
- package/lib/org/protocol.checksum.test.mjs +0 -90
- package/lib/org/protocol.test.mjs +0 -323
- package/lib/org/push.test.mjs +0 -792
- package/lib/org/registry.test.mjs +0 -100
- package/lib/org/resource-tools.test.mjs +0 -361
- package/lib/org/tool-access.test.mjs +0 -144
- package/lib/org/tool-surface-integration.test.mjs +0 -120
- package/lib/org/tool-surface.test.mjs +0 -1268
- package/lib/org/typing.test.mjs +0 -291
- package/lib/org/ui-parity.test.mjs +0 -560
- package/lib/org/verify.test.mjs +0 -194
- package/lib/org/work-ledger.test.mjs +0 -273
- package/lib/plan/adoption-e2e.test.mjs +0 -366
- package/lib/plan/budget-enforcement.test.mjs +0 -400
- package/lib/plan/compile.test.mjs +0 -382
- package/lib/plan/emit.test.mjs +0 -269
- package/lib/plan/explain.test.mjs +0 -188
- package/lib/prompts/parallelism.test.mjs +0 -177
- package/lib/rag/rag.test.mjs +0 -505
- package/lib/rate-guard.test.mjs +0 -272
- package/lib/reactive-gate.test.mjs +0 -57
- package/lib/render.test.mjs +0 -68
- package/lib/resource-governor.test.mjs +0 -488
- package/lib/scheduling/dynamic-jobs.test.mjs +0 -344
- package/lib/scheduling/jitter.test.mjs +0 -140
- package/lib/secrets/broker.test.mjs +0 -280
- package/lib/secrets/providers.test.mjs +0 -274
- package/lib/security/audit-engine.test.mjs +0 -424
- package/lib/security/coerce-args.test.mjs +0 -281
- package/lib/security/dangerous-tools.test.mjs +0 -68
- package/lib/security/external-content.test.mjs +0 -84
- package/lib/security/redact.test.mjs +0 -441
- package/lib/security/secret-equal.test.mjs +0 -55
- package/lib/session/config.test.mjs +0 -92
- package/lib/session/feed-core.test.mjs +0 -198
- package/lib/session/first-run.test.mjs +0 -121
- package/lib/session/frontdoor.test.mjs +0 -205
- package/lib/session/handoffs.test.mjs +0 -183
- package/lib/session/identity.test.mjs +0 -180
- package/lib/session/inbox-claims.test.mjs +0 -286
- package/lib/session/launch-args.test.mjs +0 -157
- package/lib/session/liveness.test.mjs +0 -100
- package/lib/session/status-summary.test.mjs +0 -118
- package/lib/session-permissions.test.mjs +0 -120
- package/lib/setup/claude-probe.test.mjs +0 -187
- package/lib/setup/completeness.test.mjs +0 -110
- package/lib/setup/context-pack.test.mjs +0 -89
- package/lib/setup/enrich.test.mjs +0 -115
- package/lib/setup/enroll-from-cohort.test.mjs +0 -300
- package/lib/setup/integration.test.mjs +0 -162
- package/lib/setup/io.test.mjs +0 -77
- package/lib/setup/runner.test.mjs +0 -132
- package/lib/setup/sections/identity.test.mjs +0 -234
- package/lib/setup/sections/inventory.test.mjs +0 -198
- package/lib/setup/sections/learning.test.mjs +0 -81
- package/lib/setup/sections/mandate.test.mjs +0 -388
- package/lib/setup/sections/messaging.test.mjs +0 -127
- package/lib/setup/sections/model.test.mjs +0 -240
- package/lib/setup/sections/org.test.mjs +0 -346
- package/lib/setup/sections/orgmail.test.mjs +0 -118
- package/lib/setup/sections/recovery.test.mjs +0 -98
- package/lib/setup/sections/subagents.test.mjs +0 -429
- package/lib/setup/sections/verify.test.mjs +0 -175
- package/lib/setup/sot.test.mjs +0 -81
- package/lib/setup/state.test.mjs +0 -115
- package/lib/singleton.test.mjs +0 -151
- package/lib/subagents/cli.test.mjs +0 -389
- package/lib/subagents/client.test.mjs +0 -309
- package/lib/subagents/gap.test.mjs +0 -234
- package/lib/subagents/lock.test.mjs +0 -248
- package/lib/subagents/manifest.test.mjs +0 -175
- package/lib/subagents/refs.test.mjs +0 -204
- package/lib/subagents/resolve.test.mjs +0 -422
- package/lib/subagents/schema.test.mjs +0 -328
- package/lib/telemetry/alerts.test.mjs +0 -109
- package/lib/telemetry/collect.test.mjs +0 -1274
- package/lib/tool-definitions-integration.test.mjs +0 -83
- package/lib/tool-definitions.test.mjs +0 -437
- package/lib/upgrade/global-refresh.test.mjs +0 -65
- package/lib/upgrade/launchd-reconcile.test.mjs +0 -272
- package/lib/upgrade/post-steps.test.mjs +0 -200
- package/lib/upgrade/verify.test.mjs +0 -164
- package/lib/util/fetch-timeout.test.mjs +0 -202
- package/lib/util/reconnect.test.mjs +0 -369
- package/lib/util/unhandled.test.mjs +0 -216
- package/lib/voice/outbound.test.mjs +0 -69
- package/lib/voice/session-rotation.test.mjs +0 -114
- package/lib/voice/stt.test.mjs +0 -226
- package/lib/voice/voice.test.mjs +0 -990
- package/scripts/cadence/enqueue-cadence-tick.test.mjs +0 -187
- package/scripts/ci/check-docs-accuracy.test.mjs +0 -409
- package/scripts/ci/check-durable-write-seam.test.mjs +0 -90
- package/scripts/ci/check-no-build-artifacts.test.mjs +0 -71
- package/scripts/ci/check-no-residual-identity.test.mjs +0 -202
- package/scripts/ci/check-skill-packs.test.mjs +0 -495
- package/scripts/ci/check-subagent-frontmatter.test.mjs +0 -124
- package/scripts/ci/check.test.mjs +0 -194
- package/scripts/ci/conformance-org-api.test.mjs +0 -425
- package/scripts/cloud-relay/voice/relay-identity.test.mjs +0 -96
- package/scripts/collective/hook-runner.test.mjs +0 -173
- package/scripts/cost/fleet-digest.test.mjs +0 -207
- package/scripts/cost/track-claude-usage-pricing.test.mjs +0 -183
- package/scripts/cost/track-claude-usage.test.mjs +0 -148
- package/scripts/daemon/agent-daemon-board-mine.test.mjs +0 -96
- package/scripts/daemon/agent-daemon-design.test.mjs +0 -238
- package/scripts/daemon/agent-daemon-frontdoor.test.mjs +0 -60
- package/scripts/daemon/agent-daemon.test.mjs +0 -995
- package/scripts/daemon/assurance-e2e.test.mjs +0 -613
- package/scripts/daemon/assurance.test.mjs +0 -1791
- package/scripts/daemon/board-mirror.test.mjs +0 -165
- package/scripts/daemon/cadence-consumer-frontdoor.test.mjs +0 -393
- package/scripts/daemon/cadence-consumer-governance.test.mjs +0 -276
- package/scripts/daemon/cadence-consumer.test.mjs +0 -776
- package/scripts/daemon/cadence-handlers.test.mjs +0 -837
- package/scripts/daemon/classifier-identity.test.mjs +0 -137
- package/scripts/daemon/classifier.test.mjs +0 -266
- package/scripts/daemon/classify-kind.test.mjs +0 -40
- package/scripts/daemon/context-compiler.test.mjs +0 -406
- package/scripts/daemon/deliver.test.mjs +0 -564
- package/scripts/daemon/dispatcher-cooldown.test.mjs +0 -122
- package/scripts/daemon/dispatcher-governance.test.mjs +0 -1013
- package/scripts/daemon/dispatcher-resume.test.mjs +0 -166
- package/scripts/daemon/dispatcher-session-continuity.test.mjs +0 -365
- package/scripts/daemon/execution-ladder.test.mjs +0 -470
- package/scripts/daemon/goal-steward-cadence.test.mjs +0 -312
- package/scripts/daemon/inbox-deferral-session.test.mjs +0 -49
- package/scripts/daemon/inbox-deferral.test.mjs +0 -336
- package/scripts/daemon/inbox-wake.test.mjs +0 -199
- package/scripts/daemon/integration.test.mjs +0 -149
- package/scripts/daemon/lib/self-echo.test.mjs +0 -153
- package/scripts/daemon/lib/session-router.test.mjs +0 -554
- package/scripts/daemon/prompt-builder-preamble.test.mjs +0 -210
- package/scripts/daemon/prompt-builder.test.mjs +0 -556
- package/scripts/daemon/responder-cost.test.mjs +0 -68
- package/scripts/daemon/responder-history.test.mjs +0 -221
- package/scripts/daemon/sdk-version.test.mjs +0 -31
- package/scripts/daemon/session-lock.test.mjs +0 -252
- package/scripts/daemon/session-outcomes.test.mjs +0 -533
- package/scripts/daemon/typing-registry.test.mjs +0 -102
- package/scripts/hooks/pre-send-audit.test.mjs +0 -354
- package/scripts/huddle/huddle-prompt.test.mjs +0 -176
- package/scripts/local-triggers/autoupdate.test.mjs +0 -518
- package/scripts/local-triggers/generate-plists.test.mjs +0 -456
- package/scripts/media-generation/brand-clause.test.mjs +0 -135
- package/scripts/org/send-orgmail.first-contact.test.mjs +0 -102
- package/scripts/poller/inbox-privilege-injection.test.mjs +0 -167
- package/scripts/poller/inbox-scan-poller.test.mjs +0 -295
- package/scripts/poller/lib/cloud-relay-dedup.test.mjs +0 -133
- package/scripts/poller/slack-socket-mode.test.mjs +0 -805
- package/scripts/poller-launchd/install.test.mjs +0 -243
- package/scripts/restore-from-backup.test.mjs +0 -181
- package/scripts/session/feed.test.mjs +0 -196
- package/scripts/session/supervisor-sh.test.mjs +0 -218
- package/scripts/session/supervisor.test.mjs +0 -482
- package/scripts/setup/configure-macos.test.mjs +0 -306
- package/scripts/setup/gen-subagent-manifest.test.mjs +0 -124
- package/scripts/setup/generate-agent-package-json.test.mjs +0 -143
- package/scripts/setup/generate-capability.test.mjs +0 -134
- package/scripts/setup/init-agent.test.mjs +0 -370
- package/scripts/setup/init-skill-marketplace.test.mjs +0 -193
- package/scripts/vendor/sync-skill-packs.test.mjs +0 -103
- package/scripts/watchdog/memory-watchdog.test.mjs +0 -64
package/lib/org/verify.test.mjs
DELETED
|
@@ -1,194 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* verify.test.mjs — detective layer: pinned-signer snapshot verification +
|
|
3
|
-
* event-chain re-derivation + quarantine (no-throw) semantics.
|
|
4
|
-
* Run: node --test lib/org/verify.test.mjs
|
|
5
|
-
*/
|
|
6
|
-
"use strict";
|
|
7
|
-
|
|
8
|
-
import { test } from "node:test";
|
|
9
|
-
import assert from "node:assert/strict";
|
|
10
|
-
|
|
11
|
-
import { createPublicKey } from "node:crypto";
|
|
12
|
-
|
|
13
|
-
import { verifySnapshot, verifyEvents, quarantine, sshEd25519ToSpkiPem } from "./verify.mjs";
|
|
14
|
-
import { canonicalize, computeRowHash } from "./protocol.mjs";
|
|
15
|
-
import { generateDeviceKey, signWith, verifySignature } from "./keys.mjs";
|
|
16
|
-
|
|
17
|
-
/** Encode an Ed25519 SPKI PEM into the OpenSSH `ssh-ed25519 <base64>` blob. */
|
|
18
|
-
function spkiToSshEd25519(spkiPem) {
|
|
19
|
-
const der = createPublicKey(spkiPem).export({ type: "spki", format: "der" });
|
|
20
|
-
const raw = der.subarray(12); // 12-byte Ed25519 SPKI prefix, then 32 raw bytes
|
|
21
|
-
const sshStr = (buf) => {
|
|
22
|
-
const len = Buffer.alloc(4); len.writeUInt32BE(buf.length);
|
|
23
|
-
return Buffer.concat([len, buf]);
|
|
24
|
-
};
|
|
25
|
-
const blob = Buffer.concat([sshStr(Buffer.from("ssh-ed25519")), sshStr(raw)]);
|
|
26
|
-
return blob.toString("base64");
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
// --- helpers ---------------------------------------------------------------
|
|
30
|
-
|
|
31
|
-
/** Sign a snapshot body the SAME way verify expects (canonical body w/o sig). */
|
|
32
|
-
function signSnapshot(privateKeyPem, body) {
|
|
33
|
-
const material = Buffer.from(canonicalize(body));
|
|
34
|
-
return { ...body, sig: signWith(privateKeyPem, material) };
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/** Build a valid chain of N events from genesis. */
|
|
38
|
-
function buildChain(n) {
|
|
39
|
-
const events = [];
|
|
40
|
-
let prev = "";
|
|
41
|
-
for (let i = 0; i < n; i++) {
|
|
42
|
-
const row = {
|
|
43
|
-
seq: i + 1,
|
|
44
|
-
family: "board",
|
|
45
|
-
kind: "create",
|
|
46
|
-
entity_id: `item-${i}`,
|
|
47
|
-
actor: "agent-a",
|
|
48
|
-
at: `2026-06-10T00:00:0${i}.000Z`,
|
|
49
|
-
payload: { i },
|
|
50
|
-
};
|
|
51
|
-
const row_hash = computeRowHash(prev, row);
|
|
52
|
-
events.push({ ...row, prev_hash: prev, row_hash });
|
|
53
|
-
prev = row_hash;
|
|
54
|
-
}
|
|
55
|
-
return events;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
// --- verifySnapshot --------------------------------------------------------
|
|
59
|
-
|
|
60
|
-
test("verifySnapshot: accepts a snapshot signed by a pinned signer", () => {
|
|
61
|
-
const server = generateDeviceKey();
|
|
62
|
-
const snap = signSnapshot(server.privateKeyPem, { orgId: "acme", agents: [{ id: "a" }], seq: 7 });
|
|
63
|
-
const v = verifySnapshot(snap, [server.publicKeyPem]);
|
|
64
|
-
assert.equal(v.ok, true);
|
|
65
|
-
assert.equal(v.signer, server.publicKeyPem);
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
test("verifySnapshot: rejects when signer NOT in pinned list (forgery)", () => {
|
|
69
|
-
const evil = generateDeviceKey();
|
|
70
|
-
const pinned = generateDeviceKey();
|
|
71
|
-
const snap = signSnapshot(evil.privateKeyPem, { orgId: "acme", agents: [] });
|
|
72
|
-
const v = verifySnapshot(snap, [pinned.publicKeyPem]);
|
|
73
|
-
assert.equal(v.ok, false);
|
|
74
|
-
assert.match(v.reason, /pinned signer/);
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
test("verifySnapshot: rejects a tampered body (sig no longer matches)", () => {
|
|
78
|
-
const server = generateDeviceKey();
|
|
79
|
-
const snap = signSnapshot(server.privateKeyPem, { orgId: "acme", agents: [{ id: "a" }] });
|
|
80
|
-
snap.agents.push({ id: "injected" }); // mutate after signing
|
|
81
|
-
const v = verifySnapshot(snap, [server.publicKeyPem]);
|
|
82
|
-
assert.equal(v.ok, false);
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
test("verifySnapshot: default-deny when no pinned signers", () => {
|
|
86
|
-
const server = generateDeviceKey();
|
|
87
|
-
const snap = signSnapshot(server.privateKeyPem, { orgId: "acme" });
|
|
88
|
-
assert.equal(verifySnapshot(snap, []).ok, false);
|
|
89
|
-
assert.equal(verifySnapshot(snap, null).ok, false);
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
test("verifySnapshot: rejects unsigned / malformed snapshot", () => {
|
|
93
|
-
const server = generateDeviceKey();
|
|
94
|
-
assert.equal(verifySnapshot({ orgId: "x" }, [server.publicKeyPem]).ok, false);
|
|
95
|
-
assert.equal(verifySnapshot(null, [server.publicKeyPem]).ok, false);
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
test("verifySnapshot: tries multiple pinned signers (key rotation window)", () => {
|
|
99
|
-
const oldKey = generateDeviceKey();
|
|
100
|
-
const newKey = generateDeviceKey();
|
|
101
|
-
const snap = signSnapshot(newKey.privateKeyPem, { orgId: "acme" });
|
|
102
|
-
const v = verifySnapshot(snap, [oldKey.publicKeyPem, newKey.publicKeyPem]);
|
|
103
|
-
assert.equal(v.ok, true);
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
// --- verifyEvents ----------------------------------------------------------
|
|
107
|
-
|
|
108
|
-
test("verifyEvents: accepts a valid chain from genesis", () => {
|
|
109
|
-
const events = buildChain(5);
|
|
110
|
-
assert.deepEqual(verifyEvents(events, ""), { ok: true });
|
|
111
|
-
});
|
|
112
|
-
|
|
113
|
-
test("verifyEvents: catches a rewritten payload at the right index", () => {
|
|
114
|
-
const events = buildChain(4);
|
|
115
|
-
events[2].payload = { i: 999 }; // rewrite without re-hashing
|
|
116
|
-
const v = verifyEvents(events, "");
|
|
117
|
-
assert.equal(v.ok, false);
|
|
118
|
-
assert.equal(v.index, 2);
|
|
119
|
-
assert.match(v.reason, /row_hash/);
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
test("verifyEvents: catches a reordered/broken prev link", () => {
|
|
123
|
-
const events = buildChain(3);
|
|
124
|
-
events[1].prev_hash = "deadbeef";
|
|
125
|
-
const v = verifyEvents(events, "");
|
|
126
|
-
assert.equal(v.ok, false);
|
|
127
|
-
assert.equal(v.index, 1);
|
|
128
|
-
assert.match(v.reason, /prev_hash/);
|
|
129
|
-
});
|
|
130
|
-
|
|
131
|
-
test("verifyEvents: honours a non-genesis startPrev", () => {
|
|
132
|
-
const all = buildChain(5);
|
|
133
|
-
const slice = all.slice(2); // events 3..5
|
|
134
|
-
const startPrev = all[1].row_hash;
|
|
135
|
-
assert.deepEqual(verifyEvents(slice, startPrev), { ok: true });
|
|
136
|
-
// wrong startPrev → first link breaks
|
|
137
|
-
assert.equal(verifyEvents(slice, "wrong").ok, false);
|
|
138
|
-
});
|
|
139
|
-
|
|
140
|
-
test("verifyEvents: non-array input → quarantine verdict, no throw", () => {
|
|
141
|
-
const v = verifyEvents(null, "");
|
|
142
|
-
assert.equal(v.ok, false);
|
|
143
|
-
assert.match(v.reason, /array/);
|
|
144
|
-
});
|
|
145
|
-
|
|
146
|
-
// --- quarantine ------------------------------------------------------------
|
|
147
|
-
|
|
148
|
-
test("quarantine: passes through a verdict", () => {
|
|
149
|
-
assert.deepEqual(quarantine(() => ({ ok: true })), { ok: true });
|
|
150
|
-
assert.deepEqual(quarantine(() => ({ ok: false, reason: "x" })), { ok: false, reason: "x" });
|
|
151
|
-
});
|
|
152
|
-
|
|
153
|
-
test("quarantine: converts a throw into {ok:false}", () => {
|
|
154
|
-
const v = quarantine(() => { throw new Error("hostile payload"); });
|
|
155
|
-
assert.equal(v.ok, false);
|
|
156
|
-
assert.match(v.reason, /hostile payload/);
|
|
157
|
-
});
|
|
158
|
-
|
|
159
|
-
test("quarantine: rejects a non-verdict return", () => {
|
|
160
|
-
assert.equal(quarantine(() => 42).ok, false);
|
|
161
|
-
});
|
|
162
|
-
|
|
163
|
-
// --- sshEd25519ToSpkiPem (the SPEC-format trust-root loader) ----------------
|
|
164
|
-
|
|
165
|
-
test("sshEd25519ToSpkiPem: round-trips an SSH one-liner back to the exact SPKI PEM", () => {
|
|
166
|
-
const key = generateDeviceKey();
|
|
167
|
-
const sshBlob = spkiToSshEd25519(key.publicKeyPem);
|
|
168
|
-
// bare base64 blob
|
|
169
|
-
const fromBlob = sshEd25519ToSpkiPem(sshBlob);
|
|
170
|
-
assert.equal(createPublicKey(fromBlob).export({ type: "spki", format: "pem" }).toString(),
|
|
171
|
-
createPublicKey(key.publicKeyPem).export({ type: "spki", format: "pem" }).toString(),
|
|
172
|
-
"the reconstructed SPKI PEM matches the original key");
|
|
173
|
-
// a full allowed-signers / authorized line
|
|
174
|
-
const fromLine = sshEd25519ToSpkiPem(`alice@example.com namespaces="org" ssh-ed25519 ${sshBlob} comment`);
|
|
175
|
-
assert.ok(fromLine, "parses a full allowed-signers line");
|
|
176
|
-
// a signature made with the private key verifies against the reconstructed PEM.
|
|
177
|
-
const msg = Buffer.from("policy-bundle-bytes");
|
|
178
|
-
const sigB64 = signWith(key.privateKeyPem, msg);
|
|
179
|
-
assert.equal(verifySignature(fromBlob, msg, sigB64), true, "signature verifies against the converted PEM");
|
|
180
|
-
assert.equal(verifySignature(fromLine, msg, sigB64), true);
|
|
181
|
-
});
|
|
182
|
-
|
|
183
|
-
test("sshEd25519ToSpkiPem: returns null on malformed / non-ed25519 input", () => {
|
|
184
|
-
assert.equal(sshEd25519ToSpkiPem(""), null);
|
|
185
|
-
assert.equal(sshEd25519ToSpkiPem(null), null);
|
|
186
|
-
assert.equal(sshEd25519ToSpkiPem("not-base64-!!!"), null);
|
|
187
|
-
// a valid base64 but wrong key type (ssh-rsa) blob → null
|
|
188
|
-
const sshStr = (buf) => { const l = Buffer.alloc(4); l.writeUInt32BE(buf.length); return Buffer.concat([l, buf]); };
|
|
189
|
-
const rsaBlob = Buffer.concat([sshStr(Buffer.from("ssh-rsa")), sshStr(Buffer.alloc(32))]).toString("base64");
|
|
190
|
-
assert.equal(sshEd25519ToSpkiPem(rsaBlob), null);
|
|
191
|
-
// an ed25519 tag with the wrong raw-key length → null
|
|
192
|
-
const shortBlob = Buffer.concat([sshStr(Buffer.from("ssh-ed25519")), sshStr(Buffer.alloc(16))]).toString("base64");
|
|
193
|
-
assert.equal(sshEd25519ToSpkiPem(shortBlob), null);
|
|
194
|
-
});
|
|
@@ -1,273 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* lib/org/work-ledger.test.mjs — the daemon's half of the work ledger.
|
|
3
|
-
*
|
|
4
|
-
* What is worth testing here is deliberately NARROW, because the policy is not
|
|
5
|
-
* here: the threshold, the idempotency and the tagging all live server-side in
|
|
6
|
-
* hq's `server/work/ledger.ts`, so re-testing them in this repo would be
|
|
7
|
-
* testing a second implementation that must not exist. What IS this module's
|
|
8
|
-
* job — and what these tests pin — is the mapping: does a real flattened inbox
|
|
9
|
-
* item become the right wire call, does a re-delivery of the SAME item produce
|
|
10
|
-
* the SAME identity, and does every failure path come back as a benign,
|
|
11
|
-
* REASONED refusal rather than an exception on the dispatch path.
|
|
12
|
-
*
|
|
13
|
-
* Run: node --test lib/org/work-ledger.test.mjs
|
|
14
|
-
*/
|
|
15
|
-
"use strict";
|
|
16
|
-
|
|
17
|
-
import { test } from "node:test";
|
|
18
|
-
import assert from "node:assert/strict";
|
|
19
|
-
|
|
20
|
-
import {
|
|
21
|
-
WORK_STAGES,
|
|
22
|
-
cohortChannelId,
|
|
23
|
-
cohortMessageId,
|
|
24
|
-
requesterMemberId,
|
|
25
|
-
recordWorkStep,
|
|
26
|
-
} from "./work-ledger.mjs";
|
|
27
|
-
|
|
28
|
-
const CFG = {
|
|
29
|
-
org: { cohort: { enabled: true, base: "https://os.example.test", token: "tok_test" } },
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
/** A flattened Cohort inbox item, in the shape lib/channels/inbox-item.mjs writes. */
|
|
33
|
-
function inboxItem(over = {}) {
|
|
34
|
-
return {
|
|
35
|
-
id: "cohort-msg_abc123",
|
|
36
|
-
service: "cohort",
|
|
37
|
-
channel: "Owner ↔ Isla",
|
|
38
|
-
channel_id: "ch_dm_1",
|
|
39
|
-
sender: "The Owner",
|
|
40
|
-
sender_privilege: "ceo",
|
|
41
|
-
timestamp: "2026-08-13T05:47:51.000Z",
|
|
42
|
-
subject: "Direct message",
|
|
43
|
-
content: "fix these issues you just noted please and push their fixes to git",
|
|
44
|
-
thread_id: "",
|
|
45
|
-
is_reply: false,
|
|
46
|
-
priority_signals: {
|
|
47
|
-
from_ceo: true,
|
|
48
|
-
tagged_urgent: false,
|
|
49
|
-
contains_deadline: false,
|
|
50
|
-
mentions_agent: true,
|
|
51
|
-
},
|
|
52
|
-
raw_ref: "cohort:message:msg_abc123:4821",
|
|
53
|
-
source: "cohort",
|
|
54
|
-
...over,
|
|
55
|
-
};
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
/** Capture the params a step would send, without a server. */
|
|
59
|
-
function capture(result = { result: { tracked: true, taskId: "t_1", col: "triage", created: true } }) {
|
|
60
|
-
const calls = [];
|
|
61
|
-
const impl = async (params, opts) => {
|
|
62
|
-
calls.push({ params, opts });
|
|
63
|
-
return result;
|
|
64
|
-
};
|
|
65
|
-
return { calls, impl };
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
// ---------------------------------------------------------------------------
|
|
69
|
-
// Identity — the thing that makes a re-delivery find the same row
|
|
70
|
-
// ---------------------------------------------------------------------------
|
|
71
|
-
|
|
72
|
-
test("cohortMessageId reads the entity id off raw_ref", () => {
|
|
73
|
-
assert.equal(cohortMessageId(inboxItem()), "msg_abc123");
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
test("cohortMessageId falls back to the inbox id when raw_ref is absent", () => {
|
|
77
|
-
assert.equal(cohortMessageId(inboxItem({ raw_ref: "" })), "msg_abc123");
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
test("a re-delivered item yields the SAME identity", () => {
|
|
81
|
-
// The dead-session case: the item was never marked processed, so the poller
|
|
82
|
-
// hands back a byte-identical file. Same channel + same message = same key
|
|
83
|
-
// server-side = the same board row, not a second one.
|
|
84
|
-
const first = inboxItem();
|
|
85
|
-
const again = inboxItem();
|
|
86
|
-
assert.equal(cohortChannelId(first), cohortChannelId(again));
|
|
87
|
-
assert.equal(cohortMessageId(first), cohortMessageId(again));
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
test("requesterMemberId is null for a normal item — the server resolves it", () => {
|
|
91
|
-
// The flat item carries the sender's NAME, not their id. Guessing a member id
|
|
92
|
-
// from a display name is how you tag the wrong person.
|
|
93
|
-
assert.equal(requesterMemberId(inboxItem()), null);
|
|
94
|
-
assert.equal(requesterMemberId({ sender_member_id: "m_owner" }), "m_owner");
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
// ---------------------------------------------------------------------------
|
|
98
|
-
// The mapping
|
|
99
|
-
// ---------------------------------------------------------------------------
|
|
100
|
-
|
|
101
|
-
test("an accepted step sends the ask's identity and the deferred fact", async () => {
|
|
102
|
-
const { calls, impl } = capture();
|
|
103
|
-
const res = await recordWorkStep({
|
|
104
|
-
item: inboxItem(),
|
|
105
|
-
stage: "accepted",
|
|
106
|
-
deferred: true,
|
|
107
|
-
note: "Picked this up.",
|
|
108
|
-
cfg: CFG,
|
|
109
|
-
trackImpl: impl,
|
|
110
|
-
source: { service: "cohort", trace_id: "tr_9" },
|
|
111
|
-
});
|
|
112
|
-
|
|
113
|
-
assert.equal(calls.length, 1);
|
|
114
|
-
const { params, opts } = calls[0];
|
|
115
|
-
assert.equal(params.service, "cohort");
|
|
116
|
-
assert.equal(params.channelId, "ch_dm_1");
|
|
117
|
-
assert.equal(params.messageId, "msg_abc123");
|
|
118
|
-
assert.equal(params.stage, "accepted");
|
|
119
|
-
assert.equal(params.deferred, true);
|
|
120
|
-
assert.equal(params.directed, true);
|
|
121
|
-
assert.equal(params.authorKind, "HUMAN");
|
|
122
|
-
assert.equal(
|
|
123
|
-
params.body,
|
|
124
|
-
"fix these issues you just noted please and push their fixes to git",
|
|
125
|
-
);
|
|
126
|
-
assert.equal(params.note, "Picked this up.");
|
|
127
|
-
assert.deepEqual(params.source, { service: "cohort", trace_id: "tr_9" });
|
|
128
|
-
// Same ask + same stage = the same call, so a retry after a timeout cannot
|
|
129
|
-
// double-post the tag.
|
|
130
|
-
assert.equal(opts.idempotencyKey, "board.track:ch_dm_1:msg_abc123:accepted");
|
|
131
|
-
assert.equal(opts.base, CFG.org.cohort.base);
|
|
132
|
-
assert.equal(res.tracked, true);
|
|
133
|
-
assert.equal(res.taskId, "t_1");
|
|
134
|
-
});
|
|
135
|
-
|
|
136
|
-
test("a `working` step is what puts the row where the live-work surface looks", async () => {
|
|
137
|
-
// `accepted` lands the row in `triage`, and NO live-work surface reads
|
|
138
|
-
// `triage` — hq's "On now" banner reads `running` and nothing else. Sending
|
|
139
|
-
// only the first step is why every daemon row existed and was still
|
|
140
|
-
// invisible, and why a second, ungated board writer came to exist beside it.
|
|
141
|
-
const { calls, impl } = capture();
|
|
142
|
-
await recordWorkStep({
|
|
143
|
-
item: inboxItem(),
|
|
144
|
-
stage: "working",
|
|
145
|
-
deferred: true,
|
|
146
|
-
cfg: CFG,
|
|
147
|
-
trackImpl: impl,
|
|
148
|
-
});
|
|
149
|
-
assert.equal(calls[0].params.stage, "working");
|
|
150
|
-
assert.equal(calls[0].opts.idempotencyKey, "board.track:ch_dm_1:msg_abc123:working");
|
|
151
|
-
});
|
|
152
|
-
|
|
153
|
-
test("the classifier's urgency rides along, already on hq's P-scale", async () => {
|
|
154
|
-
// hq validates `priority` against z.enum(["P0".."P4"]) and 400s the whole
|
|
155
|
-
// call on anything else, so the mapping happens before the wire
|
|
156
|
-
// (scripts/daemon/board-mirror.mjs#boardPriority) and this only forwards it.
|
|
157
|
-
const { calls, impl } = capture();
|
|
158
|
-
await recordWorkStep({
|
|
159
|
-
item: inboxItem(),
|
|
160
|
-
stage: "accepted",
|
|
161
|
-
priority: "P0",
|
|
162
|
-
cfg: CFG,
|
|
163
|
-
trackImpl: impl,
|
|
164
|
-
});
|
|
165
|
-
assert.equal(calls[0].params.priority, "P0");
|
|
166
|
-
|
|
167
|
-
const plain = capture();
|
|
168
|
-
await recordWorkStep({ item: inboxItem(), stage: "accepted", cfg: CFG, trackImpl: plain.impl });
|
|
169
|
-
assert.ok(!("priority" in plain.calls[0].params), "no priority ⇒ hq's own default answers");
|
|
170
|
-
});
|
|
171
|
-
|
|
172
|
-
test("directedness comes from the ingest layer's real verdict, never a guess", async () => {
|
|
173
|
-
const { calls, impl } = capture();
|
|
174
|
-
await recordWorkStep({
|
|
175
|
-
item: inboxItem({
|
|
176
|
-
priority_signals: { mentions_agent: false, from_ceo: false, tagged_urgent: false, contains_deadline: false },
|
|
177
|
-
}),
|
|
178
|
-
stage: "accepted",
|
|
179
|
-
cfg: CFG,
|
|
180
|
-
trackImpl: impl,
|
|
181
|
-
});
|
|
182
|
-
// An overheard channel message is not assigned work — and the SERVER is what
|
|
183
|
-
// acts on that, so the client's job is only to report it honestly.
|
|
184
|
-
assert.equal(calls[0].params.directed, false);
|
|
185
|
-
});
|
|
186
|
-
|
|
187
|
-
test("a session death is reported as `failed`, not swallowed", async () => {
|
|
188
|
-
const { calls, impl } = capture({ result: { tracked: true, taskId: "t_1", col: "blocked", moved: true } });
|
|
189
|
-
const res = await recordWorkStep({
|
|
190
|
-
item: inboxItem(),
|
|
191
|
-
stage: "failed",
|
|
192
|
-
deferred: true,
|
|
193
|
-
note: "The session handling this ended without completing (exit 143).",
|
|
194
|
-
cfg: CFG,
|
|
195
|
-
trackImpl: impl,
|
|
196
|
-
});
|
|
197
|
-
assert.equal(calls[0].params.stage, "failed");
|
|
198
|
-
assert.equal(res.col, "blocked");
|
|
199
|
-
});
|
|
200
|
-
|
|
201
|
-
test("attachments and extra tags are forwarded, and bounded", async () => {
|
|
202
|
-
const { calls, impl } = capture();
|
|
203
|
-
await recordWorkStep({
|
|
204
|
-
item: inboxItem(),
|
|
205
|
-
stage: "review",
|
|
206
|
-
cfg: CFG,
|
|
207
|
-
trackImpl: impl,
|
|
208
|
-
notify: Array.from({ length: 20 }, (_, i) => `m_${i}`),
|
|
209
|
-
attachments: Array.from({ length: 20 }, (_, i) => ({ name: `f${i}.txt`, dataUrl: "data:text/plain;base64,eA==" })),
|
|
210
|
-
});
|
|
211
|
-
assert.equal(calls[0].params.notify.length, 8);
|
|
212
|
-
assert.equal(calls[0].params.attachments.length, 6);
|
|
213
|
-
});
|
|
214
|
-
|
|
215
|
-
// ---------------------------------------------------------------------------
|
|
216
|
-
// Fail-open, never silent
|
|
217
|
-
// ---------------------------------------------------------------------------
|
|
218
|
-
|
|
219
|
-
test("a non-Cohort item is refused with a reason, not an exception", async () => {
|
|
220
|
-
const { calls, impl } = capture();
|
|
221
|
-
const res = await recordWorkStep({ item: inboxItem({ service: "slack" }), stage: "accepted", cfg: CFG, trackImpl: impl });
|
|
222
|
-
assert.equal(res.tracked, false);
|
|
223
|
-
assert.equal(res.reason, "not-a-cohort-channel");
|
|
224
|
-
assert.equal(calls.length, 0, "no wire call for a board we cannot write to");
|
|
225
|
-
});
|
|
226
|
-
|
|
227
|
-
test("an item with no resolvable channel is refused", async () => {
|
|
228
|
-
const res = await recordWorkStep({ item: inboxItem({ channel_id: "" }), stage: "accepted", cfg: CFG, trackImpl: async () => ({}) });
|
|
229
|
-
assert.equal(res.tracked, false);
|
|
230
|
-
assert.equal(res.reason, "not-a-cohort-channel");
|
|
231
|
-
});
|
|
232
|
-
|
|
233
|
-
test("a bad stage is refused rather than sent", async () => {
|
|
234
|
-
const { calls, impl } = capture();
|
|
235
|
-
const res = await recordWorkStep({ item: inboxItem(), stage: "finished", cfg: CFG, trackImpl: impl });
|
|
236
|
-
assert.equal(res.reason, "bad-stage");
|
|
237
|
-
assert.equal(calls.length, 0);
|
|
238
|
-
});
|
|
239
|
-
|
|
240
|
-
test("org integration off ⇒ a clean skip", async () => {
|
|
241
|
-
const res = await recordWorkStep({ item: inboxItem(), stage: "accepted", cfg: {}, trackImpl: async () => ({}) });
|
|
242
|
-
assert.equal(res.tracked, false);
|
|
243
|
-
assert.equal(res.reason, "org-disabled");
|
|
244
|
-
});
|
|
245
|
-
|
|
246
|
-
test("a server error frame comes back as a REASON, never a throw", async () => {
|
|
247
|
-
const res = await recordWorkStep({
|
|
248
|
-
item: inboxItem(),
|
|
249
|
-
stage: "accepted",
|
|
250
|
-
cfg: CFG,
|
|
251
|
-
trackImpl: async () => ({ error: { code: "FORBIDDEN_SCOPE", message: "board.write not granted" } }),
|
|
252
|
-
});
|
|
253
|
-
assert.equal(res.tracked, false);
|
|
254
|
-
assert.match(res.reason, /FORBIDDEN_SCOPE/);
|
|
255
|
-
assert.match(res.reason, /board\.write not granted/);
|
|
256
|
-
});
|
|
257
|
-
|
|
258
|
-
test("a thrown transport error is contained — the dispatch path never sees it", async () => {
|
|
259
|
-
const res = await recordWorkStep({
|
|
260
|
-
item: inboxItem(),
|
|
261
|
-
stage: "accepted",
|
|
262
|
-
cfg: CFG,
|
|
263
|
-
trackImpl: async () => {
|
|
264
|
-
throw new Error("ECONNREFUSED");
|
|
265
|
-
},
|
|
266
|
-
});
|
|
267
|
-
assert.equal(res.tracked, false);
|
|
268
|
-
assert.match(res.reason, /ECONNREFUSED/);
|
|
269
|
-
});
|
|
270
|
-
|
|
271
|
-
test("WORK_STAGES matches the server's ladder", () => {
|
|
272
|
-
assert.deepEqual(WORK_STAGES, ["accepted", "working", "blocked", "review", "done", "failed"]);
|
|
273
|
-
});
|