@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
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
|
|
19
19
|
import { resolve, join } from "path";
|
|
20
20
|
import { readdirSync, readFileSync, renameSync, mkdirSync, appendFileSync, writeFileSync, unlinkSync } from "fs";
|
|
21
|
+
import { createRequire } from "module";
|
|
21
22
|
|
|
22
23
|
// Load .env before anything else. dotenv is present on every agent machine but
|
|
23
24
|
// is not a packaged dependency, so we import it dynamically + best-effort: this
|
|
@@ -57,7 +58,14 @@ import { makeInboxScanPoller } from "../poller/inbox-scan-poller.mjs";
|
|
|
57
58
|
import { existsSync as _existsSync } from "fs";
|
|
58
59
|
import { isPriorityItem } from "../poller/utils.mjs";
|
|
59
60
|
import { classifyItem, isDirectedAtAgent } from "./classifier.mjs";
|
|
60
|
-
|
|
61
|
+
// A human's roll-call to the whole room ("everyone, status?"). Every addressed
|
|
62
|
+
// seat answers for itself — so it is EXEMPT from the single-winner election,
|
|
63
|
+
// gets a deterministic classification (no LLM triage), no holding ack, and a
|
|
64
|
+
// per-seat jitter so thirteen answers do not land in the same second.
|
|
65
|
+
import { isRollCallItem, rollCallJitterMs } from "../../lib/org/inbound/broadcast.mjs";
|
|
66
|
+
import { itemCollectiveIntent } from "../../lib/org/inbound/collective.mjs";
|
|
67
|
+
import { dispatch, getStatus, availableSlots, canDispatchBacklog, resetActiveSessions, yieldBacklogForReactive, linkActiveSessionBoard } from "./dispatcher.mjs";
|
|
68
|
+
import { boardItemRefFor } from "../../lib/session/current-work.mjs";
|
|
61
69
|
import { markReactiveStart, markReactiveEnd } from "../../lib/reactive-gate.mjs";
|
|
62
70
|
import { buildPrompt } from "./prompt-builder.mjs";
|
|
63
71
|
import { sendQuickResponse, sendHoldingMessage, isQuickReply } from "./responder.mjs";
|
|
@@ -93,7 +101,7 @@ const RUNG_MECHANISM = Object.fromEntries(RUNGS.map((r) => [r.id, r.mechanism]))
|
|
|
93
101
|
// work and land it in the org's shared, ACL'd store so the fleet's memory
|
|
94
102
|
// compounds. Fail-open + best-effort — never blocks or throws into the hot path;
|
|
95
103
|
// local SQLite/interaction logs remain the fail-open cache.
|
|
96
|
-
import { isEnabled as orgEnabled, loadOrgConfig, configFromAgent, electResponder, fetchSelfProfile, listMineResult as orgListMineResult } from "../../lib/org/client.mjs";
|
|
104
|
+
import { isEnabled as orgEnabled, loadOrgConfig, configFromAgent, electResponder, fetchSelfProfile, listMineResult as orgListMineResult, call as orgCall } from "../../lib/org/client.mjs";
|
|
97
105
|
// board.mine cache (design §3.5, WP-M6): the daemon writes state/org/board-mine.json
|
|
98
106
|
// every 5 min; the SessionStart primer, session_status and `maestro board mine`
|
|
99
107
|
// read it. The when/what is pure (lib/org/board-mine-cache.mjs); the I/O is here.
|
|
@@ -109,6 +117,9 @@ import { recordWorkStep as orgRecordWorkStep, cohortChannelId, cohortMessageId }
|
|
|
109
117
|
// it: a holding "let me look into it" must not go out when the reply itself can
|
|
110
118
|
// never be generated (the storm's other half — a promise the seat cannot keep).
|
|
111
119
|
import { resolveClaudeBin, isSafeExecutable } from "../../lib/claude-bin.mjs";
|
|
120
|
+
// W4 integration (conformance row 1): an engine-cohort seat never spawns claude, so its
|
|
121
|
+
// presence check is the engine entry point, not the claude binary.
|
|
122
|
+
import { readSeatEngine as _seatEngineForAvailability, seatGenerationAvailable as _seatGenerationAvailable, ENGINE_CLI_PATH as _ENGINE_CLI_PATH } from "../../lib/runtime/adapter.mjs";
|
|
112
123
|
import { boardPriority } from "./board-mirror.mjs";
|
|
113
124
|
// Observability spine (WS — diagnostics). mintTraceId + withTrace give each
|
|
114
125
|
// inbound item ONE trace_id that deep callees inherit via AsyncLocalStorage;
|
|
@@ -139,6 +150,7 @@ import { processOne } from "../../lib/execution/pipeline.mjs";
|
|
|
139
150
|
// gate is pure (lib/session/frontdoor.mjs); the assurance sweep reopens a
|
|
140
151
|
// session claim that was neither replied nor done within 20 min.
|
|
141
152
|
import { makeFrontDoorGate } from "../../lib/session/frontdoor.mjs";
|
|
153
|
+
import { CADENCE_REGISTRY } from "./cadence-handlers.mjs";
|
|
142
154
|
import { sweepSessionInboxForDaemon } from "../../lib/session/inbox-claims.mjs";
|
|
143
155
|
import { defaultEffects, scheduleToQueue } from "../../lib/execution/effects.mjs";
|
|
144
156
|
import { loadReactObligations } from "../../lib/execution/match.mjs";
|
|
@@ -459,13 +471,17 @@ export function enrichItem(item, service) {
|
|
|
459
471
|
* @returns {boolean}
|
|
460
472
|
*/
|
|
461
473
|
function claudeAvailable() {
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
474
|
+
let engine = "claude";
|
|
475
|
+
try { engine = _seatEngineForAvailability(AGENT_REPO_DIR); } catch { engine = "claude"; /* unreadable config: the default engine */ }
|
|
476
|
+
return _seatGenerationAvailable({
|
|
477
|
+
engine,
|
|
478
|
+
claudeBinOk: () => {
|
|
479
|
+
const bin = resolveClaudeBin();
|
|
480
|
+
if (!bin || bin === "claude") return false; // bare fallback = nothing found
|
|
481
|
+
return isSafeExecutable(bin);
|
|
482
|
+
},
|
|
483
|
+
engineCliPresent: () => _existsSync(_ENGINE_CLI_PATH),
|
|
484
|
+
});
|
|
469
485
|
}
|
|
470
486
|
|
|
471
487
|
/**
|
|
@@ -553,15 +569,113 @@ const FASTPATH_WORK_RE = /\b(analy[sz]e|create|build|writ(e|ing)|draft|prepare|r
|
|
|
553
569
|
// A DM that reads like a question / greeting / quick ask — safe to answer directly.
|
|
554
570
|
const FASTPATH_ASK_RE = /(\?|^(what|how|when|where|who|why|which|is |are |was |were |do |did |does |can |could |would |will |have |has |should |hey|hi |hello|thanks|thank you|yo |quick (q|question)|you (there|around|up)))/i;
|
|
555
571
|
|
|
572
|
+
/**
|
|
573
|
+
* Resolve the sender_privilege band handed to the classifier.
|
|
574
|
+
*
|
|
575
|
+
* A populated band from upstream wins. The inbox-item writer defaults a
|
|
576
|
+
* missing band to the string "unknown" (lib/channels/inbox-item.mjs), so
|
|
577
|
+
* "unknown" is treated as a miss, and only then does the CEO priority signal
|
|
578
|
+
* decide between "ceo" and "unknown".
|
|
579
|
+
*
|
|
580
|
+
* The previous inline form was
|
|
581
|
+
* `item.sender_privilege || item.priority_signals?.from_ceo ? "ceo" : "unknown"`
|
|
582
|
+
* which parses as `(a || b) ? "ceo" : "unknown"`: every non-empty band —
|
|
583
|
+
* including the default "unknown" — resolved to "ceo", and the classifier
|
|
584
|
+
* escalated the item as if the CEO had sent it.
|
|
585
|
+
*
|
|
586
|
+
* @param {object} item
|
|
587
|
+
* @returns {string}
|
|
588
|
+
*/
|
|
589
|
+
export function resolveSenderPrivilege(item) {
|
|
590
|
+
const band = item?.sender_privilege;
|
|
591
|
+
if (band && band !== "unknown") return band;
|
|
592
|
+
return item?.priority_signals?.from_ceo ? "ceo" : "unknown";
|
|
593
|
+
}
|
|
594
|
+
|
|
556
595
|
/**
|
|
557
596
|
* Decide whether a directed DM is a "plainly simple question" that can skip the
|
|
558
597
|
* LLM classifier and be answered directly. Returns a default classResult (the
|
|
559
598
|
* quick-reply shape) when it qualifies, else null (→ full classify path).
|
|
560
599
|
* Deliberately CONSERVATIVE: 1:1 DM, short, question-shaped, no work-verbs.
|
|
561
|
-
* `deps.fastPathClassify` can override (tests); `deps.disableFastPath`
|
|
600
|
+
* `deps.fastPathClassify` can override (tests); `deps.disableFastPath` (or
|
|
601
|
+
* DAEMON_DISABLE_FASTPATH=1) disables the DM heuristic — NOT the roll-call
|
|
602
|
+
* branch, which is deterministic and is checked before the switch.
|
|
603
|
+
*/
|
|
604
|
+
/**
|
|
605
|
+
* The identity string that makes this seat's roll-call jitter DIFFERENT from
|
|
606
|
+
* every other seat's: the org member slug (COHORT_AGENT_ID / config
|
|
607
|
+
* org.cohort.agentId), falling back to the configured first name. Empty only
|
|
608
|
+
* on an unconfigured seat, where every seat would share a key — harmless
|
|
609
|
+
* (they still wait), just not spread.
|
|
562
610
|
*/
|
|
611
|
+
function rollCallSeatKey(deps = {}) {
|
|
612
|
+
try {
|
|
613
|
+
const cfg = deps.orgCfg !== undefined ? deps.orgCfg : daemonOrgCfg();
|
|
614
|
+
return String(
|
|
615
|
+
process.env.COHORT_AGENT_ID ||
|
|
616
|
+
(cfg && cfg.org && cfg.org.cohort && cfg.org.cohort.agentId) ||
|
|
617
|
+
(loadAgent().firstName || ""),
|
|
618
|
+
).trim().toLowerCase();
|
|
619
|
+
} catch {
|
|
620
|
+
return "";
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
|
|
563
624
|
export function fastPathClassify(item, isDm, deps = {}) {
|
|
564
625
|
if (deps && typeof deps.fastPathClassify === "function") return deps.fastPathClassify(item, isDm);
|
|
626
|
+
// A COLLECTIVE ADDRESS is classified deterministically. The inbound join
|
|
627
|
+
// already proved the four broadcast conditions (human author, my room, not
|
|
628
|
+
// threaded, collective address) before it stamped the kind, and the answer
|
|
629
|
+
// shape is fixed (≤ 5 lines, for myself). Sending it through the LLM
|
|
630
|
+
// classifier would only add ~5-8 s and a chance of "archive" — the exact
|
|
631
|
+
// silence this surface exists to end, and one that would now also swallow a
|
|
632
|
+
// work ask carrying a deadline. `answerable + respond` routes it to the
|
|
633
|
+
// in-process quick-reply responder, which is where the collective block is
|
|
634
|
+
// injected.
|
|
635
|
+
//
|
|
636
|
+
// This branch sits ABOVE the fast-path kill switches on purpose. The
|
|
637
|
+
// switches (`deps.disableFastPath`, DAEMON_DISABLE_FASTPATH=1) exist to
|
|
638
|
+
// force a plain DM through the full LLM classify path when the heuristic
|
|
639
|
+
// regexes below misjudge one; a roll-call is not a heuristic call — its
|
|
640
|
+
// conditions were proven upstream — so routing it to the classifier under
|
|
641
|
+
// the switch would re-open the "archive" hole the branch closes. Pinned by
|
|
642
|
+
// the "ROLL-CALL: survives the fast-path kill switch" test.
|
|
643
|
+
if (isRollCallItem(item)) {
|
|
644
|
+
// WHICH KIND of collective address this is (`lib/org/inbound/collective.mjs`,
|
|
645
|
+
// the twin of hq's `work/collective.ts`). It does NOT change the ROUTE —
|
|
646
|
+
// every one of the three still takes the quick path — it changes the
|
|
647
|
+
// prompt block and the summary line.
|
|
648
|
+
//
|
|
649
|
+
// ── WHY A WORK BROADCAST DOES NOT SPAWN THIRTEEN SESSIONS ─────────────
|
|
650
|
+
//
|
|
651
|
+
// "Everyone, please review the binder by Friday" is real work for thirteen
|
|
652
|
+
// seats, and the tempting move is to dispatch a session per seat. That is
|
|
653
|
+
// thirteen fifteen-to-forty-minute Claude runs fired by one sentence, on
|
|
654
|
+
// thirteen machines, inside one poll tick — the storm shape this daemon has
|
|
655
|
+
// been burned by before, bought for an ask with a Friday deadline.
|
|
656
|
+
//
|
|
657
|
+
// The proportionate answer is the one the org already has: the quick reply
|
|
658
|
+
// says what this seat will do and by when, and the SAME quick path's
|
|
659
|
+
// `trackWorkStep` opens the seat's OWN board row (hq's gate accepts it now
|
|
660
|
+
// that the audience travels as a fact — see lib/org/work-ledger.mjs). The
|
|
661
|
+
// row is then pulled through the ordinary board-work lane, which is rate-
|
|
662
|
+
// controlled, resumable, and visible. A broadcast makes work VISIBLE AND
|
|
663
|
+
// OWNED here; it does not make thirteen machines start at once.
|
|
664
|
+
const intent = itemCollectiveIntent(item, isRollCallItem) || "roll-call";
|
|
665
|
+
const label = intent === "work" ? "Everyone-work" : intent === "ambient" ? "Everyone" : "Roll-call";
|
|
666
|
+
return {
|
|
667
|
+
priority: "normal",
|
|
668
|
+
action: "respond",
|
|
669
|
+
model: "sonnet",
|
|
670
|
+
summary: `${label}: ${String(item.content || item.subject || "").slice(0, 80)}`,
|
|
671
|
+
category: "action_required",
|
|
672
|
+
directed_at_agent: true,
|
|
673
|
+
answerable: true,
|
|
674
|
+
fast_path: true,
|
|
675
|
+
roll_call: true,
|
|
676
|
+
collective_intent: intent,
|
|
677
|
+
};
|
|
678
|
+
}
|
|
565
679
|
if (deps && deps.disableFastPath) return null;
|
|
566
680
|
if (process.env.DAEMON_DISABLE_FASTPATH === "1") return null;
|
|
567
681
|
if (!isDm) return null; // 1:1 DMs only (never group/ambient)
|
|
@@ -627,12 +741,12 @@ export async function answerItem(item, service, itemId, trace_id, deps = {}, rou
|
|
|
627
741
|
let classResult = fastPathClassify(item, isDm, deps);
|
|
628
742
|
if (classResult) {
|
|
629
743
|
recordClassification(true);
|
|
630
|
-
console.log(`[daemon] fast-path DM — skipped classify (${classResult.model}): ${classResult.summary}`);
|
|
744
|
+
console.log(`[daemon] fast-path ${classResult.roll_call ? "roll-call" : "DM"} — skipped classify (${classResult.model}): ${classResult.summary}`);
|
|
631
745
|
} else {
|
|
632
746
|
// Classify via Haiku CLI
|
|
633
747
|
classResult = await _classify({
|
|
634
748
|
sender: item.sender || "unknown",
|
|
635
|
-
sender_privilege: item
|
|
749
|
+
sender_privilege: resolveSenderPrivilege(item),
|
|
636
750
|
channel: item.channel || "unknown",
|
|
637
751
|
service,
|
|
638
752
|
content: item.content || item.subject || "",
|
|
@@ -681,7 +795,15 @@ export async function answerItem(item, service, itemId, trace_id, deps = {}, rou
|
|
|
681
795
|
// respond-to-all. The directed fast path is untouched, so an org outage can
|
|
682
796
|
// never drop a genuinely-directed DM. Slack keeps its own local gate below,
|
|
683
797
|
// where no org roster exists to elect against.
|
|
684
|
-
|
|
798
|
+
//
|
|
799
|
+
// A ROLL-CALL IS EXEMPT. The election elects ONE responder for an ambient
|
|
800
|
+
// message; a human's "everyone, status?" is the opposite — every addressed
|
|
801
|
+
// member owes its own line, and reducing it to a single winner would leave
|
|
802
|
+
// twelve seats silent on a message that named all thirteen. The inbound
|
|
803
|
+
// join proved the address (human author, my room, collective marker), so
|
|
804
|
+
// this is not ambient traffic and there is nothing to hold an election over.
|
|
805
|
+
const rollCall = isRollCallItem(item);
|
|
806
|
+
if (service === "cohort" && !isDm && !rollCall && !isDirectedAtAgent(item)) {
|
|
685
807
|
const verdict = await _electResponderVerdict(item);
|
|
686
808
|
if (!verdict.electedMe) {
|
|
687
809
|
console.log(`[daemon] Election: not elected to respond (${verdict.reason}) — staying silent for ${item.sender} in ${item.channel}`);
|
|
@@ -834,6 +956,26 @@ export async function answerItem(item, service, itemId, trace_id, deps = {}, rou
|
|
|
834
956
|
// path does not have, so a quick reply would be a wrong answer delivered
|
|
835
957
|
// fast. With no routing decision (an item the ladder could not place) this
|
|
836
958
|
// is exactly today's behaviour.
|
|
959
|
+
// ROLL-CALL RATE SHAPING. Thirteen seats received the same event in the
|
|
960
|
+
// same poll tick; without this their answers land in the same second and
|
|
961
|
+
// the room reads as a machine chorus. Each seat waits a deterministic slice
|
|
962
|
+
// of a 0-20 s window keyed on (seat, channel, message) — the same seat
|
|
963
|
+
// re-answering after a crash waits the same amount, and no two seats share
|
|
964
|
+
// a key. Applied to BOTH reply paths (quick reply here, session below).
|
|
965
|
+
let rollCallDelayMs = 0;
|
|
966
|
+
if (rollCall) {
|
|
967
|
+
rollCallDelayMs = rollCallJitterMs({
|
|
968
|
+
seat: rollCallSeatKey(deps),
|
|
969
|
+
channelId: item.channel_id || item.channel || "",
|
|
970
|
+
messageId: cohortMessageId(item) || item.id || "",
|
|
971
|
+
});
|
|
972
|
+
const _sleep = deps.sleep || ((ms) => new Promise((r) => setTimeout(r, ms)));
|
|
973
|
+
if (rollCallDelayMs > 0) {
|
|
974
|
+
console.log(`[daemon] roll-call: waiting ${rollCallDelayMs}ms before answering in ${item.channel} (rate shaping)`);
|
|
975
|
+
await _sleep(rollCallDelayMs);
|
|
976
|
+
}
|
|
977
|
+
}
|
|
978
|
+
|
|
837
979
|
if (_isQuickReply(classResult, routed)) {
|
|
838
980
|
console.log(`[daemon] Quick reply path for ${item.sender} (${classResult.model})`);
|
|
839
981
|
// Free the subscription NOW: evict any already-running backlog sessions
|
|
@@ -859,6 +1001,29 @@ export async function answerItem(item, service, itemId, trace_id, deps = {}, rou
|
|
|
859
1001
|
// created and closed in the same instant claims the ask was delivered by
|
|
860
1002
|
// a message; leaving it open is the truthful reading, and the stable
|
|
861
1003
|
// askKey means whatever really finishes it closes THIS row.
|
|
1004
|
+
//
|
|
1005
|
+
// THIS IS ALSO THE COLLECTIVE PATH. A work-shaped broadcast is answered
|
|
1006
|
+
// here and gets its row here — one per addressee, keyed on the calling
|
|
1007
|
+
// seat server-side, tagged into the channel exactly once across all of
|
|
1008
|
+
// them. A roll-call reaches the same call and is refused by name
|
|
1009
|
+
// (`collective-roll-call`), which is the gate working, not a miss.
|
|
1010
|
+
//
|
|
1011
|
+
// ── AND IT IS ONLY ONE OF THE TWO PLANES. SAY SO. ─────────────────
|
|
1012
|
+
//
|
|
1013
|
+
// Everything in this function is downstream of `answerItem`, which is
|
|
1014
|
+
// only ever reached through `pollService` — and `pollService` returns 0
|
|
1015
|
+
// at its third line (`if (!fd.dispatch) return 0`) whenever
|
|
1016
|
+
// `makeFrontDoorGate` says a live main session owns this service's
|
|
1017
|
+
// inbox. That is the NORMAL state of a healthy seat, and it is the same
|
|
1018
|
+
// fork the 13-day board blackout hid behind: a test that calls
|
|
1019
|
+
// `answerItem` directly is green over a path production does not take.
|
|
1020
|
+
//
|
|
1021
|
+
// On a front-door seat the item never reaches here at all. It becomes
|
|
1022
|
+
// an `inbound` feed line (lib/session/feed-core.mjs), which is why that
|
|
1023
|
+
// line now carries `collective` — the session reads the same verdict
|
|
1024
|
+
// this branch computed and opens its own row per `inbound-triage`
|
|
1025
|
+
// §"Addressed to the whole room". Two planes, one classifier, and
|
|
1026
|
+
// neither of them silently inert.
|
|
862
1027
|
void trackWorkStep({
|
|
863
1028
|
item,
|
|
864
1029
|
stage: "accepted",
|
|
@@ -1038,7 +1203,9 @@ export async function answerItem(item, service, itemId, trace_id, deps = {}, rou
|
|
|
1038
1203
|
// (the old board mirror) came to exist. Chained rather than fired in
|
|
1039
1204
|
// parallel because `triage` is not terminal: an `accepted` that commits
|
|
1040
1205
|
// AFTER a `working` drags the row back out of `running` for good.
|
|
1041
|
-
|
|
1206
|
+
const _trackWorkStep = deps.trackWorkStep || trackWorkStep;
|
|
1207
|
+
const _linkSessionToBoardRow = deps.linkSessionToBoardRow || linkSessionToBoardRow;
|
|
1208
|
+
void _trackWorkStep({
|
|
1042
1209
|
item,
|
|
1043
1210
|
stage: "accepted",
|
|
1044
1211
|
deferred: true,
|
|
@@ -1046,13 +1213,23 @@ export async function answerItem(item, service, itemId, trace_id, deps = {}, rou
|
|
|
1046
1213
|
note: classResult && classResult.summary ? `Picked this up. ${classResult.summary}` : "Picked this up — starting work now.",
|
|
1047
1214
|
source: { service, trace_id, classified: String(classResult && classResult.action) },
|
|
1048
1215
|
}).then(() =>
|
|
1049
|
-
|
|
1216
|
+
_trackWorkStep({
|
|
1050
1217
|
item,
|
|
1051
1218
|
stage: "working",
|
|
1052
1219
|
deferred: true,
|
|
1053
1220
|
source: { service, trace_id },
|
|
1054
1221
|
}),
|
|
1055
|
-
)
|
|
1222
|
+
).then((res) => {
|
|
1223
|
+
// THE LINK BACK. hq's row id reaches the live session's active.json entry,
|
|
1224
|
+
// so a "what are you doing right now?" reply can cite the board item a
|
|
1225
|
+
// person could open — instead of a session that knows it is working and
|
|
1226
|
+
// a board that knows what on, with nothing joining the two.
|
|
1227
|
+
_linkSessionToBoardRow(item, res);
|
|
1228
|
+
}).catch((err) => {
|
|
1229
|
+
// The chain is fire-and-forget; a rejection here must be a log line, not
|
|
1230
|
+
// an unhandled rejection that takes the daemon down.
|
|
1231
|
+
console.warn(`[board] track chain failed: ${err && err.message}`);
|
|
1232
|
+
});
|
|
1056
1233
|
|
|
1057
1234
|
// Build prompt with holding message context and dispatch
|
|
1058
1235
|
const prompt = await buildPrompt(item, classResult, {
|
|
@@ -1796,6 +1973,92 @@ export function _resetDaemonOrgCfgCache() {
|
|
|
1796
1973
|
_cachedDaemonOrgCfg = undefined;
|
|
1797
1974
|
}
|
|
1798
1975
|
|
|
1976
|
+
/**
|
|
1977
|
+
* Startup guard: a cadence with a producer but no prompt file dead-letters on
|
|
1978
|
+
* every tick, identically, forever. A cadence resolves to `mode: escalate` /
|
|
1979
|
+
* `guarded` with a prompt path via config/.cadence-registry.json, or via the
|
|
1980
|
+
* "conventional" schedules/triggers/<id>.md that cadence-consumer.mjs's
|
|
1981
|
+
* getCadenceDef() falls back to for an unknown id; when that file is absent
|
|
1982
|
+
* the tick can never be rendered, and the DLQ refills — 42 times on the seat
|
|
1983
|
+
* this was written on (ib-2026-09-03-eight-cadences-fire-with-no-prompt-file).
|
|
1984
|
+
* Ported from that seat's fork 2026-09-22.
|
|
1985
|
+
*
|
|
1986
|
+
* CADENCE_REGISTRY (the hardcoded, code-reviewed map in cadence-handlers.mjs)
|
|
1987
|
+
* is deliberately EXCLUDED: getCadenceDef() checks it first and it wins, so an
|
|
1988
|
+
* entry there with no `prompt` (skill-curator, nightly-backup — inline handlers
|
|
1989
|
+
* or guards documented as "never escalates") is by design, not a defect.
|
|
1990
|
+
* Checking config/cadences.yaml directly would false-positive on exactly these,
|
|
1991
|
+
* since that compiled file still carries a `prompt:` for every id.
|
|
1992
|
+
*
|
|
1993
|
+
* Sources checked (both are inputs to what actually resolves at tick time):
|
|
1994
|
+
* - config/.cadence-registry.json — the compiled config-driven registry
|
|
1995
|
+
* getCadenceDef() reads once CADENCE_REGISTRY misses.
|
|
1996
|
+
* - config/plan.yaml — active SCHEDULE obligations feed resolveCadences()'s
|
|
1997
|
+
* obligations leg directly (lib/cadences.mjs), independent of the JSON
|
|
1998
|
+
* registry, which is how a cadence kept firing with no plist at all.
|
|
1999
|
+
*
|
|
2000
|
+
* Fail-open throughout: a read/parse failure logs a warning and is skipped,
|
|
2001
|
+
* never blocks daemon startup. Logs once, at boot; returns the ids so a test
|
|
2002
|
+
* (and doctor) can read the answer without the log.
|
|
2003
|
+
* @param {{agentRoot?:string, registry?:object}} [deps] test seams
|
|
2004
|
+
* @returns {string[]} cadence ids whose prompt file is missing
|
|
2005
|
+
*/
|
|
2006
|
+
export function assertCadencePromptsExist(deps = {}) {
|
|
2007
|
+
const root = deps.agentRoot || AGENT_REPO_DIR;
|
|
2008
|
+
const hardcoded = deps.registry || CADENCE_REGISTRY;
|
|
2009
|
+
const candidates = new Map(); // id -> prompt path (repo-relative)
|
|
2010
|
+
|
|
2011
|
+
try {
|
|
2012
|
+
const registryPath = join(root, "config", ".cadence-registry.json");
|
|
2013
|
+
if (_existsSync(registryPath)) {
|
|
2014
|
+
const registry = JSON.parse(readFileSync(registryPath, "utf-8")) || {};
|
|
2015
|
+
for (const [id, entry] of Object.entries(registry)) {
|
|
2016
|
+
if (hardcoded[id]) continue; // hardcoded handler wins — trust it
|
|
2017
|
+
candidates.set(id, (entry && entry.prompt) || `schedules/triggers/${id}.md`);
|
|
2018
|
+
}
|
|
2019
|
+
}
|
|
2020
|
+
} catch (err) {
|
|
2021
|
+
console.warn(`[daemon] cadence prompt assertion: could not read .cadence-registry.json (${err && err.message ? err.message : err})`);
|
|
2022
|
+
}
|
|
2023
|
+
|
|
2024
|
+
try {
|
|
2025
|
+
const planPath = join(root, "config", "plan.yaml");
|
|
2026
|
+
if (_existsSync(planPath)) {
|
|
2027
|
+
const plan = yamlLoadSync(readFileSync(planPath, "utf-8"));
|
|
2028
|
+
for (const ob of Array.isArray(plan && plan.obligations) ? plan.obligations : []) {
|
|
2029
|
+
if (ob.kind !== "SCHEDULE" || !ob.cadence_id) continue;
|
|
2030
|
+
if (ob.status && ob.status !== "active") continue;
|
|
2031
|
+
if (hardcoded[ob.cadence_id]) continue; // hardcoded handler wins — trust it
|
|
2032
|
+
candidates.set(ob.cadence_id, ob.prompt || `schedules/triggers/${ob.cadence_id}.md`);
|
|
2033
|
+
}
|
|
2034
|
+
}
|
|
2035
|
+
} catch (err) {
|
|
2036
|
+
console.warn(`[daemon] cadence prompt assertion: could not read plan.yaml (${err && err.message ? err.message : err})`);
|
|
2037
|
+
}
|
|
2038
|
+
|
|
2039
|
+
const missing = [];
|
|
2040
|
+
for (const [id, prompt] of candidates) {
|
|
2041
|
+
if (!_existsSync(join(root, prompt))) missing.push(id);
|
|
2042
|
+
}
|
|
2043
|
+
if (missing.length > 0) {
|
|
2044
|
+
console.warn(
|
|
2045
|
+
`[daemon] ${missing.length} cadence(s) have a producer but no prompt file — every tick will dead-letter: ${missing.sort().join(", ")}`
|
|
2046
|
+
);
|
|
2047
|
+
}
|
|
2048
|
+
return missing;
|
|
2049
|
+
}
|
|
2050
|
+
|
|
2051
|
+
let _yamlLoadSyncCache = null;
|
|
2052
|
+
function yamlLoadSync(text) {
|
|
2053
|
+
if (!_yamlLoadSyncCache) {
|
|
2054
|
+
// require() rather than a dynamic import keeps this assertion synchronous
|
|
2055
|
+
// so it can run inline at the top of main(), before the first poll —
|
|
2056
|
+
// js-yaml ships a CJS build so this resolves cleanly.
|
|
2057
|
+
_yamlLoadSyncCache = createRequire(import.meta.url)("js-yaml");
|
|
2058
|
+
}
|
|
2059
|
+
return _yamlLoadSyncCache.load(text);
|
|
2060
|
+
}
|
|
2061
|
+
|
|
1799
2062
|
/**
|
|
1800
2063
|
* Refresh state/org/board-mine.json from hq `board.mine` when the cache is
|
|
1801
2064
|
* 5 min old (or absent). Fail-open end to end: an un-enrolled seat writes
|
|
@@ -1987,6 +2250,87 @@ export async function trackWorkStep(a = {}) {
|
|
|
1987
2250
|
}
|
|
1988
2251
|
}
|
|
1989
2252
|
|
|
2253
|
+
/**
|
|
2254
|
+
* Join hq's board row to the daemon session working the same ask (dispatcher
|
|
2255
|
+
* `linkActiveSessionBoard`), so the current-work block names the item. Pure
|
|
2256
|
+
* over its arguments except for the one active.json rewrite the dispatcher
|
|
2257
|
+
* does; fail-open; `linkImpl` is the test seam.
|
|
2258
|
+
*
|
|
2259
|
+
* @param {object} item the inbox item the row was tracked for
|
|
2260
|
+
* @param {object} res the `working` trackWorkStep result
|
|
2261
|
+
* @param {{linkImpl?:Function}} [deps]
|
|
2262
|
+
* @returns {{linked:boolean, sessionId:string|null, parked:boolean}|null} null when there was nothing to link
|
|
2263
|
+
*/
|
|
2264
|
+
export function linkSessionToBoardRow(item, res, deps = {}) {
|
|
2265
|
+
try {
|
|
2266
|
+
if (!res || !res.tracked || !res.taskId) return null;
|
|
2267
|
+
const channelId = cohortChannelId(item);
|
|
2268
|
+
const messageId = cohortMessageId(item);
|
|
2269
|
+
if (!channelId || !messageId) return null;
|
|
2270
|
+
const impl = deps.linkImpl || linkActiveSessionBoard;
|
|
2271
|
+
const out = impl({ channelId, messageId }, { taskId: res.taskId, col: res.col || null });
|
|
2272
|
+
if (out && (out.linked || out.parked)) {
|
|
2273
|
+
console.log(`[board] task ${res.taskId} ${out.linked ? `linked to session ${out.sessionId}` : "parked until its session registers"}`);
|
|
2274
|
+
}
|
|
2275
|
+
return out || null;
|
|
2276
|
+
} catch (err) {
|
|
2277
|
+
console.warn(`[board] link to session failed: ${err && err.message}`);
|
|
2278
|
+
return null;
|
|
2279
|
+
}
|
|
2280
|
+
}
|
|
2281
|
+
|
|
2282
|
+
/**
|
|
2283
|
+
* WORK VISIBILITY for a BACKLOG session that is for a BOARD ITEM. An inbox ask
|
|
2284
|
+
* gets accepted→working→done/failed through `trackWorkStep` (board.track,
|
|
2285
|
+
* keyed on the message). A backlog row the execution ladder queued for a
|
|
2286
|
+
* `task_assigned` / `task_comment` event is about an item that ALREADY EXISTS
|
|
2287
|
+
* on a board and was assigned to this agent — `board.track` has no message to
|
|
2288
|
+
* key on and `board.claim` refuses an item that is already assigned. What the
|
|
2289
|
+
* item's thread CAN carry is a comment: one when the session starts on it,
|
|
2290
|
+
* one when the session ends, each naming the session ref so the row and the
|
|
2291
|
+
* session log join. `board.comment` dedupes nothing server-side, so the
|
|
2292
|
+
* idempotency key is (item, stage, session).
|
|
2293
|
+
*
|
|
2294
|
+
* Fire-and-forget, fail-open, never silent — same contract as trackWorkStep.
|
|
2295
|
+
*
|
|
2296
|
+
* @param {object} a - { item (queue row), stage: "working"|"done"|"failed", sessionRef, note?, cfg?, callImpl? }
|
|
2297
|
+
* @returns {Promise<{tracked:boolean, reason:string, itemId?:string|null}>}
|
|
2298
|
+
*/
|
|
2299
|
+
export async function trackBoardItemStep(a = {}) {
|
|
2300
|
+
try {
|
|
2301
|
+
const ref = boardItemRefFor(a.item);
|
|
2302
|
+
if (!ref) return { tracked: false, reason: "not-a-board-item" };
|
|
2303
|
+
const stage = String(a.stage || "").trim();
|
|
2304
|
+
if (!["working", "done", "failed"].includes(stage)) return { tracked: false, reason: "bad-stage" };
|
|
2305
|
+
const cfg = a.cfg !== undefined ? a.cfg : daemonOrgCfg();
|
|
2306
|
+
if (!orgEnabled(cfg)) return { tracked: false, reason: "org-disabled" };
|
|
2307
|
+
const conn = configFromAgent(cfg);
|
|
2308
|
+
const sessionRef = String(a.sessionRef || "").trim() || "unknown";
|
|
2309
|
+
const lead =
|
|
2310
|
+
stage === "working" ? "Started on this." :
|
|
2311
|
+
stage === "done" ? "Finished a working session on this." :
|
|
2312
|
+
"A working session on this ended without completing; it is still owed.";
|
|
2313
|
+
const body = `${lead}${a.note ? ` ${String(a.note).slice(0, 2000)}` : ""} (session ${sessionRef})`;
|
|
2314
|
+
const impl = a.callImpl || orgCall;
|
|
2315
|
+
const res = await impl("board.comment", { itemId: ref.itemId, body }, {
|
|
2316
|
+
base: conn.base,
|
|
2317
|
+
token: conn.token,
|
|
2318
|
+
fetchImpl: a.fetchImpl,
|
|
2319
|
+
idempotencyKey: `board.comment:${ref.itemId}:${stage}:${sessionRef}`,
|
|
2320
|
+
});
|
|
2321
|
+
if (!res || res.error) {
|
|
2322
|
+
const reason = res && res.error ? `${res.error.code || "error"}: ${res.error.message || ""}` : "no-response";
|
|
2323
|
+
console.log(`[board] item ${ref.itemId} ${stage} not tracked: ${reason.slice(0, 200)}`);
|
|
2324
|
+
return { tracked: false, reason: reason.slice(0, 200), itemId: ref.itemId };
|
|
2325
|
+
}
|
|
2326
|
+
console.log(`[board] item ${ref.itemId} ${stage} — commented (session ${sessionRef})`);
|
|
2327
|
+
return { tracked: true, reason: "ok", itemId: ref.itemId };
|
|
2328
|
+
} catch (err) {
|
|
2329
|
+
console.warn(`[board] item track failed (${a.stage}): ${err && err.message}`);
|
|
2330
|
+
return { tracked: false, reason: "error" };
|
|
2331
|
+
}
|
|
2332
|
+
}
|
|
2333
|
+
|
|
1990
2334
|
// A distillation longer than this is truncated before it lands in the store —
|
|
1991
2335
|
// the shared memory holds a salient one-liner, never a transcript.
|
|
1992
2336
|
const SESSION_DISTILL_MAX_CHARS = 400;
|
|
@@ -2224,9 +2568,34 @@ export function recoverInFlight() {
|
|
|
2224
2568
|
// Backlog Sweep
|
|
2225
2569
|
// ---------------------------------------------------------------------------
|
|
2226
2570
|
|
|
2227
|
-
|
|
2228
|
-
|
|
2571
|
+
/**
|
|
2572
|
+
* Drain the backlog queues into sessions. `deps` are test seams only —
|
|
2573
|
+
* `{dispatch, buildPrompt, trackBoardItemStep, availableSlots}` — so the
|
|
2574
|
+
* board-item bookkeeping around a dispatch can be pinned without a child
|
|
2575
|
+
* process or an hq.
|
|
2576
|
+
*/
|
|
2577
|
+
export async function sweepBacklog(deps = {}) {
|
|
2578
|
+
const dispatchImpl = deps.dispatch || dispatch;
|
|
2579
|
+
const buildPromptImpl = deps.buildPrompt || buildPrompt;
|
|
2580
|
+
const trackBoardItem = deps.trackBoardItemStep || trackBoardItemStep;
|
|
2581
|
+
const slots = (deps.availableSlots || availableSlots)();
|
|
2229
2582
|
if (slots <= 0) return; // No capacity
|
|
2583
|
+
// FRONT DOOR (design §3.1, ported from a seat fork 2026-09-22): while the
|
|
2584
|
+
// main session is live it works the board itself (main-session skill, idle
|
|
2585
|
+
// loop). A daemon backlog session on the same rows holds the seat's pen and
|
|
2586
|
+
// voice beside it — four such sessions edited a pack and DM'd a dark seat on
|
|
2587
|
+
// one seat on 9 Sep 2026 while the front door was open. pollService already
|
|
2588
|
+
// steps back from the INBOX on this gate; the backlog sweep is the other
|
|
2589
|
+
// lane that spawns in the seat's name, and it must step back on the same
|
|
2590
|
+
// signal. Fail-open on a reader error (the gate's worst case is the old
|
|
2591
|
+
// behaviour: the daemon sweeps). `deps.frontDoorGate` is the test seam.
|
|
2592
|
+
{
|
|
2593
|
+
const fd = (deps.frontDoorGate || _frontDoorGate).check("cohort");
|
|
2594
|
+
if (!fd.dispatch) {
|
|
2595
|
+
if (fd.changed) console.log(`[daemon] front door live (${fd.reason}) — backlog belongs to the main session; sweep skipped`);
|
|
2596
|
+
return;
|
|
2597
|
+
}
|
|
2598
|
+
}
|
|
2230
2599
|
|
|
2231
2600
|
try {
|
|
2232
2601
|
const queueDir = join(AGENT_REPO_DIR, "state", "queues");
|
|
@@ -2352,7 +2721,7 @@ async function sweepBacklog() {
|
|
|
2352
2721
|
);
|
|
2353
2722
|
}
|
|
2354
2723
|
|
|
2355
|
-
const prompt = await
|
|
2724
|
+
const prompt = await buildPromptImpl(null, classResult, {
|
|
2356
2725
|
type: "backlog",
|
|
2357
2726
|
queueItem,
|
|
2358
2727
|
});
|
|
@@ -2361,8 +2730,26 @@ async function sweepBacklog() {
|
|
|
2361
2730
|
// they get the same outcome discipline. This onClose does NOT touch the
|
|
2362
2731
|
// backlog finalize path (the session updates its own queue item) — it is
|
|
2363
2732
|
// purely the outcome sweep, gated off by default.
|
|
2364
|
-
|
|
2733
|
+
// "Started on this" is said when a child has STARTED — from the
|
|
2734
|
+
// dispatcher's `onSpawn`, with the session id it minted — never at the
|
|
2735
|
+
// call site: `dispatch()` returns silently on rate-limit, governor
|
|
2736
|
+
// QUEUE/DEFER, the backlog cap and a denied claim, and a comment fired
|
|
2737
|
+
// here would have told the board about a session that did not exist
|
|
2738
|
+
// until some later sweep succeeded (if it did). Both comments carry the
|
|
2739
|
+
// real session id, so a retried row that genuinely starts again says so.
|
|
2740
|
+
let startRef = null;
|
|
2741
|
+
dispatchImpl(prompt, queueItem, classResult, "backlog", {
|
|
2742
|
+
onSpawn: ({ sessionId }) => {
|
|
2743
|
+
startRef = String(sessionId || "");
|
|
2744
|
+
void trackBoardItem({ item: queueItem, stage: "working", sessionRef: startRef, note: classResult.summary });
|
|
2745
|
+
},
|
|
2365
2746
|
onClose: ({ ok, sessionId, stdout }) => {
|
|
2747
|
+
void trackBoardItem({
|
|
2748
|
+
item: queueItem,
|
|
2749
|
+
stage: ok ? "done" : "failed",
|
|
2750
|
+
sessionRef: String(sessionId || startRef || "unknown"),
|
|
2751
|
+
note: ok ? sessionCompletionNote(stdout) : null,
|
|
2752
|
+
});
|
|
2366
2753
|
if (!ok) return;
|
|
2367
2754
|
Promise.resolve(sweepOutcomesForSession({ sessionId, stdout, item: queueItem, classResult }))
|
|
2368
2755
|
.catch(() => { /* fail-open */ });
|
|
@@ -2432,6 +2819,7 @@ async function main() {
|
|
|
2432
2819
|
// that doctor/operators read. subscribe + bump are both fail-open; an init
|
|
2433
2820
|
// failure here can never stop the daemon coming up.
|
|
2434
2821
|
registerHookSubscribers();
|
|
2822
|
+
assertCadencePromptsExist();
|
|
2435
2823
|
|
|
2436
2824
|
// Check for emergency stop
|
|
2437
2825
|
try {
|