@cohortapp/agent-sdk 2.16.0 → 2.18.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/settings.json +18 -0
- package/.env.example +23 -7
- package/README.md +1 -0
- package/bin/maestro.mjs +62 -0
- package/docs/guides/billing-console-keys.md +60 -0
- package/docs/guides/front-door-session.md +54 -9
- package/docs/guides/mac-mini.md +20 -25
- package/docs/guides/poller-daemon-setup.md +4 -1
- package/docs/guides/setup-wizard.md +1 -1
- package/docs/runbooks/fleet-rollout.md +156 -0
- package/docs/runbooks/mac-mini-bootstrap.md +12 -14
- package/lib/action-executor.js +19 -3
- package/lib/budget-guard.mjs +279 -3
- package/lib/channels/base-adapter.mjs +3 -1
- package/lib/channels/contract.mjs +2 -1
- package/lib/channels/inbox-item.mjs +8 -0
- package/lib/claude-bin.mjs +5 -6
- package/lib/cli/doctor-checks.mjs +141 -10
- package/lib/cli/global-setup-extras.mjs +5 -1
- package/lib/cli/inbox.mjs +100 -15
- package/lib/cli/seat-auth.mjs +463 -0
- package/lib/cli/session.mjs +80 -12
- package/lib/collective/capture.mjs +8 -6
- package/lib/collective/global-config.mjs +63 -1
- package/lib/collective/presence.mjs +142 -5
- package/lib/comms/send-gate.mjs +559 -1
- package/lib/context/budget.mjs +327 -0
- package/lib/context/history-scope.mjs +138 -0
- package/lib/diagnostics/alerts.mjs +49 -0
- package/lib/diagnostics/cadence-output-freshness.mjs +288 -0
- package/lib/engine/agents/definitions.mjs +343 -0
- package/lib/engine/agents/persist.mjs +275 -0
- package/lib/engine/agents/runtime.mjs +748 -0
- package/lib/engine/agents/usage.mjs +95 -0
- package/lib/engine/auth-status.mjs +139 -0
- package/lib/engine/budget.mjs +194 -0
- package/lib/engine/cli.mjs +1204 -0
- package/lib/engine/commands/index.mjs +269 -0
- package/lib/engine/context/budget.mjs +219 -0
- package/lib/engine/context/cache.mjs +125 -0
- package/lib/engine/context/child-env.mjs +215 -0
- package/lib/engine/context/compaction.mjs +342 -0
- package/lib/engine/context/images.mjs +90 -0
- package/lib/engine/context/instructions.mjs +327 -0
- package/lib/engine/context/lazy-instructions.mjs +169 -0
- package/lib/engine/context/manager.mjs +182 -0
- package/lib/engine/context/real-path.mjs +91 -0
- package/lib/engine/context/secret-values.mjs +163 -0
- package/lib/engine/context/settings.mjs +274 -0
- package/lib/engine/context/stream-input.mjs +159 -0
- package/lib/engine/guard.mjs +152 -0
- package/lib/engine/hooks.mjs +713 -0
- package/lib/engine/loop.mjs +560 -0
- package/lib/engine/mcp/client.mjs +254 -0
- package/lib/engine/mcp/config.mjs +301 -0
- package/lib/engine/mcp/http.mjs +201 -0
- package/lib/engine/mcp/index.mjs +146 -0
- package/lib/engine/mcp/jsonrpc.mjs +147 -0
- package/lib/engine/mcp/naming.mjs +66 -0
- package/lib/engine/mcp/resources.mjs +89 -0
- package/lib/engine/mcp/results.mjs +133 -0
- package/lib/engine/mcp/stdio.mjs +137 -0
- package/lib/engine/mcp/supervisor.mjs +116 -0
- package/lib/engine/messages.mjs +104 -0
- package/lib/engine/output/json.mjs +164 -0
- package/lib/engine/output/stream-json.mjs +266 -0
- package/lib/engine/permissions.mjs +845 -0
- package/lib/engine/process-identity.mjs +164 -0
- package/lib/engine/process-tree.mjs +551 -0
- package/lib/engine/prompt.mjs +60 -0
- package/lib/engine/session/store.mjs +299 -0
- package/lib/engine/session-runtime/args.mjs +97 -0
- package/lib/engine/session-runtime/host.mjs +143 -0
- package/lib/engine/session-runtime/inbox.mjs +122 -0
- package/lib/engine/session-runtime/notifications.mjs +129 -0
- package/lib/engine/session-runtime/registry.mjs +328 -0
- package/lib/engine/session-runtime/runner.mjs +344 -0
- package/lib/engine/session-runtime/socket.mjs +212 -0
- package/lib/engine/session-runtime/wakeup.mjs +115 -0
- package/lib/engine/skills/index.mjs +321 -0
- package/lib/engine/tools/bash-background.mjs +533 -0
- package/lib/engine/tools/bash.mjs +216 -0
- package/lib/engine/tools/edit.mjs +97 -0
- package/lib/engine/tools/glob.mjs +81 -0
- package/lib/engine/tools/grep.mjs +224 -0
- package/lib/engine/tools/index.mjs +84 -0
- package/lib/engine/tools/list-agents.mjs +32 -0
- package/lib/engine/tools/ls.mjs +127 -0
- package/lib/engine/tools/monitor.mjs +82 -0
- package/lib/engine/tools/notebook-edit.mjs +218 -0
- package/lib/engine/tools/read.mjs +103 -0
- package/lib/engine/tools/schedule-wakeup.mjs +45 -0
- package/lib/engine/tools/schema.mjs +144 -0
- package/lib/engine/tools/send-message.mjs +77 -0
- package/lib/engine/tools/session.mjs +70 -0
- package/lib/engine/tools/todo.mjs +144 -0
- package/lib/engine/tools/toolsearch.mjs +217 -0
- package/lib/engine/tools/walk.mjs +193 -0
- package/lib/engine/tools/web-switch.mjs +31 -0
- package/lib/engine/tools/webfetch-html.mjs +387 -0
- package/lib/engine/tools/webfetch-net.mjs +340 -0
- package/lib/engine/tools/webfetch.mjs +198 -0
- package/lib/engine/tools/websearch.mjs +91 -0
- package/lib/engine/tools/workflow.mjs +95 -0
- package/lib/engine/tools/write.mjs +76 -0
- package/lib/engine/tui/line-editor.mjs +137 -0
- package/lib/engine/tui/render.mjs +86 -0
- package/lib/engine/tui/tui.mjs +274 -0
- package/lib/engine/wire/anthropic-messages.mjs +263 -0
- package/lib/engine/wire/effort.mjs +36 -0
- package/lib/engine/wire/errors.mjs +496 -0
- package/lib/engine/wire/http.mjs +441 -0
- package/lib/engine/wire/index.mjs +76 -0
- package/lib/engine/wire/openai-chat.mjs +332 -0
- package/lib/engine/wire/prompt-cache.mjs +79 -0
- package/lib/engine/wire/search.mjs +140 -0
- package/lib/engine/wire/sse.mjs +114 -0
- package/lib/engine/wire/stall.mjs +349 -0
- package/lib/engine/wire/token-provider.mjs +175 -0
- package/lib/engine/wire/usage.mjs +192 -0
- package/lib/engine/workflow/host.mjs +524 -0
- package/lib/engine/workflow/journal.mjs +188 -0
- package/lib/engine/workflow/json-schema.mjs +171 -0
- package/lib/engine/workflow/meta.mjs +329 -0
- package/lib/engine/workflow/notifications.mjs +52 -0
- package/lib/engine/workflow/runtime.mjs +447 -0
- package/lib/engine/workflow/sandbox.mjs +534 -0
- package/lib/engine/workflow/worker.mjs +141 -0
- package/lib/engine/workflow/worktree.mjs +74 -0
- package/lib/execution/disposition.mjs +1 -1
- package/lib/execution/intake.mjs +10 -0
- package/lib/execution/surface-policy.mjs +15 -0
- package/lib/learning/curator.mjs +8 -6
- package/lib/learning/reflect.mjs +8 -6
- package/lib/model-router/catalog/cohort.yaml +137 -0
- package/lib/model-router/catalog.mjs +118 -1
- package/lib/model-router/economics.mjs +9 -0
- package/lib/model-router/failover.mjs +67 -16
- package/lib/model-router/llm-task.mjs +39 -3
- package/lib/model-router/resolve.mjs +95 -3
- package/lib/model-router/spawn.mjs +46 -47
- package/lib/model-router/taxonomy.mjs +126 -4
- package/lib/org/cost-sync.mjs +141 -11
- package/lib/org/inbound/broadcast.mjs +289 -0
- package/lib/org/inbound/collective.mjs +375 -0
- package/lib/org/inbound/directedness.mjs +96 -8
- package/lib/org/inbound/facts.mjs +82 -4
- package/lib/org/inbound/hydrate.mjs +555 -51
- package/lib/org/inbound/project.mjs +22 -0
- package/lib/org/inbound/surfaces.mjs +14 -0
- package/lib/org/llm-token.mjs +879 -0
- package/lib/org/mesh.mjs +61 -0
- package/lib/org/messaging.mjs +3 -1
- package/lib/org/protocol.checksum +1 -1
- package/lib/org/protocol.mjs +15 -0
- package/lib/org/quota.mjs +520 -0
- package/lib/org/tool-surface.mjs +104 -16
- package/lib/org/ui-parity.mjs +16 -1
- package/lib/org/work-ledger.mjs +37 -6
- package/lib/rate-guard.mjs +114 -1
- package/lib/resource-governor.mjs +41 -6
- package/lib/runtime/adapter.mjs +823 -0
- package/lib/runtime/child-env.mjs +191 -0
- package/lib/runtime/legacy-shell-guard.mjs +97 -0
- package/lib/runtime/seat-engine.mjs +162 -0
- package/lib/session/ask-ledger.mjs +271 -0
- package/lib/session/current-work.mjs +676 -0
- package/lib/session/feed-core.mjs +40 -3
- package/lib/session/launch-args.mjs +56 -4
- package/lib/session/status-summary.mjs +26 -9
- package/lib/session/upgrade-notice.mjs +42 -0
- package/lib/setup/claude-probe.mjs +117 -13
- package/lib/setup/enrich.mjs +13 -10
- package/lib/setup/sections/model.mjs +39 -13
- package/lib/telemetry/collect.mjs +208 -9
- package/lib/upgrade/ignored-drift.mjs +105 -0
- package/lib/voice/post-call-brief.mjs +30 -17
- package/package.json +15 -3
- package/plugins/maestro-skills/skills/board-work.md +5 -0
- package/plugins/maestro-skills/skills/inbound-triage.md +56 -15
- package/plugins/maestro-skills/skills/main-session.md +18 -7
- package/scripts/ci/check-tarball-fidelity.mjs +126 -2
- package/scripts/ci/run-tests.mjs +47 -19
- package/scripts/cohort-llm/api-key-helper.mjs +92 -0
- package/scripts/collective/hook-runner.mjs +29 -2
- package/scripts/continuous-monitor.sh +13 -0
- package/scripts/cost/track-claude-usage.mjs +15 -0
- package/scripts/daemon/agent-daemon.mjs +408 -20
- package/scripts/daemon/assurance.mjs +48 -12
- package/scripts/daemon/cadence-consumer.mjs +218 -68
- package/scripts/daemon/cadence-handlers.mjs +73 -4
- package/scripts/daemon/classifier.mjs +75 -26
- package/scripts/daemon/context-compiler.mjs +104 -59
- package/scripts/daemon/deliver.mjs +30 -1
- package/scripts/daemon/dispatcher.mjs +804 -157
- package/scripts/daemon/health.mjs +14 -1
- package/scripts/daemon/lib/session-router.mjs +310 -42
- package/scripts/daemon/maestro-daemon.mjs +11 -0
- package/scripts/daemon/prompt-builder.mjs +121 -12
- package/scripts/daemon/responder.mjs +315 -146
- package/scripts/daemon/sdk-version.mjs +98 -16
- package/scripts/eval/probe-gateway.mjs +635 -0
- package/scripts/eval/replay/extract.mjs +270 -0
- package/scripts/eval/replay/grade.mjs +260 -0
- package/scripts/eval/replay/lib/config.mjs +50 -0
- package/scripts/eval/replay/lib/effects.mjs +65 -0
- package/scripts/eval/replay/lib/fixture.mjs +188 -0
- package/scripts/eval/replay/lib/judge.mjs +72 -0
- package/scripts/eval/replay/lib/redact.mjs +136 -0
- package/scripts/eval/replay/lib/sandbox.mjs +170 -0
- package/scripts/eval/replay/lib/schema-check.mjs +63 -0
- package/scripts/eval/replay/lib/transcript.mjs +76 -0
- package/scripts/eval/replay/mcp-replay-stub.mjs +101 -0
- package/scripts/eval/replay/report.mjs +185 -0
- package/scripts/eval/replay/run.mjs +404 -0
- package/scripts/fleet/rollout.mjs +1094 -0
- package/scripts/hooks/pre-send-audit.sh +36 -245
- package/scripts/hooks/pre-write-yaml-validate.mjs +275 -0
- package/scripts/hooks/validate-state-yaml.sh +190 -0
- package/scripts/huddle/huddle-llm.mjs +361 -0
- package/scripts/huddle/huddle-server.mjs +46 -121
- package/scripts/local-triggers/autoupdate.sh +448 -78
- package/scripts/local-triggers/run-trigger.sh +13 -0
- package/scripts/maintenance/pin-integrity.mjs +364 -0
- package/scripts/poll-slack-events.sh +41 -9
- package/scripts/poller/slack-socket-mode.mjs +28 -3
- package/scripts/session/supervisor.mjs +80 -13
- package/scripts/spawn-session.sh +13 -0
- package/bin/maestro.test.mjs +0 -1574
- package/lib/action-executor.test.mjs +0 -871
- package/lib/archetype.test.mjs +0 -132
- package/lib/assurance/plan-note.test.mjs +0 -234
- package/lib/assurance/room-budget.test.mjs +0 -486
- package/lib/assurance/tier.test.mjs +0 -174
- package/lib/autonomy.test.mjs +0 -66
- package/lib/backlog.test.mjs +0 -302
- package/lib/backup/policy.test.mjs +0 -305
- package/lib/budget-escalate.test.mjs +0 -232
- package/lib/budget-guard.envelope.test.mjs +0 -476
- package/lib/budget-guard.test.mjs +0 -427
- package/lib/cadence-bus-requeue.test.mjs +0 -83
- package/lib/cadence-bus-schedule.test.mjs +0 -194
- package/lib/cadence-bus.test.mjs +0 -720
- package/lib/cadences.test.mjs +0 -230
- package/lib/capability/inventory.test.mjs +0 -232
- package/lib/capability.test.mjs +0 -78
- package/lib/channels/base-adapter.test.mjs +0 -590
- package/lib/channels/channels.test.mjs +0 -371
- package/lib/channels/contract.test.mjs +0 -162
- package/lib/channels/inbox-item.test.mjs +0 -368
- package/lib/channels/orgmail/adapter.test.mjs +0 -448
- package/lib/channels/pairing.test.mjs +0 -270
- package/lib/channels/repeat-suppressor.test.mjs +0 -134
- package/lib/channels/slack-adapter.test.mjs +0 -212
- package/lib/channels/telegram-adapter.test.mjs +0 -306
- package/lib/channels/voice/adapter.test.mjs +0 -278
- package/lib/channels/whatsapp/adapter-baileys.test.mjs +0 -359
- package/lib/channels/whatsapp/baileys-typing.test.mjs +0 -154
- package/lib/charter.test.mjs +0 -89
- package/lib/claude-bin.test.mjs +0 -131
- package/lib/cli/board.test.mjs +0 -227
- package/lib/cli/design.test.mjs +0 -270
- package/lib/cli/doctor-checks.test.mjs +0 -336
- package/lib/cli/global-setup-extras.test.mjs +0 -462
- package/lib/cli/inbox.test.mjs +0 -230
- package/lib/cli/session-ack.test.mjs +0 -63
- package/lib/cli/session.test.mjs +0 -613
- package/lib/collective/capture.test.mjs +0 -121
- package/lib/collective/cards.test.mjs +0 -114
- package/lib/collective/config.test.mjs +0 -123
- package/lib/collective/global-config.test.mjs +0 -220
- package/lib/collective/global-skills.test.mjs +0 -126
- package/lib/collective/presence.test.mjs +0 -95
- package/lib/collective/recall.test.mjs +0 -116
- package/lib/collective/vendor-skills.test.mjs +0 -306
- package/lib/comms/send-gate.test.mjs +0 -770
- package/lib/comms.test.mjs +0 -41
- package/lib/cost/ledger-row.test.mjs +0 -183
- package/lib/design/design-md.test.mjs +0 -318
- package/lib/design/fixtures/DESIGN.golden.md +0 -238
- package/lib/design/fixtures/PRODUCT.golden.md +0 -67
- package/lib/design/fixtures/foundation.json +0 -133
- package/lib/design/refresh-gate.test.mjs +0 -144
- package/lib/design/write.test.mjs +0 -241
- package/lib/diagnostics/alerts.test.mjs +0 -318
- package/lib/diagnostics/backup-freshness.test.mjs +0 -185
- package/lib/diagnostics/counters.test.mjs +0 -206
- package/lib/diagnostics/events.test.mjs +0 -290
- package/lib/diagnostics/otel.test.mjs +0 -196
- package/lib/diagnostics/trace.test.mjs +0 -251
- package/lib/env-compat.test.mjs +0 -104
- package/lib/execution/disposition.test.mjs +0 -553
- package/lib/execution/drive.test.mjs +0 -270
- package/lib/execution/effects.test.mjs +0 -344
- package/lib/execution/intake.test.mjs +0 -389
- package/lib/execution/journal.test.mjs +0 -261
- package/lib/execution/match.test.mjs +0 -235
- package/lib/execution/pipeline.test.mjs +0 -392
- package/lib/execution/route.test.mjs +0 -186
- package/lib/execution/surface-policy.test.mjs +0 -162
- package/lib/fs-atomic.test.mjs +0 -72
- package/lib/fs-ownership.test.mjs +0 -158
- package/lib/goals/admission.test.mjs +0 -164
- package/lib/goals/classify.test.mjs +0 -167
- package/lib/goals/collaborate.test.mjs +0 -336
- package/lib/goals/gaps.test.mjs +0 -284
- package/lib/goals/loop.test.mjs +0 -845
- package/lib/hooks/bus.test.mjs +0 -387
- package/lib/identity/persona.test.mjs +0 -142
- package/lib/kpi-sensors.test.mjs +0 -278
- package/lib/kpi.test.mjs +0 -244
- package/lib/learning/config.test.mjs +0 -75
- package/lib/learning/counters.test.mjs +0 -69
- package/lib/learning/curator-consolidate.test.mjs +0 -238
- package/lib/learning/curator.test.mjs +0 -106
- package/lib/learning/reflect.test.mjs +0 -0
- package/lib/learning/session-index.test.mjs +0 -125
- package/lib/learning/skill-writer.test.mjs +0 -210
- package/lib/mandate/audit.test.mjs +0 -195
- package/lib/mandate/contract.test.mjs +0 -185
- package/lib/mandate/derive.test.mjs +0 -274
- package/lib/mandate/model.test.mjs +0 -164
- package/lib/mandate/refresh.test.mjs +0 -389
- package/lib/mcp/server.test.mjs +0 -426
- package/lib/model-router/auth-profiles.test.mjs +0 -580
- package/lib/model-router/catalog.test.mjs +0 -385
- package/lib/model-router/economics.test.mjs +0 -438
- package/lib/model-router/failover.test.mjs +0 -439
- package/lib/model-router/health.test.mjs +0 -338
- package/lib/model-router/integration-coverage.test.mjs +0 -831
- package/lib/model-router/integration.test.mjs +0 -564
- package/lib/model-router/ledger.test.mjs +0 -415
- package/lib/model-router/llm-task.test.mjs +0 -392
- package/lib/model-router/org-credentials.test.mjs +0 -265
- package/lib/model-router/pricing-refresh.test.mjs +0 -286
- package/lib/model-router/reconcile.test.mjs +0 -316
- package/lib/model-router/repair.test.mjs +0 -180
- package/lib/model-router/spawn.test.mjs +0 -446
- package/lib/model-router/taxonomy.test.mjs +0 -410
- package/lib/model-router.test.mjs +0 -1207
- package/lib/org/activity.test.mjs +0 -134
- package/lib/org/approvals.test.mjs +0 -216
- package/lib/org/awareness.test.mjs +0 -159
- package/lib/org/board-mine-cache.test.mjs +0 -53
- package/lib/org/board.test.mjs +0 -187
- package/lib/org/bootstrap-context.test.mjs +0 -153
- package/lib/org/client.test.mjs +0 -1206
- package/lib/org/cohort-client.test.mjs +0 -126
- package/lib/org/cost-sync.test.mjs +0 -153
- package/lib/org/doctor.test.mjs +0 -346
- package/lib/org/engagement-ledger.test.mjs +0 -112
- package/lib/org/engagement.test.mjs +0 -739
- package/lib/org/handoff.test.mjs +0 -269
- package/lib/org/inbound/directedness.test.mjs +0 -668
- package/lib/org/inbound/facts.test.mjs +0 -471
- package/lib/org/inbound/hydrate.test.mjs +0 -453
- package/lib/org/inbound/index.test.mjs +0 -429
- package/lib/org/inbound/project.test.mjs +0 -287
- package/lib/org/integration-tools.test.mjs +0 -160
- package/lib/org/keys.test.mjs +0 -92
- package/lib/org/knowledge.test.mjs +0 -326
- package/lib/org/leases.test.mjs +0 -235
- package/lib/org/mesh-directives.test.mjs +0 -110
- package/lib/org/mesh-integration.test.mjs +0 -127
- package/lib/org/mesh.test.mjs +0 -400
- package/lib/org/messaging.test.mjs +0 -471
- package/lib/org/param-contract.test.mjs +0 -477
- package/lib/org/policy.test.mjs +0 -237
- package/lib/org/protocol.checksum.test.mjs +0 -90
- package/lib/org/protocol.test.mjs +0 -323
- package/lib/org/push.test.mjs +0 -792
- package/lib/org/registry.test.mjs +0 -100
- package/lib/org/resource-tools.test.mjs +0 -361
- package/lib/org/tool-access.test.mjs +0 -144
- package/lib/org/tool-surface-integration.test.mjs +0 -120
- package/lib/org/tool-surface.test.mjs +0 -1268
- package/lib/org/typing.test.mjs +0 -291
- package/lib/org/ui-parity.test.mjs +0 -560
- package/lib/org/verify.test.mjs +0 -194
- package/lib/org/work-ledger.test.mjs +0 -273
- package/lib/plan/adoption-e2e.test.mjs +0 -366
- package/lib/plan/budget-enforcement.test.mjs +0 -400
- package/lib/plan/compile.test.mjs +0 -382
- package/lib/plan/emit.test.mjs +0 -269
- package/lib/plan/explain.test.mjs +0 -188
- package/lib/prompts/parallelism.test.mjs +0 -177
- package/lib/rag/rag.test.mjs +0 -505
- package/lib/rate-guard.test.mjs +0 -272
- package/lib/reactive-gate.test.mjs +0 -57
- package/lib/render.test.mjs +0 -68
- package/lib/resource-governor.test.mjs +0 -488
- package/lib/scheduling/dynamic-jobs.test.mjs +0 -344
- package/lib/scheduling/jitter.test.mjs +0 -140
- package/lib/secrets/broker.test.mjs +0 -280
- package/lib/secrets/providers.test.mjs +0 -274
- package/lib/security/audit-engine.test.mjs +0 -424
- package/lib/security/coerce-args.test.mjs +0 -281
- package/lib/security/dangerous-tools.test.mjs +0 -68
- package/lib/security/external-content.test.mjs +0 -84
- package/lib/security/redact.test.mjs +0 -441
- package/lib/security/secret-equal.test.mjs +0 -55
- package/lib/session/config.test.mjs +0 -92
- package/lib/session/feed-core.test.mjs +0 -198
- package/lib/session/first-run.test.mjs +0 -121
- package/lib/session/frontdoor.test.mjs +0 -205
- package/lib/session/handoffs.test.mjs +0 -183
- package/lib/session/identity.test.mjs +0 -180
- package/lib/session/inbox-claims.test.mjs +0 -286
- package/lib/session/launch-args.test.mjs +0 -157
- package/lib/session/liveness.test.mjs +0 -100
- package/lib/session/status-summary.test.mjs +0 -118
- package/lib/session-permissions.test.mjs +0 -120
- package/lib/setup/claude-probe.test.mjs +0 -187
- package/lib/setup/completeness.test.mjs +0 -110
- package/lib/setup/context-pack.test.mjs +0 -89
- package/lib/setup/enrich.test.mjs +0 -115
- package/lib/setup/enroll-from-cohort.test.mjs +0 -300
- package/lib/setup/integration.test.mjs +0 -162
- package/lib/setup/io.test.mjs +0 -77
- package/lib/setup/runner.test.mjs +0 -132
- package/lib/setup/sections/identity.test.mjs +0 -234
- package/lib/setup/sections/inventory.test.mjs +0 -198
- package/lib/setup/sections/learning.test.mjs +0 -81
- package/lib/setup/sections/mandate.test.mjs +0 -388
- package/lib/setup/sections/messaging.test.mjs +0 -127
- package/lib/setup/sections/model.test.mjs +0 -240
- package/lib/setup/sections/org.test.mjs +0 -346
- package/lib/setup/sections/orgmail.test.mjs +0 -118
- package/lib/setup/sections/recovery.test.mjs +0 -98
- package/lib/setup/sections/subagents.test.mjs +0 -429
- package/lib/setup/sections/verify.test.mjs +0 -175
- package/lib/setup/sot.test.mjs +0 -81
- package/lib/setup/state.test.mjs +0 -115
- package/lib/singleton.test.mjs +0 -151
- package/lib/subagents/cli.test.mjs +0 -389
- package/lib/subagents/client.test.mjs +0 -309
- package/lib/subagents/gap.test.mjs +0 -234
- package/lib/subagents/lock.test.mjs +0 -248
- package/lib/subagents/manifest.test.mjs +0 -175
- package/lib/subagents/refs.test.mjs +0 -204
- package/lib/subagents/resolve.test.mjs +0 -422
- package/lib/subagents/schema.test.mjs +0 -328
- package/lib/telemetry/alerts.test.mjs +0 -109
- package/lib/telemetry/collect.test.mjs +0 -1274
- package/lib/tool-definitions-integration.test.mjs +0 -83
- package/lib/tool-definitions.test.mjs +0 -437
- package/lib/upgrade/global-refresh.test.mjs +0 -65
- package/lib/upgrade/launchd-reconcile.test.mjs +0 -272
- package/lib/upgrade/post-steps.test.mjs +0 -200
- package/lib/upgrade/verify.test.mjs +0 -164
- package/lib/util/fetch-timeout.test.mjs +0 -202
- package/lib/util/reconnect.test.mjs +0 -369
- package/lib/util/unhandled.test.mjs +0 -216
- package/lib/voice/outbound.test.mjs +0 -69
- package/lib/voice/session-rotation.test.mjs +0 -114
- package/lib/voice/stt.test.mjs +0 -226
- package/lib/voice/voice.test.mjs +0 -990
- package/scripts/cadence/enqueue-cadence-tick.test.mjs +0 -187
- package/scripts/ci/check-docs-accuracy.test.mjs +0 -409
- package/scripts/ci/check-durable-write-seam.test.mjs +0 -90
- package/scripts/ci/check-no-build-artifacts.test.mjs +0 -71
- package/scripts/ci/check-no-residual-identity.test.mjs +0 -202
- package/scripts/ci/check-skill-packs.test.mjs +0 -495
- package/scripts/ci/check-subagent-frontmatter.test.mjs +0 -124
- package/scripts/ci/check.test.mjs +0 -194
- package/scripts/ci/conformance-org-api.test.mjs +0 -425
- package/scripts/cloud-relay/voice/relay-identity.test.mjs +0 -96
- package/scripts/collective/hook-runner.test.mjs +0 -173
- package/scripts/cost/fleet-digest.test.mjs +0 -207
- package/scripts/cost/track-claude-usage-pricing.test.mjs +0 -183
- package/scripts/cost/track-claude-usage.test.mjs +0 -148
- package/scripts/daemon/agent-daemon-board-mine.test.mjs +0 -96
- package/scripts/daemon/agent-daemon-design.test.mjs +0 -238
- package/scripts/daemon/agent-daemon-frontdoor.test.mjs +0 -60
- package/scripts/daemon/agent-daemon.test.mjs +0 -995
- package/scripts/daemon/assurance-e2e.test.mjs +0 -613
- package/scripts/daemon/assurance.test.mjs +0 -1791
- package/scripts/daemon/board-mirror.test.mjs +0 -165
- package/scripts/daemon/cadence-consumer-frontdoor.test.mjs +0 -393
- package/scripts/daemon/cadence-consumer-governance.test.mjs +0 -276
- package/scripts/daemon/cadence-consumer.test.mjs +0 -776
- package/scripts/daemon/cadence-handlers.test.mjs +0 -837
- package/scripts/daemon/classifier-identity.test.mjs +0 -137
- package/scripts/daemon/classifier.test.mjs +0 -266
- package/scripts/daemon/classify-kind.test.mjs +0 -40
- package/scripts/daemon/context-compiler.test.mjs +0 -300
- package/scripts/daemon/deliver.test.mjs +0 -564
- package/scripts/daemon/dispatcher-cooldown.test.mjs +0 -122
- package/scripts/daemon/dispatcher-governance.test.mjs +0 -1013
- package/scripts/daemon/dispatcher-resume.test.mjs +0 -166
- package/scripts/daemon/execution-ladder.test.mjs +0 -470
- package/scripts/daemon/goal-steward-cadence.test.mjs +0 -312
- package/scripts/daemon/inbox-deferral-session.test.mjs +0 -49
- package/scripts/daemon/inbox-deferral.test.mjs +0 -336
- package/scripts/daemon/inbox-wake.test.mjs +0 -199
- package/scripts/daemon/integration.test.mjs +0 -149
- package/scripts/daemon/lib/self-echo.test.mjs +0 -153
- package/scripts/daemon/lib/session-router.test.mjs +0 -295
- package/scripts/daemon/prompt-builder-preamble.test.mjs +0 -210
- package/scripts/daemon/prompt-builder.test.mjs +0 -344
- package/scripts/daemon/responder-cost.test.mjs +0 -68
- package/scripts/daemon/responder-history.test.mjs +0 -185
- package/scripts/daemon/sdk-version.test.mjs +0 -31
- package/scripts/daemon/session-lock.test.mjs +0 -252
- package/scripts/daemon/session-outcomes.test.mjs +0 -533
- package/scripts/daemon/typing-registry.test.mjs +0 -102
- package/scripts/hooks/pre-send-audit.test.mjs +0 -354
- package/scripts/huddle/huddle-prompt.test.mjs +0 -176
- package/scripts/local-triggers/autoupdate.test.mjs +0 -518
- package/scripts/local-triggers/generate-plists.test.mjs +0 -456
- package/scripts/media-generation/brand-clause.test.mjs +0 -135
- package/scripts/org/send-orgmail.first-contact.test.mjs +0 -102
- package/scripts/poller/inbox-privilege-injection.test.mjs +0 -167
- package/scripts/poller/inbox-scan-poller.test.mjs +0 -295
- package/scripts/poller/lib/cloud-relay-dedup.test.mjs +0 -133
- package/scripts/poller/slack-socket-mode.test.mjs +0 -805
- package/scripts/poller-launchd/install.test.mjs +0 -243
- package/scripts/restore-from-backup.test.mjs +0 -181
- package/scripts/session/feed.test.mjs +0 -196
- package/scripts/session/supervisor-sh.test.mjs +0 -218
- package/scripts/session/supervisor.test.mjs +0 -482
- package/scripts/setup/configure-macos.test.mjs +0 -306
- package/scripts/setup/gen-subagent-manifest.test.mjs +0 -124
- package/scripts/setup/generate-agent-package-json.test.mjs +0 -143
- package/scripts/setup/generate-capability.test.mjs +0 -134
- package/scripts/setup/init-agent.test.mjs +0 -370
- package/scripts/setup/init-skill-marketplace.test.mjs +0 -193
- package/scripts/vendor/sync-skill-packs.test.mjs +0 -103
- package/scripts/watchdog/memory-watchdog.test.mjs +0 -64
|
@@ -1,153 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* self-echo.test.mjs — the sender==self match that three production recurrences
|
|
3
|
-
* (2026-08-25 / 08-27 / 08-30) proved the old inline check could not make.
|
|
4
|
-
*
|
|
5
|
-
* Run: node --test scripts/daemon/lib/self-echo.test.mjs
|
|
6
|
-
*
|
|
7
|
-
* The load-bearing case is the REAL event shape: cohort inbox items carry the
|
|
8
|
-
* display name verbatim (`sender: "Isla Roselli"`), while the old filter
|
|
9
|
-
* compared against `fullName.toLowerCase().replace(/\s+/g, "-")` —
|
|
10
|
-
* "isla-roselli" — so the echo sailed through and the agent acked itself.
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
import { test, describe, beforeEach } from "node:test";
|
|
14
|
-
import assert from "node:assert/strict";
|
|
15
|
-
import { mkdtempSync, mkdirSync, writeFileSync, readFileSync, rmSync } from "node:fs";
|
|
16
|
-
import { join } from "node:path";
|
|
17
|
-
import { tmpdir } from "node:os";
|
|
18
|
-
|
|
19
|
-
import { loadSelfIdentity, isSelfSender, normalizeName, logSelfEchoDrop, _resetSelfIdentity } from "./self-echo.mjs";
|
|
20
|
-
|
|
21
|
-
const ROOT = mkdtempSync(join(tmpdir(), "self-echo-test-"));
|
|
22
|
-
mkdirSync(join(ROOT, "config"), { recursive: true });
|
|
23
|
-
writeFileSync(join(ROOT, "config", "agent.json"), JSON.stringify({
|
|
24
|
-
firstName: "Isla",
|
|
25
|
-
fullName: "Isla Roselli",
|
|
26
|
-
memberId: "cmqh0tcml004ihhl6zev6ocb5",
|
|
27
|
-
email: "isla@example.com",
|
|
28
|
-
org: { cohort: { agentId: "A016" } },
|
|
29
|
-
}));
|
|
30
|
-
|
|
31
|
-
beforeEach(() => _resetSelfIdentity());
|
|
32
|
-
|
|
33
|
-
const me = () => loadSelfIdentity(ROOT);
|
|
34
|
-
|
|
35
|
-
describe("normalizeName", () => {
|
|
36
|
-
test("folds case and the separator zoo to one comparable form", () => {
|
|
37
|
-
for (const v of ["Isla Roselli", "isla roselli", "isla-roselli", "Isla_Roselli", "isla.roselli", " Isla Roselli "]) {
|
|
38
|
-
assert.equal(normalizeName(v), "isla roselli", `"${v}" must normalise`);
|
|
39
|
-
}
|
|
40
|
-
});
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
describe("isSelfSender — the shapes real events actually carry", () => {
|
|
44
|
-
test("REGRESSION: the verbatim display name — the exact 25/27/30 Aug echo shape — matches", () => {
|
|
45
|
-
// Field-for-field the 30 Aug production echo (cmtfqjwvv01kype01t002t8jf):
|
|
46
|
-
const echo = {
|
|
47
|
-
id: "cohort-cmtfqjwvv01kype01t002t8jf",
|
|
48
|
-
service: "cohort",
|
|
49
|
-
channel: "dm-a016-a039",
|
|
50
|
-
sender: "Isla Roselli",
|
|
51
|
-
raw_ref: "cohort:dm:cmtfqjwvv01kype01t002t8jf:192025",
|
|
52
|
-
event_kind: "send",
|
|
53
|
-
is_dm: true,
|
|
54
|
-
};
|
|
55
|
-
assert.equal(isSelfSender(echo, me()), true);
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
test("the OLD filter's dashed form and other separator variants also match", () => {
|
|
59
|
-
for (const s of ["isla-roselli", "Isla_Roselli", "isla roselli"]) {
|
|
60
|
-
assert.equal(isSelfSender({ sender: s }, me()), true, `"${s}" is this agent`);
|
|
61
|
-
}
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
test("REGRESSION: the BARE first name alone is NOT self — it collides with every namesake", () => {
|
|
65
|
-
// "Isla" as a display name is any colleague, contact or CC'd person who
|
|
66
|
-
// shares the first name. Matching it dropped their genuine inbound.
|
|
67
|
-
assert.equal(isSelfSender({ sender: "Isla" }, me()), false);
|
|
68
|
-
assert.equal(isSelfSender({ sender: "isla" }, me()), false);
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
test("REGRESSION: a NON-matching id EXONERATES a colliding display name", () => {
|
|
72
|
-
// A namesake — display name "Isla Roselli", but a DIFFERENT member cuid.
|
|
73
|
-
// The id positively identifies the sender as someone else; the name rung
|
|
74
|
-
// must never be reached, so this is NOT self and must not be dropped.
|
|
75
|
-
assert.equal(
|
|
76
|
-
isSelfSender({ sender: "Isla Roselli", sender_id: "cmDIFFERENT00000000000000" }, me()),
|
|
77
|
-
false,
|
|
78
|
-
"an id that resolves to someone else outranks the name collision",
|
|
79
|
-
);
|
|
80
|
-
assert.equal(
|
|
81
|
-
isSelfSender({ sender: "isla-roselli", author_id: "cmDIFFERENT00000000000000" }, me()),
|
|
82
|
-
false,
|
|
83
|
-
);
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
test("REGRESSION: a NON-matching email exonerates a colliding display name too", () => {
|
|
87
|
-
assert.equal(
|
|
88
|
-
isSelfSender({ sender: "Isla Roselli", sender_email: "other.isla@example.com" }, me()),
|
|
89
|
-
false,
|
|
90
|
-
);
|
|
91
|
-
// …while the OWN email still matches regardless of the display name.
|
|
92
|
-
assert.equal(
|
|
93
|
-
isSelfSender({ sender: "totally different name", sender_email: "isla@example.com" }, me()),
|
|
94
|
-
true,
|
|
95
|
-
);
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
test("a member cuid on any id-ish field matches (strongest evidence)", () => {
|
|
99
|
-
for (const item of [
|
|
100
|
-
{ sender: "someone", sender_id: "cmqh0tcml004ihhl6zev6ocb5" },
|
|
101
|
-
{ sender: "someone", author_id: "CMQH0TCML004IHHL6ZEV6OCB5" },
|
|
102
|
-
{ sender: "someone", from: { id: "cmqh0tcml004ihhl6zev6ocb5" } },
|
|
103
|
-
]) {
|
|
104
|
-
assert.equal(isSelfSender(item, me()), true);
|
|
105
|
-
}
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
test("the org seat slug matches too", () => {
|
|
109
|
-
assert.equal(isSelfSender({ sender: "x", sender_id: "A016" }, me()), true);
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
test("own email matches (gmail/orgmail lanes)", () => {
|
|
113
|
-
assert.equal(isSelfSender({ sender: "Isla via mail", sender_email: "isla@example.com" }, me()), true);
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
test("genuine colleagues NEVER match — no false drops", () => {
|
|
117
|
-
for (const s of ["Jordan Blake", "Candace Wong", "Isla Fisher", "roselli", "isla2", "ceo"]) {
|
|
118
|
-
assert.equal(isSelfSender({ sender: s }, me()), false, `"${s}" must not be dropped`);
|
|
119
|
-
}
|
|
120
|
-
assert.equal(isSelfSender({ sender: "Candace Wong", sender_id: "cmOTHER000000000000000000" }, me()), false);
|
|
121
|
-
});
|
|
122
|
-
|
|
123
|
-
test("an UNCONFIGURED identity matches nothing — a half-set-up repo drops no inbound", () => {
|
|
124
|
-
const bare = mkdtempSync(join(tmpdir(), "self-echo-bare-"));
|
|
125
|
-
try {
|
|
126
|
-
const id = loadSelfIdentity(bare);
|
|
127
|
-
assert.equal(isSelfSender({ sender: "Isla Roselli" }, id), false);
|
|
128
|
-
assert.equal(isSelfSender({ sender: "Agent" }, id), false);
|
|
129
|
-
} finally {
|
|
130
|
-
rmSync(bare, { recursive: true, force: true });
|
|
131
|
-
}
|
|
132
|
-
});
|
|
133
|
-
});
|
|
134
|
-
|
|
135
|
-
describe("logSelfEchoDrop — every drop is auditable", () => {
|
|
136
|
-
test("appends one JSONL row per drop under logs/daemon/", () => {
|
|
137
|
-
const item = { id: "e-1", raw_ref: "cohort:dm:e-1:42", sender: "Isla Roselli", channel: "dm-a016-a039", service: "cohort", event_kind: "send" };
|
|
138
|
-
logSelfEchoDrop(item, { stage: "poll", service: "cohort", agentRoot: ROOT });
|
|
139
|
-
logSelfEchoDrop(item, { stage: "process", service: "cohort", agentRoot: ROOT });
|
|
140
|
-
const rows = readFileSync(join(ROOT, "logs", "daemon", "self-echo-drops.jsonl"), "utf-8")
|
|
141
|
-
.split("\n").filter(Boolean).map((l) => JSON.parse(l));
|
|
142
|
-
assert.equal(rows.length, 2);
|
|
143
|
-
assert.equal(rows[0].stage, "poll");
|
|
144
|
-
assert.equal(rows[1].stage, "process");
|
|
145
|
-
assert.equal(rows[0].raw_ref, "cohort:dm:e-1:42");
|
|
146
|
-
assert.equal(rows[0].sender, "Isla Roselli");
|
|
147
|
-
});
|
|
148
|
-
|
|
149
|
-
test("never throws, even for a junk item", () => {
|
|
150
|
-
assert.doesNotThrow(() => logSelfEchoDrop(null, { agentRoot: ROOT }));
|
|
151
|
-
assert.doesNotThrow(() => logSelfEchoDrop(undefined, { agentRoot: join(ROOT, "no", "such", "deep") }));
|
|
152
|
-
});
|
|
153
|
-
});
|
|
@@ -1,295 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* session-router.test.mjs — node:test coverage for the session router.
|
|
3
|
-
*
|
|
4
|
-
* No real subprocess spawns, no real disk writes outside os.tmpdir().
|
|
5
|
-
* Each test creates its own registry path under tmpdir and cleans up.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import { test } from "node:test";
|
|
9
|
-
import assert from "node:assert/strict";
|
|
10
|
-
import { promises as fsp } from "fs";
|
|
11
|
-
import { tmpdir } from "os";
|
|
12
|
-
import { join } from "path";
|
|
13
|
-
|
|
14
|
-
import { routingKey, createRouter } from "./session-router.mjs";
|
|
15
|
-
|
|
16
|
-
function tmpRegistryPath(suffix = "") {
|
|
17
|
-
const name = `session-router-test-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}${suffix}.json`;
|
|
18
|
-
return join(tmpdir(), name);
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
async function safeUnlink(path) {
|
|
22
|
-
try {
|
|
23
|
-
await fsp.unlink(path);
|
|
24
|
-
} catch {
|
|
25
|
-
/* ignore */
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
// ---------------------------------------------------------------------------
|
|
30
|
-
// 1. routingKey — one assertion per row of memo §4.2.
|
|
31
|
-
// ---------------------------------------------------------------------------
|
|
32
|
-
|
|
33
|
-
test("routingKey — slack threaded message", () => {
|
|
34
|
-
const k = routingKey({
|
|
35
|
-
source: "slack",
|
|
36
|
-
channel: "C0123ABC",
|
|
37
|
-
thread_ts: "1777283277.000100",
|
|
38
|
-
});
|
|
39
|
-
assert.equal(k, "slack:C0123ABC:1777283277.000100");
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
test("routingKey — slack DM (no thread)", () => {
|
|
43
|
-
const k = routingKey({
|
|
44
|
-
source: "slack",
|
|
45
|
-
channel: "D099N1JGKRQ",
|
|
46
|
-
});
|
|
47
|
-
assert.equal(k, "slack:D099N1JGKRQ");
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
test("routingKey — slack channel non-DM (no thread)", () => {
|
|
51
|
-
const k = routingKey({
|
|
52
|
-
source: "slack",
|
|
53
|
-
channel: "C0123ABC",
|
|
54
|
-
ts: "1777283277.000200",
|
|
55
|
-
});
|
|
56
|
-
assert.equal(k, "slack:C0123ABC:1777283277.000200");
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
test("routingKey — gmail", () => {
|
|
60
|
-
const k = routingKey({ source: "gmail", thread_id: "1818abc" });
|
|
61
|
-
assert.equal(k, "gmail:1818abc");
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
test("routingKey — calendar", () => {
|
|
65
|
-
const k = routingKey({ source: "calendar", event_id: "evt_42" });
|
|
66
|
-
assert.equal(k, "calendar:evt_42");
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
test("routingKey — internal queue item", () => {
|
|
70
|
-
const k = routingKey({ source: "internal", id: "ar-20260427-001" });
|
|
71
|
-
assert.equal(k, "internal:ar-20260427-001");
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
test("routingKey — backlog with topic_slug", () => {
|
|
75
|
-
const k = routingKey({
|
|
76
|
-
source: "backlog",
|
|
77
|
-
topic_slug: "session-router-step2",
|
|
78
|
-
id: "as-20260427-x",
|
|
79
|
-
});
|
|
80
|
-
assert.equal(k, "backlog:session-router-step2");
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
test("routingKey — backlog without topic_slug falls back to internal:<id>", () => {
|
|
84
|
-
const k = routingKey({ source: "backlog", id: "as-20260427-fallback" });
|
|
85
|
-
assert.equal(k, "internal:as-20260427-fallback");
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
// ---------------------------------------------------------------------------
|
|
89
|
-
// 2. route() — missing key returns EPHEMERAL.
|
|
90
|
-
// ---------------------------------------------------------------------------
|
|
91
|
-
|
|
92
|
-
test("route — missing key returns EPHEMERAL", async (t) => {
|
|
93
|
-
const path = tmpRegistryPath("-missing");
|
|
94
|
-
t.after(() => safeUnlink(path));
|
|
95
|
-
const router = await createRouter({ registryPath: path });
|
|
96
|
-
const decision = router.route("slack:CNEVERSEEN:0");
|
|
97
|
-
assert.deepEqual(decision, { decision: "EPHEMERAL", resumeId: null });
|
|
98
|
-
});
|
|
99
|
-
|
|
100
|
-
// ---------------------------------------------------------------------------
|
|
101
|
-
// 3. route() — past-TTL entry returns EPHEMERAL_REPLACE.
|
|
102
|
-
// ---------------------------------------------------------------------------
|
|
103
|
-
|
|
104
|
-
test("route — past-TTL entry returns EPHEMERAL_REPLACE", async (t) => {
|
|
105
|
-
const path = tmpRegistryPath("-ttl");
|
|
106
|
-
t.after(() => safeUnlink(path));
|
|
107
|
-
let clock = 1_000_000_000_000;
|
|
108
|
-
const router = await createRouter({
|
|
109
|
-
registryPath: path,
|
|
110
|
-
ttlSeconds: 60,
|
|
111
|
-
now: () => clock,
|
|
112
|
-
});
|
|
113
|
-
await router.touch("slack:DAAA:1", { claudeSessionId: "cli-1", model: "sonnet" });
|
|
114
|
-
// Advance clock past TTL.
|
|
115
|
-
clock += 61 * 1000;
|
|
116
|
-
const decision = router.route("slack:DAAA:1");
|
|
117
|
-
assert.equal(decision.decision, "EPHEMERAL_REPLACE");
|
|
118
|
-
assert.equal(decision.resumeId, null);
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
// ---------------------------------------------------------------------------
|
|
122
|
-
// 4. route() — live, fresh, exit_code 0 returns RESUME with the stored id.
|
|
123
|
-
// ---------------------------------------------------------------------------
|
|
124
|
-
|
|
125
|
-
test("route — live fresh entry returns RESUME with stored claude_session_id", async (t) => {
|
|
126
|
-
const path = tmpRegistryPath("-resume");
|
|
127
|
-
t.after(() => safeUnlink(path));
|
|
128
|
-
let clock = 2_000_000_000_000;
|
|
129
|
-
const router = await createRouter({
|
|
130
|
-
registryPath: path,
|
|
131
|
-
ttlSeconds: 600,
|
|
132
|
-
now: () => clock,
|
|
133
|
-
});
|
|
134
|
-
await router.touch("slack:DRESUME:1", {
|
|
135
|
-
claudeSessionId: "cli-resume-xyz",
|
|
136
|
-
model: "sonnet",
|
|
137
|
-
});
|
|
138
|
-
clock += 30_000; // 30s — well within TTL.
|
|
139
|
-
const decision = router.route("slack:DRESUME:1");
|
|
140
|
-
assert.equal(decision.decision, "RESUME");
|
|
141
|
-
assert.equal(decision.resumeId, "cli-resume-xyz");
|
|
142
|
-
});
|
|
143
|
-
|
|
144
|
-
// ---------------------------------------------------------------------------
|
|
145
|
-
// 5. route() — non-zero last_exit_code returns EPHEMERAL_REPLACE.
|
|
146
|
-
// ---------------------------------------------------------------------------
|
|
147
|
-
|
|
148
|
-
test("route — non-zero last_exit_code returns EPHEMERAL_REPLACE", async (t) => {
|
|
149
|
-
const path = tmpRegistryPath("-exitcode");
|
|
150
|
-
t.after(() => safeUnlink(path));
|
|
151
|
-
let clock = 3_000_000_000_000;
|
|
152
|
-
const router = await createRouter({
|
|
153
|
-
registryPath: path,
|
|
154
|
-
ttlSeconds: 600,
|
|
155
|
-
now: () => clock,
|
|
156
|
-
});
|
|
157
|
-
await router.touch("slack:DEXIT:1", {
|
|
158
|
-
claudeSessionId: "cli-exit",
|
|
159
|
-
model: "sonnet",
|
|
160
|
-
});
|
|
161
|
-
await router.recordExit("slack:DEXIT:1", 1);
|
|
162
|
-
clock += 5_000;
|
|
163
|
-
const decision = router.route("slack:DEXIT:1");
|
|
164
|
-
assert.equal(decision.decision, "EPHEMERAL_REPLACE");
|
|
165
|
-
assert.equal(decision.resumeId, null);
|
|
166
|
-
});
|
|
167
|
-
|
|
168
|
-
// ---------------------------------------------------------------------------
|
|
169
|
-
// 6. touch() — LRU cap enforced; oldest entry evicted on overflow.
|
|
170
|
-
// ---------------------------------------------------------------------------
|
|
171
|
-
|
|
172
|
-
test("touch — LRU cap evicts oldest when at capacity", async (t) => {
|
|
173
|
-
const path = tmpRegistryPath("-lru");
|
|
174
|
-
t.after(() => safeUnlink(path));
|
|
175
|
-
let clock = 4_000_000_000_000;
|
|
176
|
-
const router = await createRouter({
|
|
177
|
-
registryPath: path,
|
|
178
|
-
ttlSeconds: 3600,
|
|
179
|
-
maxLiveSessions: 3,
|
|
180
|
-
now: () => clock,
|
|
181
|
-
});
|
|
182
|
-
for (let i = 1; i <= 3; i++) {
|
|
183
|
-
await router.touch(`slack:DLRU${i}`, {
|
|
184
|
-
claudeSessionId: `cli-${i}`,
|
|
185
|
-
model: "sonnet",
|
|
186
|
-
});
|
|
187
|
-
clock += 1000;
|
|
188
|
-
}
|
|
189
|
-
// At cap. Add a fourth.
|
|
190
|
-
await router.touch("slack:DLRU4", {
|
|
191
|
-
claudeSessionId: "cli-4",
|
|
192
|
-
model: "sonnet",
|
|
193
|
-
});
|
|
194
|
-
const snap = router._readForTests();
|
|
195
|
-
assert.equal(snap.lru.length, 3, "lru should still hold 3 keys");
|
|
196
|
-
assert.ok(!("slack:DLRU1" in snap.sessions), "oldest (DLRU1) evicted");
|
|
197
|
-
assert.ok("slack:DLRU2" in snap.sessions);
|
|
198
|
-
assert.ok("slack:DLRU3" in snap.sessions);
|
|
199
|
-
assert.ok("slack:DLRU4" in snap.sessions);
|
|
200
|
-
});
|
|
201
|
-
|
|
202
|
-
// ---------------------------------------------------------------------------
|
|
203
|
-
// 7. recordExit() — non-zero code flips status to "killed".
|
|
204
|
-
// ---------------------------------------------------------------------------
|
|
205
|
-
|
|
206
|
-
test("recordExit — non-zero code flips status to killed", async (t) => {
|
|
207
|
-
const path = tmpRegistryPath("-killed");
|
|
208
|
-
t.after(() => safeUnlink(path));
|
|
209
|
-
const router = await createRouter({ registryPath: path });
|
|
210
|
-
await router.touch("slack:DKILL:1", {
|
|
211
|
-
claudeSessionId: "cli-kill",
|
|
212
|
-
model: "sonnet",
|
|
213
|
-
});
|
|
214
|
-
await router.recordExit("slack:DKILL:1", 137);
|
|
215
|
-
const snap = router._readForTests();
|
|
216
|
-
assert.equal(snap.sessions["slack:DKILL:1"].status, "killed");
|
|
217
|
-
assert.equal(snap.sessions["slack:DKILL:1"].last_exit_code, 137);
|
|
218
|
-
});
|
|
219
|
-
|
|
220
|
-
// ---------------------------------------------------------------------------
|
|
221
|
-
// 8. evictExpired() — removes only stale entries, returns count.
|
|
222
|
-
// ---------------------------------------------------------------------------
|
|
223
|
-
|
|
224
|
-
test("evictExpired — removes only stale entries and returns count", async (t) => {
|
|
225
|
-
const path = tmpRegistryPath("-evict");
|
|
226
|
-
t.after(() => safeUnlink(path));
|
|
227
|
-
let clock = 5_000_000_000_000;
|
|
228
|
-
const router = await createRouter({
|
|
229
|
-
registryPath: path,
|
|
230
|
-
ttlSeconds: 60,
|
|
231
|
-
now: () => clock,
|
|
232
|
-
});
|
|
233
|
-
await router.touch("slack:DEVICT:OLD", {
|
|
234
|
-
claudeSessionId: "cli-old",
|
|
235
|
-
model: "sonnet",
|
|
236
|
-
});
|
|
237
|
-
// Advance past TTL.
|
|
238
|
-
clock += 120_000;
|
|
239
|
-
await router.touch("slack:DEVICT:NEW", {
|
|
240
|
-
claudeSessionId: "cli-new",
|
|
241
|
-
model: "sonnet",
|
|
242
|
-
});
|
|
243
|
-
// Now: OLD is stale (last_used_at = clock - 120s, ttl = 60s),
|
|
244
|
-
// NEW is fresh.
|
|
245
|
-
const evicted = await router.evictExpired();
|
|
246
|
-
assert.equal(evicted, 1, "one entry should have been evicted");
|
|
247
|
-
const snap = router._readForTests();
|
|
248
|
-
assert.ok(!("slack:DEVICT:OLD" in snap.sessions));
|
|
249
|
-
assert.ok("slack:DEVICT:NEW" in snap.sessions);
|
|
250
|
-
});
|
|
251
|
-
|
|
252
|
-
// ---------------------------------------------------------------------------
|
|
253
|
-
// 9. Registry round-trip — touch two keys, re-create router, state restored.
|
|
254
|
-
// ---------------------------------------------------------------------------
|
|
255
|
-
|
|
256
|
-
test("registry round-trip — second router instance reads persisted state", async (t) => {
|
|
257
|
-
const path = tmpRegistryPath("-roundtrip");
|
|
258
|
-
t.after(() => safeUnlink(path));
|
|
259
|
-
let clock = 6_000_000_000_000;
|
|
260
|
-
|
|
261
|
-
const r1 = await createRouter({
|
|
262
|
-
registryPath: path,
|
|
263
|
-
ttlSeconds: 3600,
|
|
264
|
-
now: () => clock,
|
|
265
|
-
});
|
|
266
|
-
await r1.touch("gmail:thread-A", {
|
|
267
|
-
claudeSessionId: "cli-A",
|
|
268
|
-
model: "sonnet",
|
|
269
|
-
});
|
|
270
|
-
clock += 1000;
|
|
271
|
-
await r1.touch("gmail:thread-B", {
|
|
272
|
-
claudeSessionId: "cli-B",
|
|
273
|
-
model: "opus",
|
|
274
|
-
});
|
|
275
|
-
|
|
276
|
-
// Fresh router instance against the same path.
|
|
277
|
-
const r2 = await createRouter({
|
|
278
|
-
registryPath: path,
|
|
279
|
-
ttlSeconds: 3600,
|
|
280
|
-
now: () => clock,
|
|
281
|
-
});
|
|
282
|
-
const snap = r2._readForTests();
|
|
283
|
-
assert.equal(Object.keys(snap.sessions).length, 2);
|
|
284
|
-
assert.equal(snap.sessions["gmail:thread-A"].claude_session_id, "cli-A");
|
|
285
|
-
assert.equal(snap.sessions["gmail:thread-B"].claude_session_id, "cli-B");
|
|
286
|
-
assert.equal(snap.sessions["gmail:thread-B"].model, "opus");
|
|
287
|
-
// Both keys present in the LRU array.
|
|
288
|
-
assert.ok(snap.lru.includes("gmail:thread-A"));
|
|
289
|
-
assert.ok(snap.lru.includes("gmail:thread-B"));
|
|
290
|
-
|
|
291
|
-
// route() should return RESUME for the persisted entry.
|
|
292
|
-
const dec = r2.route("gmail:thread-A");
|
|
293
|
-
assert.equal(dec.decision, "RESUME");
|
|
294
|
-
assert.equal(dec.resumeId, "cli-A");
|
|
295
|
-
});
|
|
@@ -1,210 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* prompt-builder-preamble.test.mjs — the fallback preamble must not FABRICATE
|
|
3
|
-
* authority.
|
|
4
|
-
*
|
|
5
|
-
* WHAT BROKE
|
|
6
|
-
* `buildFallbackPreamble()` used to hardcode an autonomy grant:
|
|
7
|
-
* "<Agent> sends autonomously: all internal messages, external operational
|
|
8
|
-
* messages, candidate comms, follow-ups, calendar coordination"
|
|
9
|
-
* read from nowhere. It fires when CLAUDE.md is unreadable OR its extracted
|
|
10
|
-
* sections total <= 200 chars — i.e. on a freshly scaffolded repo that has
|
|
11
|
-
* never had autonomy bands set — while driving a real `claude --print` session
|
|
12
|
-
* that can send real messages. It also stated "You are <name>, <title> at
|
|
13
|
-
* <company>" with `company` interpolated unconditionally.
|
|
14
|
-
*
|
|
15
|
-
* WHAT THESE TESTS PIN
|
|
16
|
-
* 1. GROUNDED PATH — with a real config/agent.json + config/company.json + a
|
|
17
|
-
* GENERATED policies/action-classification.yaml, every band in the preamble
|
|
18
|
-
* comes from those files.
|
|
19
|
-
* 2. EMPTY PATH — with the scaffold's UNCONFIGURED config and no generated
|
|
20
|
-
* policy, the preamble asserts NO employer, NO autonomy grant, and says
|
|
21
|
-
* draft-not-send. Nothing is invented.
|
|
22
|
-
* 3. The hand-written TEMPLATE action-classification.yaml (which grants "fully
|
|
23
|
-
* autonomous executive operator" to every seat) is treated as ABSENT.
|
|
24
|
-
*
|
|
25
|
-
* Run: node --test scripts/daemon/prompt-builder-preamble.test.mjs
|
|
26
|
-
*/
|
|
27
|
-
|
|
28
|
-
"use strict";
|
|
29
|
-
|
|
30
|
-
import { test } from "node:test";
|
|
31
|
-
import assert from "node:assert/strict";
|
|
32
|
-
import { mkdtempSync, mkdirSync, writeFileSync, rmSync } from "node:fs";
|
|
33
|
-
import { tmpdir } from "node:os";
|
|
34
|
-
import { join } from "node:path";
|
|
35
|
-
|
|
36
|
-
import {
|
|
37
|
-
buildFallbackPreamble,
|
|
38
|
-
loadSeatAutonomyPolicy,
|
|
39
|
-
renderAutonomySection,
|
|
40
|
-
} from "./prompt-builder.mjs";
|
|
41
|
-
|
|
42
|
-
/** A repo root with whatever files the case needs. */
|
|
43
|
-
function seat(files) {
|
|
44
|
-
const root = mkdtempSync(join(tmpdir(), "seat-"));
|
|
45
|
-
for (const [rel, body] of Object.entries(files)) {
|
|
46
|
-
const abs = join(root, rel);
|
|
47
|
-
mkdirSync(join(abs, ".."), { recursive: true });
|
|
48
|
-
writeFileSync(abs, body);
|
|
49
|
-
}
|
|
50
|
-
return root;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
const SCAFFOLD_AGENT = JSON.stringify({
|
|
54
|
-
firstName: "UNCONFIGURED",
|
|
55
|
-
lastName: "AGENT",
|
|
56
|
-
fullName: "Unconfigured Agent",
|
|
57
|
-
title: "Unconfigured Role",
|
|
58
|
-
company: "",
|
|
59
|
-
principal: { firstName: "", fullName: "", title: "" },
|
|
60
|
-
responsibilities: [],
|
|
61
|
-
operatingPrinciples: [],
|
|
62
|
-
});
|
|
63
|
-
const SCAFFOLD_COMPANY = JSON.stringify({ name: "UNCONFIGURED", description: "", products: [] });
|
|
64
|
-
|
|
65
|
-
const REAL_AGENT = JSON.stringify({
|
|
66
|
-
firstName: "Robin",
|
|
67
|
-
lastName: "Okafor",
|
|
68
|
-
fullName: "Robin Okafor",
|
|
69
|
-
title: "VP Operations",
|
|
70
|
-
altitude: "vp",
|
|
71
|
-
company: "",
|
|
72
|
-
principal: { firstName: "Dana", fullName: "Dana Lee", title: "CEO" },
|
|
73
|
-
responsibilities: ["Run the ops cadence"],
|
|
74
|
-
operatingPrinciples: ["Close the loop on every commitment"],
|
|
75
|
-
});
|
|
76
|
-
const REAL_COMPANY = JSON.stringify({ name: "Meridian Freight", description: "Regional logistics." });
|
|
77
|
-
|
|
78
|
-
/** What scripts/setup/generate-autonomy.mjs writes — note archetype + delegation_breadth. */
|
|
79
|
-
const GENERATED_POLICY = `
|
|
80
|
-
archetype: operations@vp
|
|
81
|
-
autonomy_default: execute
|
|
82
|
-
delegation_breadth: moderate
|
|
83
|
-
action_levels:
|
|
84
|
-
execute_autonomous:
|
|
85
|
-
examples:
|
|
86
|
-
- Reschedule internal meetings
|
|
87
|
-
approval_preferred:
|
|
88
|
-
examples:
|
|
89
|
-
- Send a quote to a customer
|
|
90
|
-
escalate:
|
|
91
|
-
examples:
|
|
92
|
-
- Sign a supplier contract
|
|
93
|
-
escalate_to:
|
|
94
|
-
- principal
|
|
95
|
-
decision_rights:
|
|
96
|
-
- id: dr-1
|
|
97
|
-
scope: Carrier selection
|
|
98
|
-
authority: decide
|
|
99
|
-
`;
|
|
100
|
-
|
|
101
|
-
/** The hand-written framework TEMPLATE — no archetype, no delegation_breadth. */
|
|
102
|
-
const TEMPLATE_POLICY = `
|
|
103
|
-
action_levels:
|
|
104
|
-
execute_autonomous:
|
|
105
|
-
description: Operational actions the agent executes on its own authority.
|
|
106
|
-
examples:
|
|
107
|
-
- Send emails in the agent's voice to any recipient
|
|
108
|
-
- Send candidate communications (outreach, scheduling, offers, rejections)
|
|
109
|
-
`;
|
|
110
|
-
|
|
111
|
-
// ── 1. GROUNDED PATH ────────────────────────────────────────────────────────
|
|
112
|
-
|
|
113
|
-
test("grounded: identity, employer and every autonomy band come from the real files", () => {
|
|
114
|
-
const root = seat({
|
|
115
|
-
"config/agent.json": REAL_AGENT,
|
|
116
|
-
"config/company.json": REAL_COMPANY,
|
|
117
|
-
"policies/action-classification.yaml": GENERATED_POLICY,
|
|
118
|
-
});
|
|
119
|
-
const out = buildFallbackPreamble(root);
|
|
120
|
-
|
|
121
|
-
assert.match(out, /You are Robin Okafor, VP Operations at Meridian Freight\./);
|
|
122
|
-
assert.match(out, /You report to Dana Lee, CEO\./);
|
|
123
|
-
assert.match(out, /Close the loop on every commitment/, "operating principles come from config");
|
|
124
|
-
|
|
125
|
-
assert.match(out, /generated for archetype operations@vp/);
|
|
126
|
-
assert.match(out, /You act on your own authority: Reschedule internal meetings/);
|
|
127
|
-
assert.match(out, /You get agreement first: Send a quote to a customer/);
|
|
128
|
-
assert.match(out, /You escalate and do NOT act alone: Sign a supplier contract/);
|
|
129
|
-
assert.match(out, /Escalate to: principal/);
|
|
130
|
-
assert.match(out, /Decision right — Carrier selection \(decide\)/);
|
|
131
|
-
|
|
132
|
-
// The fabricated grant is gone, verbatim.
|
|
133
|
-
assert.doesNotMatch(out, /sends autonomously/i);
|
|
134
|
-
assert.doesNotMatch(out, /all internal messages, external operational messages/i);
|
|
135
|
-
assert.doesNotMatch(out, /Full autonomy, full accountability/i);
|
|
136
|
-
|
|
137
|
-
rmSync(root, { recursive: true, force: true });
|
|
138
|
-
});
|
|
139
|
-
|
|
140
|
-
// ── 2. EMPTY PATH — nothing configured means nothing asserted ────────────────
|
|
141
|
-
|
|
142
|
-
test("empty: an unconfigured seat gets NO employer and NO autonomy grant", () => {
|
|
143
|
-
const root = seat({
|
|
144
|
-
"config/agent.json": SCAFFOLD_AGENT,
|
|
145
|
-
"config/company.json": SCAFFOLD_COMPANY,
|
|
146
|
-
});
|
|
147
|
-
const out = buildFallbackPreamble(root);
|
|
148
|
-
|
|
149
|
-
// No invented employer anywhere — including the scaffold's own sentinel.
|
|
150
|
-
assert.doesNotMatch(out, /UNCONFIGURED/);
|
|
151
|
-
assert.doesNotMatch(out, / at \./, "no dangling 'at <blank>' clause");
|
|
152
|
-
|
|
153
|
-
// No grant of any kind.
|
|
154
|
-
assert.doesNotMatch(out, /sends autonomously/i);
|
|
155
|
-
assert.doesNotMatch(out, /You act on your own authority/);
|
|
156
|
-
assert.doesNotMatch(out, /escalates only/i);
|
|
157
|
-
|
|
158
|
-
// Fail closed, and say why.
|
|
159
|
-
assert.match(out, /NO AUTONOMY POLICY IS CONFIGURED FOR THIS SEAT/);
|
|
160
|
-
assert.match(out, /Draft, do not send/);
|
|
161
|
-
assert.match(out, /Nothing here grants that\./);
|
|
162
|
-
|
|
163
|
-
rmSync(root, { recursive: true, force: true });
|
|
164
|
-
});
|
|
165
|
-
|
|
166
|
-
test("empty: a repo with NO config at all still produces a safe, non-fabricated preamble", () => {
|
|
167
|
-
const root = seat({});
|
|
168
|
-
const out = buildFallbackPreamble(root);
|
|
169
|
-
assert.doesNotMatch(out, /You are .* at /, "no identity is asserted");
|
|
170
|
-
assert.match(out, /NO AUTONOMY POLICY IS CONFIGURED/);
|
|
171
|
-
assert.match(out, /Draft, do not send/);
|
|
172
|
-
rmSync(root, { recursive: true, force: true });
|
|
173
|
-
});
|
|
174
|
-
|
|
175
|
-
// ── 3. The template is NOT ground truth ─────────────────────────────────────
|
|
176
|
-
|
|
177
|
-
test("the framework TEMPLATE action-classification.yaml is treated as ABSENT", () => {
|
|
178
|
-
const root = seat({
|
|
179
|
-
"config/agent.json": REAL_AGENT,
|
|
180
|
-
"config/company.json": REAL_COMPANY,
|
|
181
|
-
"policies/action-classification.yaml": TEMPLATE_POLICY,
|
|
182
|
-
});
|
|
183
|
-
assert.equal(loadSeatAutonomyPolicy(root), null, "no archetype/delegation_breadth ⇒ not this seat's policy");
|
|
184
|
-
|
|
185
|
-
const out = buildFallbackPreamble(root);
|
|
186
|
-
assert.match(out, /NO AUTONOMY POLICY IS CONFIGURED FOR THIS SEAT/);
|
|
187
|
-
assert.doesNotMatch(
|
|
188
|
-
out,
|
|
189
|
-
/Send candidate communications/,
|
|
190
|
-
"the template's maximal grant must never reach a prompt",
|
|
191
|
-
);
|
|
192
|
-
rmSync(root, { recursive: true, force: true });
|
|
193
|
-
});
|
|
194
|
-
|
|
195
|
-
test("a GENERATED policy whose bands are all empty grants nothing", () => {
|
|
196
|
-
const section = renderAutonomySection({
|
|
197
|
-
archetype: "ops@vp",
|
|
198
|
-
delegation_breadth: "narrow",
|
|
199
|
-
action_levels: { execute_autonomous: { examples: [] }, approval_preferred: {}, escalate: {} },
|
|
200
|
-
decision_rights: [],
|
|
201
|
-
});
|
|
202
|
-
assert.match(section, /declares no bands/);
|
|
203
|
-
assert.match(section, /draft, do not send/i);
|
|
204
|
-
});
|
|
205
|
-
|
|
206
|
-
test("loadSeatAutonomyPolicy never throws on malformed YAML", () => {
|
|
207
|
-
const root = seat({ "policies/action-classification.yaml": "::: not: [valid" });
|
|
208
|
-
assert.equal(loadSeatAutonomyPolicy(root), null);
|
|
209
|
-
rmSync(root, { recursive: true, force: true });
|
|
210
|
-
});
|