@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
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* lib/engine/tools/schedule-wakeup.mjs — ScheduleWakeup {delaySeconds, prompt, reason}.
|
|
3
|
+
*
|
|
4
|
+
* Asks the session to start a turn of its own later (session-runtime/wakeup.mjs):
|
|
5
|
+
* the delay is clamped to 60–3600 seconds, a newer wakeup replaces the pending
|
|
6
|
+
* one, and the pending wakeup is kept in the transcript so a resumed session
|
|
7
|
+
* arms it again. Exists only in a `cohort session` (`sessionOnly`); a mutating
|
|
8
|
+
* tool to the permission engine.
|
|
9
|
+
*
|
|
10
|
+
* @module lib/engine/tools/schedule-wakeup
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { WAKEUP_MAX_SECONDS, WAKEUP_MIN_SECONDS } from "../session-runtime/wakeup.mjs";
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* @param {{scheduler:ReturnType<typeof import('../session-runtime/wakeup.mjs').createWakeupScheduler>}} o
|
|
17
|
+
*/
|
|
18
|
+
export function createScheduleWakeupTool({ scheduler }) {
|
|
19
|
+
return {
|
|
20
|
+
name: "ScheduleWakeup",
|
|
21
|
+
readOnly: false,
|
|
22
|
+
sessionOnly: true,
|
|
23
|
+
description:
|
|
24
|
+
`Have this session start again by itself after a delay, with the given prompt as its message. The delay is between ${WAKEUP_MIN_SECONDS} and ${WAKEUP_MAX_SECONDS} seconds (values outside are moved to the nearest limit). ` +
|
|
25
|
+
"Only one wakeup is pending at a time: scheduling another replaces it. Messages and events that arrive earlier are handled as usual; the wakeup still comes. Use it to check back on something or to resume idle work.",
|
|
26
|
+
input_schema: {
|
|
27
|
+
type: "object",
|
|
28
|
+
properties: {
|
|
29
|
+
delaySeconds: { type: "number", description: `Seconds from now (${WAKEUP_MIN_SECONDS}–${WAKEUP_MAX_SECONDS}).` },
|
|
30
|
+
prompt: { type: "string", description: "The message this session receives when it wakes." },
|
|
31
|
+
reason: { type: "string", description: "Why, in a few words (shown when it fires)." },
|
|
32
|
+
},
|
|
33
|
+
required: ["delaySeconds", "prompt", "reason"],
|
|
34
|
+
},
|
|
35
|
+
/** @param {{delaySeconds:number, prompt:string, reason:string}} input */
|
|
36
|
+
async run(input) {
|
|
37
|
+
if (input.prompt.trim() === "") return { ok: false, error: "The prompt is empty." };
|
|
38
|
+
const w = scheduler.schedule({ delaySeconds: input.delaySeconds, prompt: input.prompt, reason: input.reason });
|
|
39
|
+
const notes = [];
|
|
40
|
+
if (w.clamped) notes.push(`the delay was set to ${w.seconds} s (allowed: ${WAKEUP_MIN_SECONDS}–${WAKEUP_MAX_SECONDS})`);
|
|
41
|
+
if (w.replaced) notes.push(`it replaces the wakeup that was due at ${new Date(w.replaced.dueAt).toISOString()}`);
|
|
42
|
+
return { ok: true, content: `Wakeup scheduled in ${w.seconds} s (at ${new Date(w.dueAt).toISOString()}).${notes.length ? ` Note: ${notes.join("; ")}.` : ""}` };
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* lib/engine/tools/schema.mjs — tool-input validation and repair (pure).
|
|
3
|
+
*
|
|
4
|
+
* A small JSON-Schema subset: exactly what the built-in tool schemas use
|
|
5
|
+
* (object / string / number / integer / boolean / array, `enum`, `required`,
|
|
6
|
+
* `minimum`/`maximum`). It is not a general validator and does not pretend to
|
|
7
|
+
* be one; adding a heavier dependency for it is out of bounds (maestro is
|
|
8
|
+
* zero-dep by architecture).
|
|
9
|
+
*
|
|
10
|
+
* Open-weight models get tool arguments nearly right more often than exactly
|
|
11
|
+
* right, so validation COERCES the harmless cases before it refuses:
|
|
12
|
+
* · a numeric string for a number/integer ("40" → 40)
|
|
13
|
+
* · "true"/"false" for a boolean
|
|
14
|
+
* Unknown properties are dropped, not refused — refusing them costs a turn and
|
|
15
|
+
* teaches nothing.
|
|
16
|
+
*
|
|
17
|
+
* `repairToolJson` is the other half: argument text that did not parse as JSON
|
|
18
|
+
* gets one conservative repair pass (code fences, trailing commas, unbalanced
|
|
19
|
+
* closing brackets) before the loop reports the error back to the model.
|
|
20
|
+
*
|
|
21
|
+
* @module lib/engine/tools/schema
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* @param {any} schema an object schema
|
|
26
|
+
* @param {unknown} input
|
|
27
|
+
* @returns {{ ok:true, value: Record<string, unknown> } | { ok:false, error: string }}
|
|
28
|
+
*/
|
|
29
|
+
export function validateInput(schema, input) {
|
|
30
|
+
if (!input || typeof input !== "object" || Array.isArray(input)) {
|
|
31
|
+
return { ok: false, error: "input must be a JSON object" };
|
|
32
|
+
}
|
|
33
|
+
const props = schema?.properties || {};
|
|
34
|
+
const out = {};
|
|
35
|
+
const problems = [];
|
|
36
|
+
for (const key of schema?.required || []) {
|
|
37
|
+
if (/** @type any */ (input)[key] === undefined || /** @type any */ (input)[key] === null) {
|
|
38
|
+
problems.push(`missing required parameter "${key}"`);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
for (const [key, raw] of Object.entries(input)) {
|
|
42
|
+
const spec = props[key];
|
|
43
|
+
if (!spec || raw === undefined || raw === null) continue;
|
|
44
|
+
const r = coerce(spec, raw);
|
|
45
|
+
if (!r.ok) problems.push(`parameter "${key}" ${r.error}`);
|
|
46
|
+
else out[key] = r.value;
|
|
47
|
+
}
|
|
48
|
+
if (problems.length > 0) return { ok: false, error: problems.join("; ") };
|
|
49
|
+
return { ok: true, value: out };
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** @param {any} spec @param {unknown} v @returns {{ok:true, value:unknown}|{ok:false, error:string}} */
|
|
53
|
+
function coerce(spec, v) {
|
|
54
|
+
switch (spec.type) {
|
|
55
|
+
case "string":
|
|
56
|
+
if (typeof v !== "string") return { ok: false, error: "must be a string" };
|
|
57
|
+
if (Array.isArray(spec.enum) && !spec.enum.includes(v)) {
|
|
58
|
+
return { ok: false, error: `must be one of ${spec.enum.map((e) => JSON.stringify(e)).join(", ")}` };
|
|
59
|
+
}
|
|
60
|
+
return { ok: true, value: v };
|
|
61
|
+
case "number":
|
|
62
|
+
case "integer": {
|
|
63
|
+
const n = typeof v === "number" ? v : typeof v === "string" && v.trim() !== "" ? Number(v) : NaN;
|
|
64
|
+
if (!Number.isFinite(n)) return { ok: false, error: `must be a ${spec.type}` };
|
|
65
|
+
if (spec.type === "integer" && !Number.isInteger(n)) return { ok: false, error: "must be an integer" };
|
|
66
|
+
if (typeof spec.minimum === "number" && n < spec.minimum) return { ok: false, error: `must be ≥ ${spec.minimum}` };
|
|
67
|
+
if (typeof spec.maximum === "number" && n > spec.maximum) return { ok: false, error: `must be ≤ ${spec.maximum}` };
|
|
68
|
+
return { ok: true, value: n };
|
|
69
|
+
}
|
|
70
|
+
case "boolean":
|
|
71
|
+
if (typeof v === "boolean") return { ok: true, value: v };
|
|
72
|
+
if (v === "true" || v === "false") return { ok: true, value: v === "true" };
|
|
73
|
+
return { ok: false, error: "must be a boolean" };
|
|
74
|
+
case "array":
|
|
75
|
+
if (!Array.isArray(v)) return { ok: false, error: "must be an array" };
|
|
76
|
+
return { ok: true, value: v };
|
|
77
|
+
case "object":
|
|
78
|
+
if (!v || typeof v !== "object" || Array.isArray(v)) return { ok: false, error: "must be an object" };
|
|
79
|
+
return { ok: true, value: v };
|
|
80
|
+
default:
|
|
81
|
+
return { ok: true, value: v };
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* One conservative repair pass over tool-argument text that failed to parse.
|
|
87
|
+
* Never guesses at content — only at framing.
|
|
88
|
+
*
|
|
89
|
+
* @param {string} raw
|
|
90
|
+
* @returns {{ ok:true, value: Record<string, unknown> } | { ok:false, error: string }}
|
|
91
|
+
*/
|
|
92
|
+
export function repairToolJson(raw) {
|
|
93
|
+
if (typeof raw !== "string" || raw.trim() === "") return { ok: false, error: "empty tool arguments" };
|
|
94
|
+
let s = raw.trim();
|
|
95
|
+
// A fenced block: ```json { … } ```
|
|
96
|
+
const fence = s.match(/^```[a-zA-Z]*\s*([\s\S]*?)\s*```$/);
|
|
97
|
+
if (fence) s = fence[1].trim();
|
|
98
|
+
// Leading prose before the object ("Here are the args: {…}").
|
|
99
|
+
const first = s.indexOf("{");
|
|
100
|
+
if (first > 0) s = s.slice(first);
|
|
101
|
+
// Trailing commas before a closer.
|
|
102
|
+
s = s.replace(/,\s*([}\]])/g, "$1");
|
|
103
|
+
const attempt = tryObject(s);
|
|
104
|
+
if (attempt) return { ok: true, value: attempt };
|
|
105
|
+
// Unbalanced closers (a truncated stream tail), outside strings only.
|
|
106
|
+
const closed = closeBrackets(s);
|
|
107
|
+
if (closed !== null) {
|
|
108
|
+
const again = tryObject(closed);
|
|
109
|
+
if (again) return { ok: true, value: again };
|
|
110
|
+
}
|
|
111
|
+
return { ok: false, error: "tool arguments are not valid JSON and could not be repaired" };
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/** @param {string} s */
|
|
115
|
+
function tryObject(s) {
|
|
116
|
+
try {
|
|
117
|
+
const v = JSON.parse(s);
|
|
118
|
+
return v && typeof v === "object" && !Array.isArray(v) ? v : null;
|
|
119
|
+
} catch {
|
|
120
|
+
return null; // not parseable yet; the caller tries the next repair
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/** @param {string} s @returns {string|null} */
|
|
125
|
+
function closeBrackets(s) {
|
|
126
|
+
const stack = [];
|
|
127
|
+
let inString = false;
|
|
128
|
+
let escaped = false;
|
|
129
|
+
for (const ch of s) {
|
|
130
|
+
if (inString) {
|
|
131
|
+
if (escaped) escaped = false;
|
|
132
|
+
else if (ch === "\\") escaped = true;
|
|
133
|
+
else if (ch === '"') inString = false;
|
|
134
|
+
continue;
|
|
135
|
+
}
|
|
136
|
+
if (ch === '"') inString = true;
|
|
137
|
+
else if (ch === "{" || ch === "[") stack.push(ch === "{" ? "}" : "]");
|
|
138
|
+
else if (ch === "}" || ch === "]") {
|
|
139
|
+
if (stack.pop() !== ch) return null;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
if (inString || stack.length === 0) return null;
|
|
143
|
+
return s + stack.reverse().join("");
|
|
144
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* lib/engine/tools/send-message.mjs — SendMessage {to, message, summary?}.
|
|
3
|
+
*
|
|
4
|
+
* Local peer messaging for `cohort session` (W4-A1): the message goes to the
|
|
5
|
+
* named session's control socket (session-runtime/registry.mjs finds it) and
|
|
6
|
+
* arrives there as a user turn tagged "[from <this session>] …" — at once when
|
|
7
|
+
* the recipient is idle ("delivered"), after its current work otherwise
|
|
8
|
+
* ("queued"). A name with no live session is "not_found".
|
|
9
|
+
*
|
|
10
|
+
* This is NOT an outbound org send: nothing leaves the machine, so the
|
|
11
|
+
* in-process send gate (hooks.mjs, `messaging_send` & co.) does not screen it.
|
|
12
|
+
* It is a mutating tool to the permission engine (an allow rule, bypass, or an
|
|
13
|
+
* interactive approval), and it exists only in a session, never in a subagent
|
|
14
|
+
* (`sessionOnly`).
|
|
15
|
+
*
|
|
16
|
+
* @module lib/engine/tools/send-message
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import { isValidSessionName } from "../session-runtime/registry.mjs";
|
|
20
|
+
import { sendControl } from "../session-runtime/socket.mjs";
|
|
21
|
+
|
|
22
|
+
export const MAX_MESSAGE_CHARS = 100_000;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* @param {{selfName:string, registry:{get:(name:string)=>any, list:()=>any[]}, send?:typeof sendControl}} o
|
|
26
|
+
*/
|
|
27
|
+
export function createSendMessageTool({ selfName, registry, send = sendControl }) {
|
|
28
|
+
const liveNames = () =>
|
|
29
|
+
registry
|
|
30
|
+
.list()
|
|
31
|
+
.map((e) => e.name)
|
|
32
|
+
.filter((n) => n !== selfName);
|
|
33
|
+
return {
|
|
34
|
+
name: "SendMessage",
|
|
35
|
+
readOnly: false,
|
|
36
|
+
sessionOnly: true,
|
|
37
|
+
description:
|
|
38
|
+
"Send a message to another agent session running on this machine, addressed by its session name (ListAgents shows who is running). " +
|
|
39
|
+
"The recipient gets it as a new message that starts with [from <your session name>]; if it is in the middle of work, the message waits until that work ends. " +
|
|
40
|
+
"The answer is one of: delivered (it is being worked on now), queued (it will be worked on next), not_found (no session by that name is running). " +
|
|
41
|
+
"Replies, if any, arrive later as messages from that session. Nothing is sent outside this machine.",
|
|
42
|
+
input_schema: {
|
|
43
|
+
type: "object",
|
|
44
|
+
properties: {
|
|
45
|
+
to: { type: "string", description: "The recipient's session name." },
|
|
46
|
+
message: { type: "string", description: "The full message. The recipient sees only this, so include what it needs." },
|
|
47
|
+
summary: { type: "string", description: "A few words describing the message, shown in the recipient's terminal." },
|
|
48
|
+
},
|
|
49
|
+
required: ["to", "message"],
|
|
50
|
+
},
|
|
51
|
+
/** @param {{to:string, message:string, summary?:string}} input */
|
|
52
|
+
async run(input) {
|
|
53
|
+
const to = input.to.trim();
|
|
54
|
+
if (!isValidSessionName(to)) return { ok: false, error: `"${input.to}" is not a session name.` };
|
|
55
|
+
if (to === selfName) return { ok: false, error: "That is this session's own name; a message to yourself is not sent." };
|
|
56
|
+
if (input.message.trim() === "") return { ok: false, error: "The message is empty." };
|
|
57
|
+
if (input.message.length > MAX_MESSAGE_CHARS) return { ok: false, error: `The message is longer than ${MAX_MESSAGE_CHARS} characters; shorten it or point to a file.` };
|
|
58
|
+
const notFound = () => {
|
|
59
|
+
const live = liveNames();
|
|
60
|
+
return { ok: /** @type {false} */ (false), error: `not_found: no session named "${to}" is running on this machine. Running: ${live.join(", ") || "none"}.` };
|
|
61
|
+
};
|
|
62
|
+
const entry = registry.get(to);
|
|
63
|
+
if (!entry) return notFound();
|
|
64
|
+
const r = await send(entry.socketPath, { type: "user", message: input.message, from: selfName, ...(input.summary ? { summary: input.summary } : {}) });
|
|
65
|
+
if (!r.ok) {
|
|
66
|
+
if (r.error.code === "not_running") return notFound();
|
|
67
|
+
return { ok: false, error: `The message to ${to} may not have arrived: ${r.error.message}.` };
|
|
68
|
+
}
|
|
69
|
+
const status = r.response?.status;
|
|
70
|
+
if (r.response?.ok && (status === "delivered" || status === "queued")) {
|
|
71
|
+
return { ok: true, content: JSON.stringify({ status, to }) };
|
|
72
|
+
}
|
|
73
|
+
if (status === "ended") return notFound();
|
|
74
|
+
return { ok: false, error: `${to} did not accept the message: ${r.response?.error ?? "no reason given"}.` };
|
|
75
|
+
},
|
|
76
|
+
};
|
|
77
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* lib/engine/tools/session.mjs — the tools that hold per-run state.
|
|
3
|
+
*
|
|
4
|
+
* The registry (tools/index.mjs) holds stateless built-ins. These are created
|
|
5
|
+
* per run, because they share state with it:
|
|
6
|
+
*
|
|
7
|
+
* NotebookEdit stateless, listed here so the registry's
|
|
8
|
+
* order (a prompt-cache prefix) stays put
|
|
9
|
+
* TodoWrite the run's task list
|
|
10
|
+
* Task, Agent, TaskOutput, TaskStop subagents (agents/runtime.mjs)
|
|
11
|
+
* BashOutput, KillShell background shells (bash-background.mjs)
|
|
12
|
+
*
|
|
13
|
+
* and the registry's Bash is replaced by the session Bash (persistent working
|
|
14
|
+
* directory, run_in_background). Order in the tool list: built-ins, Skill,
|
|
15
|
+
* then these in the order above, then MCP tools.
|
|
16
|
+
*
|
|
17
|
+
* `--tools` selection: a session tool is exposed when named; BashOutput and
|
|
18
|
+
* KillShell come with Bash, TaskOutput and TaskStop with Task or Agent.
|
|
19
|
+
*
|
|
20
|
+
* @module lib/engine/tools/session
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
import { createBashOutputTool, createKillShellTool, createSessionBash, createShellManager } from "./bash-background.mjs";
|
|
24
|
+
import { notebookEditTool } from "./notebook-edit.mjs";
|
|
25
|
+
import { createTodoWriteTool } from "./todo.mjs";
|
|
26
|
+
|
|
27
|
+
export const SESSION_TOOL_NAMES = Object.freeze(["NotebookEdit", "TodoWrite", "Task", "Agent", "TaskOutput", "TaskStop", "BashOutput", "KillShell", "Workflow", "WorkflowStatus"]);
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Which session tools a `--tools` list exposes (null = every one). Pure.
|
|
31
|
+
* @param {string[]|null} names
|
|
32
|
+
* @returns {Set<string>|null}
|
|
33
|
+
*/
|
|
34
|
+
export function wantedSessionTools(names) {
|
|
35
|
+
if (names == null) return null;
|
|
36
|
+
const set = new Set(names.filter((n) => SESSION_TOOL_NAMES.includes(n)));
|
|
37
|
+
if (names.includes("Bash")) {
|
|
38
|
+
set.add("BashOutput");
|
|
39
|
+
set.add("KillShell");
|
|
40
|
+
}
|
|
41
|
+
if (set.has("Task") || set.has("Agent")) {
|
|
42
|
+
set.add("TaskOutput");
|
|
43
|
+
set.add("TaskStop");
|
|
44
|
+
}
|
|
45
|
+
if (set.has("Workflow")) set.add("WorkflowStatus");
|
|
46
|
+
return set;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* @param {{cwd:string, env:NodeJS.ProcessEnv, spillDir:string, todoState:ReturnType<typeof import('./todo.mjs').createTodoState>,
|
|
51
|
+
* onShellExit?:(summary:any)=>void, envPassthrough?:string[]}} o `onShellExit`: a background shell ended on its own (CF-50);
|
|
52
|
+
* `envPassthrough`: extended-shape secrets background shells keep (CF-22)
|
|
53
|
+
*/
|
|
54
|
+
export function createSessionToolkit({ cwd, env, spillDir, todoState, onShellExit, envPassthrough = [] }) {
|
|
55
|
+
const shells = createShellManager({ spillDir, env, onExit: onShellExit, passthrough: envPassthrough });
|
|
56
|
+
const bash = createSessionBash({ shells, projectCwd: cwd });
|
|
57
|
+
return {
|
|
58
|
+
shells,
|
|
59
|
+
bash,
|
|
60
|
+
/** The registry's tools with Bash replaced by the session Bash, order kept. @param {any[]} tools */
|
|
61
|
+
withSessionBash: (tools) => tools.map((t) => (t.name === "Bash" ? bash : t)),
|
|
62
|
+
/**
|
|
63
|
+
* @param {{wanted:Set<string>|null, agentTools:any[], workflowTools?:any[]}} p workflowTools: Workflow, WorkflowStatus (W4-A2), appended
|
|
64
|
+
*/
|
|
65
|
+
tools: ({ wanted, agentTools, workflowTools = [] }) =>
|
|
66
|
+
[notebookEditTool, createTodoWriteTool(todoState), ...agentTools, createBashOutputTool(shells), createKillShellTool(shells), ...workflowTools].filter((t) => wanted === null || wanted.has(t.name)),
|
|
67
|
+
/** Kill every background shell. */
|
|
68
|
+
dispose: () => shells.killAll(),
|
|
69
|
+
};
|
|
70
|
+
}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* lib/engine/tools/todo.mjs — the TodoWrite tool and the run's task list.
|
|
3
|
+
*
|
|
4
|
+
* Input contract: `{ todos: [{ content, status: pending|in_progress|completed, activeForm? }] }`.
|
|
5
|
+
* Each call replaces the whole list.
|
|
6
|
+
*
|
|
7
|
+
* Validation refuses what cannot be a list (a non-array, an item without
|
|
8
|
+
* content, an unknown status). Several items in progress at once is allowed
|
|
9
|
+
* but answered with a warning: one item in progress keeps the list honest.
|
|
10
|
+
*
|
|
11
|
+
* The list lives in the run's state: the CLI appends every change to the
|
|
12
|
+
* session transcript as `{type:'todos', sessionId, ts, todos}` (so `--resume`
|
|
13
|
+
* restores it), reports it in stream-json (`cohort_todos`) and in the result
|
|
14
|
+
* (`cohort.todos`). A subagent gets its own list, which is not persisted.
|
|
15
|
+
*
|
|
16
|
+
* `validateTodos` and `todosFromRecords` are pure.
|
|
17
|
+
*
|
|
18
|
+
* @module lib/engine/tools/todo
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
import { readFileSync } from "node:fs";
|
|
22
|
+
import { parseTranscript } from "../session/store.mjs";
|
|
23
|
+
|
|
24
|
+
export const TODO_STATUSES = Object.freeze(["pending", "in_progress", "completed"]);
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* @typedef {{content:string, status:'pending'|'in_progress'|'completed', activeForm?:string}} Todo
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* @param {unknown} raw
|
|
32
|
+
* @returns {{ok:true, todos:Todo[], warnings:string[]}|{ok:false, error:string}}
|
|
33
|
+
*/
|
|
34
|
+
export function validateTodos(raw) {
|
|
35
|
+
if (!Array.isArray(raw)) return { ok: false, error: "todos must be an array" };
|
|
36
|
+
/** @type {Todo[]} */
|
|
37
|
+
const todos = [];
|
|
38
|
+
const problems = [];
|
|
39
|
+
raw.forEach((item, i) => {
|
|
40
|
+
if (!item || typeof item !== "object" || Array.isArray(item)) {
|
|
41
|
+
problems.push(`todos[${i}] must be an object`);
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
const content = typeof item.content === "string" ? item.content.trim() : "";
|
|
45
|
+
if (content === "") problems.push(`todos[${i}].content must be a non-empty string`);
|
|
46
|
+
if (!TODO_STATUSES.includes(item.status)) problems.push(`todos[${i}].status must be one of ${TODO_STATUSES.join(", ")}`);
|
|
47
|
+
if (item.activeForm !== undefined && typeof item.activeForm !== "string") problems.push(`todos[${i}].activeForm must be a string`);
|
|
48
|
+
/** @type {Todo} */
|
|
49
|
+
const todo = { content, status: item.status };
|
|
50
|
+
if (typeof item.activeForm === "string" && item.activeForm.trim() !== "") todo.activeForm = item.activeForm.trim();
|
|
51
|
+
todos.push(todo);
|
|
52
|
+
});
|
|
53
|
+
if (problems.length > 0) return { ok: false, error: problems.join("; ") };
|
|
54
|
+
const inProgress = todos.filter((t) => t.status === "in_progress").length;
|
|
55
|
+
const warnings = inProgress > 1 ? [`${inProgress} items are in_progress; keep exactly one item in progress and finish it before starting the next.`] : [];
|
|
56
|
+
return { ok: true, todos, warnings };
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** The most recent list recorded in a transcript's records. @param {any[]} records @returns {Todo[]} */
|
|
60
|
+
export function todosFromRecords(records) {
|
|
61
|
+
for (let i = records.length - 1; i >= 0; i--) {
|
|
62
|
+
const r = records[i];
|
|
63
|
+
if (r?.type === "todos" && Array.isArray(r.todos)) {
|
|
64
|
+
const v = validateTodos(r.todos);
|
|
65
|
+
return v.ok ? v.todos : [];
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return [];
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** The list a resumed session had. Never throws. @param {string} transcriptPath */
|
|
72
|
+
export function loadSessionTodos(transcriptPath) {
|
|
73
|
+
try {
|
|
74
|
+
return todosFromRecords(parseTranscript(readFileSync(transcriptPath, "utf8")).records);
|
|
75
|
+
} catch {
|
|
76
|
+
return []; // no transcript yet, or unreadable: start with an empty list
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* @param {{initial?:Todo[], onChange?:(todos:Todo[])=>void}} [o]
|
|
82
|
+
*/
|
|
83
|
+
export function createTodoState({ initial = [], onChange = () => {} } = {}) {
|
|
84
|
+
let todos = [...initial];
|
|
85
|
+
return {
|
|
86
|
+
get todos() {
|
|
87
|
+
return todos;
|
|
88
|
+
},
|
|
89
|
+
/** @param {Todo[]} next */
|
|
90
|
+
set(next) {
|
|
91
|
+
todos = next;
|
|
92
|
+
onChange(next);
|
|
93
|
+
},
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/** @param {Todo[]} todos */
|
|
98
|
+
function summary(todos) {
|
|
99
|
+
const n = (/** @type string */ s) => todos.filter((t) => t.status === s).length;
|
|
100
|
+
return `${todos.length} item${todos.length === 1 ? "" : "s"}: ${n("completed")} completed, ${n("in_progress")} in progress, ${n("pending")} pending`;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* @param {ReturnType<typeof createTodoState>} state
|
|
105
|
+
* @returns {import('./index.mjs').Tool & {forChild:()=>any}}
|
|
106
|
+
*/
|
|
107
|
+
export function createTodoWriteTool(state) {
|
|
108
|
+
return {
|
|
109
|
+
name: "TodoWrite",
|
|
110
|
+
// Changes nothing outside this run's own state.
|
|
111
|
+
readOnly: true,
|
|
112
|
+
description:
|
|
113
|
+
"Keep a short task list for multi-step work, so progress stays visible. Each call sends the whole list and replaces the previous one. " +
|
|
114
|
+
"Mark an item in_progress before starting it and completed as soon as it is done; keep one item in progress at a time. " +
|
|
115
|
+
"`activeForm` is an optional present-tense phrasing of the item (\"Running the tests\").",
|
|
116
|
+
input_schema: {
|
|
117
|
+
type: "object",
|
|
118
|
+
properties: {
|
|
119
|
+
todos: {
|
|
120
|
+
type: "array",
|
|
121
|
+
description: "The complete task list.",
|
|
122
|
+
items: {
|
|
123
|
+
type: "object",
|
|
124
|
+
properties: {
|
|
125
|
+
content: { type: "string", description: "The task, in imperative form." },
|
|
126
|
+
status: { type: "string", enum: [...TODO_STATUSES] },
|
|
127
|
+
activeForm: { type: "string", description: "The task in present-continuous form." },
|
|
128
|
+
},
|
|
129
|
+
required: ["content", "status"],
|
|
130
|
+
},
|
|
131
|
+
},
|
|
132
|
+
},
|
|
133
|
+
required: ["todos"],
|
|
134
|
+
},
|
|
135
|
+
async run(input) {
|
|
136
|
+
const v = validateTodos(input.todos);
|
|
137
|
+
if (!v.ok) return { ok: false, error: `The task list was not saved: ${v.error}.` };
|
|
138
|
+
state.set(v.todos);
|
|
139
|
+
const warn = v.warnings.length ? `\n\nWarning: ${v.warnings.join(" ")}` : "";
|
|
140
|
+
return { ok: true, content: `Task list saved (${summary(v.todos)}).${warn}` };
|
|
141
|
+
},
|
|
142
|
+
forChild: () => createTodoWriteTool(createTodoState()),
|
|
143
|
+
};
|
|
144
|
+
}
|