@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
package/lib/org/tool-surface.mjs
CHANGED
|
@@ -105,6 +105,7 @@ import { WORK_STAGES } from "./work-ledger.mjs";
|
|
|
105
105
|
// sessions, open cadence handoffs and the cached board-mine count, read from
|
|
106
106
|
// local state so the tool answers with no token and no network.
|
|
107
107
|
import { readSessionStatus, statusLine } from "../session/status-summary.mjs";
|
|
108
|
+
import { currentWorkBlock } from "../session/current-work.mjs";
|
|
108
109
|
// The hq vocabularies the model must pick from (an out-of-vocabulary enum value
|
|
109
110
|
// is a BAD_REQUEST). Sourced from the wire contract so the tool table and the
|
|
110
111
|
// normaliser can never disagree about what hq accepts.
|
|
@@ -343,6 +344,23 @@ export const ORG_TOOLS = Object.freeze([
|
|
|
343
344
|
access: "read",
|
|
344
345
|
binding: { kind: "rpc", method: "messaging.history" },
|
|
345
346
|
},
|
|
347
|
+
{
|
|
348
|
+
name: "messaging_silences",
|
|
349
|
+
title: "Why a message got no reply",
|
|
350
|
+
description:
|
|
351
|
+
"WHY a person's message got NO reply from any AI colleague (messaging.silences): the routing " +
|
|
352
|
+
"verdicts recorded when a message was read as addressed to a person, or nobody was elected to " +
|
|
353
|
+
"answer. Pass messageId for ONE message, or channelId for a channel's recent silences. Use it " +
|
|
354
|
+
"when someone asks why nobody answered, before guessing.",
|
|
355
|
+
input_schema: S({
|
|
356
|
+
messageId: str("The message that went unanswered (one of messageId / channelId is required)."),
|
|
357
|
+
channelId: str("Channel id for its recent silences (one of messageId / channelId is required)."),
|
|
358
|
+
windowDays: num("Channel arm only: how far back to look (server default 14, max 90)."),
|
|
359
|
+
limit: num("Max rows (server default applies)."),
|
|
360
|
+
}),
|
|
361
|
+
access: "read",
|
|
362
|
+
binding: { kind: "rpc", method: "messaging.silences" },
|
|
363
|
+
},
|
|
346
364
|
{
|
|
347
365
|
name: "messaging_send",
|
|
348
366
|
title: "Send org message",
|
|
@@ -892,8 +910,19 @@ export const ORG_TOOLS = Object.freeze([
|
|
|
892
910
|
"needed. `live:false` with state 'stale' means the session exists but stopped beating; " +
|
|
893
911
|
"'absent' means it is not running (the daemon is then answering inbound itself). Use it " +
|
|
894
912
|
"before addressing the main session with SendMessage, or to answer \"is the front door " +
|
|
895
|
-
"up?\".
|
|
896
|
-
|
|
913
|
+
"up?\". `currentWork` is the bounded \"what am I doing right now\" block — the daemon's live " +
|
|
914
|
+
"sessions with the board item each is for, board items in progress, peers, and whether an " +
|
|
915
|
+
"interactive session is open on the box (existence only) — so a \"what are you doing?\" is " +
|
|
916
|
+
"answered from fact. It is SCOPED TO AN AUDIENCE: pass the asker (`audienceSender`, " +
|
|
917
|
+
"`audienceChannelId`, `audiencePrivate`) and a live session from another conversation is " +
|
|
918
|
+
"reported as existing only — no summary, no sender, no board id — because its summary is " +
|
|
919
|
+
"a restatement of someone else's message. With no audience given, nothing " +
|
|
920
|
+
"cross-conversation is disclosed. Never quote session names to a human — they are internal.",
|
|
921
|
+
input_schema: S({
|
|
922
|
+
audienceSender: str("The person the answer is for (their sender name as the inbound item carries it)."),
|
|
923
|
+
audienceChannelId: str("The channel (or DM) id the answer will be posted in."),
|
|
924
|
+
audiencePrivate: bool("true when that channel is a 1:1 DM with audienceSender — their own asks from it may then be described back to them."),
|
|
925
|
+
}),
|
|
897
926
|
access: "read",
|
|
898
927
|
binding: { kind: "local" },
|
|
899
928
|
},
|
|
@@ -3095,7 +3124,14 @@ async function screenOrgOutbound({ channel, recipient, text, agentRoot, screenIm
|
|
|
3095
3124
|
screen = null;
|
|
3096
3125
|
}
|
|
3097
3126
|
}
|
|
3098
|
-
if (typeof screen !== "function")
|
|
3127
|
+
if (typeof screen !== "function") {
|
|
3128
|
+
// The gate module would not load. This used to return allow SILENTLY — the
|
|
3129
|
+
// whole outbound perimeter off, for every org tool call, with nothing in the
|
|
3130
|
+
// log to say so. Now it is counted, and the one rule that needs no module
|
|
3131
|
+
// still runs.
|
|
3132
|
+
await countFailOpen({ channel, stage: "org-tool-import", error: "send-gate module unavailable", agentRoot, bumpImpl });
|
|
3133
|
+
return lastResortScaffold(text);
|
|
3134
|
+
}
|
|
3099
3135
|
try {
|
|
3100
3136
|
const verdict = await screen({
|
|
3101
3137
|
channel,
|
|
@@ -3108,19 +3144,57 @@ async function screenOrgOutbound({ channel, recipient, text, agentRoot, screenIm
|
|
|
3108
3144
|
}
|
|
3109
3145
|
return { allow: true, reason: "", text: verdict?.redactedText };
|
|
3110
3146
|
} catch (err) {
|
|
3111
|
-
|
|
3112
|
-
|
|
3113
|
-
|
|
3114
|
-
|
|
3115
|
-
|
|
3116
|
-
|
|
3117
|
-
|
|
3118
|
-
|
|
3119
|
-
|
|
3120
|
-
|
|
3147
|
+
await countFailOpen({ channel, stage: "org-tool", error: err && err.message, agentRoot, bumpImpl });
|
|
3148
|
+
return lastResortScaffold(text);
|
|
3149
|
+
}
|
|
3150
|
+
}
|
|
3151
|
+
|
|
3152
|
+
/** Count a send-gate fail-open. Never throws — a counter must not crash the path it observes. */
|
|
3153
|
+
async function countFailOpen({ channel, stage, error, agentRoot, bumpImpl }) {
|
|
3154
|
+
try {
|
|
3155
|
+
let bump = bumpImpl;
|
|
3156
|
+
if (typeof bump !== "function") ({ bump } = await import("../diagnostics/counters.mjs"));
|
|
3157
|
+
bump("channel.send_gate.fail_open", { channel, stage, error }, { agentRoot });
|
|
3158
|
+
} catch {
|
|
3159
|
+
/* a counter must never crash the path it observes */
|
|
3160
|
+
}
|
|
3161
|
+
}
|
|
3162
|
+
|
|
3163
|
+
/**
|
|
3164
|
+
* The fail-open FLOOR. Fail-open is the right posture for POLICY when the shared
|
|
3165
|
+
* gate is unreachable or threw — a transient fault must not gag the agent inside
|
|
3166
|
+
* its own workspace — and the wrong one for SCAFFOLD: the body that gets through
|
|
3167
|
+
* on the day the gate is broken is exactly the body this rule exists to stop, and
|
|
3168
|
+
* every recorded leak was internal.
|
|
3169
|
+
*
|
|
3170
|
+
* So this is the floor no fault lowers — the same literal set as SCAFFOLD_MARKERS
|
|
3171
|
+
* in lib/comms/send-gate.mjs, inlined because the branch it defends is the branch
|
|
3172
|
+
* where importing that module is what failed. `tool-surface.test.mjs` asserts the
|
|
3173
|
+
* two lists stay identical.
|
|
3174
|
+
*/
|
|
3175
|
+
export const SCAFFOLD_MARKERS_FLOOR = Object.freeze([
|
|
3176
|
+
"<user_message>", "</user_message>", "<thinking>", "</thinking>",
|
|
3177
|
+
"<system-reminder>", "</system-reminder>",
|
|
3178
|
+
"<function_calls>", "</function_calls>", "<function_results>", "</function_results>",
|
|
3179
|
+
"<invoke name=", "</invoke>", "[auto-reply:", "<<<UNTRUSTED_", "<END_UNTRUSTED_",
|
|
3180
|
+
]);
|
|
3181
|
+
|
|
3182
|
+
/** @returns {{allow:boolean, reason:string}} */
|
|
3183
|
+
export function lastResortScaffold(text) {
|
|
3184
|
+
const lc = String(text || "")
|
|
3185
|
+
.replace(/```[\s\S]*?```/g, " ")
|
|
3186
|
+
.replace(/```[\s\S]*$/g, " ")
|
|
3187
|
+
.replace(/(`+)[\s\S]*?\1/g, " ")
|
|
3188
|
+
.toLowerCase();
|
|
3189
|
+
for (const marker of SCAFFOLD_MARKERS_FLOOR) {
|
|
3190
|
+
if (lc.includes(marker.toLowerCase())) {
|
|
3191
|
+
return {
|
|
3192
|
+
allow: false,
|
|
3193
|
+
reason: `session-scaffold marker "${marker}" in the outbound body (send-gate unavailable — the scaffold floor still applies)`,
|
|
3194
|
+
};
|
|
3121
3195
|
}
|
|
3122
|
-
return { allow: true, reason: "" };
|
|
3123
3196
|
}
|
|
3197
|
+
return { allow: true, reason: "" };
|
|
3124
3198
|
}
|
|
3125
3199
|
|
|
3126
3200
|
/** Outbound screen inputs (recipient + text) for a screened tool/method. */
|
|
@@ -3355,8 +3429,22 @@ async function executeOrgToolInner(name, input = {}, o = {}) {
|
|
|
3355
3429
|
// Offline-capable local tools first.
|
|
3356
3430
|
if (name === "org_describe") return describeProtocol(input);
|
|
3357
3431
|
if (name === "session_status") {
|
|
3358
|
-
const
|
|
3359
|
-
|
|
3432
|
+
const now = Date.now();
|
|
3433
|
+
const status = readSessionStatus(cfg.agentRoot, { now });
|
|
3434
|
+
// The "what am I doing right now" block (lib/session/current-work): the
|
|
3435
|
+
// same fact the daemon's reply paths inject, so a front door answering
|
|
3436
|
+
// through tools and a daemon child answering from its prompt say the
|
|
3437
|
+
// same thing. Read-only; "" when there is nothing to say.
|
|
3438
|
+
let currentWork = "";
|
|
3439
|
+
try {
|
|
3440
|
+
const audience = {
|
|
3441
|
+
sender: input.audienceSender ? String(input.audienceSender) : "",
|
|
3442
|
+
channelId: input.audienceChannelId ? String(input.audienceChannelId) : "",
|
|
3443
|
+
isPrivate: input.audiencePrivate === true,
|
|
3444
|
+
};
|
|
3445
|
+
currentWork = await currentWorkBlock(cfg.agentRoot, { now, audience });
|
|
3446
|
+
} catch { currentWork = ""; }
|
|
3447
|
+
return okFrame({ ...status, agentRoot: cfg.agentRoot, summary: statusLine(status), currentWork });
|
|
3360
3448
|
}
|
|
3361
3449
|
|
|
3362
3450
|
// Every network tool needs a token; per §1.4 the server stays up and each
|
package/lib/org/ui-parity.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* lib/org/ui-parity.mjs — Agent UI-parity helpers (full human-action mirror).
|
|
3
3
|
*
|
|
4
|
-
* A 1:1 mirror of the human Cohort UI:
|
|
4
|
+
* A 1:1 mirror of the human Cohort UI: 374 named convenience wrappers over the
|
|
5
5
|
* /v1 RPC binding, one per method behind a HUMAN SURFACE — the things a person
|
|
6
6
|
* can do by clicking (charter, member, team, channel, persona, profile, sop,
|
|
7
7
|
* escalation, annotation, contact, file, memory, compact, notification,
|
|
@@ -3311,6 +3311,21 @@ export function messagingSearch(params, o = {}) {
|
|
|
3311
3311
|
return call("messaging.search", params || {}, o);
|
|
3312
3312
|
}
|
|
3313
3313
|
|
|
3314
|
+
/**
|
|
3315
|
+
* WHY a message in a room got no reply from the ambient responder
|
|
3316
|
+
* (messaging.silences). READ-ONLY. The `responder.silence` verdicts for ONE
|
|
3317
|
+
* message ({ messageId }) or a channel over a window ({ channelId,
|
|
3318
|
+
* windowDays? }); one of the two is required. Each row carries the operator
|
|
3319
|
+
* reason (human_mentioned | addressed_to_human | nobody_elected |
|
|
3320
|
+
* no_responders) and its text. The agent-plane twin of the "Unanswered
|
|
3321
|
+
* messages" panel on /settings/ai — same reader, same decoder.
|
|
3322
|
+
* @param {object} params - { channelId?, messageId?, windowDays?, limit? }
|
|
3323
|
+
* @param {object} o - { base, token, fetchImpl? }
|
|
3324
|
+
*/
|
|
3325
|
+
export function messagingSilences(params, o = {}) {
|
|
3326
|
+
return call("messaging.silences", params || {}, o);
|
|
3327
|
+
}
|
|
3328
|
+
|
|
3314
3329
|
/**
|
|
3315
3330
|
* The channels this seat can see (messaging.channels). READ-ONLY.
|
|
3316
3331
|
* @param {object} params - { kind?, limit? }
|
package/lib/org/work-ledger.mjs
CHANGED
|
@@ -22,11 +22,13 @@
|
|
|
22
22
|
* be two answers within a week. So there is one, and this is a client of it.
|
|
23
23
|
*
|
|
24
24
|
* IDENTITY, NOT DIGESTS. We send the ask's identity (`service`, `channelId`,
|
|
25
|
-
* `messageId
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
25
|
+
* `messageId`, and for a collective ask the ADDRESSEE, which the server takes
|
|
26
|
+
* from the calling SEAT rather than from the wire) and let the server derive
|
|
27
|
+
* the dedupe key. The hashing algorithm therefore exists in exactly one repo,
|
|
28
|
+
* and a daemon can never compute a different key than hq would for the same
|
|
29
|
+
* message — which is what makes re-delivery after a dead session find the SAME
|
|
30
|
+
* row instead of opening a second one, and what keeps thirteen seats answering
|
|
31
|
+
* one broadcast on thirteen rows instead of trampling one.
|
|
30
32
|
*
|
|
31
33
|
* FAIL-OPEN, NEVER SILENT. Every path returns a benign `{tracked:false, reason}`
|
|
32
34
|
* rather than throwing: a board write must never cost the user their reply. But
|
|
@@ -43,12 +45,15 @@
|
|
|
43
45
|
"use strict";
|
|
44
46
|
|
|
45
47
|
import { isEnabled as orgEnabled, configFromAgent, trackWork } from "./client.mjs";
|
|
48
|
+
import { isRollCallItem } from "./inbound/broadcast.mjs";
|
|
46
49
|
|
|
47
50
|
/** The stages the ledger understands. Mirrors hq's `WorkStage`. */
|
|
48
51
|
export const WORK_STAGES = ["accepted", "working", "blocked", "review", "done", "failed"];
|
|
49
52
|
|
|
50
53
|
const MAX_NOTE = 4000;
|
|
51
54
|
const MAX_TITLE = 200;
|
|
55
|
+
/** hq accepts 4000; we clip to the same 1000 `maestro board track --why` uses. */
|
|
56
|
+
const MAX_WHY = 1000;
|
|
52
57
|
const MAX_BODY = 20000;
|
|
53
58
|
|
|
54
59
|
/**
|
|
@@ -137,7 +142,10 @@ function clip(s, n) {
|
|
|
137
142
|
* @param {boolean} [a.deferred] could this NOT be answered inside the turn?
|
|
138
143
|
* (true for the session path — the same signal that fires the holding
|
|
139
144
|
* message; false for the quick-reply path). The server's gate reads it.
|
|
140
|
-
* @param {string} [a.title] row title
|
|
145
|
+
* @param {string} [a.title] row title, READ ONLY WHEN THE ROW OPENS (defaults
|
|
146
|
+
* server-side from the ask)
|
|
147
|
+
* @param {string} [a.why] why the row was opened → `why.reason`, also read
|
|
148
|
+
* only when the row opens (hq protects the key)
|
|
141
149
|
* @param {string} [a.priority] P0..P4, applied when the row is created
|
|
142
150
|
* @param {string} [a.detail] row detail, written once at open
|
|
143
151
|
* @param {string} [a.note] a progress comment for this step
|
|
@@ -179,12 +187,35 @@ export async function recordWorkStep(a = {}) {
|
|
|
179
187
|
// it only when the address is genuine, never guessed. A channel message
|
|
180
188
|
// the agent merely overheard is not assigned work.
|
|
181
189
|
directed: !!(a.item && a.item.priority_signals && a.item.priority_signals.mentions_agent),
|
|
190
|
+
// ── THE AUDIENCE, AS A FACT — NEVER AS A JUDGEMENT ──────────────────
|
|
191
|
+
//
|
|
192
|
+
// A broadcast is not an @mention and `mentions_agent` must never start
|
|
193
|
+
// claiming it is: that flag gates the daemon's own directed-message
|
|
194
|
+
// rules, and widening it to cover "everyone" would change what this seat
|
|
195
|
+
// REPLIES to, not just what it tracks. So the audience travels as its own
|
|
196
|
+
// structural fact — the inbound join already stamped the verdict reason
|
|
197
|
+
// `collective`, and `isRollCallItem` reads it back off the flattened item
|
|
198
|
+
// with no new plumbing.
|
|
199
|
+
//
|
|
200
|
+
// WHETHER a collective ask is WORK is emphatically not decided here.
|
|
201
|
+
// hq's `work/collective.ts` decides it, from the body we are sending,
|
|
202
|
+
// under the same gate that serves hq's own responder — so a roll-call
|
|
203
|
+
// ("give me an update") and a hedged aside ("take a look sometime") are
|
|
204
|
+
// refused by name, and a seat that got its local classifier wrong loses a
|
|
205
|
+
// well-shaped prompt rather than opening a row it should not have.
|
|
206
|
+
collective: isRollCallItem(a.item),
|
|
182
207
|
authorKind: a.item && a.item.author_kind === "AI_AGENT" ? "AI_AGENT" : "HUMAN",
|
|
183
208
|
deferred: a.deferred === true,
|
|
184
209
|
};
|
|
185
210
|
const requester = requesterMemberId(a.item);
|
|
186
211
|
if (requester) params.requesterId = requester;
|
|
187
212
|
if (a.title) params.title = clip(a.title, MAX_TITLE);
|
|
213
|
+
// `why` lands as the row's `why.reason` on hq (methods/board/track.ts →
|
|
214
|
+
// ledger.ts `if (!taskId)`). WRITTEN ONCE, AT OPEN: `reason` is in hq's
|
|
215
|
+
// PROTECTED_WHY_KEYS, so sending it on a later step is accepted and
|
|
216
|
+
// ignored. Same for `title`. Callers that want either must send it with
|
|
217
|
+
// the step that opens the row.
|
|
218
|
+
if (a.why) params.why = clip(a.why, MAX_WHY);
|
|
188
219
|
// The classifier's urgency, already mapped onto hq's P-scale by the caller
|
|
189
220
|
// (`board-mirror.mjs#boardPriority` — hq 400s the whole call on anything
|
|
190
221
|
// that is not P0..P4). Only ever read when the row is CREATED.
|
package/lib/rate-guard.mjs
CHANGED
|
@@ -127,6 +127,10 @@ export function readState(provider, deps) {
|
|
|
127
127
|
consecutive429: numOr0(raw.consecutive429),
|
|
128
128
|
lastBackoffMs: numOr0(raw.lastBackoffMs),
|
|
129
129
|
updatedAt: typeof raw.updatedAt === "string" ? raw.updatedAt : undefined,
|
|
130
|
+
// cohort holds only (recordCohortRefusal): what kind of hold this is.
|
|
131
|
+
...(typeof raw.kind === "string" ? { kind: raw.kind } : {}),
|
|
132
|
+
...(typeof raw.code === "string" ? { code: raw.code } : {}),
|
|
133
|
+
...(typeof raw.holdKind === "string" ? { holdKind: raw.holdKind } : {}),
|
|
130
134
|
};
|
|
131
135
|
} catch {
|
|
132
136
|
return { ...EMPTY_STATE };
|
|
@@ -204,6 +208,14 @@ export function recordSuccess(provider, deps) {
|
|
|
204
208
|
// Only rewrite if there is something to clear (avoids churning the file on
|
|
205
209
|
// every successful spawn).
|
|
206
210
|
const prev = readState(provider, deps);
|
|
211
|
+
// A cohort hold is not a breaker a clean run disproves. A window/spend
|
|
212
|
+
// (defer_until) or credits hold says "the gateway will refuse until T": a
|
|
213
|
+
// session admitted before the window ran out that exits cleanly afterwards
|
|
214
|
+
// proves nothing about T. Only a still-live `retry` hold (concurrency,
|
|
215
|
+
// overload) is cleared by success; the others stand until they expire.
|
|
216
|
+
if (sanitizeProvider(provider) === COHORT_BACKEND && prev.openUntil > clock(deps)() && prev.holdKind !== "retry") {
|
|
217
|
+
return prev;
|
|
218
|
+
}
|
|
207
219
|
if (prev.openUntil === 0 && prev.consecutive429 === 0 && prev.lastBackoffMs === 0) {
|
|
208
220
|
return { ...EMPTY_STATE };
|
|
209
221
|
}
|
|
@@ -309,4 +321,105 @@ export function recordUsageLimit(provider, resetAt, deps) {
|
|
|
309
321
|
return { openUntil: next.openUntil, resetAt: typeof resetAt === "number" && resetAt > now ? resetAt : null };
|
|
310
322
|
}
|
|
311
323
|
|
|
312
|
-
|
|
324
|
+
// ---------------------------------------------------------------------------
|
|
325
|
+
// cohort-llm refusals (design §4.6 / §8.3)
|
|
326
|
+
// ---------------------------------------------------------------------------
|
|
327
|
+
//
|
|
328
|
+
// A cohort-llm hard refusal is NOT a provider incident, and must never be read
|
|
329
|
+
// as one:
|
|
330
|
+
// - it is keyed by the backend "cohort" (state/rate-limits/cohort.json), never
|
|
331
|
+
// the Anthropic provider file, and it never touches the router's backend
|
|
332
|
+
// breaker (lib/model-router/health.mjs) — a seat that spent its weekly
|
|
333
|
+
// window has not found a broken backend;
|
|
334
|
+
// - a limit or spend refusal holds until the refusal's own resetsAt, not a
|
|
335
|
+
// decorrelated-jitter backoff (retrying a drained window only burns spawns);
|
|
336
|
+
// - the English stderr regexes above stay exactly as they were for the
|
|
337
|
+
// claude/anthropic backend. Call sites check `classifyCohortRefusal` FIRST
|
|
338
|
+
// on a cohort-routed run, because a cohort 429 body carries the words
|
|
339
|
+
// "rate_limit_error" and would otherwise trip the Anthropic breaker.
|
|
340
|
+
|
|
341
|
+
import { parseRefusalBody, refusalDisposition } from "./org/quota.mjs";
|
|
342
|
+
|
|
343
|
+
/** The backend key every cohort refusal is recorded under. */
|
|
344
|
+
export const COHORT_BACKEND = "cohort";
|
|
345
|
+
/** A credits hold re-checks after this long (a top-up can land any time). */
|
|
346
|
+
export const COHORT_CREDITS_RECHECK_MS = num(process.env.COHORT_CREDITS_RECHECK_MS, 10 * 60 * 1000);
|
|
347
|
+
|
|
348
|
+
/**
|
|
349
|
+
* Find a cohort refusal in a run's output: an engine JSON result, a gateway
|
|
350
|
+
* error body echoed by the claude binary, or a bare refusal object. Scans
|
|
351
|
+
* every balanced top-level JSON object in the text. null when there is none.
|
|
352
|
+
* @param {string} text
|
|
353
|
+
* @returns {object|null} a normalised refusal
|
|
354
|
+
*/
|
|
355
|
+
export function classifyCohortRefusal(text) {
|
|
356
|
+
if (!text || typeof text !== "string" || !/cohort_/.test(text)) return null;
|
|
357
|
+
for (const candidate of jsonObjects(text)) {
|
|
358
|
+
let o;
|
|
359
|
+
try { o = JSON.parse(candidate); } catch { continue; }
|
|
360
|
+
const direct = parseRefusalBody(o);
|
|
361
|
+
if (direct) return direct;
|
|
362
|
+
// An engine result: {is_error, error:{code,...}} or {cohort:{refusal}}.
|
|
363
|
+
const nested = o && typeof o === "object" ? (o.cohort && o.cohort.refusal) || o.refusal : null;
|
|
364
|
+
const r = nested ? parseRefusalBody({ cohort: nested }) : null;
|
|
365
|
+
if (r) return r;
|
|
366
|
+
}
|
|
367
|
+
return null;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
/** Yield balanced top-level {...} substrings (string-literal aware). */
|
|
371
|
+
function* jsonObjects(text) {
|
|
372
|
+
let depth = 0, start = -1, inStr = false, esc = false;
|
|
373
|
+
for (let i = 0; i < text.length; i++) {
|
|
374
|
+
const c = text[i];
|
|
375
|
+
if (inStr) {
|
|
376
|
+
if (esc) esc = false;
|
|
377
|
+
else if (c === "\\") esc = true;
|
|
378
|
+
else if (c === '"') inStr = false;
|
|
379
|
+
continue;
|
|
380
|
+
}
|
|
381
|
+
if (c === '"') { if (depth > 0) inStr = true; continue; }
|
|
382
|
+
if (c === "{") { if (depth === 0) start = i; depth++; }
|
|
383
|
+
else if (c === "}" && depth > 0) { depth--; if (depth === 0 && start >= 0) { yield text.slice(start, i + 1); start = -1; } }
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
/**
|
|
388
|
+
* Record a cohort refusal against the "cohort" backend per its disposition:
|
|
389
|
+
* defer_until hold until resetsAt (never shortens a longer hold)
|
|
390
|
+
* hold_for_credits hold for COHORT_CREDITS_RECHECK_MS, then look again
|
|
391
|
+
* retry hold for afterMs
|
|
392
|
+
* fail nothing recorded (not a timing problem)
|
|
393
|
+
* The Anthropic provider file and the backoff/consecutive counters are never
|
|
394
|
+
* touched.
|
|
395
|
+
*
|
|
396
|
+
* @param {object} refusal a normalised refusal (lib/org/quota.mjs)
|
|
397
|
+
* @param {object} [deps] { now?, stateDir?, agentRoot?, retryAfterMs? }
|
|
398
|
+
* @returns {{recorded:boolean, openUntil:number|null, disposition:object}}
|
|
399
|
+
*/
|
|
400
|
+
export function recordCohortRefusal(refusal, deps) {
|
|
401
|
+
const now = clock(deps)();
|
|
402
|
+
const disposition = refusalDisposition(refusal, { now, retryAfterMs: deps && deps.retryAfterMs });
|
|
403
|
+
let until = null;
|
|
404
|
+
if (disposition.kind === "defer_until") until = disposition.until;
|
|
405
|
+
else if (disposition.kind === "hold_for_credits") until = now + COHORT_CREDITS_RECHECK_MS;
|
|
406
|
+
else if (disposition.kind === "retry") until = now + disposition.afterMs;
|
|
407
|
+
if (until === null || until <= now) return { recorded: false, openUntil: null, disposition };
|
|
408
|
+
const prev = readState(COHORT_BACKEND, deps);
|
|
409
|
+
// The hold that ends LAST names the kind: a short retry recorded inside a
|
|
410
|
+
// live multi-day window hold must not relabel it as clearable.
|
|
411
|
+
const prevLive = prev.openUntil > now;
|
|
412
|
+
const keepPrev = prevLive && prev.openUntil >= until;
|
|
413
|
+
const next = {
|
|
414
|
+
openUntil: Math.max(prev.openUntil, until),
|
|
415
|
+
consecutive429: prev.consecutive429,
|
|
416
|
+
lastBackoffMs: prev.lastBackoffMs,
|
|
417
|
+
kind: "cohort_refusal",
|
|
418
|
+
code: keepPrev && prev.code ? prev.code : disposition.code,
|
|
419
|
+
holdKind: keepPrev ? (prev.holdKind || "defer_until") : disposition.kind,
|
|
420
|
+
};
|
|
421
|
+
writeState(COHORT_BACKEND, next, deps);
|
|
422
|
+
return { recorded: true, openUntil: next.openUntil, disposition };
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
export const _internals = { RATE_BASE_BACKOFF_MS, RATE_MAX_BACKOFF_MS, RATE_BACKOFF_FACTOR, RATE_USAGE_LIMIT_HOLD_MS, jsonObjects };
|
|
@@ -229,11 +229,44 @@ function reaperAgentRoot(explicit) {
|
|
|
229
229
|
* session either. Requires a FULL command line (`ps -eo … command`), not `comm`.
|
|
230
230
|
*/
|
|
231
231
|
export function isDaemonSpawnedClaude(line, agentRoot) {
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
232
|
+
const kind = classifyGovernedProcess(line, agentRoot);
|
|
233
|
+
return kind === "claude" || kind === "engine-run";
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/** The engine's headless entry: `…/lib/engine/cli.mjs run` (the adapter always emits `run -p`). */
|
|
237
|
+
const ENGINE_RUN_RE = /(^|\s)\S*\/lib\/engine\/cli\.mjs\s+run(\s|$)/;
|
|
238
|
+
/** A long-lived engine front door sets its process title to `cohort-session:<name>` (session-runtime/runner.mjs). */
|
|
239
|
+
const ENGINE_SESSION_RE = /(^|\s)cohort-session:\S/;
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* What kind of agent process a `ps` line is, as the governor sees it. Pure.
|
|
243
|
+
*
|
|
244
|
+
* "claude" a daemon-spawned `claude --print` lane for this agent
|
|
245
|
+
* "engine-run" a daemon-spawned engine lane for this agent: `node …/lib/engine/cli.mjs run -p …`
|
|
246
|
+
* (the engine twin of `claude --print`; the agent root is on its argv the same way,
|
|
247
|
+
* via `--mcp-config <AGENT_ROOT>/.mcp.json`)
|
|
248
|
+
* "engine-session" a long-lived engine front door or peer (`cohort-session:<name>`)
|
|
249
|
+
* null anything else — including a human's interactive Claude Code session
|
|
250
|
+
*
|
|
251
|
+
* The concurrency ceiling and the reaper count "claude" and "engine-run" only. An
|
|
252
|
+
* "engine-session" is the engine twin of an interactive Claude Code session (a front
|
|
253
|
+
* door `claude --name <first>-main`, or a peer): those are never `--print`, so the
|
|
254
|
+
* governor has never counted or reaped them, and a long-lived front door must not be
|
|
255
|
+
* killed at REAP_SESSION_MS. Its title also carries no agent root to scope by.
|
|
256
|
+
*
|
|
257
|
+
* @param {string} line a FULL `ps -eo …,command` line
|
|
258
|
+
* @param {string} [agentRoot]
|
|
259
|
+
* @returns {"claude"|"engine-run"|"engine-session"|null}
|
|
260
|
+
*/
|
|
261
|
+
export function classifyGovernedProcess(line, agentRoot) {
|
|
262
|
+
const l = String(line || "");
|
|
263
|
+
if (ENGINE_SESSION_RE.test(l)) return "engine-session";
|
|
264
|
+
if (/Claude\.app|Claude Helper|watchdog|grep|ps -eo/.test(l)) return null;
|
|
265
|
+
if (agentRoot && !l.includes(agentRoot)) return null; // scope to THIS agent's own sessions
|
|
266
|
+
if (ENGINE_RUN_RE.test(l) && /(^|\s)(-p|--print)(\s|$)/.test(l)) return "engine-run";
|
|
267
|
+
if (!/claude/i.test(l)) return null;
|
|
268
|
+
if (!/(^|\s)--print(\s|$)/.test(l)) return null; // interactive Claude Code is never --print
|
|
269
|
+
return "claude";
|
|
237
270
|
}
|
|
238
271
|
|
|
239
272
|
export function liveClaudeStats(opts = {}) {
|
|
@@ -248,7 +281,9 @@ export function liveClaudeStats(opts = {}) {
|
|
|
248
281
|
// FULL command (not comm) so we can require `--print` + the agent root — i.e.
|
|
249
282
|
// count ONLY this daemon's own sessions, never a human's interactive Claude
|
|
250
283
|
// Code session (which would otherwise inflate the count and get reaped).
|
|
251
|
-
const out =
|
|
284
|
+
const out = typeof opts.psImpl === "function"
|
|
285
|
+
? String(opts.psImpl())
|
|
286
|
+
: execFileSync("/bin/ps", ["-eo", "pid,etime,rss,command"], { encoding: "utf-8", timeout: 4000 });
|
|
252
287
|
let count = 0;
|
|
253
288
|
let rssKb = 0;
|
|
254
289
|
let stale = 0;
|