@cohortapp/agent-sdk 2.17.0 → 2.18.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/settings.json +18 -0
- package/.env.example +18 -5
- package/README.md +1 -0
- package/bin/maestro.mjs +62 -0
- package/docs/guides/billing-console-keys.md +60 -0
- package/docs/guides/front-door-session.md +54 -9
- package/docs/guides/mac-mini.md +20 -25
- package/docs/guides/setup-wizard.md +1 -1
- package/docs/runbooks/fleet-rollout.md +156 -0
- package/docs/runbooks/mac-mini-bootstrap.md +12 -14
- package/lib/action-executor.js +19 -3
- package/lib/budget-guard.mjs +279 -3
- package/lib/channels/base-adapter.mjs +3 -1
- package/lib/channels/contract.mjs +2 -1
- package/lib/channels/inbox-item.mjs +8 -0
- package/lib/claude-bin.mjs +5 -6
- package/lib/cli/doctor-checks.mjs +141 -10
- package/lib/cli/global-setup-extras.mjs +5 -1
- package/lib/cli/inbox.mjs +100 -15
- package/lib/cli/seat-auth.mjs +463 -0
- package/lib/cli/session.mjs +80 -12
- package/lib/collective/capture.mjs +8 -6
- package/lib/collective/global-config.mjs +63 -1
- package/lib/collective/presence.mjs +142 -5
- package/lib/comms/send-gate.mjs +559 -1
- package/lib/diagnostics/alerts.mjs +49 -0
- package/lib/diagnostics/cadence-output-freshness.mjs +288 -0
- package/lib/engine/agents/definitions.mjs +343 -0
- package/lib/engine/agents/persist.mjs +275 -0
- package/lib/engine/agents/runtime.mjs +748 -0
- package/lib/engine/agents/usage.mjs +95 -0
- package/lib/engine/auth-status.mjs +139 -0
- package/lib/engine/budget.mjs +194 -0
- package/lib/engine/cli.mjs +1204 -0
- package/lib/engine/commands/index.mjs +269 -0
- package/lib/engine/context/budget.mjs +219 -0
- package/lib/engine/context/cache.mjs +125 -0
- package/lib/engine/context/child-env.mjs +215 -0
- package/lib/engine/context/compaction.mjs +342 -0
- package/lib/engine/context/images.mjs +90 -0
- package/lib/engine/context/instructions.mjs +327 -0
- package/lib/engine/context/lazy-instructions.mjs +169 -0
- package/lib/engine/context/manager.mjs +182 -0
- package/lib/engine/context/real-path.mjs +91 -0
- package/lib/engine/context/secret-values.mjs +163 -0
- package/lib/engine/context/settings.mjs +274 -0
- package/lib/engine/context/stream-input.mjs +159 -0
- package/lib/engine/guard.mjs +152 -0
- package/lib/engine/hooks.mjs +713 -0
- package/lib/engine/loop.mjs +560 -0
- package/lib/engine/mcp/client.mjs +254 -0
- package/lib/engine/mcp/config.mjs +301 -0
- package/lib/engine/mcp/http.mjs +201 -0
- package/lib/engine/mcp/index.mjs +146 -0
- package/lib/engine/mcp/jsonrpc.mjs +147 -0
- package/lib/engine/mcp/naming.mjs +66 -0
- package/lib/engine/mcp/resources.mjs +89 -0
- package/lib/engine/mcp/results.mjs +133 -0
- package/lib/engine/mcp/stdio.mjs +137 -0
- package/lib/engine/mcp/supervisor.mjs +116 -0
- package/lib/engine/messages.mjs +104 -0
- package/lib/engine/output/json.mjs +164 -0
- package/lib/engine/output/stream-json.mjs +266 -0
- package/lib/engine/permissions.mjs +845 -0
- package/lib/engine/process-identity.mjs +164 -0
- package/lib/engine/process-tree.mjs +551 -0
- package/lib/engine/prompt.mjs +60 -0
- package/lib/engine/session/store.mjs +299 -0
- package/lib/engine/session-runtime/args.mjs +97 -0
- package/lib/engine/session-runtime/host.mjs +143 -0
- package/lib/engine/session-runtime/inbox.mjs +122 -0
- package/lib/engine/session-runtime/notifications.mjs +129 -0
- package/lib/engine/session-runtime/registry.mjs +328 -0
- package/lib/engine/session-runtime/runner.mjs +344 -0
- package/lib/engine/session-runtime/socket.mjs +212 -0
- package/lib/engine/session-runtime/wakeup.mjs +115 -0
- package/lib/engine/skills/index.mjs +321 -0
- package/lib/engine/tools/bash-background.mjs +533 -0
- package/lib/engine/tools/bash.mjs +216 -0
- package/lib/engine/tools/edit.mjs +97 -0
- package/lib/engine/tools/glob.mjs +81 -0
- package/lib/engine/tools/grep.mjs +224 -0
- package/lib/engine/tools/index.mjs +84 -0
- package/lib/engine/tools/list-agents.mjs +32 -0
- package/lib/engine/tools/ls.mjs +127 -0
- package/lib/engine/tools/monitor.mjs +82 -0
- package/lib/engine/tools/notebook-edit.mjs +218 -0
- package/lib/engine/tools/read.mjs +103 -0
- package/lib/engine/tools/schedule-wakeup.mjs +45 -0
- package/lib/engine/tools/schema.mjs +144 -0
- package/lib/engine/tools/send-message.mjs +77 -0
- package/lib/engine/tools/session.mjs +70 -0
- package/lib/engine/tools/todo.mjs +144 -0
- package/lib/engine/tools/toolsearch.mjs +217 -0
- package/lib/engine/tools/walk.mjs +193 -0
- package/lib/engine/tools/web-switch.mjs +31 -0
- package/lib/engine/tools/webfetch-html.mjs +387 -0
- package/lib/engine/tools/webfetch-net.mjs +340 -0
- package/lib/engine/tools/webfetch.mjs +198 -0
- package/lib/engine/tools/websearch.mjs +91 -0
- package/lib/engine/tools/workflow.mjs +95 -0
- package/lib/engine/tools/write.mjs +76 -0
- package/lib/engine/tui/line-editor.mjs +137 -0
- package/lib/engine/tui/render.mjs +86 -0
- package/lib/engine/tui/tui.mjs +274 -0
- package/lib/engine/wire/anthropic-messages.mjs +263 -0
- package/lib/engine/wire/effort.mjs +36 -0
- package/lib/engine/wire/errors.mjs +496 -0
- package/lib/engine/wire/http.mjs +441 -0
- package/lib/engine/wire/index.mjs +76 -0
- package/lib/engine/wire/openai-chat.mjs +332 -0
- package/lib/engine/wire/prompt-cache.mjs +79 -0
- package/lib/engine/wire/search.mjs +140 -0
- package/lib/engine/wire/sse.mjs +114 -0
- package/lib/engine/wire/stall.mjs +349 -0
- package/lib/engine/wire/token-provider.mjs +175 -0
- package/lib/engine/wire/usage.mjs +192 -0
- package/lib/engine/workflow/host.mjs +524 -0
- package/lib/engine/workflow/journal.mjs +188 -0
- package/lib/engine/workflow/json-schema.mjs +171 -0
- package/lib/engine/workflow/meta.mjs +329 -0
- package/lib/engine/workflow/notifications.mjs +52 -0
- package/lib/engine/workflow/runtime.mjs +447 -0
- package/lib/engine/workflow/sandbox.mjs +534 -0
- package/lib/engine/workflow/worker.mjs +141 -0
- package/lib/engine/workflow/worktree.mjs +74 -0
- package/lib/execution/disposition.mjs +1 -1
- package/lib/execution/intake.mjs +10 -0
- package/lib/execution/surface-policy.mjs +15 -0
- package/lib/learning/curator.mjs +8 -6
- package/lib/learning/reflect.mjs +8 -6
- package/lib/model-router/catalog/cohort.yaml +137 -0
- package/lib/model-router/catalog.mjs +118 -1
- package/lib/model-router/failover.mjs +67 -16
- package/lib/model-router/llm-task.mjs +39 -3
- package/lib/model-router/resolve.mjs +89 -3
- package/lib/model-router/spawn.mjs +46 -47
- package/lib/model-router/taxonomy.mjs +126 -4
- package/lib/org/cost-sync.mjs +141 -11
- package/lib/org/inbound/broadcast.mjs +289 -0
- package/lib/org/inbound/collective.mjs +375 -0
- package/lib/org/inbound/directedness.mjs +96 -8
- package/lib/org/inbound/facts.mjs +78 -2
- package/lib/org/inbound/project.mjs +22 -0
- package/lib/org/inbound/surfaces.mjs +14 -0
- package/lib/org/llm-token.mjs +879 -0
- package/lib/org/mesh.mjs +61 -0
- package/lib/org/messaging.mjs +3 -1
- package/lib/org/protocol.checksum +1 -1
- package/lib/org/protocol.mjs +15 -0
- package/lib/org/quota.mjs +520 -0
- package/lib/org/tool-surface.mjs +104 -16
- package/lib/org/ui-parity.mjs +16 -1
- package/lib/org/work-ledger.mjs +37 -6
- package/lib/rate-guard.mjs +114 -1
- package/lib/resource-governor.mjs +41 -6
- package/lib/runtime/adapter.mjs +823 -0
- package/lib/runtime/child-env.mjs +191 -0
- package/lib/runtime/legacy-shell-guard.mjs +97 -0
- package/lib/runtime/seat-engine.mjs +162 -0
- package/lib/session/ask-ledger.mjs +271 -0
- package/lib/session/current-work.mjs +676 -0
- package/lib/session/feed-core.mjs +40 -3
- package/lib/session/launch-args.mjs +56 -4
- package/lib/session/status-summary.mjs +26 -9
- package/lib/session/upgrade-notice.mjs +42 -0
- package/lib/setup/claude-probe.mjs +117 -13
- package/lib/setup/enrich.mjs +13 -10
- package/lib/setup/sections/model.mjs +39 -13
- package/lib/telemetry/collect.mjs +208 -9
- package/lib/upgrade/ignored-drift.mjs +105 -0
- package/lib/voice/post-call-brief.mjs +30 -17
- package/package.json +13 -3
- package/plugins/maestro-skills/skills/board-work.md +5 -0
- package/plugins/maestro-skills/skills/inbound-triage.md +56 -15
- package/plugins/maestro-skills/skills/main-session.md +18 -7
- package/scripts/ci/check-tarball-fidelity.mjs +126 -2
- package/scripts/ci/run-tests.mjs +47 -19
- package/scripts/cohort-llm/api-key-helper.mjs +92 -0
- package/scripts/collective/hook-runner.mjs +29 -2
- package/scripts/continuous-monitor.sh +13 -0
- package/scripts/cost/track-claude-usage.mjs +15 -0
- package/scripts/daemon/agent-daemon.mjs +408 -20
- package/scripts/daemon/assurance.mjs +48 -12
- package/scripts/daemon/cadence-consumer.mjs +218 -68
- package/scripts/daemon/cadence-handlers.mjs +73 -4
- package/scripts/daemon/classifier.mjs +75 -26
- package/scripts/daemon/context-compiler.mjs +51 -37
- package/scripts/daemon/deliver.mjs +30 -1
- package/scripts/daemon/dispatcher.mjs +595 -149
- package/scripts/daemon/health.mjs +14 -1
- package/scripts/daemon/maestro-daemon.mjs +11 -0
- package/scripts/daemon/prompt-builder.mjs +24 -0
- package/scripts/daemon/responder.mjs +246 -79
- package/scripts/daemon/sdk-version.mjs +98 -16
- package/scripts/eval/probe-gateway.mjs +635 -0
- package/scripts/eval/replay/extract.mjs +270 -0
- package/scripts/eval/replay/grade.mjs +260 -0
- package/scripts/eval/replay/lib/config.mjs +50 -0
- package/scripts/eval/replay/lib/effects.mjs +65 -0
- package/scripts/eval/replay/lib/fixture.mjs +188 -0
- package/scripts/eval/replay/lib/judge.mjs +72 -0
- package/scripts/eval/replay/lib/redact.mjs +136 -0
- package/scripts/eval/replay/lib/sandbox.mjs +170 -0
- package/scripts/eval/replay/lib/schema-check.mjs +63 -0
- package/scripts/eval/replay/lib/transcript.mjs +76 -0
- package/scripts/eval/replay/mcp-replay-stub.mjs +101 -0
- package/scripts/eval/replay/report.mjs +185 -0
- package/scripts/eval/replay/run.mjs +404 -0
- package/scripts/fleet/rollout.mjs +1094 -0
- package/scripts/hooks/pre-send-audit.sh +36 -245
- package/scripts/hooks/pre-write-yaml-validate.mjs +275 -0
- package/scripts/hooks/validate-state-yaml.sh +190 -0
- package/scripts/huddle/huddle-llm.mjs +361 -0
- package/scripts/huddle/huddle-server.mjs +46 -121
- package/scripts/local-triggers/autoupdate.sh +448 -78
- package/scripts/local-triggers/run-trigger.sh +13 -0
- package/scripts/maintenance/pin-integrity.mjs +364 -0
- package/scripts/poll-slack-events.sh +41 -9
- package/scripts/poller/slack-socket-mode.mjs +28 -3
- package/scripts/session/supervisor.mjs +80 -13
- package/scripts/spawn-session.sh +13 -0
- package/bin/maestro.test.mjs +0 -1574
- package/lib/action-executor.test.mjs +0 -871
- package/lib/archetype.test.mjs +0 -132
- package/lib/assurance/plan-note.test.mjs +0 -234
- package/lib/assurance/room-budget.test.mjs +0 -486
- package/lib/assurance/tier.test.mjs +0 -174
- package/lib/autonomy.test.mjs +0 -66
- package/lib/backlog.test.mjs +0 -302
- package/lib/backup/policy.test.mjs +0 -305
- package/lib/budget-escalate.test.mjs +0 -232
- package/lib/budget-guard.envelope.test.mjs +0 -476
- package/lib/budget-guard.test.mjs +0 -427
- package/lib/cadence-bus-requeue.test.mjs +0 -83
- package/lib/cadence-bus-schedule.test.mjs +0 -194
- package/lib/cadence-bus.test.mjs +0 -720
- package/lib/cadences.test.mjs +0 -230
- package/lib/capability/inventory.test.mjs +0 -232
- package/lib/capability.test.mjs +0 -78
- package/lib/channels/base-adapter.test.mjs +0 -590
- package/lib/channels/channels.test.mjs +0 -371
- package/lib/channels/contract.test.mjs +0 -162
- package/lib/channels/inbox-item.test.mjs +0 -368
- package/lib/channels/orgmail/adapter.test.mjs +0 -448
- package/lib/channels/pairing.test.mjs +0 -270
- package/lib/channels/repeat-suppressor.test.mjs +0 -134
- package/lib/channels/slack-adapter.test.mjs +0 -212
- package/lib/channels/telegram-adapter.test.mjs +0 -306
- package/lib/channels/voice/adapter.test.mjs +0 -278
- package/lib/channels/whatsapp/adapter-baileys.test.mjs +0 -359
- package/lib/channels/whatsapp/baileys-typing.test.mjs +0 -154
- package/lib/charter.test.mjs +0 -89
- package/lib/claude-bin.test.mjs +0 -131
- package/lib/cli/board.test.mjs +0 -227
- package/lib/cli/design.test.mjs +0 -270
- package/lib/cli/doctor-checks.test.mjs +0 -336
- package/lib/cli/global-setup-extras.test.mjs +0 -462
- package/lib/cli/inbox.test.mjs +0 -230
- package/lib/cli/session-ack.test.mjs +0 -63
- package/lib/cli/session.test.mjs +0 -613
- package/lib/collective/capture.test.mjs +0 -121
- package/lib/collective/cards.test.mjs +0 -114
- package/lib/collective/config.test.mjs +0 -123
- package/lib/collective/global-config.test.mjs +0 -220
- package/lib/collective/global-skills.test.mjs +0 -126
- package/lib/collective/presence.test.mjs +0 -95
- package/lib/collective/recall.test.mjs +0 -116
- package/lib/collective/vendor-skills.test.mjs +0 -306
- package/lib/comms/send-gate.test.mjs +0 -770
- package/lib/comms.test.mjs +0 -41
- package/lib/context/budget.test.mjs +0 -252
- package/lib/context/history-scope.test.mjs +0 -79
- package/lib/cost/ledger-row.test.mjs +0 -183
- package/lib/design/design-md.test.mjs +0 -318
- package/lib/design/fixtures/DESIGN.golden.md +0 -238
- package/lib/design/fixtures/PRODUCT.golden.md +0 -67
- package/lib/design/fixtures/foundation.json +0 -133
- package/lib/design/refresh-gate.test.mjs +0 -144
- package/lib/design/write.test.mjs +0 -241
- package/lib/diagnostics/alerts.test.mjs +0 -318
- package/lib/diagnostics/backup-freshness.test.mjs +0 -185
- package/lib/diagnostics/counters.test.mjs +0 -206
- package/lib/diagnostics/events.test.mjs +0 -290
- package/lib/diagnostics/otel.test.mjs +0 -196
- package/lib/diagnostics/trace.test.mjs +0 -251
- package/lib/env-compat.test.mjs +0 -104
- package/lib/execution/disposition.test.mjs +0 -553
- package/lib/execution/drive.test.mjs +0 -270
- package/lib/execution/effects.test.mjs +0 -344
- package/lib/execution/intake.test.mjs +0 -389
- package/lib/execution/journal.test.mjs +0 -261
- package/lib/execution/match.test.mjs +0 -235
- package/lib/execution/pipeline.test.mjs +0 -392
- package/lib/execution/route.test.mjs +0 -186
- package/lib/execution/surface-policy.test.mjs +0 -162
- package/lib/fs-atomic.test.mjs +0 -72
- package/lib/fs-ownership.test.mjs +0 -158
- package/lib/goals/admission.test.mjs +0 -164
- package/lib/goals/classify.test.mjs +0 -167
- package/lib/goals/collaborate.test.mjs +0 -336
- package/lib/goals/gaps.test.mjs +0 -284
- package/lib/goals/loop.test.mjs +0 -845
- package/lib/hooks/bus.test.mjs +0 -387
- package/lib/identity/persona.test.mjs +0 -142
- package/lib/kpi-sensors.test.mjs +0 -278
- package/lib/kpi.test.mjs +0 -244
- package/lib/learning/config.test.mjs +0 -75
- package/lib/learning/counters.test.mjs +0 -69
- package/lib/learning/curator-consolidate.test.mjs +0 -238
- package/lib/learning/curator.test.mjs +0 -106
- package/lib/learning/reflect.test.mjs +0 -0
- package/lib/learning/session-index.test.mjs +0 -125
- package/lib/learning/skill-writer.test.mjs +0 -210
- package/lib/mandate/audit.test.mjs +0 -195
- package/lib/mandate/contract.test.mjs +0 -185
- package/lib/mandate/derive.test.mjs +0 -274
- package/lib/mandate/model.test.mjs +0 -164
- package/lib/mandate/refresh.test.mjs +0 -389
- package/lib/mcp/server.test.mjs +0 -426
- package/lib/model-router/auth-profiles.test.mjs +0 -580
- package/lib/model-router/catalog.test.mjs +0 -385
- package/lib/model-router/economics.test.mjs +0 -438
- package/lib/model-router/failover.test.mjs +0 -439
- package/lib/model-router/health.test.mjs +0 -338
- package/lib/model-router/integration-coverage.test.mjs +0 -831
- package/lib/model-router/integration.test.mjs +0 -564
- package/lib/model-router/ledger.test.mjs +0 -415
- package/lib/model-router/llm-task.test.mjs +0 -392
- package/lib/model-router/org-credentials.test.mjs +0 -265
- package/lib/model-router/pricing-refresh.test.mjs +0 -286
- package/lib/model-router/reconcile.test.mjs +0 -316
- package/lib/model-router/repair.test.mjs +0 -180
- package/lib/model-router/spawn.test.mjs +0 -446
- package/lib/model-router/taxonomy.test.mjs +0 -410
- package/lib/model-router.test.mjs +0 -1207
- package/lib/org/activity.test.mjs +0 -134
- package/lib/org/approvals.test.mjs +0 -216
- package/lib/org/awareness.test.mjs +0 -159
- package/lib/org/board-mine-cache.test.mjs +0 -53
- package/lib/org/board.test.mjs +0 -187
- package/lib/org/bootstrap-context.test.mjs +0 -153
- package/lib/org/client.test.mjs +0 -1206
- package/lib/org/cohort-client.test.mjs +0 -126
- package/lib/org/cost-sync.test.mjs +0 -153
- package/lib/org/doctor.test.mjs +0 -346
- package/lib/org/engagement-ledger.test.mjs +0 -112
- package/lib/org/engagement.test.mjs +0 -739
- package/lib/org/handoff.test.mjs +0 -269
- package/lib/org/inbound/directedness.test.mjs +0 -668
- package/lib/org/inbound/facts.test.mjs +0 -471
- package/lib/org/inbound/hydrate.test.mjs +0 -908
- package/lib/org/inbound/index.test.mjs +0 -429
- package/lib/org/inbound/project.test.mjs +0 -287
- package/lib/org/integration-tools.test.mjs +0 -160
- package/lib/org/keys.test.mjs +0 -92
- package/lib/org/knowledge.test.mjs +0 -326
- package/lib/org/leases.test.mjs +0 -235
- package/lib/org/mesh-directives.test.mjs +0 -110
- package/lib/org/mesh-integration.test.mjs +0 -127
- package/lib/org/mesh.test.mjs +0 -400
- package/lib/org/messaging.test.mjs +0 -471
- package/lib/org/param-contract.test.mjs +0 -477
- package/lib/org/policy.test.mjs +0 -237
- package/lib/org/protocol.checksum.test.mjs +0 -90
- package/lib/org/protocol.test.mjs +0 -323
- package/lib/org/push.test.mjs +0 -792
- package/lib/org/registry.test.mjs +0 -100
- package/lib/org/resource-tools.test.mjs +0 -361
- package/lib/org/tool-access.test.mjs +0 -144
- package/lib/org/tool-surface-integration.test.mjs +0 -120
- package/lib/org/tool-surface.test.mjs +0 -1268
- package/lib/org/typing.test.mjs +0 -291
- package/lib/org/ui-parity.test.mjs +0 -560
- package/lib/org/verify.test.mjs +0 -194
- package/lib/org/work-ledger.test.mjs +0 -273
- package/lib/plan/adoption-e2e.test.mjs +0 -366
- package/lib/plan/budget-enforcement.test.mjs +0 -400
- package/lib/plan/compile.test.mjs +0 -382
- package/lib/plan/emit.test.mjs +0 -269
- package/lib/plan/explain.test.mjs +0 -188
- package/lib/prompts/parallelism.test.mjs +0 -177
- package/lib/rag/rag.test.mjs +0 -505
- package/lib/rate-guard.test.mjs +0 -272
- package/lib/reactive-gate.test.mjs +0 -57
- package/lib/render.test.mjs +0 -68
- package/lib/resource-governor.test.mjs +0 -488
- package/lib/scheduling/dynamic-jobs.test.mjs +0 -344
- package/lib/scheduling/jitter.test.mjs +0 -140
- package/lib/secrets/broker.test.mjs +0 -280
- package/lib/secrets/providers.test.mjs +0 -274
- package/lib/security/audit-engine.test.mjs +0 -424
- package/lib/security/coerce-args.test.mjs +0 -281
- package/lib/security/dangerous-tools.test.mjs +0 -68
- package/lib/security/external-content.test.mjs +0 -84
- package/lib/security/redact.test.mjs +0 -441
- package/lib/security/secret-equal.test.mjs +0 -55
- package/lib/session/config.test.mjs +0 -92
- package/lib/session/feed-core.test.mjs +0 -198
- package/lib/session/first-run.test.mjs +0 -121
- package/lib/session/frontdoor.test.mjs +0 -205
- package/lib/session/handoffs.test.mjs +0 -183
- package/lib/session/identity.test.mjs +0 -180
- package/lib/session/inbox-claims.test.mjs +0 -286
- package/lib/session/launch-args.test.mjs +0 -157
- package/lib/session/liveness.test.mjs +0 -100
- package/lib/session/status-summary.test.mjs +0 -118
- package/lib/session-permissions.test.mjs +0 -120
- package/lib/setup/claude-probe.test.mjs +0 -187
- package/lib/setup/completeness.test.mjs +0 -110
- package/lib/setup/context-pack.test.mjs +0 -89
- package/lib/setup/enrich.test.mjs +0 -115
- package/lib/setup/enroll-from-cohort.test.mjs +0 -300
- package/lib/setup/integration.test.mjs +0 -162
- package/lib/setup/io.test.mjs +0 -77
- package/lib/setup/runner.test.mjs +0 -132
- package/lib/setup/sections/identity.test.mjs +0 -234
- package/lib/setup/sections/inventory.test.mjs +0 -198
- package/lib/setup/sections/learning.test.mjs +0 -81
- package/lib/setup/sections/mandate.test.mjs +0 -388
- package/lib/setup/sections/messaging.test.mjs +0 -127
- package/lib/setup/sections/model.test.mjs +0 -240
- package/lib/setup/sections/org.test.mjs +0 -346
- package/lib/setup/sections/orgmail.test.mjs +0 -118
- package/lib/setup/sections/recovery.test.mjs +0 -98
- package/lib/setup/sections/subagents.test.mjs +0 -429
- package/lib/setup/sections/verify.test.mjs +0 -175
- package/lib/setup/sot.test.mjs +0 -81
- package/lib/setup/state.test.mjs +0 -115
- package/lib/singleton.test.mjs +0 -151
- package/lib/subagents/cli.test.mjs +0 -389
- package/lib/subagents/client.test.mjs +0 -309
- package/lib/subagents/gap.test.mjs +0 -234
- package/lib/subagents/lock.test.mjs +0 -248
- package/lib/subagents/manifest.test.mjs +0 -175
- package/lib/subagents/refs.test.mjs +0 -204
- package/lib/subagents/resolve.test.mjs +0 -422
- package/lib/subagents/schema.test.mjs +0 -328
- package/lib/telemetry/alerts.test.mjs +0 -109
- package/lib/telemetry/collect.test.mjs +0 -1274
- package/lib/tool-definitions-integration.test.mjs +0 -83
- package/lib/tool-definitions.test.mjs +0 -437
- package/lib/upgrade/global-refresh.test.mjs +0 -65
- package/lib/upgrade/launchd-reconcile.test.mjs +0 -272
- package/lib/upgrade/post-steps.test.mjs +0 -200
- package/lib/upgrade/verify.test.mjs +0 -164
- package/lib/util/fetch-timeout.test.mjs +0 -202
- package/lib/util/reconnect.test.mjs +0 -369
- package/lib/util/unhandled.test.mjs +0 -216
- package/lib/voice/outbound.test.mjs +0 -69
- package/lib/voice/session-rotation.test.mjs +0 -114
- package/lib/voice/stt.test.mjs +0 -226
- package/lib/voice/voice.test.mjs +0 -990
- package/scripts/cadence/enqueue-cadence-tick.test.mjs +0 -187
- package/scripts/ci/check-docs-accuracy.test.mjs +0 -409
- package/scripts/ci/check-durable-write-seam.test.mjs +0 -90
- package/scripts/ci/check-no-build-artifacts.test.mjs +0 -71
- package/scripts/ci/check-no-residual-identity.test.mjs +0 -202
- package/scripts/ci/check-skill-packs.test.mjs +0 -495
- package/scripts/ci/check-subagent-frontmatter.test.mjs +0 -124
- package/scripts/ci/check.test.mjs +0 -194
- package/scripts/ci/conformance-org-api.test.mjs +0 -425
- package/scripts/cloud-relay/voice/relay-identity.test.mjs +0 -96
- package/scripts/collective/hook-runner.test.mjs +0 -173
- package/scripts/cost/fleet-digest.test.mjs +0 -207
- package/scripts/cost/track-claude-usage-pricing.test.mjs +0 -183
- package/scripts/cost/track-claude-usage.test.mjs +0 -148
- package/scripts/daemon/agent-daemon-board-mine.test.mjs +0 -96
- package/scripts/daemon/agent-daemon-design.test.mjs +0 -238
- package/scripts/daemon/agent-daemon-frontdoor.test.mjs +0 -60
- package/scripts/daemon/agent-daemon.test.mjs +0 -995
- package/scripts/daemon/assurance-e2e.test.mjs +0 -613
- package/scripts/daemon/assurance.test.mjs +0 -1791
- package/scripts/daemon/board-mirror.test.mjs +0 -165
- package/scripts/daemon/cadence-consumer-frontdoor.test.mjs +0 -393
- package/scripts/daemon/cadence-consumer-governance.test.mjs +0 -276
- package/scripts/daemon/cadence-consumer.test.mjs +0 -776
- package/scripts/daemon/cadence-handlers.test.mjs +0 -837
- package/scripts/daemon/classifier-identity.test.mjs +0 -137
- package/scripts/daemon/classifier.test.mjs +0 -266
- package/scripts/daemon/classify-kind.test.mjs +0 -40
- package/scripts/daemon/context-compiler.test.mjs +0 -406
- package/scripts/daemon/deliver.test.mjs +0 -564
- package/scripts/daemon/dispatcher-cooldown.test.mjs +0 -122
- package/scripts/daemon/dispatcher-governance.test.mjs +0 -1013
- package/scripts/daemon/dispatcher-resume.test.mjs +0 -166
- package/scripts/daemon/dispatcher-session-continuity.test.mjs +0 -365
- package/scripts/daemon/execution-ladder.test.mjs +0 -470
- package/scripts/daemon/goal-steward-cadence.test.mjs +0 -312
- package/scripts/daemon/inbox-deferral-session.test.mjs +0 -49
- package/scripts/daemon/inbox-deferral.test.mjs +0 -336
- package/scripts/daemon/inbox-wake.test.mjs +0 -199
- package/scripts/daemon/integration.test.mjs +0 -149
- package/scripts/daemon/lib/self-echo.test.mjs +0 -153
- package/scripts/daemon/lib/session-router.test.mjs +0 -554
- package/scripts/daemon/prompt-builder-preamble.test.mjs +0 -210
- package/scripts/daemon/prompt-builder.test.mjs +0 -556
- package/scripts/daemon/responder-cost.test.mjs +0 -68
- package/scripts/daemon/responder-history.test.mjs +0 -221
- package/scripts/daemon/sdk-version.test.mjs +0 -31
- package/scripts/daemon/session-lock.test.mjs +0 -252
- package/scripts/daemon/session-outcomes.test.mjs +0 -533
- package/scripts/daemon/typing-registry.test.mjs +0 -102
- package/scripts/hooks/pre-send-audit.test.mjs +0 -354
- package/scripts/huddle/huddle-prompt.test.mjs +0 -176
- package/scripts/local-triggers/autoupdate.test.mjs +0 -518
- package/scripts/local-triggers/generate-plists.test.mjs +0 -456
- package/scripts/media-generation/brand-clause.test.mjs +0 -135
- package/scripts/org/send-orgmail.first-contact.test.mjs +0 -102
- package/scripts/poller/inbox-privilege-injection.test.mjs +0 -167
- package/scripts/poller/inbox-scan-poller.test.mjs +0 -295
- package/scripts/poller/lib/cloud-relay-dedup.test.mjs +0 -133
- package/scripts/poller/slack-socket-mode.test.mjs +0 -805
- package/scripts/poller-launchd/install.test.mjs +0 -243
- package/scripts/restore-from-backup.test.mjs +0 -181
- package/scripts/session/feed.test.mjs +0 -196
- package/scripts/session/supervisor-sh.test.mjs +0 -218
- package/scripts/session/supervisor.test.mjs +0 -482
- package/scripts/setup/configure-macos.test.mjs +0 -306
- package/scripts/setup/gen-subagent-manifest.test.mjs +0 -124
- package/scripts/setup/generate-agent-package-json.test.mjs +0 -143
- package/scripts/setup/generate-capability.test.mjs +0 -134
- package/scripts/setup/init-agent.test.mjs +0 -370
- package/scripts/setup/init-skill-marketplace.test.mjs +0 -193
- package/scripts/vendor/sync-skill-packs.test.mjs +0 -103
- package/scripts/watchdog/memory-watchdog.test.mjs +0 -64
|
@@ -1,137 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* classifier-identity.test.mjs — the classifier's frame of reference must be
|
|
3
|
-
* READ, not asserted.
|
|
4
|
-
*
|
|
5
|
-
* WHAT BROKE
|
|
6
|
-
* `buildSystemPrompt()` opened with:
|
|
7
|
-
* "You are a message classifier for ${agentName}, ${agentRole} (AI-operated)
|
|
8
|
-
* at Northwind."
|
|
9
|
-
* — a hardcoded employer sitting between two interpolated fields, on a file
|
|
10
|
-
* that already loads config/agent.json two functions above. It matters more
|
|
11
|
-
* than a naming slip: the classifier's `summary` is carried verbatim into
|
|
12
|
-
* assurance.composeAck (text a waiting human reads) and board-mirror's board
|
|
13
|
-
* row title, so a fabricated employer in the classifier's frame leaks into the
|
|
14
|
-
* human ack and the org record.
|
|
15
|
-
*
|
|
16
|
-
* WHAT THESE TESTS PIN
|
|
17
|
-
* 1. GROUNDED — the employer comes from config/agent.json, and from
|
|
18
|
-
* config/company.json when agent.json leaves `company` blank.
|
|
19
|
-
* 2. EMPTY — no employer configured ⇒ the "at <company>" clause is OMITTED
|
|
20
|
-
* entirely. No sentinel, no default, no invented name.
|
|
21
|
-
* 3. No principal on record ⇒ no principal sentence at all.
|
|
22
|
-
*
|
|
23
|
-
* Run: node --test scripts/daemon/classifier-identity.test.mjs
|
|
24
|
-
*/
|
|
25
|
-
|
|
26
|
-
"use strict";
|
|
27
|
-
|
|
28
|
-
import { test } from "node:test";
|
|
29
|
-
import assert from "node:assert/strict";
|
|
30
|
-
import { mkdtempSync, mkdirSync, writeFileSync, rmSync } from "node:fs";
|
|
31
|
-
import { tmpdir } from "node:os";
|
|
32
|
-
import { join } from "node:path";
|
|
33
|
-
|
|
34
|
-
import { renderClassifierIdentityLine, buildSystemPrompt, _resetAgentRegistry } from "./classifier.mjs";
|
|
35
|
-
|
|
36
|
-
function seat(agent, company) {
|
|
37
|
-
const root = mkdtempSync(join(tmpdir(), "clf-"));
|
|
38
|
-
mkdirSync(join(root, "config"), { recursive: true });
|
|
39
|
-
if (agent) writeFileSync(join(root, "config/agent.json"), JSON.stringify(agent));
|
|
40
|
-
if (company) writeFileSync(join(root, "config/company.json"), JSON.stringify(company));
|
|
41
|
-
return root;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
// ── 1. GROUNDED ─────────────────────────────────────────────────────────────
|
|
45
|
-
|
|
46
|
-
test("grounded: the employer is interpolated from config", () => {
|
|
47
|
-
const line = renderClassifierIdentityLine({
|
|
48
|
-
name: "Robin",
|
|
49
|
-
role: "VP Operations",
|
|
50
|
-
company: "Meridian Freight",
|
|
51
|
-
principal: { fullName: "Dana Lee", title: "CEO" },
|
|
52
|
-
});
|
|
53
|
-
assert.equal(
|
|
54
|
-
line,
|
|
55
|
-
"You are a message classifier for Robin, VP Operations (AI-operated) at Meridian Freight. " +
|
|
56
|
-
"Robin is the autonomous executive command layer for CEO Dana Lee.",
|
|
57
|
-
);
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
test("grounded: config/company.json supplies the employer when agent.json leaves it blank", () => {
|
|
61
|
-
const root = seat(
|
|
62
|
-
{ firstName: "Robin", fullName: "Robin Okafor", title: "VP Operations", company: "" },
|
|
63
|
-
{ name: "Meridian Freight" },
|
|
64
|
-
);
|
|
65
|
-
const prev = process.env.AGENT_DIR;
|
|
66
|
-
process.env.AGENT_DIR = root;
|
|
67
|
-
_resetAgentRegistry();
|
|
68
|
-
try {
|
|
69
|
-
// The whole system prompt, built by the real loader against real files.
|
|
70
|
-
const prompt = buildSystemPrompt();
|
|
71
|
-
assert.match(prompt, /^You are a message classifier for Robin, VP Operations \(AI-operated\) at Meridian Freight\./);
|
|
72
|
-
} finally {
|
|
73
|
-
if (prev === undefined) delete process.env.AGENT_DIR; else process.env.AGENT_DIR = prev;
|
|
74
|
-
_resetAgentRegistry();
|
|
75
|
-
rmSync(root, { recursive: true, force: true });
|
|
76
|
-
}
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
// ── 2. EMPTY — omit, never fake ─────────────────────────────────────────────
|
|
80
|
-
|
|
81
|
-
test("empty: an unconfigured seat's system prompt names NO employer", () => {
|
|
82
|
-
const root = seat(
|
|
83
|
-
{ firstName: "UNCONFIGURED", lastName: "AGENT", fullName: "Unconfigured Agent", title: "Unconfigured Role", company: "" },
|
|
84
|
-
{ name: "UNCONFIGURED" },
|
|
85
|
-
);
|
|
86
|
-
const prev = process.env.AGENT_DIR;
|
|
87
|
-
process.env.AGENT_DIR = root;
|
|
88
|
-
_resetAgentRegistry();
|
|
89
|
-
try {
|
|
90
|
-
const prompt = buildSystemPrompt();
|
|
91
|
-
assert.doesNotMatch(prompt.split("\n")[0], / at /, "no employer clause at all");
|
|
92
|
-
assert.doesNotMatch(prompt, /UNCONFIGURED/);
|
|
93
|
-
} finally {
|
|
94
|
-
if (prev === undefined) delete process.env.AGENT_DIR; else process.env.AGENT_DIR = prev;
|
|
95
|
-
_resetAgentRegistry();
|
|
96
|
-
rmSync(root, { recursive: true, force: true });
|
|
97
|
-
}
|
|
98
|
-
});
|
|
99
|
-
|
|
100
|
-
test("empty: no employer configured ⇒ the 'at <company>' clause is dropped", () => {
|
|
101
|
-
const line = renderClassifierIdentityLine({ name: "Robin", role: "VP Operations", company: "" });
|
|
102
|
-
assert.equal(line, "You are a message classifier for Robin, VP Operations (AI-operated).");
|
|
103
|
-
assert.doesNotMatch(line, / at /);
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
test("empty: the scaffold's UNCONFIGURED sentinel counts as absent", () => {
|
|
107
|
-
const line = renderClassifierIdentityLine({ name: "Robin", role: "VP Operations", company: "UNCONFIGURED" });
|
|
108
|
-
assert.doesNotMatch(line, /UNCONFIGURED/);
|
|
109
|
-
assert.doesNotMatch(line, / at /);
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
test("empty: no principal on record ⇒ no principal sentence", () => {
|
|
113
|
-
const withNone = renderClassifierIdentityLine({ name: "Robin", role: "VP", principal: {} });
|
|
114
|
-
assert.doesNotMatch(withNone, /command layer/);
|
|
115
|
-
|
|
116
|
-
// The loader's "the principal" string is an ABSENCE marker, not a person.
|
|
117
|
-
const withPlaceholder = renderClassifierIdentityLine({
|
|
118
|
-
name: "Robin",
|
|
119
|
-
role: "VP",
|
|
120
|
-
principal: { firstName: "the principal", fullName: "the principal", title: "principal" },
|
|
121
|
-
});
|
|
122
|
-
assert.doesNotMatch(withPlaceholder, /command layer/);
|
|
123
|
-
});
|
|
124
|
-
|
|
125
|
-
test("empty: nothing at all still yields a coherent, non-fabricated line", () => {
|
|
126
|
-
const line = renderClassifierIdentityLine({});
|
|
127
|
-
assert.equal(line, "You are a message classifier for the agent, agent (AI-operated).");
|
|
128
|
-
});
|
|
129
|
-
|
|
130
|
-
// ── 3. The literal is gone from the source ──────────────────────────────────
|
|
131
|
-
|
|
132
|
-
test("the classifier source carries no hardcoded employer", async () => {
|
|
133
|
-
const { readFileSync } = await import("node:fs");
|
|
134
|
-
const src = readFileSync(new URL("./classifier.mjs", import.meta.url), "utf8");
|
|
135
|
-
assert.doesNotMatch(src, /\bNorthwind\b/i);
|
|
136
|
-
assert.doesNotMatch(src, /\bPartnerCo\b/i);
|
|
137
|
-
});
|
|
@@ -1,266 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* classifier.test.mjs — node:test coverage for the classifier's Tier-2 seam.
|
|
3
|
-
*
|
|
4
|
-
* The classifier's Tier-2 fallback used to depend on a TOP-LEVEL
|
|
5
|
-
* `import OpenAI from "openai"`, but `openai` is NOT a dependency of this
|
|
6
|
-
* package — so in a clean install / CI the module failed to import and the
|
|
7
|
-
* whole Tier-2 path was dead. It now routes Tier-2 through
|
|
8
|
-
* lib/model-router/llm-task.mjs `llmTask()` (cheapest direct catalog model,
|
|
9
|
-
* no SDK), with an injectable `fetchImpl` so tests need no network.
|
|
10
|
-
*
|
|
11
|
-
* These tests assert:
|
|
12
|
-
* 1. the module imports CLEANLY with no `openai` package present;
|
|
13
|
-
* 2. when Tier-1 (claude --print) fails, Tier-2 `llmTask` produces the
|
|
14
|
-
* classification (injected fetchImpl returns a JSON object), and the
|
|
15
|
-
* output keeps the documented schema;
|
|
16
|
-
* 3. when both Tier-1 AND Tier-2 fail, classification falls through to the
|
|
17
|
-
* Tier-3 rule-based path (unchanged behaviour);
|
|
18
|
-
* 4. wrapExternalContent fencing still wraps untrusted content in the
|
|
19
|
-
* prompt handed to Tier-2.
|
|
20
|
-
*
|
|
21
|
-
* Hermetic: we inject a catalog + config + env + fetchImpl into classifyItem
|
|
22
|
-
* via its opts arg, force Tier-1 to fail by pointing CLAUDE_BIN at a missing
|
|
23
|
-
* binary, and never touch the network.
|
|
24
|
-
*/
|
|
25
|
-
|
|
26
|
-
import { test } from "node:test";
|
|
27
|
-
import assert from "node:assert/strict";
|
|
28
|
-
import { readFileSync } from "node:fs";
|
|
29
|
-
|
|
30
|
-
import * as classifier from "./classifier.mjs";
|
|
31
|
-
|
|
32
|
-
// ── 1. Imports cleanly with no openai package present ───────────────────────
|
|
33
|
-
|
|
34
|
-
test("classifier.mjs imports cleanly and does not depend on openai", () => {
|
|
35
|
-
// The module-level import above already succeeded (this file loaded), which
|
|
36
|
-
// is the load-bearing assertion.
|
|
37
|
-
//
|
|
38
|
-
// The invariant is about THIS MODULE's source, not the ambient node_modules
|
|
39
|
-
// tree. An earlier version asserted `require.resolve("openai")` throws, but
|
|
40
|
-
// that is a proxy that only holds inside the SDK's own checkout: every agent
|
|
41
|
-
// repo that pulls openai in transitively made this fail while the real
|
|
42
|
-
// invariant stayed true. A guard that goes red on every downstream machine
|
|
43
|
-
// just teaches operators to ignore red suites, so assert on the source.
|
|
44
|
-
const src = readFileSync(new URL("./classifier.mjs", import.meta.url), "utf-8");
|
|
45
|
-
const importsOpenai = /(?:^|\n)\s*import[^\n]*["']openai["']/.test(src)
|
|
46
|
-
|| /\bimport\(\s*["']openai["']\s*\)/.test(src)
|
|
47
|
-
|| /\brequire\(\s*["']openai["']\s*\)/.test(src);
|
|
48
|
-
assert.equal(
|
|
49
|
-
importsOpenai,
|
|
50
|
-
false,
|
|
51
|
-
"classifier.mjs must not import openai — a hard dependency here broke the whole module on agents that lack it"
|
|
52
|
-
);
|
|
53
|
-
assert.equal(typeof classifier.classifyItem, "function", "classifyItem is exported");
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
// ── Hermetic fixtures for the Tier-2 llmTask seam ───────────────────────────
|
|
57
|
-
|
|
58
|
-
// A minimal direct-harness catalog with one cheap anthropic row so llmTask can
|
|
59
|
-
// pick a model for "sensitive" inbox data (deny-default permits anthropic).
|
|
60
|
-
const ANTH_YAML = `
|
|
61
|
-
provider: anthropic
|
|
62
|
-
auth_env: ANTHROPIC_API_KEY
|
|
63
|
-
endpoints:
|
|
64
|
-
anthropic: https://api.anthropic.com
|
|
65
|
-
models:
|
|
66
|
-
- id: claude-haiku-test
|
|
67
|
-
status: available
|
|
68
|
-
context_tokens: 200000
|
|
69
|
-
cost: { input: 1.0, output: 5.0 }
|
|
70
|
-
cost_provenance: { source: official, fetched: "2026-06-10", volatile: false }
|
|
71
|
-
compat: { cache_control: anthropic }
|
|
72
|
-
tool_reliability: A
|
|
73
|
-
harness: { session: true, direct: true, batch: true }
|
|
74
|
-
`;
|
|
75
|
-
|
|
76
|
-
async function hermeticCatalog() {
|
|
77
|
-
const { loadCatalog } = await import("../../lib/model-router/catalog.mjs");
|
|
78
|
-
return loadCatalog("/tmp/nope", { catalogText: { anthropic: ANTH_YAML } });
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
function publicConfig() {
|
|
82
|
-
return {
|
|
83
|
-
schema_version: 2,
|
|
84
|
-
backends: {
|
|
85
|
-
anthropic: { allowed_data_classes: ["public", "internal", "sensitive"] },
|
|
86
|
-
},
|
|
87
|
-
defaults: { data_class: "sensitive" },
|
|
88
|
-
};
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
/** An anthropic-messages response carrying a JSON classification as text. */
|
|
92
|
-
function anthropicJsonResponse(obj) {
|
|
93
|
-
return {
|
|
94
|
-
ok: true,
|
|
95
|
-
status: 200,
|
|
96
|
-
async json() {
|
|
97
|
-
return {
|
|
98
|
-
content: [{ type: "text", text: JSON.stringify(obj) }],
|
|
99
|
-
usage: { input_tokens: 50, output_tokens: 12 },
|
|
100
|
-
};
|
|
101
|
-
},
|
|
102
|
-
};
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
/**
|
|
106
|
-
* Build the llmTask deps that classifyItem threads through to its Tier-2 path.
|
|
107
|
-
* `fetchImpl` is REQUIRED so no network is touched; ledger is a no-op.
|
|
108
|
-
*/
|
|
109
|
-
async function tier2Deps(over = {}) {
|
|
110
|
-
return {
|
|
111
|
-
// Tier-1 always fails in these tests → Tier-2 (or Tier-3) engages.
|
|
112
|
-
runClaudeCLI: failingTier1,
|
|
113
|
-
llmTaskOpts: {
|
|
114
|
-
catalog: await hermeticCatalog(),
|
|
115
|
-
config: publicConfig(),
|
|
116
|
-
env: { ANTHROPIC_API_KEY: "test-key" },
|
|
117
|
-
now: () => 1_700_000_000_000,
|
|
118
|
-
isOpen: () => ({ open: false, until: 0, reason: null, strikes: 0 }),
|
|
119
|
-
writeLedger: () => {},
|
|
120
|
-
...over,
|
|
121
|
-
},
|
|
122
|
-
};
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
// Force Tier-1 (claude --print) to fail deterministically via the injectable
|
|
126
|
-
// runner seam — no dependency on a `claude` binary being installed. The
|
|
127
|
-
// classifier turns a thrown Tier-1 runner into a fall-through to Tier-2.
|
|
128
|
-
const failingTier1 = async () => {
|
|
129
|
-
throw new Error("claude CLI unavailable (test)");
|
|
130
|
-
};
|
|
131
|
-
|
|
132
|
-
const baseItem = {
|
|
133
|
-
sender: "casey",
|
|
134
|
-
sender_privilege: "external",
|
|
135
|
-
channel: "dm/casey",
|
|
136
|
-
service: "slack",
|
|
137
|
-
content: "Following up on the Singapore entity paperwork.",
|
|
138
|
-
is_dm: true,
|
|
139
|
-
};
|
|
140
|
-
|
|
141
|
-
// ── 2. Tier-1 fails → Tier-2 llmTask classifies ─────────────────────────────
|
|
142
|
-
|
|
143
|
-
test("Tier-2 llmTask produces the classification when Tier-1 fails", async () => {
|
|
144
|
-
let sawFetch = false;
|
|
145
|
-
let sawPrompt = "";
|
|
146
|
-
const deps = await tier2Deps({
|
|
147
|
-
fetchImpl: async (url, init) => {
|
|
148
|
-
sawFetch = true;
|
|
149
|
-
const body = JSON.parse(init.body);
|
|
150
|
-
// tool-less invariant inherited from llmTask
|
|
151
|
-
assert.equal(body.tools, undefined, "Tier-2 request is tool-less");
|
|
152
|
-
// Capture the user prompt to assert fencing later.
|
|
153
|
-
const userMsg = (body.messages || []).find((m) => m.role === "user");
|
|
154
|
-
sawPrompt = userMsg ? userMsg.content : "";
|
|
155
|
-
return anthropicJsonResponse({
|
|
156
|
-
priority: "high",
|
|
157
|
-
action: "respond",
|
|
158
|
-
model: "sonnet",
|
|
159
|
-
summary: "External follow-up on Singapore entity",
|
|
160
|
-
category: "action_required",
|
|
161
|
-
directed_at_agent: true,
|
|
162
|
-
});
|
|
163
|
-
},
|
|
164
|
-
});
|
|
165
|
-
|
|
166
|
-
const out = await classifier.classifyItem(baseItem, deps);
|
|
167
|
-
|
|
168
|
-
assert.equal(sawFetch, true, "Tier-2 actually called the injected fetchImpl");
|
|
169
|
-
assert.equal(out.priority, "high");
|
|
170
|
-
assert.equal(out.action, "respond");
|
|
171
|
-
assert.equal(out.model, "sonnet");
|
|
172
|
-
assert.equal(out.category, "action_required");
|
|
173
|
-
assert.equal(out.directed_at_agent, true);
|
|
174
|
-
assert.equal(typeof out.summary, "string");
|
|
175
|
-
|
|
176
|
-
// 4. wrapExternalContent fencing: untrusted content must be fenced in the
|
|
177
|
-
// prompt handed to Tier-2 (the boundary marker is randomised per call, but
|
|
178
|
-
// the raw content must never appear unfenced as a bare line).
|
|
179
|
-
assert.match(sawPrompt, /BEGIN UNTRUSTED|UNTRUSTED|⟦|external-content/i,
|
|
180
|
-
"untrusted content is wrapped by wrapExternalContent before Tier-2");
|
|
181
|
-
assert.match(sawPrompt, /Singapore entity/, "the content still reaches the model (fenced)");
|
|
182
|
-
});
|
|
183
|
-
|
|
184
|
-
// ── 3. Tier-1 AND Tier-2 fail → Tier-3 rules ────────────────────────────────
|
|
185
|
-
|
|
186
|
-
test("falls through to Tier-3 rules when llmTask fails", async () => {
|
|
187
|
-
// fetchImpl throws → llmTask classifies it as a transport error and returns
|
|
188
|
-
// { ok:false } (fail-open, never throws) → classifier drops to rules.
|
|
189
|
-
const deps = await tier2Deps({
|
|
190
|
-
fetchImpl: async () => {
|
|
191
|
-
throw new Error("network down");
|
|
192
|
-
},
|
|
193
|
-
});
|
|
194
|
-
|
|
195
|
-
const item = {
|
|
196
|
-
...baseItem,
|
|
197
|
-
sender: "Avery (CEO)",
|
|
198
|
-
sender_privilege: "ceo",
|
|
199
|
-
};
|
|
200
|
-
|
|
201
|
-
const out = await classifier.classifyItem(item, deps);
|
|
202
|
-
|
|
203
|
-
// Rule-based path: CEO DM → critical / opus / respond / directed.
|
|
204
|
-
assert.equal(out.priority, "critical");
|
|
205
|
-
assert.equal(out.model, "opus");
|
|
206
|
-
assert.equal(out.action, "respond");
|
|
207
|
-
assert.equal(out.directed_at_agent, true);
|
|
208
|
-
assert.equal(out.category, "action_required");
|
|
209
|
-
});
|
|
210
|
-
|
|
211
|
-
test("falls through to Tier-3 rules when llmTask finds no candidate model", async () => {
|
|
212
|
-
// No catalog row qualifies for "sensitive" data without an anthropic
|
|
213
|
-
// credential → llmTask returns { ok:false, reason:"missing_credential" }.
|
|
214
|
-
const deps = await tier2Deps({
|
|
215
|
-
env: {}, // no ANTHROPIC_API_KEY
|
|
216
|
-
fetchImpl: async () => {
|
|
217
|
-
throw new Error("fetch should not be reached when no model qualifies");
|
|
218
|
-
},
|
|
219
|
-
});
|
|
220
|
-
|
|
221
|
-
const out = await classifier.classifyItem(
|
|
222
|
-
{ ...baseItem, content: "urgent: blocker on the deploy" },
|
|
223
|
-
deps
|
|
224
|
-
);
|
|
225
|
-
|
|
226
|
-
// Urgent keyword rule → high priority via Tier-3.
|
|
227
|
-
assert.equal(out.priority, "high");
|
|
228
|
-
// DM → directed → respond.
|
|
229
|
-
assert.equal(out.action, "respond");
|
|
230
|
-
assert.equal(out.directed_at_agent, true);
|
|
231
|
-
});
|
|
232
|
-
|
|
233
|
-
// ── classify-by-kind short-circuit is preserved (no LLM) ────────────────────
|
|
234
|
-
|
|
235
|
-
test("reaction kind short-circuits before any LLM tier (no fetch)", async () => {
|
|
236
|
-
const deps = await tier2Deps({
|
|
237
|
-
fetchImpl: async () => {
|
|
238
|
-
throw new Error("fetch must not be called for reaction kind");
|
|
239
|
-
},
|
|
240
|
-
});
|
|
241
|
-
const out = await classifier.classifyItem(
|
|
242
|
-
{ kind: "reaction", sender: "casey", content: "added :eyes:" },
|
|
243
|
-
deps
|
|
244
|
-
);
|
|
245
|
-
assert.equal(out.action, "archive");
|
|
246
|
-
assert.equal(out.directed_at_agent, false);
|
|
247
|
-
});
|
|
248
|
-
|
|
249
|
-
test("parseClassification floors a directed-at-agent message at 'high' so it preempts backlog", () => {
|
|
250
|
-
// A directed DM/mention is latency-sensitive — someone (human OR peer agent)
|
|
251
|
-
// is waiting. Bumping normal→high lets dispatcher.isPriorityInbox PREEMPT
|
|
252
|
-
// self-directed backlog for it, so a peer's routine question isn't starved.
|
|
253
|
-
const directedNormal = classifier.parseClassification(
|
|
254
|
-
'{"priority":"normal","action":"respond","model":"sonnet","summary":"peer asks for status","category":"action_required","directed_at_agent":true,"answerable":false}'
|
|
255
|
-
);
|
|
256
|
-
assert.equal(directedNormal.priority, "high");
|
|
257
|
-
assert.equal(directedNormal.directed_at_agent, true);
|
|
258
|
-
|
|
259
|
-
// Ambient (not directed) work stays normal — no preemption for the firehose.
|
|
260
|
-
const ambient = classifier.parseClassification('{"priority":"normal","action":"archive","directed_at_agent":false}');
|
|
261
|
-
assert.equal(ambient.priority, "normal");
|
|
262
|
-
|
|
263
|
-
// Critical is never downgraded, and an "ignore" is never escalated.
|
|
264
|
-
assert.equal(classifier.parseClassification('{"priority":"critical","directed_at_agent":true}').priority, "critical");
|
|
265
|
-
assert.equal(classifier.parseClassification('{"priority":"ignore","directed_at_agent":true}').priority, "ignore");
|
|
266
|
-
});
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* classify-kind.test.mjs — node:test coverage for classify-kind.mjs
|
|
3
|
-
*
|
|
4
|
-
* Pure + hermetic (no openai/dotenv import). Verifies reaction/channel_cc
|
|
5
|
-
* short-circuit deterministically to observe-not-reply, and ordinary messages
|
|
6
|
-
* fall through (null) to the LLM path.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import { test } from "node:test";
|
|
10
|
-
import assert from "node:assert/strict";
|
|
11
|
-
|
|
12
|
-
import { classifyByKind } from "./classify-kind.mjs";
|
|
13
|
-
|
|
14
|
-
test("reaction kind → archive / fyi / not directed", () => {
|
|
15
|
-
const r = classifyByKind({ kind: "reaction", sender: "casey", content: "added :eyes:" });
|
|
16
|
-
assert.equal(r.action, "archive");
|
|
17
|
-
assert.equal(r.category, "fyi");
|
|
18
|
-
assert.equal(r.directed_at_agent, false);
|
|
19
|
-
assert.match(r.summary, /Reaction from casey/);
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
test("channel_cc kind → archive / fyi / not directed (observe-not-reply)", () => {
|
|
23
|
-
const r = classifyByKind({ kind: "channel_cc", content: "fyi the deploy finished" });
|
|
24
|
-
assert.equal(r.action, "archive");
|
|
25
|
-
assert.equal(r.directed_at_agent, false);
|
|
26
|
-
assert.match(r.summary, /Channel CC/);
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
test("ordinary message kind → null (falls through to LLM)", () => {
|
|
30
|
-
assert.equal(classifyByKind({ kind: "message", content: "hi" }), null);
|
|
31
|
-
assert.equal(classifyByKind({ content: "no kind field" }), null);
|
|
32
|
-
assert.equal(classifyByKind({}), null);
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
test("channel_cc summary truncates long content", () => {
|
|
36
|
-
const long = "x".repeat(200);
|
|
37
|
-
const r = classifyByKind({ kind: "channel_cc", content: long });
|
|
38
|
-
// "Channel CC: " + 80 chars
|
|
39
|
-
assert.ok(r.summary.length <= "Channel CC: ".length + 80);
|
|
40
|
-
});
|