@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,470 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* scripts/daemon/execution-ladder.test.mjs — the daemon actually ENTERS
|
|
3
|
-
* lib/execution/**.
|
|
4
|
-
*
|
|
5
|
-
* Run: node --test scripts/daemon/execution-ladder.test.mjs
|
|
6
|
-
*
|
|
7
|
-
* `lib/execution/**` had exactly one production importer (lib/goals/loop.mjs,
|
|
8
|
-
* for `routeRung`); `processItem` went from ITEM_RECEIVED straight to the Haiku
|
|
9
|
-
* classifier and never entered the ladder. These tests drive the daemon's OWN
|
|
10
|
-
* entry point — `processItem` — with the network/subprocess collaborators
|
|
11
|
-
* injected, and assert on what lands in `state/execution/journal.jsonl`,
|
|
12
|
-
* `state/queues/inbound.yaml` and the fakes. Nothing about intake, match, route,
|
|
13
|
-
* disposition, drive or journal is faked.
|
|
14
|
-
*
|
|
15
|
-
* The two regression tests at the bottom encode bugs a live run of this wiring
|
|
16
|
-
* actually produced:
|
|
17
|
-
* - with no `need` estimate every DM routed to rung 3, which vetoed the quick
|
|
18
|
-
* reply and turned a 4-second answer into a spawned Claude session;
|
|
19
|
-
* - with the rule-based directedness override applied to ALL ignore reasons,
|
|
20
|
-
* a DM could never be `duplicate` (a DM is always "directed"), so the agent
|
|
21
|
-
* answered every redelivery twice.
|
|
22
|
-
*/
|
|
23
|
-
import { test, before, beforeEach } from "node:test";
|
|
24
|
-
import assert from "node:assert/strict";
|
|
25
|
-
import { register } from "node:module";
|
|
26
|
-
import { mkdtempSync, mkdirSync, writeFileSync, readFileSync, existsSync, rmSync, readdirSync } from "node:fs";
|
|
27
|
-
import { join } from "node:path";
|
|
28
|
-
import { tmpdir } from "node:os";
|
|
29
|
-
|
|
30
|
-
// --- stub the agent-only `imapflow` dep so the daemon module can be imported ---
|
|
31
|
-
register(
|
|
32
|
-
"data:text/javascript," +
|
|
33
|
-
encodeURIComponent(`
|
|
34
|
-
export async function resolve(s, c, next) {
|
|
35
|
-
if (s === "imapflow") return { url: "imapflow-stub:main", shortCircuit: true };
|
|
36
|
-
return next(s, c);
|
|
37
|
-
}
|
|
38
|
-
export async function load(u, c, next) {
|
|
39
|
-
if (u === "imapflow-stub:main") return { format: "module", shortCircuit: true, source: "export class ImapFlow {}" };
|
|
40
|
-
return next(u, c);
|
|
41
|
-
}
|
|
42
|
-
`),
|
|
43
|
-
);
|
|
44
|
-
|
|
45
|
-
const AGENT_DIR = mkdtempSync(join(tmpdir(), "maestro-exec-ladder-"));
|
|
46
|
-
process.env.AGENT_DIR = AGENT_DIR;
|
|
47
|
-
process.env.MAESTRO_DAEMON_NO_AUTOSTART = "1";
|
|
48
|
-
process.env.COHORT_AGENT_ID = "mem_self_0001";
|
|
49
|
-
process.env.ANTHROPIC_API_KEY = "";
|
|
50
|
-
|
|
51
|
-
const PLAN = `version: 1
|
|
52
|
-
enforcement: enforce
|
|
53
|
-
obligations:
|
|
54
|
-
- key: react.message.dm
|
|
55
|
-
kind: REACT
|
|
56
|
-
status: active
|
|
57
|
-
trigger: { topic: message, kind: null, predicate: null }
|
|
58
|
-
uses: []
|
|
59
|
-
allowed_tools: []
|
|
60
|
-
action_classes: []
|
|
61
|
-
offline_safe: true
|
|
62
|
-
- key: react.task.assigned
|
|
63
|
-
kind: REACT
|
|
64
|
-
status: active
|
|
65
|
-
trigger: { topic: task, kind: assigned, predicate: null }
|
|
66
|
-
uses: []
|
|
67
|
-
allowed_tools: []
|
|
68
|
-
action_classes: []
|
|
69
|
-
offline_safe: true
|
|
70
|
-
`;
|
|
71
|
-
|
|
72
|
-
let daemon;
|
|
73
|
-
let responder;
|
|
74
|
-
|
|
75
|
-
before(async () => {
|
|
76
|
-
mkdirSync(join(AGENT_DIR, "config"), { recursive: true });
|
|
77
|
-
writeFileSync(
|
|
78
|
-
join(AGENT_DIR, "config", "agent.json"),
|
|
79
|
-
JSON.stringify({ firstName: "Nova", lastName: "Vale", slackMemberId: "U09NOVA" }),
|
|
80
|
-
);
|
|
81
|
-
writeFileSync(join(AGENT_DIR, "config", "plan.yaml"), PLAN);
|
|
82
|
-
daemon = await import("./agent-daemon.mjs");
|
|
83
|
-
responder = await import("./responder.mjs");
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
beforeEach(() => {
|
|
87
|
-
for (const rel of [["state", "execution"], ["state", "queues"], ["state", "inbox"], ["state", "locks"], ["logs", "daemon"]]) {
|
|
88
|
-
rmSync(join(AGENT_DIR, ...rel), { recursive: true, force: true });
|
|
89
|
-
}
|
|
90
|
-
if (daemon) daemon._resetLadderCaches();
|
|
91
|
-
delete process.env.DAEMON_EXECUTION_LADDER;
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
// --- helpers ---------------------------------------------------------------
|
|
95
|
-
|
|
96
|
-
function writeLive(item, service) {
|
|
97
|
-
const dir = join(AGENT_DIR, "state", "inbox", service);
|
|
98
|
-
mkdirSync(dir, { recursive: true });
|
|
99
|
-
writeFileSync(join(dir, `${item.id}.json`), JSON.stringify(item));
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
/** Fakes for exactly the network / subprocess boundary. Nothing else. */
|
|
103
|
-
function spy(classResult) {
|
|
104
|
-
const seen = { classify: 0, quick: [], dispatch: [], holding: 0 };
|
|
105
|
-
return {
|
|
106
|
-
seen,
|
|
107
|
-
deps: {
|
|
108
|
-
classify: async () => {
|
|
109
|
-
seen.classify += 1;
|
|
110
|
-
return classResult;
|
|
111
|
-
},
|
|
112
|
-
sendQuickResponse: async (item, _cr, routed) => {
|
|
113
|
-
seen.quick.push({ id: item.id, rung: routed ? routed.rung : null });
|
|
114
|
-
return { sent: true, text: "ack", via: "fake" };
|
|
115
|
-
},
|
|
116
|
-
sendHoldingMessage: async () => {
|
|
117
|
-
seen.holding += 1;
|
|
118
|
-
return { sent: false, holdingText: null };
|
|
119
|
-
},
|
|
120
|
-
dispatch: (_p, item, _cr, _s, opts) => {
|
|
121
|
-
seen.dispatch.push({ id: item.id, execution: item.execution || null });
|
|
122
|
-
opts.onClose({ ok: true, code: 0 });
|
|
123
|
-
},
|
|
124
|
-
},
|
|
125
|
-
};
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
function journal() {
|
|
129
|
-
const p = join(AGENT_DIR, "state", "execution", "journal.jsonl");
|
|
130
|
-
if (!existsSync(p)) return [];
|
|
131
|
-
return readFileSync(p, "utf-8").trim().split("\n").filter(Boolean).map((l) => JSON.parse(l));
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
const decisions = () => journal().filter((r) => r.event === "decision");
|
|
135
|
-
const outcomes = () => journal().filter((r) => r.event === "outcome");
|
|
136
|
-
|
|
137
|
-
function dm(over = {}) {
|
|
138
|
-
return {
|
|
139
|
-
id: "MSG-DM",
|
|
140
|
-
raw_ref: "slack:D77:1",
|
|
141
|
-
channel: "dm/priya",
|
|
142
|
-
channel_id: "D77",
|
|
143
|
-
sender: "Priya Raman",
|
|
144
|
-
sender_id: "U_PRIYA",
|
|
145
|
-
// A work-request ("review") so this shared fixture takes the full classify →
|
|
146
|
-
// route path — the fast-path (fastPathClassify) deliberately skips the
|
|
147
|
-
// classifier only for PLAIN short questions; that path has its own test below.
|
|
148
|
-
content: "can you review and confirm the deadline for the launch plan?",
|
|
149
|
-
timestamp: new Date().toISOString(),
|
|
150
|
-
...over,
|
|
151
|
-
};
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
const RESPOND = {
|
|
155
|
-
priority: "normal",
|
|
156
|
-
action: "respond",
|
|
157
|
-
model: "sonnet",
|
|
158
|
-
summary: "deadline question",
|
|
159
|
-
category: "action_required",
|
|
160
|
-
directed_at_agent: true,
|
|
161
|
-
};
|
|
162
|
-
|
|
163
|
-
// ---------------------------------------------------------------------------
|
|
164
|
-
// 1) A DM goes THROUGH the ladder — and is still answered exactly as before.
|
|
165
|
-
// ---------------------------------------------------------------------------
|
|
166
|
-
|
|
167
|
-
test("LADDER: a DM is journalled intake→match→route→outcome AND still quick-replies", async () => {
|
|
168
|
-
const item = dm();
|
|
169
|
-
writeLive(item, "slack");
|
|
170
|
-
const s = spy(RESPOND);
|
|
171
|
-
await daemon.processItem(item, "slack", s.deps);
|
|
172
|
-
|
|
173
|
-
const d = decisions();
|
|
174
|
-
assert.equal(d.length, 1, "one decision row");
|
|
175
|
-
assert.equal(d[0].surface, "dm");
|
|
176
|
-
assert.equal(d[0].disposition, "react_now");
|
|
177
|
-
assert.equal(d[0].reason, "directed_now");
|
|
178
|
-
assert.equal(d[0].obligationKey, "react.message.dm", "match bound the DM to the plan's REACT obligation");
|
|
179
|
-
assert.equal(d[0].rung, 0, "route chose rung 0 — one protocol method with the params in hand");
|
|
180
|
-
assert.equal(d[0].mechanism, "tool-surface");
|
|
181
|
-
assert.ok(d[0].why.some((w) => w.includes("single method messaging.send")), "the why records the rung reasoning");
|
|
182
|
-
|
|
183
|
-
const o = outcomes();
|
|
184
|
-
assert.equal(o.length, 1);
|
|
185
|
-
assert.equal(o[0].effect, "react", "the react effect ran");
|
|
186
|
-
assert.equal(o[0].ok, true);
|
|
187
|
-
assert.equal(o[0].spoke, true, "the agent spoke, so the ping-pong chain deepens");
|
|
188
|
-
assert.equal(o[0].ref.path, "quick_reply", "the effect drove the daemon's real quick-reply path");
|
|
189
|
-
|
|
190
|
-
// NO REGRESSION: the classifier ran and the reply was sent.
|
|
191
|
-
assert.equal(s.seen.classify, 1, "the Haiku classifier still ran for a directed DM");
|
|
192
|
-
assert.deepEqual(s.seen.quick, [{ id: "MSG-DM", rung: 0 }], "the quick reply was sent, carrying its rung");
|
|
193
|
-
assert.equal(s.seen.dispatch.length, 0, "no session spawned for a rung-0 reply");
|
|
194
|
-
});
|
|
195
|
-
|
|
196
|
-
test("LADDER: a plain short question DM FAST-PATHS — quick-replies WITHOUT a classifier call", async () => {
|
|
197
|
-
const item = dm({ id: "MSG-DM-FAST", raw_ref: "slack:D91:1", channel_id: "D91", content: "what's the deadline?" });
|
|
198
|
-
writeLive(item, "slack");
|
|
199
|
-
const s = spy(RESPOND);
|
|
200
|
-
await daemon.processItem(item, "slack", s.deps);
|
|
201
|
-
|
|
202
|
-
// The whole point: the ~5-8s Haiku classify call is SKIPPED for a plain question.
|
|
203
|
-
assert.equal(s.seen.classify, 0, "fast-path: a plain directed question skips the classifier");
|
|
204
|
-
// ...but the DM is still answered via the quick-reply path, still journalled.
|
|
205
|
-
assert.deepEqual(s.seen.quick, [{ id: "MSG-DM-FAST", rung: 0 }], "the quick reply still went out at rung 0");
|
|
206
|
-
assert.equal(s.seen.dispatch.length, 0, "no session spawned");
|
|
207
|
-
assert.ok(decisions().some((r) => r.surface === "dm" && r.disposition === "react_now"), "still journalled intake→route→outcome");
|
|
208
|
-
});
|
|
209
|
-
|
|
210
|
-
// ---------------------------------------------------------------------------
|
|
211
|
-
// 2) A board task is WORK: it queues, and never reaches the classifier.
|
|
212
|
-
// ---------------------------------------------------------------------------
|
|
213
|
-
|
|
214
|
-
test("LADDER: an assigned board task is queued to state/queues/inbound.yaml, not answered", async () => {
|
|
215
|
-
const task = {
|
|
216
|
-
id: "cohort-task_assigned-149",
|
|
217
|
-
raw_ref: "cohort:task_assigned:cmql3j5j301jwhhv2ipp8gmir:149",
|
|
218
|
-
service: "cohort",
|
|
219
|
-
kind: "task_assigned",
|
|
220
|
-
sender: "Priya Raman",
|
|
221
|
-
sender_id: "mem_priya_0002",
|
|
222
|
-
content: "Ship the Q3 revenue reconciliation",
|
|
223
|
-
channel_id: "board_main",
|
|
224
|
-
timestamp: new Date().toISOString(),
|
|
225
|
-
};
|
|
226
|
-
writeLive(task, "cohort");
|
|
227
|
-
const s = spy(RESPOND);
|
|
228
|
-
await daemon.processItem(task, "cohort", s.deps);
|
|
229
|
-
|
|
230
|
-
const d = decisions()[0];
|
|
231
|
-
assert.equal(d.surface, "task_assigned");
|
|
232
|
-
assert.equal(d.disposition, "schedule");
|
|
233
|
-
assert.equal(d.reason, "not_respondable", "a board assignment is work, not conversation");
|
|
234
|
-
assert.equal(d.obligationKey, "react.task.assigned");
|
|
235
|
-
assert.equal(d.rung, 3, "no lower predicate holds for open work → a bounded session");
|
|
236
|
-
|
|
237
|
-
const o = outcomes()[0];
|
|
238
|
-
assert.equal(o.effect, "schedule");
|
|
239
|
-
assert.equal(o.ok, true);
|
|
240
|
-
|
|
241
|
-
// The queue row is real, and it is the file `sweepBacklog` already reads.
|
|
242
|
-
const q = readFileSync(join(AGENT_DIR, "state", "queues", "inbound.yaml"), "utf-8");
|
|
243
|
-
assert.match(q, /id: "inb-board\.task_assigned#149"/);
|
|
244
|
-
assert.match(q, /rung: 3/);
|
|
245
|
-
assert.match(q, /obligation_key: "react\.task\.assigned"/);
|
|
246
|
-
assert.match(q, /why: ".*not a conversational surface/, "the reasoning travels with the work");
|
|
247
|
-
|
|
248
|
-
assert.equal(s.seen.classify, 0, "no Haiku call — the ladder placed it without one");
|
|
249
|
-
assert.equal(s.seen.quick.length, 0);
|
|
250
|
-
assert.equal(s.seen.dispatch.length, 0);
|
|
251
|
-
});
|
|
252
|
-
|
|
253
|
-
// ---------------------------------------------------------------------------
|
|
254
|
-
// 3) `ignore` is first-class: journalled, cheap, and it does not call Haiku.
|
|
255
|
-
// ---------------------------------------------------------------------------
|
|
256
|
-
|
|
257
|
-
test("LADDER: ambient channel chatter is IGNORED, with a journalled reason, and costs no classifier call", async () => {
|
|
258
|
-
const ambient = {
|
|
259
|
-
id: "MSG-AMBIENT",
|
|
260
|
-
raw_ref: "slack:C90:1",
|
|
261
|
-
channel: "#engineering",
|
|
262
|
-
channel_id: "C90",
|
|
263
|
-
sender: "Tomas Lind",
|
|
264
|
-
sender_id: "U_TOMAS",
|
|
265
|
-
content: "lunch anyone",
|
|
266
|
-
timestamp: new Date().toISOString(),
|
|
267
|
-
};
|
|
268
|
-
writeLive(ambient, "slack");
|
|
269
|
-
const s = spy(RESPOND);
|
|
270
|
-
await daemon.processItem(ambient, "slack", s.deps);
|
|
271
|
-
|
|
272
|
-
const d = decisions()[0];
|
|
273
|
-
assert.equal(d.disposition, "ignore");
|
|
274
|
-
assert.equal(d.reason, "ambient_channel");
|
|
275
|
-
assert.ok(d.why.some((w) => w.includes("park for the ambient sweep")), "the ignore explains itself");
|
|
276
|
-
|
|
277
|
-
const o = outcomes()[0];
|
|
278
|
-
assert.equal(o.effect, "none", "an ignore is a completed decision with no effect");
|
|
279
|
-
assert.equal(o.ok, true, "an ignore is a SUCCESS, not a failure");
|
|
280
|
-
assert.equal(o.spoke, false);
|
|
281
|
-
|
|
282
|
-
assert.equal(s.seen.classify, 0, "an ambient message no longer costs a Haiku call");
|
|
283
|
-
assert.equal(s.seen.quick.length, 0);
|
|
284
|
-
assert.equal(s.seen.dispatch.length, 0);
|
|
285
|
-
|
|
286
|
-
// It is finished with, not left to be re-polled forever.
|
|
287
|
-
const files = readdirSync(join(AGENT_DIR, "state", "inbox", "slack"));
|
|
288
|
-
assert.deepEqual(files, ["MSG-AMBIENT.json.processed"]);
|
|
289
|
-
});
|
|
290
|
-
|
|
291
|
-
// ---------------------------------------------------------------------------
|
|
292
|
-
// 4) REQUIREMENT: an event no obligation covers still reaches today's path.
|
|
293
|
-
// ---------------------------------------------------------------------------
|
|
294
|
-
|
|
295
|
-
test("LADDER: an event no obligation covers is still answered, capped at rung ≤1, and proposes the missing obligation", async () => {
|
|
296
|
-
rmSync(join(AGENT_DIR, "config", "plan.yaml"), { force: true });
|
|
297
|
-
daemon._resetLadderCaches();
|
|
298
|
-
try {
|
|
299
|
-
const item = dm({ id: "MSG-DM-UNCOVERED", raw_ref: "slack:D78:1", channel_id: "D78" });
|
|
300
|
-
writeLive(item, "slack");
|
|
301
|
-
const s = spy(RESPOND);
|
|
302
|
-
await daemon.processItem(item, "slack", s.deps);
|
|
303
|
-
|
|
304
|
-
const d = decisions()[0];
|
|
305
|
-
assert.equal(d.disposition, "react_now", "an uncovered event is still answered");
|
|
306
|
-
assert.equal(d.obligationKey, null, "…but it is recorded as uncovered");
|
|
307
|
-
assert.ok(d.rung <= 1, `uncovered work is capped at rung ≤1, got ${d.rung}`);
|
|
308
|
-
assert.ok(
|
|
309
|
-
d.why.some((w) => w.includes("no obligation in the compiled plan covers this event")),
|
|
310
|
-
"the plan gap is stated in the reasoning",
|
|
311
|
-
);
|
|
312
|
-
assert.equal(s.seen.quick.length, 1, "the sender still got an answer");
|
|
313
|
-
|
|
314
|
-
// §6.1's other half: the gap becomes a proposal, not just a complaint.
|
|
315
|
-
const proposed = join(AGENT_DIR, "state", "plan", "proposed.jsonl");
|
|
316
|
-
assert.ok(existsSync(proposed), "an obligation was proposed to cover the gap");
|
|
317
|
-
assert.match(readFileSync(proposed, "utf-8"), /react\.message\./);
|
|
318
|
-
} finally {
|
|
319
|
-
writeFileSync(join(AGENT_DIR, "config", "plan.yaml"), PLAN);
|
|
320
|
-
daemon._resetLadderCaches();
|
|
321
|
-
}
|
|
322
|
-
});
|
|
323
|
-
|
|
324
|
-
// ---------------------------------------------------------------------------
|
|
325
|
-
// 5) REGRESSION (live run): the rule override must not defeat the GUARDS.
|
|
326
|
-
// ---------------------------------------------------------------------------
|
|
327
|
-
|
|
328
|
-
test("LADDER REGRESSION: a redelivered DM is dropped as `duplicate` and is NOT answered twice", async () => {
|
|
329
|
-
const item = dm({ id: "MSG-DM-DUP", raw_ref: "slack:D79:1", channel_id: "D79" });
|
|
330
|
-
writeLive(item, "slack");
|
|
331
|
-
|
|
332
|
-
const first = spy(RESPOND);
|
|
333
|
-
await daemon.processItem(item, "slack", first.deps);
|
|
334
|
-
assert.equal(first.seen.quick.length, 1, "the first delivery is answered");
|
|
335
|
-
|
|
336
|
-
// The SSE push and the poll backstop converge — that convergence is the design.
|
|
337
|
-
const again = { ...item };
|
|
338
|
-
writeLive(again, "slack");
|
|
339
|
-
const second = spy(RESPOND);
|
|
340
|
-
await daemon.processItem(again, "slack", second.deps);
|
|
341
|
-
|
|
342
|
-
const d = decisions();
|
|
343
|
-
assert.equal(d.length, 2);
|
|
344
|
-
assert.equal(d[1].disposition, "ignore");
|
|
345
|
-
assert.equal(d[1].reason, "duplicate", "the dedupe guard fired");
|
|
346
|
-
|
|
347
|
-
// The bug this encodes: `isDirectedAtAgent` returns true for ANY DM, so
|
|
348
|
-
// consulting it on every ignore reason made `duplicate` unreachable and the
|
|
349
|
-
// agent answered both deliveries.
|
|
350
|
-
assert.equal(second.seen.quick.length, 0, "the redelivery was NOT answered");
|
|
351
|
-
assert.equal(second.seen.classify, 0, "…and did not even reach the classifier");
|
|
352
|
-
});
|
|
353
|
-
|
|
354
|
-
// ---------------------------------------------------------------------------
|
|
355
|
-
// 6) …but a directedness MISS must still fall through. Silence is the expensive
|
|
356
|
-
// failure, so the ladder never has the last word on "is this mine".
|
|
357
|
-
// ---------------------------------------------------------------------------
|
|
358
|
-
|
|
359
|
-
test("LADDER: a directedness disagreement falls through to the classifier path rather than gagging the agent", async () => {
|
|
360
|
-
// Ambient by intake's rules (no mention flag, not a DM, not a thread reply),
|
|
361
|
-
// but the agent's own name is in the text — which `isDirectedAtAgent` catches
|
|
362
|
-
// and intake does not.
|
|
363
|
-
const item = {
|
|
364
|
-
id: "MSG-NAMED",
|
|
365
|
-
raw_ref: "slack:C91:1",
|
|
366
|
-
channel: "#engineering",
|
|
367
|
-
channel_id: "C91",
|
|
368
|
-
sender: "Tomas Lind",
|
|
369
|
-
sender_id: "U_TOMAS",
|
|
370
|
-
content: "nova can you take the reconciliation?",
|
|
371
|
-
timestamp: new Date().toISOString(),
|
|
372
|
-
};
|
|
373
|
-
writeLive(item, "slack");
|
|
374
|
-
const s = spy(RESPOND);
|
|
375
|
-
const gate = await daemon.runExecutionLadder(item, "slack", item.raw_ref, "trace-x", s.deps);
|
|
376
|
-
|
|
377
|
-
assert.equal(gate.handled, false, "the ladder did not take responsibility");
|
|
378
|
-
assert.equal(gate.reason, "rule_override");
|
|
379
|
-
assert.equal(decisions()[0].disposition, "ignore", "the ignore is still journalled — the disagreement is on record");
|
|
380
|
-
});
|
|
381
|
-
|
|
382
|
-
// ---------------------------------------------------------------------------
|
|
383
|
-
// 7) The rung is a VETO on the quick path, never a promotion.
|
|
384
|
-
// ---------------------------------------------------------------------------
|
|
385
|
-
|
|
386
|
-
test("RUNG: the quick-reply responder accepts rungs 0-1 and refuses 2+", async () => {
|
|
387
|
-
assert.equal(responder.rungPermitsQuickReply(null), true, "no routing decision → today's behaviour");
|
|
388
|
-
assert.equal(responder.rungPermitsQuickReply({ rung: 0 }), true);
|
|
389
|
-
assert.equal(responder.rungPermitsQuickReply({ rung: 1 }), true);
|
|
390
|
-
assert.equal(responder.rungPermitsQuickReply({ rung: 2 }), false);
|
|
391
|
-
assert.equal(responder.rungPermitsQuickReply({ rung: 3 }), false);
|
|
392
|
-
assert.equal(responder.rungPermitsQuickReply({ rung: 5 }), false, "no subagent-fanout mechanism exists here");
|
|
393
|
-
|
|
394
|
-
// A veto, not a promotion: rung 0 does not make research quick-repliable.
|
|
395
|
-
assert.equal(responder.isQuickReply({ action: "research", priority: "normal", model: "sonnet" }, { rung: 0 }), false);
|
|
396
|
-
assert.equal(responder.isQuickReply({ action: "respond", priority: "normal", model: "sonnet" }, { rung: 0 }), true);
|
|
397
|
-
assert.equal(
|
|
398
|
-
responder.isQuickReply({ action: "respond", priority: "normal", model: "sonnet" }, { rung: 3 }),
|
|
399
|
-
false,
|
|
400
|
-
"a rung-3 answer needs a session even when the classifier called it simple",
|
|
401
|
-
);
|
|
402
|
-
});
|
|
403
|
-
|
|
404
|
-
test("ANSWERABLE: a directly-answerable question is quick-replied, not sessioned + acked", () => {
|
|
405
|
-
// "what machine are you running on?" from the CEO classifies critical/opus,
|
|
406
|
-
// which would otherwise force a session and a robotic holding ack. Flagged
|
|
407
|
-
// answerable, it is answered in the turn instead — the defect being fixed.
|
|
408
|
-
assert.equal(
|
|
409
|
-
responder.isQuickReply({ action: "respond", priority: "critical", model: "opus", answerable: true }),
|
|
410
|
-
true,
|
|
411
|
-
"an answerable question skips the session even at critical/opus",
|
|
412
|
-
);
|
|
413
|
-
// The flag only fast-paths a `respond` — a draft/research item stays on the
|
|
414
|
-
// session path even if the classifier also (contradictorily) flags it.
|
|
415
|
-
assert.equal(responder.isQuickReply({ action: "draft", priority: "high", model: "opus", answerable: true }), false);
|
|
416
|
-
assert.equal(responder.isQuickReply({ action: "research", priority: "normal", model: "sonnet", answerable: true }), false);
|
|
417
|
-
// And the rung veto still wins over answerable.
|
|
418
|
-
assert.equal(
|
|
419
|
-
responder.isQuickReply({ action: "respond", priority: "critical", model: "opus", answerable: true }, { rung: 3 }),
|
|
420
|
-
false,
|
|
421
|
-
"a rung-3 answer needs a session even when the classifier called it answerable",
|
|
422
|
-
);
|
|
423
|
-
// Without the flag, a critical/opus item still takes the session path.
|
|
424
|
-
assert.equal(responder.isQuickReply({ action: "respond", priority: "critical", model: "opus" }), false);
|
|
425
|
-
});
|
|
426
|
-
|
|
427
|
-
// ---------------------------------------------------------------------------
|
|
428
|
-
// 8) REGRESSION (live run): with no need estimate every DM routed to rung 3,
|
|
429
|
-
// which vetoed the quick reply and spawned a session for a two-line message.
|
|
430
|
-
// ---------------------------------------------------------------------------
|
|
431
|
-
|
|
432
|
-
test("LADDER REGRESSION: the need estimate marks a resolvable reply target as params-known", () => {
|
|
433
|
-
assert.equal(daemon.ladderNeed({ channel_id: "D1" }).paramsKnown, true, "a resolvable channel → rung 0 is available");
|
|
434
|
-
assert.equal(daemon.ladderNeed({ raw_ref: "cohort:dm:x:1" }).paramsKnown, true);
|
|
435
|
-
assert.equal(
|
|
436
|
-
daemon.ladderNeed({ sender: "someone" }).paramsKnown,
|
|
437
|
-
false,
|
|
438
|
-
"no resolvable target → params are NOT known and the ladder walks up to a session that can find it",
|
|
439
|
-
);
|
|
440
|
-
assert.equal(daemon.ladderNeed({ channel_id: "D1" }).steps, 1);
|
|
441
|
-
});
|
|
442
|
-
|
|
443
|
-
// ---------------------------------------------------------------------------
|
|
444
|
-
// 9) The escape hatch preserves the old behaviour exactly — and says so.
|
|
445
|
-
// ---------------------------------------------------------------------------
|
|
446
|
-
|
|
447
|
-
test("LADDER: DAEMON_EXECUTION_LADDER=0 restores the pre-ladder path and warns about what goes dark", async () => {
|
|
448
|
-
process.env.DAEMON_EXECUTION_LADDER = "0";
|
|
449
|
-
daemon._resetLadderCaches();
|
|
450
|
-
const warnings = [];
|
|
451
|
-
const realWarn = console.warn;
|
|
452
|
-
console.warn = (...a) => warnings.push(a.join(" "));
|
|
453
|
-
try {
|
|
454
|
-
const item = dm({ id: "MSG-DM-OFF", raw_ref: "slack:D80:1", channel_id: "D80" });
|
|
455
|
-
writeLive(item, "slack");
|
|
456
|
-
const s = spy(RESPOND);
|
|
457
|
-
await daemon.processItem(item, "slack", s.deps);
|
|
458
|
-
|
|
459
|
-
assert.equal(journal().length, 0, "nothing is journalled with the ladder off");
|
|
460
|
-
assert.equal(s.seen.classify, 1, "the classifier path is untouched");
|
|
461
|
-
assert.equal(s.seen.quick.length, 1, "the DM is still answered");
|
|
462
|
-
assert.ok(
|
|
463
|
-
warnings.some((w) => w.includes("DAEMON_EXECUTION_LADDER=0") && w.includes("no record of what was ignored")),
|
|
464
|
-
"turning it off is never silent",
|
|
465
|
-
);
|
|
466
|
-
} finally {
|
|
467
|
-
console.warn = realWarn;
|
|
468
|
-
delete process.env.DAEMON_EXECUTION_LADDER;
|
|
469
|
-
}
|
|
470
|
-
});
|