@cohortapp/agent-sdk 2.17.0 → 2.18.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/settings.json +18 -0
- package/.env.example +18 -5
- package/README.md +1 -0
- package/bin/maestro.mjs +62 -0
- package/docs/guides/billing-console-keys.md +60 -0
- package/docs/guides/front-door-session.md +54 -9
- package/docs/guides/mac-mini.md +20 -25
- package/docs/guides/setup-wizard.md +1 -1
- package/docs/runbooks/fleet-rollout.md +156 -0
- package/docs/runbooks/mac-mini-bootstrap.md +12 -14
- package/lib/action-executor.js +19 -3
- package/lib/budget-guard.mjs +279 -3
- package/lib/channels/base-adapter.mjs +3 -1
- package/lib/channels/contract.mjs +2 -1
- package/lib/channels/inbox-item.mjs +8 -0
- package/lib/claude-bin.mjs +5 -6
- package/lib/cli/doctor-checks.mjs +141 -10
- package/lib/cli/global-setup-extras.mjs +5 -1
- package/lib/cli/inbox.mjs +100 -15
- package/lib/cli/seat-auth.mjs +463 -0
- package/lib/cli/session.mjs +80 -12
- package/lib/collective/capture.mjs +8 -6
- package/lib/collective/global-config.mjs +63 -1
- package/lib/collective/presence.mjs +142 -5
- package/lib/comms/send-gate.mjs +559 -1
- package/lib/diagnostics/alerts.mjs +49 -0
- package/lib/diagnostics/cadence-output-freshness.mjs +288 -0
- package/lib/engine/agents/definitions.mjs +343 -0
- package/lib/engine/agents/persist.mjs +275 -0
- package/lib/engine/agents/runtime.mjs +748 -0
- package/lib/engine/agents/usage.mjs +95 -0
- package/lib/engine/auth-status.mjs +139 -0
- package/lib/engine/budget.mjs +194 -0
- package/lib/engine/cli.mjs +1204 -0
- package/lib/engine/commands/index.mjs +269 -0
- package/lib/engine/context/budget.mjs +219 -0
- package/lib/engine/context/cache.mjs +125 -0
- package/lib/engine/context/child-env.mjs +215 -0
- package/lib/engine/context/compaction.mjs +342 -0
- package/lib/engine/context/images.mjs +90 -0
- package/lib/engine/context/instructions.mjs +327 -0
- package/lib/engine/context/lazy-instructions.mjs +169 -0
- package/lib/engine/context/manager.mjs +182 -0
- package/lib/engine/context/real-path.mjs +91 -0
- package/lib/engine/context/secret-values.mjs +163 -0
- package/lib/engine/context/settings.mjs +274 -0
- package/lib/engine/context/stream-input.mjs +159 -0
- package/lib/engine/guard.mjs +152 -0
- package/lib/engine/hooks.mjs +713 -0
- package/lib/engine/loop.mjs +560 -0
- package/lib/engine/mcp/client.mjs +254 -0
- package/lib/engine/mcp/config.mjs +301 -0
- package/lib/engine/mcp/http.mjs +201 -0
- package/lib/engine/mcp/index.mjs +146 -0
- package/lib/engine/mcp/jsonrpc.mjs +147 -0
- package/lib/engine/mcp/naming.mjs +66 -0
- package/lib/engine/mcp/resources.mjs +89 -0
- package/lib/engine/mcp/results.mjs +133 -0
- package/lib/engine/mcp/stdio.mjs +137 -0
- package/lib/engine/mcp/supervisor.mjs +116 -0
- package/lib/engine/messages.mjs +104 -0
- package/lib/engine/output/json.mjs +164 -0
- package/lib/engine/output/stream-json.mjs +266 -0
- package/lib/engine/permissions.mjs +845 -0
- package/lib/engine/process-identity.mjs +164 -0
- package/lib/engine/process-tree.mjs +551 -0
- package/lib/engine/prompt.mjs +60 -0
- package/lib/engine/session/store.mjs +299 -0
- package/lib/engine/session-runtime/args.mjs +97 -0
- package/lib/engine/session-runtime/host.mjs +143 -0
- package/lib/engine/session-runtime/inbox.mjs +122 -0
- package/lib/engine/session-runtime/notifications.mjs +129 -0
- package/lib/engine/session-runtime/registry.mjs +328 -0
- package/lib/engine/session-runtime/runner.mjs +344 -0
- package/lib/engine/session-runtime/socket.mjs +212 -0
- package/lib/engine/session-runtime/wakeup.mjs +115 -0
- package/lib/engine/skills/index.mjs +321 -0
- package/lib/engine/tools/bash-background.mjs +533 -0
- package/lib/engine/tools/bash.mjs +216 -0
- package/lib/engine/tools/edit.mjs +97 -0
- package/lib/engine/tools/glob.mjs +81 -0
- package/lib/engine/tools/grep.mjs +224 -0
- package/lib/engine/tools/index.mjs +84 -0
- package/lib/engine/tools/list-agents.mjs +32 -0
- package/lib/engine/tools/ls.mjs +127 -0
- package/lib/engine/tools/monitor.mjs +82 -0
- package/lib/engine/tools/notebook-edit.mjs +218 -0
- package/lib/engine/tools/read.mjs +103 -0
- package/lib/engine/tools/schedule-wakeup.mjs +45 -0
- package/lib/engine/tools/schema.mjs +144 -0
- package/lib/engine/tools/send-message.mjs +77 -0
- package/lib/engine/tools/session.mjs +70 -0
- package/lib/engine/tools/todo.mjs +144 -0
- package/lib/engine/tools/toolsearch.mjs +217 -0
- package/lib/engine/tools/walk.mjs +193 -0
- package/lib/engine/tools/web-switch.mjs +31 -0
- package/lib/engine/tools/webfetch-html.mjs +387 -0
- package/lib/engine/tools/webfetch-net.mjs +340 -0
- package/lib/engine/tools/webfetch.mjs +198 -0
- package/lib/engine/tools/websearch.mjs +91 -0
- package/lib/engine/tools/workflow.mjs +95 -0
- package/lib/engine/tools/write.mjs +76 -0
- package/lib/engine/tui/line-editor.mjs +137 -0
- package/lib/engine/tui/render.mjs +86 -0
- package/lib/engine/tui/tui.mjs +274 -0
- package/lib/engine/wire/anthropic-messages.mjs +263 -0
- package/lib/engine/wire/effort.mjs +36 -0
- package/lib/engine/wire/errors.mjs +496 -0
- package/lib/engine/wire/http.mjs +441 -0
- package/lib/engine/wire/index.mjs +76 -0
- package/lib/engine/wire/openai-chat.mjs +332 -0
- package/lib/engine/wire/prompt-cache.mjs +79 -0
- package/lib/engine/wire/search.mjs +140 -0
- package/lib/engine/wire/sse.mjs +114 -0
- package/lib/engine/wire/stall.mjs +349 -0
- package/lib/engine/wire/token-provider.mjs +175 -0
- package/lib/engine/wire/usage.mjs +192 -0
- package/lib/engine/workflow/host.mjs +524 -0
- package/lib/engine/workflow/journal.mjs +188 -0
- package/lib/engine/workflow/json-schema.mjs +171 -0
- package/lib/engine/workflow/meta.mjs +329 -0
- package/lib/engine/workflow/notifications.mjs +52 -0
- package/lib/engine/workflow/runtime.mjs +447 -0
- package/lib/engine/workflow/sandbox.mjs +534 -0
- package/lib/engine/workflow/worker.mjs +141 -0
- package/lib/engine/workflow/worktree.mjs +74 -0
- package/lib/execution/disposition.mjs +1 -1
- package/lib/execution/intake.mjs +10 -0
- package/lib/execution/surface-policy.mjs +15 -0
- package/lib/learning/curator.mjs +8 -6
- package/lib/learning/reflect.mjs +8 -6
- package/lib/model-router/catalog/cohort.yaml +137 -0
- package/lib/model-router/catalog.mjs +118 -1
- package/lib/model-router/failover.mjs +67 -16
- package/lib/model-router/llm-task.mjs +39 -3
- package/lib/model-router/resolve.mjs +89 -3
- package/lib/model-router/spawn.mjs +46 -47
- package/lib/model-router/taxonomy.mjs +126 -4
- package/lib/org/cost-sync.mjs +141 -11
- package/lib/org/inbound/broadcast.mjs +289 -0
- package/lib/org/inbound/collective.mjs +375 -0
- package/lib/org/inbound/directedness.mjs +96 -8
- package/lib/org/inbound/facts.mjs +78 -2
- package/lib/org/inbound/project.mjs +22 -0
- package/lib/org/inbound/surfaces.mjs +14 -0
- package/lib/org/llm-token.mjs +879 -0
- package/lib/org/mesh.mjs +61 -0
- package/lib/org/messaging.mjs +3 -1
- package/lib/org/protocol.checksum +1 -1
- package/lib/org/protocol.mjs +15 -0
- package/lib/org/quota.mjs +520 -0
- package/lib/org/tool-surface.mjs +104 -16
- package/lib/org/ui-parity.mjs +16 -1
- package/lib/org/work-ledger.mjs +37 -6
- package/lib/rate-guard.mjs +114 -1
- package/lib/resource-governor.mjs +41 -6
- package/lib/runtime/adapter.mjs +823 -0
- package/lib/runtime/child-env.mjs +191 -0
- package/lib/runtime/legacy-shell-guard.mjs +97 -0
- package/lib/runtime/seat-engine.mjs +162 -0
- package/lib/session/ask-ledger.mjs +271 -0
- package/lib/session/current-work.mjs +676 -0
- package/lib/session/feed-core.mjs +40 -3
- package/lib/session/launch-args.mjs +56 -4
- package/lib/session/status-summary.mjs +26 -9
- package/lib/session/upgrade-notice.mjs +42 -0
- package/lib/setup/claude-probe.mjs +117 -13
- package/lib/setup/enrich.mjs +13 -10
- package/lib/setup/sections/model.mjs +39 -13
- package/lib/telemetry/collect.mjs +208 -9
- package/lib/upgrade/ignored-drift.mjs +105 -0
- package/lib/voice/post-call-brief.mjs +30 -17
- package/package.json +13 -3
- package/plugins/maestro-skills/skills/board-work.md +5 -0
- package/plugins/maestro-skills/skills/inbound-triage.md +56 -15
- package/plugins/maestro-skills/skills/main-session.md +18 -7
- package/scripts/ci/check-tarball-fidelity.mjs +126 -2
- package/scripts/ci/run-tests.mjs +47 -19
- package/scripts/cohort-llm/api-key-helper.mjs +92 -0
- package/scripts/collective/hook-runner.mjs +29 -2
- package/scripts/continuous-monitor.sh +13 -0
- package/scripts/cost/track-claude-usage.mjs +15 -0
- package/scripts/daemon/agent-daemon.mjs +408 -20
- package/scripts/daemon/assurance.mjs +48 -12
- package/scripts/daemon/cadence-consumer.mjs +218 -68
- package/scripts/daemon/cadence-handlers.mjs +73 -4
- package/scripts/daemon/classifier.mjs +75 -26
- package/scripts/daemon/context-compiler.mjs +51 -37
- package/scripts/daemon/deliver.mjs +30 -1
- package/scripts/daemon/dispatcher.mjs +595 -149
- package/scripts/daemon/health.mjs +14 -1
- package/scripts/daemon/maestro-daemon.mjs +11 -0
- package/scripts/daemon/prompt-builder.mjs +24 -0
- package/scripts/daemon/responder.mjs +246 -79
- package/scripts/daemon/sdk-version.mjs +98 -16
- package/scripts/eval/probe-gateway.mjs +635 -0
- package/scripts/eval/replay/extract.mjs +270 -0
- package/scripts/eval/replay/grade.mjs +260 -0
- package/scripts/eval/replay/lib/config.mjs +50 -0
- package/scripts/eval/replay/lib/effects.mjs +65 -0
- package/scripts/eval/replay/lib/fixture.mjs +188 -0
- package/scripts/eval/replay/lib/judge.mjs +72 -0
- package/scripts/eval/replay/lib/redact.mjs +136 -0
- package/scripts/eval/replay/lib/sandbox.mjs +170 -0
- package/scripts/eval/replay/lib/schema-check.mjs +63 -0
- package/scripts/eval/replay/lib/transcript.mjs +76 -0
- package/scripts/eval/replay/mcp-replay-stub.mjs +101 -0
- package/scripts/eval/replay/report.mjs +185 -0
- package/scripts/eval/replay/run.mjs +404 -0
- package/scripts/fleet/rollout.mjs +1094 -0
- package/scripts/hooks/pre-send-audit.sh +36 -245
- package/scripts/hooks/pre-write-yaml-validate.mjs +275 -0
- package/scripts/hooks/validate-state-yaml.sh +190 -0
- package/scripts/huddle/huddle-llm.mjs +361 -0
- package/scripts/huddle/huddle-server.mjs +46 -121
- package/scripts/local-triggers/autoupdate.sh +448 -78
- package/scripts/local-triggers/run-trigger.sh +13 -0
- package/scripts/maintenance/pin-integrity.mjs +364 -0
- package/scripts/poll-slack-events.sh +41 -9
- package/scripts/poller/slack-socket-mode.mjs +28 -3
- package/scripts/session/supervisor.mjs +80 -13
- package/scripts/spawn-session.sh +13 -0
- package/bin/maestro.test.mjs +0 -1574
- package/lib/action-executor.test.mjs +0 -871
- package/lib/archetype.test.mjs +0 -132
- package/lib/assurance/plan-note.test.mjs +0 -234
- package/lib/assurance/room-budget.test.mjs +0 -486
- package/lib/assurance/tier.test.mjs +0 -174
- package/lib/autonomy.test.mjs +0 -66
- package/lib/backlog.test.mjs +0 -302
- package/lib/backup/policy.test.mjs +0 -305
- package/lib/budget-escalate.test.mjs +0 -232
- package/lib/budget-guard.envelope.test.mjs +0 -476
- package/lib/budget-guard.test.mjs +0 -427
- package/lib/cadence-bus-requeue.test.mjs +0 -83
- package/lib/cadence-bus-schedule.test.mjs +0 -194
- package/lib/cadence-bus.test.mjs +0 -720
- package/lib/cadences.test.mjs +0 -230
- package/lib/capability/inventory.test.mjs +0 -232
- package/lib/capability.test.mjs +0 -78
- package/lib/channels/base-adapter.test.mjs +0 -590
- package/lib/channels/channels.test.mjs +0 -371
- package/lib/channels/contract.test.mjs +0 -162
- package/lib/channels/inbox-item.test.mjs +0 -368
- package/lib/channels/orgmail/adapter.test.mjs +0 -448
- package/lib/channels/pairing.test.mjs +0 -270
- package/lib/channels/repeat-suppressor.test.mjs +0 -134
- package/lib/channels/slack-adapter.test.mjs +0 -212
- package/lib/channels/telegram-adapter.test.mjs +0 -306
- package/lib/channels/voice/adapter.test.mjs +0 -278
- package/lib/channels/whatsapp/adapter-baileys.test.mjs +0 -359
- package/lib/channels/whatsapp/baileys-typing.test.mjs +0 -154
- package/lib/charter.test.mjs +0 -89
- package/lib/claude-bin.test.mjs +0 -131
- package/lib/cli/board.test.mjs +0 -227
- package/lib/cli/design.test.mjs +0 -270
- package/lib/cli/doctor-checks.test.mjs +0 -336
- package/lib/cli/global-setup-extras.test.mjs +0 -462
- package/lib/cli/inbox.test.mjs +0 -230
- package/lib/cli/session-ack.test.mjs +0 -63
- package/lib/cli/session.test.mjs +0 -613
- package/lib/collective/capture.test.mjs +0 -121
- package/lib/collective/cards.test.mjs +0 -114
- package/lib/collective/config.test.mjs +0 -123
- package/lib/collective/global-config.test.mjs +0 -220
- package/lib/collective/global-skills.test.mjs +0 -126
- package/lib/collective/presence.test.mjs +0 -95
- package/lib/collective/recall.test.mjs +0 -116
- package/lib/collective/vendor-skills.test.mjs +0 -306
- package/lib/comms/send-gate.test.mjs +0 -770
- package/lib/comms.test.mjs +0 -41
- package/lib/context/budget.test.mjs +0 -252
- package/lib/context/history-scope.test.mjs +0 -79
- package/lib/cost/ledger-row.test.mjs +0 -183
- package/lib/design/design-md.test.mjs +0 -318
- package/lib/design/fixtures/DESIGN.golden.md +0 -238
- package/lib/design/fixtures/PRODUCT.golden.md +0 -67
- package/lib/design/fixtures/foundation.json +0 -133
- package/lib/design/refresh-gate.test.mjs +0 -144
- package/lib/design/write.test.mjs +0 -241
- package/lib/diagnostics/alerts.test.mjs +0 -318
- package/lib/diagnostics/backup-freshness.test.mjs +0 -185
- package/lib/diagnostics/counters.test.mjs +0 -206
- package/lib/diagnostics/events.test.mjs +0 -290
- package/lib/diagnostics/otel.test.mjs +0 -196
- package/lib/diagnostics/trace.test.mjs +0 -251
- package/lib/env-compat.test.mjs +0 -104
- package/lib/execution/disposition.test.mjs +0 -553
- package/lib/execution/drive.test.mjs +0 -270
- package/lib/execution/effects.test.mjs +0 -344
- package/lib/execution/intake.test.mjs +0 -389
- package/lib/execution/journal.test.mjs +0 -261
- package/lib/execution/match.test.mjs +0 -235
- package/lib/execution/pipeline.test.mjs +0 -392
- package/lib/execution/route.test.mjs +0 -186
- package/lib/execution/surface-policy.test.mjs +0 -162
- package/lib/fs-atomic.test.mjs +0 -72
- package/lib/fs-ownership.test.mjs +0 -158
- package/lib/goals/admission.test.mjs +0 -164
- package/lib/goals/classify.test.mjs +0 -167
- package/lib/goals/collaborate.test.mjs +0 -336
- package/lib/goals/gaps.test.mjs +0 -284
- package/lib/goals/loop.test.mjs +0 -845
- package/lib/hooks/bus.test.mjs +0 -387
- package/lib/identity/persona.test.mjs +0 -142
- package/lib/kpi-sensors.test.mjs +0 -278
- package/lib/kpi.test.mjs +0 -244
- package/lib/learning/config.test.mjs +0 -75
- package/lib/learning/counters.test.mjs +0 -69
- package/lib/learning/curator-consolidate.test.mjs +0 -238
- package/lib/learning/curator.test.mjs +0 -106
- package/lib/learning/reflect.test.mjs +0 -0
- package/lib/learning/session-index.test.mjs +0 -125
- package/lib/learning/skill-writer.test.mjs +0 -210
- package/lib/mandate/audit.test.mjs +0 -195
- package/lib/mandate/contract.test.mjs +0 -185
- package/lib/mandate/derive.test.mjs +0 -274
- package/lib/mandate/model.test.mjs +0 -164
- package/lib/mandate/refresh.test.mjs +0 -389
- package/lib/mcp/server.test.mjs +0 -426
- package/lib/model-router/auth-profiles.test.mjs +0 -580
- package/lib/model-router/catalog.test.mjs +0 -385
- package/lib/model-router/economics.test.mjs +0 -438
- package/lib/model-router/failover.test.mjs +0 -439
- package/lib/model-router/health.test.mjs +0 -338
- package/lib/model-router/integration-coverage.test.mjs +0 -831
- package/lib/model-router/integration.test.mjs +0 -564
- package/lib/model-router/ledger.test.mjs +0 -415
- package/lib/model-router/llm-task.test.mjs +0 -392
- package/lib/model-router/org-credentials.test.mjs +0 -265
- package/lib/model-router/pricing-refresh.test.mjs +0 -286
- package/lib/model-router/reconcile.test.mjs +0 -316
- package/lib/model-router/repair.test.mjs +0 -180
- package/lib/model-router/spawn.test.mjs +0 -446
- package/lib/model-router/taxonomy.test.mjs +0 -410
- package/lib/model-router.test.mjs +0 -1207
- package/lib/org/activity.test.mjs +0 -134
- package/lib/org/approvals.test.mjs +0 -216
- package/lib/org/awareness.test.mjs +0 -159
- package/lib/org/board-mine-cache.test.mjs +0 -53
- package/lib/org/board.test.mjs +0 -187
- package/lib/org/bootstrap-context.test.mjs +0 -153
- package/lib/org/client.test.mjs +0 -1206
- package/lib/org/cohort-client.test.mjs +0 -126
- package/lib/org/cost-sync.test.mjs +0 -153
- package/lib/org/doctor.test.mjs +0 -346
- package/lib/org/engagement-ledger.test.mjs +0 -112
- package/lib/org/engagement.test.mjs +0 -739
- package/lib/org/handoff.test.mjs +0 -269
- package/lib/org/inbound/directedness.test.mjs +0 -668
- package/lib/org/inbound/facts.test.mjs +0 -471
- package/lib/org/inbound/hydrate.test.mjs +0 -908
- package/lib/org/inbound/index.test.mjs +0 -429
- package/lib/org/inbound/project.test.mjs +0 -287
- package/lib/org/integration-tools.test.mjs +0 -160
- package/lib/org/keys.test.mjs +0 -92
- package/lib/org/knowledge.test.mjs +0 -326
- package/lib/org/leases.test.mjs +0 -235
- package/lib/org/mesh-directives.test.mjs +0 -110
- package/lib/org/mesh-integration.test.mjs +0 -127
- package/lib/org/mesh.test.mjs +0 -400
- package/lib/org/messaging.test.mjs +0 -471
- package/lib/org/param-contract.test.mjs +0 -477
- package/lib/org/policy.test.mjs +0 -237
- package/lib/org/protocol.checksum.test.mjs +0 -90
- package/lib/org/protocol.test.mjs +0 -323
- package/lib/org/push.test.mjs +0 -792
- package/lib/org/registry.test.mjs +0 -100
- package/lib/org/resource-tools.test.mjs +0 -361
- package/lib/org/tool-access.test.mjs +0 -144
- package/lib/org/tool-surface-integration.test.mjs +0 -120
- package/lib/org/tool-surface.test.mjs +0 -1268
- package/lib/org/typing.test.mjs +0 -291
- package/lib/org/ui-parity.test.mjs +0 -560
- package/lib/org/verify.test.mjs +0 -194
- package/lib/org/work-ledger.test.mjs +0 -273
- package/lib/plan/adoption-e2e.test.mjs +0 -366
- package/lib/plan/budget-enforcement.test.mjs +0 -400
- package/lib/plan/compile.test.mjs +0 -382
- package/lib/plan/emit.test.mjs +0 -269
- package/lib/plan/explain.test.mjs +0 -188
- package/lib/prompts/parallelism.test.mjs +0 -177
- package/lib/rag/rag.test.mjs +0 -505
- package/lib/rate-guard.test.mjs +0 -272
- package/lib/reactive-gate.test.mjs +0 -57
- package/lib/render.test.mjs +0 -68
- package/lib/resource-governor.test.mjs +0 -488
- package/lib/scheduling/dynamic-jobs.test.mjs +0 -344
- package/lib/scheduling/jitter.test.mjs +0 -140
- package/lib/secrets/broker.test.mjs +0 -280
- package/lib/secrets/providers.test.mjs +0 -274
- package/lib/security/audit-engine.test.mjs +0 -424
- package/lib/security/coerce-args.test.mjs +0 -281
- package/lib/security/dangerous-tools.test.mjs +0 -68
- package/lib/security/external-content.test.mjs +0 -84
- package/lib/security/redact.test.mjs +0 -441
- package/lib/security/secret-equal.test.mjs +0 -55
- package/lib/session/config.test.mjs +0 -92
- package/lib/session/feed-core.test.mjs +0 -198
- package/lib/session/first-run.test.mjs +0 -121
- package/lib/session/frontdoor.test.mjs +0 -205
- package/lib/session/handoffs.test.mjs +0 -183
- package/lib/session/identity.test.mjs +0 -180
- package/lib/session/inbox-claims.test.mjs +0 -286
- package/lib/session/launch-args.test.mjs +0 -157
- package/lib/session/liveness.test.mjs +0 -100
- package/lib/session/status-summary.test.mjs +0 -118
- package/lib/session-permissions.test.mjs +0 -120
- package/lib/setup/claude-probe.test.mjs +0 -187
- package/lib/setup/completeness.test.mjs +0 -110
- package/lib/setup/context-pack.test.mjs +0 -89
- package/lib/setup/enrich.test.mjs +0 -115
- package/lib/setup/enroll-from-cohort.test.mjs +0 -300
- package/lib/setup/integration.test.mjs +0 -162
- package/lib/setup/io.test.mjs +0 -77
- package/lib/setup/runner.test.mjs +0 -132
- package/lib/setup/sections/identity.test.mjs +0 -234
- package/lib/setup/sections/inventory.test.mjs +0 -198
- package/lib/setup/sections/learning.test.mjs +0 -81
- package/lib/setup/sections/mandate.test.mjs +0 -388
- package/lib/setup/sections/messaging.test.mjs +0 -127
- package/lib/setup/sections/model.test.mjs +0 -240
- package/lib/setup/sections/org.test.mjs +0 -346
- package/lib/setup/sections/orgmail.test.mjs +0 -118
- package/lib/setup/sections/recovery.test.mjs +0 -98
- package/lib/setup/sections/subagents.test.mjs +0 -429
- package/lib/setup/sections/verify.test.mjs +0 -175
- package/lib/setup/sot.test.mjs +0 -81
- package/lib/setup/state.test.mjs +0 -115
- package/lib/singleton.test.mjs +0 -151
- package/lib/subagents/cli.test.mjs +0 -389
- package/lib/subagents/client.test.mjs +0 -309
- package/lib/subagents/gap.test.mjs +0 -234
- package/lib/subagents/lock.test.mjs +0 -248
- package/lib/subagents/manifest.test.mjs +0 -175
- package/lib/subagents/refs.test.mjs +0 -204
- package/lib/subagents/resolve.test.mjs +0 -422
- package/lib/subagents/schema.test.mjs +0 -328
- package/lib/telemetry/alerts.test.mjs +0 -109
- package/lib/telemetry/collect.test.mjs +0 -1274
- package/lib/tool-definitions-integration.test.mjs +0 -83
- package/lib/tool-definitions.test.mjs +0 -437
- package/lib/upgrade/global-refresh.test.mjs +0 -65
- package/lib/upgrade/launchd-reconcile.test.mjs +0 -272
- package/lib/upgrade/post-steps.test.mjs +0 -200
- package/lib/upgrade/verify.test.mjs +0 -164
- package/lib/util/fetch-timeout.test.mjs +0 -202
- package/lib/util/reconnect.test.mjs +0 -369
- package/lib/util/unhandled.test.mjs +0 -216
- package/lib/voice/outbound.test.mjs +0 -69
- package/lib/voice/session-rotation.test.mjs +0 -114
- package/lib/voice/stt.test.mjs +0 -226
- package/lib/voice/voice.test.mjs +0 -990
- package/scripts/cadence/enqueue-cadence-tick.test.mjs +0 -187
- package/scripts/ci/check-docs-accuracy.test.mjs +0 -409
- package/scripts/ci/check-durable-write-seam.test.mjs +0 -90
- package/scripts/ci/check-no-build-artifacts.test.mjs +0 -71
- package/scripts/ci/check-no-residual-identity.test.mjs +0 -202
- package/scripts/ci/check-skill-packs.test.mjs +0 -495
- package/scripts/ci/check-subagent-frontmatter.test.mjs +0 -124
- package/scripts/ci/check.test.mjs +0 -194
- package/scripts/ci/conformance-org-api.test.mjs +0 -425
- package/scripts/cloud-relay/voice/relay-identity.test.mjs +0 -96
- package/scripts/collective/hook-runner.test.mjs +0 -173
- package/scripts/cost/fleet-digest.test.mjs +0 -207
- package/scripts/cost/track-claude-usage-pricing.test.mjs +0 -183
- package/scripts/cost/track-claude-usage.test.mjs +0 -148
- package/scripts/daemon/agent-daemon-board-mine.test.mjs +0 -96
- package/scripts/daemon/agent-daemon-design.test.mjs +0 -238
- package/scripts/daemon/agent-daemon-frontdoor.test.mjs +0 -60
- package/scripts/daemon/agent-daemon.test.mjs +0 -995
- package/scripts/daemon/assurance-e2e.test.mjs +0 -613
- package/scripts/daemon/assurance.test.mjs +0 -1791
- package/scripts/daemon/board-mirror.test.mjs +0 -165
- package/scripts/daemon/cadence-consumer-frontdoor.test.mjs +0 -393
- package/scripts/daemon/cadence-consumer-governance.test.mjs +0 -276
- package/scripts/daemon/cadence-consumer.test.mjs +0 -776
- package/scripts/daemon/cadence-handlers.test.mjs +0 -837
- package/scripts/daemon/classifier-identity.test.mjs +0 -137
- package/scripts/daemon/classifier.test.mjs +0 -266
- package/scripts/daemon/classify-kind.test.mjs +0 -40
- package/scripts/daemon/context-compiler.test.mjs +0 -406
- package/scripts/daemon/deliver.test.mjs +0 -564
- package/scripts/daemon/dispatcher-cooldown.test.mjs +0 -122
- package/scripts/daemon/dispatcher-governance.test.mjs +0 -1013
- package/scripts/daemon/dispatcher-resume.test.mjs +0 -166
- package/scripts/daemon/dispatcher-session-continuity.test.mjs +0 -365
- package/scripts/daemon/execution-ladder.test.mjs +0 -470
- package/scripts/daemon/goal-steward-cadence.test.mjs +0 -312
- package/scripts/daemon/inbox-deferral-session.test.mjs +0 -49
- package/scripts/daemon/inbox-deferral.test.mjs +0 -336
- package/scripts/daemon/inbox-wake.test.mjs +0 -199
- package/scripts/daemon/integration.test.mjs +0 -149
- package/scripts/daemon/lib/self-echo.test.mjs +0 -153
- package/scripts/daemon/lib/session-router.test.mjs +0 -554
- package/scripts/daemon/prompt-builder-preamble.test.mjs +0 -210
- package/scripts/daemon/prompt-builder.test.mjs +0 -556
- package/scripts/daemon/responder-cost.test.mjs +0 -68
- package/scripts/daemon/responder-history.test.mjs +0 -221
- package/scripts/daemon/sdk-version.test.mjs +0 -31
- package/scripts/daemon/session-lock.test.mjs +0 -252
- package/scripts/daemon/session-outcomes.test.mjs +0 -533
- package/scripts/daemon/typing-registry.test.mjs +0 -102
- package/scripts/hooks/pre-send-audit.test.mjs +0 -354
- package/scripts/huddle/huddle-prompt.test.mjs +0 -176
- package/scripts/local-triggers/autoupdate.test.mjs +0 -518
- package/scripts/local-triggers/generate-plists.test.mjs +0 -456
- package/scripts/media-generation/brand-clause.test.mjs +0 -135
- package/scripts/org/send-orgmail.first-contact.test.mjs +0 -102
- package/scripts/poller/inbox-privilege-injection.test.mjs +0 -167
- package/scripts/poller/inbox-scan-poller.test.mjs +0 -295
- package/scripts/poller/lib/cloud-relay-dedup.test.mjs +0 -133
- package/scripts/poller/slack-socket-mode.test.mjs +0 -805
- package/scripts/poller-launchd/install.test.mjs +0 -243
- package/scripts/restore-from-backup.test.mjs +0 -181
- package/scripts/session/feed.test.mjs +0 -196
- package/scripts/session/supervisor-sh.test.mjs +0 -218
- package/scripts/session/supervisor.test.mjs +0 -482
- package/scripts/setup/configure-macos.test.mjs +0 -306
- package/scripts/setup/gen-subagent-manifest.test.mjs +0 -124
- package/scripts/setup/generate-agent-package-json.test.mjs +0 -143
- package/scripts/setup/generate-capability.test.mjs +0 -134
- package/scripts/setup/init-agent.test.mjs +0 -370
- package/scripts/setup/init-skill-marketplace.test.mjs +0 -193
- package/scripts/vendor/sync-skill-packs.test.mjs +0 -103
- package/scripts/watchdog/memory-watchdog.test.mjs +0 -64
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* lib/engine/context/instructions.mjs — the instruction files a run reads.
|
|
3
|
+
*
|
|
4
|
+
* Every file found is included, in this order, each under a header naming it:
|
|
5
|
+
*
|
|
6
|
+
* 1. managed the organisation-managed CLAUDE.md
|
|
7
|
+
* (/Library/Application Support/ClaudeCode/CLAUDE.md on macOS,
|
|
8
|
+
* /etc/claude-code/CLAUDE.md on Linux)
|
|
9
|
+
* 2. user ~/.claude/CLAUDE.md ($CLAUDE_CONFIG_DIR/CLAUDE.md)
|
|
10
|
+
* 3. then for each directory from the filesystem root down to the working
|
|
11
|
+
* directory, in this order at each level:
|
|
12
|
+
* CLAUDE.md, .claude/CLAUDE.md, CLAUDE.local.md, COHORT.md, AGENTS.md
|
|
13
|
+
* rules every `*.md` under ~/.claude/rules (after the user file) and
|
|
14
|
+
* <cwd>/.claude/rules (after the last level), recursively, in
|
|
15
|
+
* path order. A rule whose frontmatter has `paths:` (globs,
|
|
16
|
+
* relative to cwd) is not loaded here: it is returned in
|
|
17
|
+
* `conditionalRules` and injected when a matching file is
|
|
18
|
+
* touched (lazy-instructions.mjs), as are CLAUDE.md files in
|
|
19
|
+
* directories below cwd.
|
|
20
|
+
* 4. memory the project's auto-memory index,
|
|
21
|
+
* ~/.claude/projects/<cwd with every non-alphanumeric as "->/memory/MEMORY.md,
|
|
22
|
+
* capped at its first 200 lines and 25 KB
|
|
23
|
+
*
|
|
24
|
+
* Nothing shadows anything: a global AGENTS.md does not hide ~/.claude/CLAUDE.md
|
|
25
|
+
* (where maestro writes an agent's identity block), and COHORT.md sits beside
|
|
26
|
+
* CLAUDE.md rather than replacing it. The same file reached twice (a home
|
|
27
|
+
* directory that is also an ancestor of cwd) is included once.
|
|
28
|
+
*
|
|
29
|
+
* Imports: `@path` in a file's text (outside code spans and fenced blocks)
|
|
30
|
+
* includes that file after the line that names it. Relative paths resolve
|
|
31
|
+
* against the importing file's directory; `~/` against the home directory.
|
|
32
|
+
* Imports nest to a depth of 5; a file already on the import chain, or already
|
|
33
|
+
* included, is not included again. A token that does not name an existing file
|
|
34
|
+
* is left as text (it is probably an @-mention).
|
|
35
|
+
*
|
|
36
|
+
* The text is deterministic for a given set of files, so it can sit in the
|
|
37
|
+
* cached prefix of the system prompt.
|
|
38
|
+
*
|
|
39
|
+
* All reads go through the injected `readFile` / `isFile`.
|
|
40
|
+
*
|
|
41
|
+
* @module lib/engine/context/instructions
|
|
42
|
+
*/
|
|
43
|
+
|
|
44
|
+
import path from "node:path";
|
|
45
|
+
import { parseSkillFile } from "../skills/index.mjs";
|
|
46
|
+
|
|
47
|
+
export const RULES_DIR = path.join(".claude", "rules");
|
|
48
|
+
const MAX_RULE_DEPTH = 8;
|
|
49
|
+
|
|
50
|
+
export const FILES_PER_LEVEL = Object.freeze(["CLAUDE.md", ".claude/CLAUDE.md", "CLAUDE.local.md", "COHORT.md", "AGENTS.md"]);
|
|
51
|
+
export const MAX_IMPORT_DEPTH = 5;
|
|
52
|
+
export const MEMORY_MAX_LINES = 200;
|
|
53
|
+
export const MEMORY_MAX_BYTES = 25_000;
|
|
54
|
+
|
|
55
|
+
/** @param {string} platform */
|
|
56
|
+
export function defaultManagedInstructionsPath(platform) {
|
|
57
|
+
if (platform === "darwin") return "/Library/Application Support/ClaudeCode/CLAUDE.md";
|
|
58
|
+
if (platform === "linux") return "/etc/claude-code/CLAUDE.md";
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Directories from the filesystem root down to `cwd`, inclusive.
|
|
64
|
+
* @param {string} cwd
|
|
65
|
+
*/
|
|
66
|
+
export function ancestorDirs(cwd) {
|
|
67
|
+
const abs = path.resolve(cwd);
|
|
68
|
+
const dirs = [];
|
|
69
|
+
let cur = abs;
|
|
70
|
+
for (;;) {
|
|
71
|
+
dirs.unshift(cur);
|
|
72
|
+
const parent = path.dirname(cur);
|
|
73
|
+
if (parent === cur) break;
|
|
74
|
+
cur = parent;
|
|
75
|
+
}
|
|
76
|
+
return dirs;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/** @param {string} name */
|
|
80
|
+
function scopeOf(name) {
|
|
81
|
+
return name === "CLAUDE.local.md" ? "local" : "project";
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* @param {{cwd:string, userDir:string|null, managedPath?:string|null}} p
|
|
86
|
+
* @returns {Array<{path:string, scope:'managed'|'user'|'project'|'local'}>}
|
|
87
|
+
*/
|
|
88
|
+
export function instructionCandidates({ cwd, userDir, managedPath = null }) {
|
|
89
|
+
const out = [];
|
|
90
|
+
if (managedPath) out.push({ path: managedPath, scope: /** @type const */ ("managed") });
|
|
91
|
+
if (userDir) out.push({ path: path.join(userDir, "CLAUDE.md"), scope: /** @type const */ ("user") });
|
|
92
|
+
for (const dir of ancestorDirs(cwd)) {
|
|
93
|
+
for (const name of FILES_PER_LEVEL) out.push({ path: path.join(dir, name), scope: scopeOf(name) });
|
|
94
|
+
}
|
|
95
|
+
return /** @type any */ (out);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* The @path tokens of a text, outside fenced blocks and inline code.
|
|
100
|
+
* @param {string} text
|
|
101
|
+
* @returns {Array<{line:number, target:string}>}
|
|
102
|
+
*/
|
|
103
|
+
export function findImports(text) {
|
|
104
|
+
const found = [];
|
|
105
|
+
let fenced = false;
|
|
106
|
+
const lines = String(text).split("\n");
|
|
107
|
+
lines.forEach((line, i) => {
|
|
108
|
+
if (/^\s*(```|~~~)/.test(line)) {
|
|
109
|
+
fenced = !fenced;
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
if (fenced) return;
|
|
113
|
+
const visible = line.replace(/`[^`]*`/g, (m) => " ".repeat(m.length));
|
|
114
|
+
const re = /(^|\s)@((?:~\/|\.{1,2}\/|\/)?[^\s`'"()<>]+)/g;
|
|
115
|
+
let m;
|
|
116
|
+
while ((m = re.exec(visible)) !== null) {
|
|
117
|
+
const target = m[2].replace(/[.,;:!?]+$/, "");
|
|
118
|
+
if (target !== "") found.push({ line: i, target });
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
return found;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* @param {string} target @param {{baseDir:string, homedir:string|null}} o
|
|
126
|
+
*/
|
|
127
|
+
export function resolveImportPath(target, { baseDir, homedir }) {
|
|
128
|
+
if (target.startsWith("~/")) return homedir ? path.join(homedir, target.slice(2)) : null;
|
|
129
|
+
return path.resolve(baseDir, target);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* @param {string} text
|
|
134
|
+
* @param {object} o
|
|
135
|
+
* @param {string} o.file the file this text came from
|
|
136
|
+
* @param {string|null} o.homedir
|
|
137
|
+
* @param {(p:string)=>string} o.readFile
|
|
138
|
+
* @param {(p:string)=>boolean} o.isFile
|
|
139
|
+
* @param {Set<string>} o.included every file already in the output (mutated)
|
|
140
|
+
* @param {string[]} [o.chain] the import chain above this file
|
|
141
|
+
* @returns {string}
|
|
142
|
+
*/
|
|
143
|
+
export function expandImports(text, o) {
|
|
144
|
+
const chain = [...(o.chain ?? []), o.file];
|
|
145
|
+
const depth = chain.length - 1;
|
|
146
|
+
const imports = findImports(text);
|
|
147
|
+
if (imports.length === 0 || depth >= MAX_IMPORT_DEPTH) return text;
|
|
148
|
+
const byLine = new Map();
|
|
149
|
+
for (const imp of imports) {
|
|
150
|
+
const abs = resolveImportPath(imp.target, { baseDir: path.dirname(o.file), homedir: o.homedir });
|
|
151
|
+
if (!abs || chain.includes(abs) || o.included.has(abs) || !o.isFile(abs)) continue;
|
|
152
|
+
let body;
|
|
153
|
+
try {
|
|
154
|
+
body = o.readFile(abs);
|
|
155
|
+
} catch {
|
|
156
|
+
continue; // unreadable: leave the token as written
|
|
157
|
+
}
|
|
158
|
+
o.included.add(abs);
|
|
159
|
+
const expanded = expandImports(body, { ...o, file: abs, chain });
|
|
160
|
+
const list = byLine.get(imp.line) ?? [];
|
|
161
|
+
list.push(`[imported from ${abs}]\n${expanded.trimEnd()}\n[end of ${abs}]`);
|
|
162
|
+
byLine.set(imp.line, list);
|
|
163
|
+
}
|
|
164
|
+
if (byLine.size === 0) return text;
|
|
165
|
+
return String(text)
|
|
166
|
+
.split("\n")
|
|
167
|
+
.flatMap((line, i) => (byLine.has(i) ? [line, ...byLine.get(i)] : [line]))
|
|
168
|
+
.join("\n");
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* @param {{cwd:string, userDir:string|null}} p
|
|
173
|
+
*/
|
|
174
|
+
export function memoryIndexPath({ cwd, userDir }) {
|
|
175
|
+
if (!userDir) return null;
|
|
176
|
+
return path.join(userDir, "projects", path.resolve(cwd).replace(/[^A-Za-z0-9]/g, "-"), "memory", "MEMORY.md");
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/** First 200 lines, then at most 25 KB (cut at a line boundary where possible). @param {string} text */
|
|
180
|
+
export function capMemory(text) {
|
|
181
|
+
const lines = String(text).split("\n");
|
|
182
|
+
let out = lines.slice(0, MEMORY_MAX_LINES).join("\n");
|
|
183
|
+
const truncatedLines = lines.length > MEMORY_MAX_LINES;
|
|
184
|
+
let truncatedBytes = false;
|
|
185
|
+
if (Buffer.byteLength(out, "utf8") > MEMORY_MAX_BYTES) {
|
|
186
|
+
truncatedBytes = true;
|
|
187
|
+
let buf = Buffer.from(out, "utf8").subarray(0, MEMORY_MAX_BYTES).toString("utf8");
|
|
188
|
+
buf = buf.replace(/�$/, "");
|
|
189
|
+
const nl = buf.lastIndexOf("\n");
|
|
190
|
+
out = nl > 0 ? buf.slice(0, nl) : buf;
|
|
191
|
+
}
|
|
192
|
+
return { text: out, truncated: truncatedLines || truncatedBytes };
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Every `.md` file under a rules directory, recursively, sorted by path.
|
|
197
|
+
* @param {string} dir @param {{readdir?:(p:string)=>string[], isDir?:(p:string)=>boolean, isFile:(p:string)=>boolean}} fs
|
|
198
|
+
*/
|
|
199
|
+
export function listRuleFiles(dir, fs) {
|
|
200
|
+
if (!fs.readdir || !fs.isDir || !fs.isDir(dir)) return [];
|
|
201
|
+
const out = [];
|
|
202
|
+
const walk = (/** @type string */ d, /** @type number */ depth) => {
|
|
203
|
+
let names;
|
|
204
|
+
try {
|
|
205
|
+
names = /** @type {(p:string)=>string[]} */ (fs.readdir)(d).sort();
|
|
206
|
+
} catch {
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
for (const n of names) {
|
|
210
|
+
const full = path.join(d, n);
|
|
211
|
+
if (/** @type {(p:string)=>boolean} */ (fs.isDir)(full)) {
|
|
212
|
+
if (depth < MAX_RULE_DEPTH) walk(full, depth + 1);
|
|
213
|
+
} else if (n.endsWith(".md") && fs.isFile(full)) out.push(full);
|
|
214
|
+
}
|
|
215
|
+
};
|
|
216
|
+
walk(dir, 0);
|
|
217
|
+
return out;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* A rule file's `paths` frontmatter (string or list, comma separated) and body.
|
|
222
|
+
* @param {string} text @returns {{patterns:string[], body:string}}
|
|
223
|
+
*/
|
|
224
|
+
export function parseRule(text) {
|
|
225
|
+
const { frontmatter, body } = parseSkillFile(text);
|
|
226
|
+
const raw = frontmatter.paths;
|
|
227
|
+
const list = Array.isArray(raw) ? raw.map(String) : typeof raw === "string" ? raw.split(",") : [];
|
|
228
|
+
return { patterns: list.map((s) => s.trim().replace(/^["']|["']$/g, "").trim()).filter(Boolean), body };
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* @param {object} p
|
|
233
|
+
* @param {string} p.cwd
|
|
234
|
+
* @param {string|null} p.homedir
|
|
235
|
+
* @param {string|null} p.userDir
|
|
236
|
+
* @param {string|null} [p.managedPath]
|
|
237
|
+
* @param {(p:string)=>string} p.readFile
|
|
238
|
+
* @param {(p:string)=>boolean} p.isFile
|
|
239
|
+
* @param {(p:string)=>string} [p.realpath]
|
|
240
|
+
* @param {(p:string)=>string[]} [p.readdir] with isDir: enables .claude/rules
|
|
241
|
+
* @param {(p:string)=>boolean} [p.isDir]
|
|
242
|
+
*/
|
|
243
|
+
export function loadInstructions({ cwd, homedir, userDir, managedPath = null, readFile, isFile, realpath = (p) => p, readdir, isDir }) {
|
|
244
|
+
const included = new Set();
|
|
245
|
+
const seenReal = new Set();
|
|
246
|
+
/** @type {Array<{path:string, scope:string, content:string}>} */
|
|
247
|
+
const files = [];
|
|
248
|
+
const errors = [];
|
|
249
|
+
/** @type {Array<{path:string, patterns:string[], content:string}>} */
|
|
250
|
+
const conditionalRules = [];
|
|
251
|
+
const addRules = (/** @type string */ dir, /** @type string */ scope) => {
|
|
252
|
+
for (const file of listRuleFiles(dir, { readdir, isDir, isFile })) {
|
|
253
|
+
let real;
|
|
254
|
+
try {
|
|
255
|
+
real = realpath(file);
|
|
256
|
+
} catch {
|
|
257
|
+
real = file;
|
|
258
|
+
}
|
|
259
|
+
// A rule already pulled in by an @import (or reached twice) is included once.
|
|
260
|
+
if (seenReal.has(real) || included.has(file)) continue;
|
|
261
|
+
seenReal.add(real);
|
|
262
|
+
let text;
|
|
263
|
+
try {
|
|
264
|
+
text = readFile(file);
|
|
265
|
+
} catch (e) {
|
|
266
|
+
errors.push(`${file}: ${e instanceof Error ? e.message : e}`);
|
|
267
|
+
continue;
|
|
268
|
+
}
|
|
269
|
+
included.add(file);
|
|
270
|
+
const { patterns, body } = parseRule(text);
|
|
271
|
+
const content = expandImports(body, { file, homedir, readFile, isFile, included });
|
|
272
|
+
if (patterns.length > 0) conditionalRules.push({ path: file, patterns, content });
|
|
273
|
+
else files.push({ path: file, scope, content });
|
|
274
|
+
}
|
|
275
|
+
};
|
|
276
|
+
let userRulesDone = false;
|
|
277
|
+
for (const c of instructionCandidates({ cwd, userDir, managedPath })) {
|
|
278
|
+
if (!userRulesDone && c.scope !== "managed" && c.scope !== "user") {
|
|
279
|
+
userRulesDone = true;
|
|
280
|
+
if (userDir) addRules(path.join(userDir, "rules"), "user rule");
|
|
281
|
+
}
|
|
282
|
+
if (!isFile(c.path)) continue;
|
|
283
|
+
let real;
|
|
284
|
+
try {
|
|
285
|
+
real = realpath(c.path);
|
|
286
|
+
} catch {
|
|
287
|
+
real = c.path;
|
|
288
|
+
}
|
|
289
|
+
if (seenReal.has(real) || included.has(c.path)) continue;
|
|
290
|
+
seenReal.add(real);
|
|
291
|
+
let text;
|
|
292
|
+
try {
|
|
293
|
+
text = readFile(c.path);
|
|
294
|
+
} catch (e) {
|
|
295
|
+
errors.push(`${c.path}: ${e instanceof Error ? e.message : e}`);
|
|
296
|
+
continue;
|
|
297
|
+
}
|
|
298
|
+
included.add(c.path);
|
|
299
|
+
files.push({ path: c.path, scope: c.scope, content: expandImports(text, { file: c.path, homedir, readFile, isFile, included }) });
|
|
300
|
+
}
|
|
301
|
+
addRules(path.join(cwd, RULES_DIR), "project rule");
|
|
302
|
+
let memory = null;
|
|
303
|
+
const mem = memoryIndexPath({ cwd, userDir });
|
|
304
|
+
if (mem && isFile(mem)) {
|
|
305
|
+
try {
|
|
306
|
+
const capped = capMemory(readFile(mem));
|
|
307
|
+
memory = { path: mem, content: capped.text, truncated: capped.truncated };
|
|
308
|
+
} catch (e) {
|
|
309
|
+
errors.push(`${mem}: ${e instanceof Error ? e.message : e}`);
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
return { files, memory, errors, conditionalRules };
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* The system-prompt block (pure). Null when nothing was found.
|
|
317
|
+
* @param {{files:Array<{path:string, scope:string, content:string}>, memory:{path:string, content:string, truncated?:boolean}|null}} loaded
|
|
318
|
+
*/
|
|
319
|
+
export function formatInstructions({ files, memory }) {
|
|
320
|
+
if (files.length === 0 && !memory) return null;
|
|
321
|
+
const parts = ["Instructions for this work follow, from the most general file to the most specific. Where they conflict, the more specific file wins."];
|
|
322
|
+
for (const f of files) parts.push(`## ${f.path} (${f.scope})\n\n${f.content.trim()}`);
|
|
323
|
+
if (memory) {
|
|
324
|
+
parts.push(`## ${memory.path} (memory index${memory.truncated ? ", truncated" : ""})\n\n${memory.content.trim()}`);
|
|
325
|
+
}
|
|
326
|
+
return parts.join("\n\n");
|
|
327
|
+
}
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* lib/engine/context/lazy-instructions.mjs — instructions found as the run moves (CF-21).
|
|
3
|
+
*
|
|
4
|
+
* At startup the engine reads the instruction files from the filesystem root
|
|
5
|
+
* down to the working directory (instructions.mjs). A repository often keeps
|
|
6
|
+
* more specific files further down — `packages/api/CLAUDE.md` — and
|
|
7
|
+
* `.claude/rules/*.md` files can say which paths they apply to (`paths:`
|
|
8
|
+
* frontmatter). Loading all of those up front would spend context on parts
|
|
9
|
+
* of the tree the run never touches, so they are loaded LAZILY:
|
|
10
|
+
*
|
|
11
|
+
* · when Read, Edit, Write, MultiEdit or NotebookEdit touches a file below
|
|
12
|
+
* the working directory, every instruction file (CLAUDE.md,
|
|
13
|
+
* .claude/CLAUDE.md, CLAUDE.local.md, COHORT.md, AGENTS.md) in each
|
|
14
|
+
* directory from just below cwd down to that file's directory is read,
|
|
15
|
+
* with its @imports;
|
|
16
|
+
* · a path-scoped rule is read when a touched file matches one of its globs
|
|
17
|
+
* (relative to the working directory);
|
|
18
|
+
* · each file is injected ONCE per run, as text appended to the message that
|
|
19
|
+
* carries the tool results — never into the system prompt, whose bytes are
|
|
20
|
+
* the cached prefix.
|
|
21
|
+
*
|
|
22
|
+
* The loader remembers what it injected, so compaction can carry it forward.
|
|
23
|
+
*
|
|
24
|
+
* @module lib/engine/context/lazy-instructions
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
import path from "node:path";
|
|
28
|
+
import { FILES_PER_LEVEL, expandImports } from "./instructions.mjs";
|
|
29
|
+
|
|
30
|
+
export const TOUCH_TOOLS = Object.freeze(new Set(["Read", "Edit", "Write", "MultiEdit", "NotebookEdit"]));
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* The absolute file a tool call touches, or null.
|
|
34
|
+
* @param {string} toolName @param {any} input @param {string} cwd
|
|
35
|
+
*/
|
|
36
|
+
export function touchedPath(toolName, input, cwd) {
|
|
37
|
+
if (!TOUCH_TOOLS.has(toolName) || !input || typeof input !== "object") return null;
|
|
38
|
+
const p = input.file_path ?? input.notebook_path;
|
|
39
|
+
if (typeof p !== "string" || p === "") return null;
|
|
40
|
+
return path.resolve(cwd, p);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Instruction files for a touched file: directories strictly below `cwd` down
|
|
45
|
+
* to the file's directory, outermost first. Empty for a file outside cwd.
|
|
46
|
+
* @param {{cwd:string, file:string}} p
|
|
47
|
+
*/
|
|
48
|
+
export function nestedInstructionCandidates({ cwd, file }) {
|
|
49
|
+
const root = path.resolve(cwd);
|
|
50
|
+
const dir = path.dirname(path.resolve(file));
|
|
51
|
+
const rel = path.relative(root, dir);
|
|
52
|
+
if (rel === "" || rel.startsWith("..") || path.isAbsolute(rel)) return [];
|
|
53
|
+
const out = [];
|
|
54
|
+
let cur = root;
|
|
55
|
+
for (const part of rel.split(path.sep)) {
|
|
56
|
+
cur = path.join(cur, part);
|
|
57
|
+
for (const name of FILES_PER_LEVEL) out.push(path.join(cur, name));
|
|
58
|
+
}
|
|
59
|
+
return out;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* A glob as a regular expression over a forward-slash relative path:
|
|
64
|
+
* `**` any depth, `*` within a segment, `?` one character, `{a,b}` either.
|
|
65
|
+
* A pattern with no slash matches the file name at any depth.
|
|
66
|
+
* @param {string} glob
|
|
67
|
+
*/
|
|
68
|
+
export function globToRegExp(glob) {
|
|
69
|
+
let g = String(glob).trim().replace(/^\.\//, "");
|
|
70
|
+
if (!g.includes("/")) g = `**/${g}`;
|
|
71
|
+
let re = "";
|
|
72
|
+
for (let i = 0; i < g.length; i++) {
|
|
73
|
+
const ch = g[i];
|
|
74
|
+
if (ch === "*") {
|
|
75
|
+
if (g[i + 1] === "*") {
|
|
76
|
+
const slash = g[i + 2] === "/";
|
|
77
|
+
re += slash ? "(?:.*/)?" : ".*";
|
|
78
|
+
i += slash ? 2 : 1;
|
|
79
|
+
} else re += "[^/]*";
|
|
80
|
+
} else if (ch === "?") re += "[^/]";
|
|
81
|
+
else if (ch === "{") {
|
|
82
|
+
const end = g.indexOf("}", i);
|
|
83
|
+
if (end === -1) re += "\\{";
|
|
84
|
+
else {
|
|
85
|
+
re += `(?:${g.slice(i + 1, end).split(",").map((s) => s.replace(/[.+^$()|[\]\\]/g, "\\$&").replace(/\*/g, "[^/]*")).join("|")})`;
|
|
86
|
+
i = end;
|
|
87
|
+
}
|
|
88
|
+
} else re += ch.replace(/[.+^$()|[\]\\]/g, "\\$&");
|
|
89
|
+
}
|
|
90
|
+
return new RegExp(`^${re}$`);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* @typedef {{path:string, patterns:string[], content:string}} ConditionalRule
|
|
95
|
+
*/
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* @param {object} p
|
|
99
|
+
* @param {string} p.cwd
|
|
100
|
+
* @param {string|null} p.homedir
|
|
101
|
+
* @param {(p:string)=>string} p.readFile
|
|
102
|
+
* @param {(p:string)=>boolean} p.isFile
|
|
103
|
+
* @param {(p:string)=>string} [p.realpath]
|
|
104
|
+
* @param {string[]} [p.loaded] files already in the system prompt
|
|
105
|
+
* @param {ConditionalRule[]} [p.conditionalRules]
|
|
106
|
+
*/
|
|
107
|
+
export function createLazyInstructions({ cwd, homedir, readFile, isFile, realpath = (x) => x, loaded = [], conditionalRules = [] }) {
|
|
108
|
+
const real = (/** @type string */ p) => {
|
|
109
|
+
try {
|
|
110
|
+
return realpath(p);
|
|
111
|
+
} catch {
|
|
112
|
+
return p;
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
const seen = new Set(loaded.map(real));
|
|
116
|
+
const included = new Set(loaded);
|
|
117
|
+
/** @type {Array<{path:string, content:string, reason:string}>} */
|
|
118
|
+
const injected = [];
|
|
119
|
+
const rules = conditionalRules.map((r) => ({ ...r, res: r.patterns.map(globToRegExp) }));
|
|
120
|
+
|
|
121
|
+
return {
|
|
122
|
+
/**
|
|
123
|
+
* Instruction files to inject because this file was touched (each once).
|
|
124
|
+
* @param {string} file absolute
|
|
125
|
+
* @returns {Array<{path:string, content:string, reason:string}>}
|
|
126
|
+
*/
|
|
127
|
+
touch(file) {
|
|
128
|
+
const out = [];
|
|
129
|
+
for (const candidate of nestedInstructionCandidates({ cwd, file })) {
|
|
130
|
+
if (!isFile(candidate)) continue;
|
|
131
|
+
const r = real(candidate);
|
|
132
|
+
if (seen.has(r)) continue;
|
|
133
|
+
seen.add(r);
|
|
134
|
+
let text;
|
|
135
|
+
try {
|
|
136
|
+
text = readFile(candidate);
|
|
137
|
+
} catch {
|
|
138
|
+
continue;
|
|
139
|
+
}
|
|
140
|
+
included.add(candidate);
|
|
141
|
+
const content = expandImports(text, { file: candidate, homedir, readFile, isFile, included });
|
|
142
|
+
out.push({ path: candidate, content, reason: `it applies to files under ${path.dirname(candidate)}` });
|
|
143
|
+
}
|
|
144
|
+
const rel = path.relative(path.resolve(cwd), path.resolve(file)).split(path.sep).join("/");
|
|
145
|
+
if (!rel.startsWith("..") && !path.isAbsolute(rel)) {
|
|
146
|
+
for (const rule of rules) {
|
|
147
|
+
if (seen.has(real(rule.path)) || !rule.res.some((re) => re.test(rel))) continue;
|
|
148
|
+
seen.add(real(rule.path));
|
|
149
|
+
out.push({ path: rule.path, content: rule.content, reason: `its paths (${rule.patterns.join(", ")}) match ${rel}` });
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
injected.push(...out);
|
|
153
|
+
return out;
|
|
154
|
+
},
|
|
155
|
+
/** Everything injected so far, in order. */
|
|
156
|
+
injected: () => injected.map(({ path: p, content }) => ({ path: p, content })),
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* The text block appended after tool results (pure).
|
|
162
|
+
* @param {Array<{path:string, content:string, reason:string}>} entries
|
|
163
|
+
*/
|
|
164
|
+
export function formatLazyInstructions(entries) {
|
|
165
|
+
if (entries.length === 0) return null;
|
|
166
|
+
return entries
|
|
167
|
+
.map((e) => `Instructions from ${e.path} now apply (${e.reason}). Follow them alongside the instructions you already have; where they conflict, these more specific ones win.\n\n${e.content.trim()}`)
|
|
168
|
+
.join("\n\n");
|
|
169
|
+
}
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* lib/engine/context/manager.mjs — what the loop asks at every turn boundary.
|
|
3
|
+
*
|
|
4
|
+
* The loop (loop.mjs `p.context`) calls three hooks:
|
|
5
|
+
*
|
|
6
|
+
* beforeTurn before each model request:
|
|
7
|
+
* 1. servers that announced a tool-list change are re-listed
|
|
8
|
+
* and the tool set rebuilt (never mid-turn);
|
|
9
|
+
* 2. queued input is drained — a stream-json
|
|
10
|
+
* `{type:"control", subtype:"compact"}` requests manual
|
|
11
|
+
* compaction; queued user messages are appended;
|
|
12
|
+
* 3. images leave all but the most recent image-carrying
|
|
13
|
+
* tool results (images.mjs);
|
|
14
|
+
* the request is measured and projected (budget.mjs);
|
|
15
|
+
* at the threshold — or on a manual request — compaction
|
|
16
|
+
* runs (compaction.mjs) and the history is replaced;
|
|
17
|
+
* 4. the definitions to send are computed: the fixed tools and
|
|
18
|
+
* loaded deferred tools, canonical and in stable order.
|
|
19
|
+
* observe after each model request: the provider's input-token count
|
|
20
|
+
* calibrates the estimator.
|
|
21
|
+
* afterTools after each tool batch: a deferred tool called by name is
|
|
22
|
+
* loaded; files touched by Read/Edit/Write bring in the
|
|
23
|
+
* instruction files that apply to them (lazy-instructions.mjs),
|
|
24
|
+
* returned as a text block appended to the tool results.
|
|
25
|
+
*
|
|
26
|
+
* Automatic compaction does not repeat on the next turn if it could not get
|
|
27
|
+
* under the limit: it waits until the projection has grown by a tenth of the
|
|
28
|
+
* window since, so a history that cannot shrink further is not re-summarised
|
|
29
|
+
* every turn.
|
|
30
|
+
*
|
|
31
|
+
* @module lib/engine/context/manager
|
|
32
|
+
*/
|
|
33
|
+
|
|
34
|
+
import { measureRequest, initialCalibration, calibrate, projectTokens, compactionLimit, charsForTokens, reportedInputTokens } from "./budget.mjs";
|
|
35
|
+
import { stableToolDefinitions } from "./cache.mjs";
|
|
36
|
+
import { runCompaction } from "./compaction.mjs";
|
|
37
|
+
import { touchedPath, formatLazyInstructions } from "./lazy-instructions.mjs";
|
|
38
|
+
import { pruneOldImages, DEFAULT_KEEP_IMAGE_RESULTS } from "./images.mjs";
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* @param {object} p
|
|
42
|
+
* @param {import('./budget.mjs').ContextConfig} p.config
|
|
43
|
+
* @param {string} p.cwd
|
|
44
|
+
* @param {(turn:{system:string, messages:any[], tools:any[], signal?:AbortSignal})=>Promise<any>} p.summarise same wire, same tier
|
|
45
|
+
* @param {any[]} p.tools every callable tool, in stable order
|
|
46
|
+
* @param {ReturnType<typeof import('../tools/toolsearch.mjs').createDeferredTools>|null} [p.deferred]
|
|
47
|
+
* @param {{preCompact:(o:{trigger:string, customInstructions?:string})=>Promise<{blockReason?:string|null}>}|null} [p.hooks]
|
|
48
|
+
* @param {ReturnType<typeof import('./lazy-instructions.mjs').createLazyInstructions>|null} [p.lazy]
|
|
49
|
+
* @param {{refreshChanged:()=>Promise<{changed:boolean, tools:any[], errors:string[]}>}|null} [p.mcp]
|
|
50
|
+
* @param {(mcpTools:any[]) => any[]} [p.composeTools] the full tool list for a new MCP tool list
|
|
51
|
+
* @param {{drain:()=>Array<{kind:'user', message:any}|{kind:'control', subtype:string, customInstructions?:string}>}|null} [p.input]
|
|
52
|
+
* @param {(e:{trigger:string, stage:number, message:any}) => void} [p.onCompact] a stage-2 summary was made
|
|
53
|
+
* @param {(line:string) => void} [p.log]
|
|
54
|
+
*/
|
|
55
|
+
export function createContextManager(p) {
|
|
56
|
+
const log = p.log ?? (() => {});
|
|
57
|
+
let cal = initialCalibration();
|
|
58
|
+
let tools = p.tools;
|
|
59
|
+
/** @type {{chars:number, images:number}|null} */
|
|
60
|
+
let lastSize = null;
|
|
61
|
+
/** @type {number|null} projection right after the last automatic compaction */
|
|
62
|
+
let lastAfter = null;
|
|
63
|
+
/** @type {Array<{trigger:string, stage:number, cleared:number, before:number, after:number, blocked?:string, error?:string}>} */
|
|
64
|
+
const compactions = [];
|
|
65
|
+
|
|
66
|
+
const active = () => (p.deferred ? p.deferred.active() : tools);
|
|
67
|
+
|
|
68
|
+
return {
|
|
69
|
+
async beforeTurn({ system, messages, signal, recordModelCall }) {
|
|
70
|
+
/** @type {{messages?:any[], append:any[], tools:any[], definitions:any[]}} */
|
|
71
|
+
const out = { append: [], tools, definitions: [] };
|
|
72
|
+
|
|
73
|
+
if (p.mcp) {
|
|
74
|
+
const r = await p.mcp.refreshChanged();
|
|
75
|
+
for (const e of r.errors) log(`MCP: ${e}`);
|
|
76
|
+
if (r.changed) {
|
|
77
|
+
tools = p.composeTools ? p.composeTools(r.tools) : tools;
|
|
78
|
+
p.deferred?.replace(tools);
|
|
79
|
+
out.tools = tools;
|
|
80
|
+
log(`MCP tool list changed; ${tools.length} tools now available`);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** @type {{customInstructions?:string}|null} */
|
|
85
|
+
let manual = null;
|
|
86
|
+
for (const item of p.input?.drain() ?? []) {
|
|
87
|
+
if (item.kind === "control" && item.subtype === "compact") manual = item;
|
|
88
|
+
else if (item.kind === "user") out.append.push(item.message);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Images on older tool results leave the history (each rewrite happens once, as an image ages out).
|
|
92
|
+
const pruned = pruneOldImages(messages, p.config.keepImageResults ?? DEFAULT_KEEP_IMAGE_RESULTS);
|
|
93
|
+
if (pruned.removed > 0) {
|
|
94
|
+
messages = pruned.messages;
|
|
95
|
+
out.messages = pruned.messages;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const definitions = stableToolDefinitions(active());
|
|
99
|
+
out.definitions = definitions;
|
|
100
|
+
const measure = (/** @type any[] */ history) => measureRequest({ system, messages: [...history, ...out.append], tools: definitions });
|
|
101
|
+
let size = measure(messages);
|
|
102
|
+
const projected = projectTokens(cal, size);
|
|
103
|
+
const { over, limit } = compactionLimit({ projected, contextWindow: p.config.contextWindow, threshold: p.config.threshold });
|
|
104
|
+
const minGrowth = lastAfter !== null && lastAfter >= limit ? Math.ceil(p.config.contextWindow * 0.1) : 0;
|
|
105
|
+
const auto = p.config.autoCompact && over && (lastAfter === null || projected - lastAfter >= minGrowth);
|
|
106
|
+
|
|
107
|
+
if (manual || auto) {
|
|
108
|
+
const trigger = manual ? "manual" : "auto";
|
|
109
|
+
const r = await runCompaction({
|
|
110
|
+
messages,
|
|
111
|
+
trigger,
|
|
112
|
+
config: p.config,
|
|
113
|
+
callModel: p.summarise,
|
|
114
|
+
hooks: p.hooks ?? null,
|
|
115
|
+
stillOver: (m) => compactionLimit({ projected: projectTokens(cal, measure(m)), contextWindow: p.config.contextWindow, threshold: p.config.threshold }).over,
|
|
116
|
+
instructions: p.lazy?.injected() ?? [],
|
|
117
|
+
charsPerToken: cal.charsPerToken,
|
|
118
|
+
maxTranscriptChars: charsForTokens(cal, Math.floor(p.config.contextWindow * 0.5)),
|
|
119
|
+
customInstructions: manual?.customInstructions,
|
|
120
|
+
record: recordModelCall,
|
|
121
|
+
signal,
|
|
122
|
+
});
|
|
123
|
+
if (r.stage > 0) out.messages = r.messages;
|
|
124
|
+
size = measure(r.messages);
|
|
125
|
+
const after = projectTokens(cal, size);
|
|
126
|
+
if (trigger === "auto") lastAfter = after;
|
|
127
|
+
const entry = { trigger, stage: r.stage, cleared: r.cleared, before: projected, after };
|
|
128
|
+
if (r.blocked) Object.assign(entry, { blocked: r.blocked });
|
|
129
|
+
if (r.error) Object.assign(entry, { error: r.error });
|
|
130
|
+
compactions.push(entry);
|
|
131
|
+
log(
|
|
132
|
+
`context: ${trigger} compaction, stage ${r.stage} (${r.cleared} tool result${r.cleared === 1 ? "" : "s"} cleared; ~${projected} → ~${after} tokens of ${p.config.contextWindow})` +
|
|
133
|
+
(r.blocked ? `; a PreCompact hook limited it to clearing tool results: ${r.blocked}` : "") +
|
|
134
|
+
(r.error ? `; ${r.error}` : ""),
|
|
135
|
+
);
|
|
136
|
+
if (r.stage === 2) p.onCompact?.({ trigger, stage: 2, message: r.messages[0] });
|
|
137
|
+
}
|
|
138
|
+
lastSize = size;
|
|
139
|
+
return out;
|
|
140
|
+
},
|
|
141
|
+
|
|
142
|
+
observe({ res }) {
|
|
143
|
+
if (!lastSize || !res?.ok) return;
|
|
144
|
+
const inputTokens = reportedInputTokens(res.value?.usage);
|
|
145
|
+
if (res.value?.usage?.usageSource === "estimated") return;
|
|
146
|
+
cal = calibrate(cal, { ...lastSize, inputTokens });
|
|
147
|
+
},
|
|
148
|
+
|
|
149
|
+
async afterTools({ calls, results }) {
|
|
150
|
+
if (p.deferred) {
|
|
151
|
+
const direct = calls.filter((c) => p.deferred?.isDeferred(c.name) && !p.deferred.isLoaded(c.name)).map((c) => c.name);
|
|
152
|
+
if (direct.length > 0) p.deferred.load(direct);
|
|
153
|
+
}
|
|
154
|
+
if (!p.lazy) return null;
|
|
155
|
+
const entries = [];
|
|
156
|
+
calls.forEach((c, i) => {
|
|
157
|
+
if (results[i]?.is_error) return;
|
|
158
|
+
const file = touchedPath(c.name, c.input, p.cwd);
|
|
159
|
+
if (file) entries.push(...(/** @type any */ (p.lazy).touch(file)));
|
|
160
|
+
});
|
|
161
|
+
const text = formatLazyInstructions(entries);
|
|
162
|
+
return text ? [{ type: "text", text }] : null;
|
|
163
|
+
},
|
|
164
|
+
|
|
165
|
+
/** For the result object. */
|
|
166
|
+
stats() {
|
|
167
|
+
return {
|
|
168
|
+
contextWindow: p.config.contextWindow,
|
|
169
|
+
threshold: p.config.threshold,
|
|
170
|
+
charsPerToken: Math.round(cal.charsPerToken * 100) / 100,
|
|
171
|
+
compactions: [...compactions],
|
|
172
|
+
deferredTools: p.deferred ? p.deferred.deferred().length : 0,
|
|
173
|
+
loadedTools: p.deferred ? p.deferred.loadedNames() : [],
|
|
174
|
+
instructionsLoaded: p.lazy ? p.lazy.injected().map((f) => f.path) : [],
|
|
175
|
+
};
|
|
176
|
+
},
|
|
177
|
+
/** The tools currently callable. */
|
|
178
|
+
get tools() {
|
|
179
|
+
return tools;
|
|
180
|
+
},
|
|
181
|
+
};
|
|
182
|
+
}
|