mixdog 1.3.1 → 1.3.2
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/README.md +8 -4
- package/package.json +2 -2
- package/scripts/native-release-status.mjs +238 -0
- package/src/defaults/shipped-history.json +5 -1
- package/src/defaults/skills/pptx/scripts/qc-pages.mjs +3 -2
- package/src/defaults/skills/setup/SKILL.md +23 -1
- package/src/defaults/skills/setup/references/actions.md +50 -3
- package/src/defaults/skills/setup/references/surfaces.md +23 -3
- package/src/defaults/skills/setup-guide/SKILL.md +7 -3
- package/src/defaults/skills/visualize/SKILL.md +9 -6
- package/src/defaults/skills/visualize/references/craft.md +8 -1
- package/src/help.mjs +1 -1
- package/src/lib/keychain-cjs.cjs +4 -1
- package/src/lib/rules-builder.cjs +14 -7
- package/src/rules/agent-mode/AGENT_MODE.md +8 -5
- package/src/rules/agent-mode/MIXY.md +6 -8
- package/src/rules/lead/LEAD.md +2 -2
- package/src/rules/shared/10-tool-workflow.md +4 -0
- package/src/rules/shared/20-research.md +2 -0
- package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch/preset.mjs +5 -0
- package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch/session-prep.mjs +8 -0
- package/src/runtime/agent/orchestrator/agent-runtime/agent-progress-watchdog.mjs +6 -1
- package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +114 -26
- package/src/runtime/agent/orchestrator/agent-runtime/maintenance-route.mjs +8 -0
- package/src/runtime/agent/orchestrator/agent-runtime/room-supervisor-completion.mjs +5 -4
- package/src/runtime/agent/orchestrator/agent-trace.mjs +3 -0
- package/src/runtime/agent/orchestrator/cache-break-trace.mjs +4 -0
- package/src/runtime/agent/orchestrator/config-presets.mjs +2 -0
- package/src/runtime/agent/orchestrator/config-storage.mjs +10 -3
- package/src/runtime/agent/orchestrator/config.mjs +11 -0
- package/src/runtime/agent/orchestrator/context/role-instructions.mjs +9 -2
- package/src/runtime/agent/orchestrator/mcp/agent-connectors.mjs +97 -5
- package/src/runtime/agent/orchestrator/mcp/child-tree.mjs +6 -10
- package/src/runtime/agent/orchestrator/mcp/client.mjs +7 -4
- package/src/runtime/agent/orchestrator/providers/anthropic-api-request.mjs +80 -31
- package/src/runtime/agent/orchestrator/providers/anthropic-model-resolve.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-client-version.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +67 -38
- package/src/runtime/agent/orchestrator/providers/anthropic-turn-result.mjs +2 -0
- package/src/runtime/agent/orchestrator/providers/antigravity-request.mjs +6 -2
- package/src/runtime/agent/orchestrator/providers/auto-effort.mjs +27 -8
- package/src/runtime/agent/orchestrator/providers/cursor-wire.mjs +31 -4
- package/src/runtime/agent/orchestrator/providers/cursor.mjs +4 -1
- package/src/runtime/agent/orchestrator/providers/custom-provider.mjs +11 -8
- package/src/runtime/agent/orchestrator/providers/lib/anthropic-prefix-trace.mjs +76 -0
- package/src/runtime/agent/orchestrator/providers/lib/anthropic-request-utils.mjs +16 -0
- package/src/runtime/agent/orchestrator/providers/lib/tail-reminder-split.mjs +13 -0
- package/src/runtime/agent/orchestrator/providers/media-normalization.mjs +16 -1
- package/src/runtime/agent/orchestrator/providers/mixdog-local.mjs +3 -1
- package/src/runtime/agent/orchestrator/providers/model-proxy-provider.mjs +1 -0
- package/src/runtime/agent/orchestrator/providers/openai-codex-metadata.mjs +5 -5
- package/src/runtime/agent/orchestrator/providers/openai-compat-models.mjs +9 -5
- package/src/runtime/agent/orchestrator/providers/openai-compat-responses.mjs +9 -1
- package/src/runtime/agent/orchestrator/providers/openai-compat-wire.mjs +2 -1
- package/src/runtime/agent/orchestrator/providers/openai-direct-request.mjs +30 -3
- package/src/runtime/agent/orchestrator/providers/openai-model-capabilities.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +3 -1
- package/src/runtime/agent/orchestrator/providers/opencode-go-oauth.mjs +61 -8
- package/src/runtime/agent/orchestrator/providers/registry.mjs +9 -0
- package/src/runtime/agent/orchestrator/runtime-core/config-helpers.mjs +90 -24
- package/src/runtime/agent/orchestrator/runtime-core/fs-utils.mjs +8 -1
- package/src/runtime/agent/orchestrator/runtime-core/goal-mutations.mjs +1 -1
- package/src/runtime/agent/orchestrator/runtime-core/goal-storage.mjs +68 -22
- package/src/runtime/agent/orchestrator/runtime-core/goal-tool-exec.mjs +1 -1
- package/src/runtime/agent/orchestrator/runtime-core/orchestration.mjs +4 -6
- package/src/runtime/agent/orchestrator/runtime-core/route-context-state.mjs +81 -0
- package/src/runtime/agent/orchestrator/runtime-core/session-text.mjs +2 -1
- package/src/runtime/agent/orchestrator/runtime-core/tool-catalog.mjs +12 -1
- package/src/runtime/agent/orchestrator/runtime-core/tool-defs.mjs +6 -5
- package/src/runtime/agent/orchestrator/runtime-core/workflow/agents.mjs +7 -6
- package/src/runtime/agent/orchestrator/runtime-core/workflow/context.mjs +1 -1
- package/src/runtime/agent/orchestrator/runtime-core/workflow/packs.mjs +8 -4
- package/src/runtime/agent/orchestrator/runtime-core/workflow.mjs +2 -0
- package/src/runtime/agent/orchestrator/session/agent-mode.mjs +0 -6
- package/src/runtime/agent/orchestrator/session/agent-room-supervisor.d.mts +1 -1
- package/src/runtime/agent/orchestrator/session/agent-room-supervisor.mjs +22 -4
- package/src/runtime/agent/orchestrator/session/agent-room.d.mts +17 -2
- package/src/runtime/agent/orchestrator/session/agent-room.mjs +133 -38
- package/src/runtime/agent/orchestrator/session/attachment-preflight.mjs +80 -0
- package/src/runtime/agent/orchestrator/session/catalog-db/schema.mjs +4 -1
- package/src/runtime/agent/orchestrator/session/compact/execution-tail.mjs +14 -11
- package/src/runtime/agent/orchestrator/session/compact/forked-summary.mjs +3 -0
- package/src/runtime/agent/orchestrator/session/compact/runner.mjs +68 -18
- package/src/runtime/agent/orchestrator/session/compact/summary-mode.mjs +16 -0
- package/src/runtime/agent/orchestrator/session/compact/summary-route.mjs +87 -0
- package/src/runtime/agent/orchestrator/session/compact/summary.mjs +2 -0
- package/src/runtime/agent/orchestrator/session/compact/text-utils.mjs +5 -1
- package/src/runtime/agent/orchestrator/session/compact/user-message-kind.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/compaction-archive-capture.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/context-utils.mjs +41 -10
- package/src/runtime/agent/orchestrator/session/human-gate/delegation.mjs +230 -0
- package/src/runtime/agent/orchestrator/session/human-gate/gate.mjs +136 -32
- package/src/runtime/agent/orchestrator/session/human-gate/outbound-policy.mjs +20 -0
- package/src/runtime/agent/orchestrator/session/human-gate/requests.mjs +21 -9
- package/src/runtime/agent/orchestrator/session/human-gate/run-policy.mjs +73 -0
- package/src/runtime/agent/orchestrator/session/human-gate/store.mjs +55 -27
- package/src/runtime/agent/orchestrator/session/human-gate/unattended-port.mjs +15 -0
- package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +4 -1
- package/src/runtime/agent/orchestrator/session/loop/fresh-context.mjs +189 -105
- package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +6 -2
- package/src/runtime/agent/orchestrator/session/loop/route-fallback.mjs +5 -5
- package/src/runtime/agent/orchestrator/session/loop/send-phase.mjs +3 -0
- package/src/runtime/agent/orchestrator/session/loop/steering.mjs +3 -0
- package/src/runtime/agent/orchestrator/session/loop/tool-exec/before-hook.mjs +38 -5
- package/src/runtime/agent/orchestrator/session/loop/tool-exec/dispatch.mjs +21 -0
- package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +2 -1
- package/src/runtime/agent/orchestrator/session/loop/tool-exposure-migration.mjs +12 -0
- package/src/runtime/agent/orchestrator/session/loop/tool-helpers.mjs +11 -5
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-auto-effort.mjs +13 -2
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-execute.mjs +4 -0
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-prepare.mjs +6 -0
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-settle.mjs +4 -0
- package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +44 -18
- package/src/runtime/agent/orchestrator/session/manager/orchestration-mode-switch-notice.mjs +99 -0
- package/src/runtime/agent/orchestrator/session/manager/owner-route-fallbacks.mjs +42 -0
- package/src/runtime/agent/orchestrator/session/manager/pending-messages.mjs +3 -9
- package/src/runtime/agent/orchestrator/session/manager/pending-spool-file.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/manager/pending-spool-shard.mjs +5 -3
- package/src/runtime/agent/orchestrator/session/manager/rules-cache.mjs +40 -1
- package/src/runtime/agent/orchestrator/session/manager/runtime-liveness.mjs +12 -0
- package/src/runtime/agent/orchestrator/session/manager/runtime-state.mjs +20 -0
- package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +55 -27
- package/src/runtime/agent/orchestrator/session/manager/session-prompt-composition.mjs +3 -8
- package/src/runtime/agent/orchestrator/session/manager/session-record.mjs +12 -1
- package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint-journal.mjs +6 -15
- package/src/runtime/agent/orchestrator/session/manager/turn-interruption.mjs +24 -8
- package/src/runtime/agent/orchestrator/session/manager/workflow-switch-notice.mjs +94 -13
- package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +42 -10
- package/src/runtime/agent/orchestrator/session/provider-prefix-guard.mjs +21 -3
- package/src/runtime/agent/orchestrator/session/routine-result.d.mts +18 -3
- package/src/runtime/agent/orchestrator/session/routine-result.mjs +120 -9
- package/src/runtime/agent/orchestrator/session/send-with-recovery.mjs +5 -1
- package/src/runtime/agent/orchestrator/session/side-question-store.mjs +3 -1
- package/src/runtime/agent/orchestrator/session/store/canonical-reader.mjs +41 -7
- package/src/runtime/agent/orchestrator/session/store/lifecycle-barriers.mjs +10 -2
- package/src/runtime/agent/orchestrator/session/store/load-cache.mjs +14 -2
- package/src/runtime/agent/orchestrator/session/store/session-delete.mjs +15 -1
- package/src/runtime/agent/orchestrator/session/store/sweep/sweep-record.mjs +8 -3
- package/src/runtime/agent/orchestrator/session/store/sweep/sweep-row.mjs +36 -1
- package/src/runtime/agent/orchestrator/session/store-summary-catalog.mjs +4 -1
- package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +40 -5
- package/src/runtime/agent/orchestrator/session/store-summary-last-message.mjs +47 -2
- package/src/runtime/agent/orchestrator/session/tail-reminder-rules.mjs +125 -0
- package/src/runtime/agent/orchestrator/session/tool-batch/finalize.mjs +4 -0
- package/src/runtime/agent/orchestrator/session/tool-result-offload.mjs +78 -43
- package/src/runtime/agent/orchestrator/session/transcript-restore/restore.mjs +35 -5
- package/src/runtime/agent/orchestrator/session/transcript-restore/user-text.mjs +42 -2
- package/src/runtime/agent/orchestrator/tools/builtin/advisory-lock.mjs +35 -13
- package/src/runtime/agent/orchestrator/tools/builtin/atomic-write.mjs +25 -5
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/backgrounded-result.mjs +4 -3
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/completed-result.mjs +13 -14
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/transport-artifacts.mjs +9 -0
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +19 -14
- package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +7 -1
- package/src/runtime/agent/orchestrator/tools/builtin/lib/shell-job-records.mjs +6 -14
- package/src/runtime/agent/orchestrator/tools/builtin/pdf-test-fixtures.mjs +5 -3
- package/src/runtime/agent/orchestrator/tools/builtin/read-image.mjs +4 -2
- package/src/runtime/agent/orchestrator/tools/builtin/read-single-media.mjs +47 -2
- package/src/runtime/agent/orchestrator/tools/builtin/read-special-files.mjs +264 -87
- package/src/runtime/agent/orchestrator/tools/builtin/shell-analysis.mjs +10 -4
- package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +34 -21
- package/src/runtime/agent/orchestrator/tools/code-graph/constants.mjs +8 -4
- package/src/runtime/agent/orchestrator/tools/lib/shell-descendants.mjs +9 -30
- package/src/runtime/agent/orchestrator/tools/lib/shell-run-settle.mjs +24 -12
- package/src/runtime/agent/orchestrator/tools/lib/shell-run-spawn.mjs +3 -2
- package/src/runtime/agent/orchestrator/tools/lib/shell-run-state.mjs +6 -2
- package/src/runtime/agent/orchestrator/tools/patch/delete-quarantine.mjs +2 -1
- package/src/runtime/agent/orchestrator/tools/patch/replay-capture.mjs +4 -2
- package/src/runtime/agent/orchestrator/tools/patch/rollback-state.mjs +33 -2
- package/src/runtime/agent/orchestrator/tools/patch-manifest.json +13 -13
- package/src/runtime/agent/orchestrator/tools/shell-command.mjs +5 -3
- package/src/runtime/agent/orchestrator/tools/shell-exec-output.mjs +3 -1
- package/src/runtime/attachments/attachment-reference.mjs +155 -0
- package/src/runtime/attachments/limits.mjs +5 -1
- package/src/runtime/attachments/office-extract.mjs +3 -6
- package/src/runtime/attachments/pdf-extract.mjs +84 -10
- package/src/runtime/attachments/store.mjs +49 -30
- package/src/runtime/browser-bridge/client.mjs +73 -20
- package/src/runtime/channels/lib/runtime-paths.mjs +1 -1
- package/src/runtime/channels/lib/scheduler.mjs +14 -7
- package/src/runtime/channels/lib/state-file.mjs +15 -15
- package/src/runtime/channels/lib/voice-runtime-fetcher.mjs +14 -13
- package/src/runtime/channels/lib/webhook/relay-tunnel.mjs +22 -18
- package/src/runtime/channels/lib/webhook/session-dispatch.mjs +14 -36
- package/src/runtime/channels/lib/worker-main.mjs +3 -2
- package/src/runtime/effort-judge/compact-tokenizer.mjs +8 -2
- package/src/runtime/effort-judge/judge-client.mjs +5 -2
- package/src/runtime/effort-judge/model-install.mjs +16 -8
- package/src/runtime/local-provider/asset-installer.mjs +10 -4
- package/src/runtime/local-provider/catalog.mjs +5 -2
- package/src/runtime/local-provider/registered-models.mjs +64 -27
- package/src/runtime/local-provider/server.mjs +2 -3
- package/src/runtime/media/adapters/image-api.mjs +89 -0
- package/src/runtime/media/auth.mjs +64 -1
- package/src/runtime/media/catalog.mjs +66 -4
- package/src/runtime/media/jobs.mjs +4 -0
- package/src/runtime/media/lanes.mjs +46 -5
- package/src/runtime/media/media-usage.mjs +17 -0
- package/src/runtime/media/renditions.mjs +15 -15
- package/src/runtime/media/store.mjs +36 -35
- package/src/runtime/media/tool.mjs +8 -1
- package/src/runtime/memory/lib/cycle-scheduler/health-ledger.mjs +8 -2
- package/src/runtime/memory/lib/embedding-model-files.mjs +30 -7
- package/src/runtime/memory/lib/ingest/transcript-tail.mjs +16 -7
- package/src/runtime/memory/lib/pg/process.mjs +8 -4
- package/src/runtime/memory/lib/project-id-resolver.mjs +5 -1
- package/src/runtime/memory/lib/runtime-fetcher.mjs +6 -1
- package/src/runtime/office/core/journal.mjs +7 -14
- package/src/runtime/office/design/library/design-library-core.mjs +4 -16
- package/src/runtime/office/pdf/ocr-engine-worker.mjs +37 -0
- package/src/runtime/office/pdf/ocr-engine.mjs +146 -0
- package/src/runtime/office/pdf/pdf-ocr.mjs +51 -21
- package/src/runtime/office/pdf/pdf-render.mjs +30 -18
- package/src/runtime/office/portable/ooxml-validator.mjs +2 -2
- package/src/runtime/office/portable/portable-soffice.mjs +4 -0
- package/src/runtime/shared/agent-kind-migration.mjs +200 -0
- package/src/runtime/shared/agent-message-envelope.d.mts +6 -0
- package/src/runtime/shared/agent-message-envelope.mjs +44 -0
- package/src/runtime/shared/agent-reap-state.d.mts +4 -0
- package/src/runtime/shared/agent-store.mjs +24 -7
- package/src/runtime/shared/agent-task-starts.mjs +27 -0
- package/src/runtime/shared/atomic-file.d.mts +50 -0
- package/src/runtime/shared/atomic-file.mjs +394 -132
- package/src/runtime/shared/background-tasks.mjs +7 -4
- package/src/runtime/shared/bot-hops.mjs +30 -0
- package/src/runtime/shared/bot-split-migration.mjs +1 -1
- package/src/runtime/shared/bounded-download.mjs +4 -2
- package/src/runtime/shared/config.mjs +39 -9
- package/src/runtime/shared/defaults-separation.mjs +6 -2
- package/src/runtime/shared/delegated-approval-host.mjs +74 -0
- package/src/runtime/shared/err-text.mjs +4 -0
- package/src/runtime/shared/error-presentation.mjs +22 -4
- package/src/runtime/shared/file-has-data.mjs +28 -0
- package/src/runtime/shared/llm/usage-ledger-import.mjs +37 -18
- package/src/runtime/shared/pending-agent-work.mjs +54 -0
- package/src/runtime/shared/process-lifecycle.mjs +8 -12
- package/src/runtime/shared/projects.mjs +50 -41
- package/src/runtime/shared/provider-accounts.mjs +17 -12
- package/src/runtime/shared/routine-trigger.d.mts +6 -1
- package/src/runtime/shared/routine-trigger.mjs +14 -6
- package/src/runtime/shared/run-child.d.mts +3 -0
- package/src/runtime/shared/run-child.mjs +24 -3
- package/src/runtime/shared/session-baseline-store.mjs +8 -2
- package/src/runtime/shared/singleton-owner.mjs +7 -6
- package/src/runtime/shared/skill-tool-dependencies.mjs +4 -10
- package/src/runtime/shared/staged-update.mjs +4 -2
- package/src/runtime/shared/statusline/statusline-segments.mjs +4 -15
- package/src/runtime/shared/tool-execution-contract.mjs +6 -0
- package/src/runtime/shared/transcript-metadata.mjs +28 -3
- package/src/runtime/shared/transcript-writer/session-record.mjs +7 -6
- package/src/runtime/shared/turn-snapshot-store.mjs +5 -4
- package/src/runtime/shared/turn-snapshot.mjs +44 -2
- package/src/runtime/shared/user-cwd.mjs +9 -4
- package/src/runtime/shared/user-data-guard.mjs +41 -42
- package/src/runtime/shared/utf8-cut.mjs +10 -0
- package/src/runtime/shared/worktrees/activity-gate.mjs +269 -61
- package/src/runtime/shared/worktrees/registry.mjs +26 -26
- package/src/runtime/tidy/core-install.mjs +26 -4
- package/src/runtime/tidy/install.mjs +91 -23
- package/src/runtime/web-search/lib/config.mjs +4 -16
- package/src/session-runtime/agent-home-delivery.mjs +555 -199
- package/src/session-runtime/agent-room-delivery.mjs +143 -36
- package/src/session-runtime/agent-task-sessions.mjs +667 -165
- package/src/session-runtime/agent-tool-routing.mjs +1 -0
- package/src/session-runtime/boot/apis.mjs +45 -24
- package/src/session-runtime/boot/config.mjs +1 -0
- package/src/session-runtime/boot/facade.mjs +11 -1
- package/src/session-runtime/boot/infrastructure.mjs +14 -2
- package/src/session-runtime/boot/providers.mjs +2 -2
- package/src/session-runtime/boot/shared.mjs +12 -6
- package/src/session-runtime/boot/tools.mjs +2 -2
- package/src/session-runtime/bot-status.mjs +432 -0
- package/src/session-runtime/config-lifecycle/lead-route-sync.mjs +29 -0
- package/src/session-runtime/config-lifecycle.mjs +60 -55
- package/src/session-runtime/cwd-plugins/core-memory-context.mjs +2 -4
- package/src/session-runtime/held-requests.mjs +36 -33
- package/src/session-runtime/internal-tool-executor/bridge-tools.mjs +10 -1
- package/src/session-runtime/internal-tool-executor/feature-tools.mjs +2 -2
- package/src/session-runtime/internal-tool-executor/workspace-tools.mjs +19 -4
- package/src/session-runtime/lifecycle/session-catalog.mjs +12 -2
- package/src/session-runtime/lifecycle/teardown.mjs +11 -3
- package/src/session-runtime/mcp-glue/connect.mjs +100 -186
- package/src/session-runtime/mcp-glue/initial-connect-gate.mjs +57 -0
- package/src/session-runtime/mcp-glue/server-connection.mjs +71 -0
- package/src/session-runtime/media-api.mjs +5 -2
- package/src/session-runtime/model-route/route-tuning.mjs +4 -1
- package/src/session-runtime/model-route/set-route.mjs +61 -1
- package/src/session-runtime/owned-automation-run.mjs +8 -1
- package/src/session-runtime/room-member-gate.mjs +16 -3
- package/src/session-runtime/route-resolution.mjs +16 -8
- package/src/session-runtime/route-state.mjs +1 -59
- package/src/session-runtime/routine-home-turn.mjs +5 -3
- package/src/session-runtime/runtime-bootstrap.mjs +5 -3
- package/src/session-runtime/runtime-paths.mjs +11 -5
- package/src/session-runtime/runtime-tunables.mjs +6 -0
- package/src/session-runtime/services/agent-tool/close-flow/close-all.mjs +11 -2
- package/src/session-runtime/services/agent-tool/close-flow.mjs +4 -2
- package/src/session-runtime/services/agent-tool/execute-send.mjs +1 -0
- package/src/session-runtime/services/agent-tool/execute-spawn.mjs +1 -0
- package/src/session-runtime/services/agent-tool/restart-notify.mjs +10 -0
- package/src/session-runtime/services/agent-tool/send-flow.mjs +25 -21
- package/src/session-runtime/services/agent-tool/spawn-flow/job-start.mjs +4 -0
- package/src/session-runtime/services/agent-tool/spawn-flow/spawn-prep.mjs +6 -3
- package/src/session-runtime/services/agent-tool/spawn-flow/spawn-run.mjs +8 -5
- package/src/session-runtime/services/agent-tool/worker-index/row-liveness.mjs +22 -2
- package/src/session-runtime/services/agent-tool/worker-index.mjs +4 -6
- package/src/session-runtime/services/agent-tool.mjs +6 -0
- package/src/session-runtime/services/daemon-crash-capture/daemon-exit-record.mjs +1 -1
- package/src/session-runtime/services/daemon-crash-capture/retention.mjs +4 -3
- package/src/session-runtime/services/hook-bus/config-loader.mjs +14 -5
- package/src/session-runtime/services/hook-bus/rule-file.mjs +49 -11
- package/src/session-runtime/services/plugin-admin.mjs +26 -26
- package/src/session-runtime/services/provider-admin.mjs +14 -4
- package/src/session-runtime/services/schedule-access.mjs +33 -4
- package/src/session-runtime/services/seeds.mjs +4 -3
- package/src/session-runtime/services/usage-dashboard.mjs +1 -1
- package/src/session-runtime/services/usage-stats-model.mjs +2 -1
- package/src/session-runtime/session-create/materialize.mjs +2 -3
- package/src/session-runtime/session-create/session-options.mjs +15 -2
- package/src/session-runtime/settings-builtin-tools-api.mjs +63 -1
- package/src/session-runtime/settings-compaction-api.mjs +86 -39
- package/src/session-runtime/setup-tool/executor.mjs +8 -2
- package/src/session-runtime/setup-tool/settings-contract.mjs +31 -1
- package/src/session-runtime/setup-tool/tool-defs.mjs +20 -3
- package/src/session-runtime/skills-api.mjs +12 -5
- package/src/session-runtime/tool-policy/agent-profile-fold.mjs +265 -0
- package/src/session-runtime/tool-policy/mode-switch.mjs +54 -0
- package/src/session-runtime/tool-policy/pending-switches.mjs +59 -0
- package/src/session-runtime/tool-policy/policy-stage.mjs +165 -0
- package/src/session-runtime/tool-policy/session-switches.mjs +169 -0
- package/src/session-runtime/tool-policy/surface-helpers.mjs +114 -0
- package/src/session-runtime/tool-policy/workflow-overlay.mjs +145 -0
- package/src/session-runtime/tool-policy-refresh.mjs +47 -369
- package/src/session-runtime/tool-surface.mjs +5 -3
- package/src/session-runtime/turn/turn-run/turn-prepare.mjs +95 -88
- package/src/session-runtime/turn/turn-run/turn-timing.mjs +4 -3
- package/src/session-runtime/turn/turn-run.mjs +2 -1
- package/src/session-runtime/workflow-agents-api/agent-connectors.mjs +54 -7
- package/src/session-runtime/workflow-agents-api/agent-editor.mjs +519 -333
- package/src/session-runtime/workflow-agents-api/agent-route.mjs +46 -46
- package/src/session-runtime/workflow-agents-api/shared.mjs +5 -1
- package/src/session-runtime/workflow-agents-api/style-and-mode.mjs +14 -1
- package/src/session-runtime/workflow-agents-api/workflow-packs.mjs +6 -3
- package/src/session-runtime/workspace/workspace-state.mjs +7 -6
- package/src/standalone/daemon-agent-control.mjs +4 -1
- package/src/standalone/daemon-session-control.mjs +51 -10
- package/src/standalone/daemon.mjs +28 -10
- package/src/standalone/local-session-runtime.mjs +22 -14
- package/src/standalone/session-client.mjs +7 -2
- package/src/standalone/session-protocol.mjs +7 -0
- package/src/standalone/session-service.mjs +11 -0
- package/src/standalone/session-state-projection.mjs +19 -9
- package/src/standalone/session-tool-output.mjs +35 -9
- package/src/tui/app/maintenance-pickers/auto-clear-picker.mjs +19 -13
- package/src/tui/app/settings-picker/settings-rows.mjs +2 -2
- package/src/tui/app/slash-commands.mjs +1 -1
- package/src/tui/app/usage-context-panels/context-model.mjs +8 -2
- package/src/tui/dist/index.mjs +529 -333
- package/src/tui/prompt-history-store.mjs +16 -21
- package/src/tui/session/context-state.mjs +4 -0
- package/src/tui/session/session-api/intake/abort.mjs +19 -1
- package/src/tui/session/session-api/media.mjs +2 -1
- package/src/tui/session/session-api/routes-agent-definitions.mjs +75 -0
- package/src/tui/session/session-api/routes-write.mjs +203 -0
- package/src/tui/session/session-api/routes.mjs +23 -247
- package/src/tui/session/session-api/settings.mjs +14 -1
- package/src/tui/session/session-flow/drain.mjs +28 -3
- package/src/tui/session/session-flow/failed-turn-rewind.mjs +4 -2
- package/src/tui/session/session-flow/queue/take-entries.mjs +8 -0
- package/src/tui/session/session-flow/steering.mjs +8 -3
- package/src/tui/session/tool-approval.mjs +185 -20
- package/src/tui/session/transcript-intake.mjs +2 -0
- package/src/tui/session/turn.mjs +23 -14
- package/src/tui/transcript-tool-failures.mjs +8 -2
- package/src/vendor/statusline/src/gateway/route-meta.mjs +7 -8
- package/src/vendor/statusline/src/gateway/session-routes.mjs +6 -7
- package/src/rules/agent-mode/ROUTINE.md +0 -6
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
// or a project may use. Every installed connector is on by default for every
|
|
3
3
|
// agent and project, existing and future ones; a policy only switches some off:
|
|
4
4
|
// agents/<id>/agent.json connectors: { off: [serverId…], tools?: { [serverId]: { [tool]: permission } } }
|
|
5
|
+
// (bots/<id>/ for a store bot; Mixy — runtime id `lead` — in her identity folder)
|
|
5
6
|
// <data>/project-connectors.json { projects: { [projectRoot]: { off: [serverId…] } } }
|
|
6
7
|
// `tools` keeps per-tool overrides (auto | needs_approval | disabled); a tool
|
|
7
8
|
// without one is auto, except a catalog connector's high-risk tools, which
|
|
@@ -12,11 +13,16 @@
|
|
|
12
13
|
// connected tool, so the schema and the BP2 manifest stay shared.
|
|
13
14
|
import { readFileSync, statSync } from 'node:fs';
|
|
14
15
|
import { join } from 'node:path';
|
|
16
|
+
import { holdsNoData, parseJsonText, readJsonStateSync } from '../../../shared/atomic-file.mjs';
|
|
17
|
+
|
|
18
|
+
const NO_DATA = new Error('the file holds no data');
|
|
15
19
|
import { cwdWithinProjects } from '../../../shared/extension-scopes.mjs';
|
|
16
20
|
import { resolvePluginData } from '../../../shared/plugin-paths.mjs';
|
|
17
|
-
import { userDefinitionDir } from '../../../shared/agent-store.mjs';
|
|
21
|
+
import { mixyIdentityDir, userDefinitionDir } from '../../../shared/agent-store.mjs';
|
|
18
22
|
import { isSelfMcpToolName, parseMcpToolName } from '../../../shared/tool-primitives.mjs';
|
|
19
23
|
import { connectorToolDefault } from '../../../shared/connector-catalog.mjs';
|
|
24
|
+
import { MIXY_AGENT_ID } from '../session/agent-home.mjs';
|
|
25
|
+
import { AGENT_MODE, sessionModeOf } from '../session/agent-mode.mjs';
|
|
20
26
|
import { serverConnector } from './server-connectors.mjs';
|
|
21
27
|
|
|
22
28
|
export const CONNECTOR_PERMISSIONS = Object.freeze(['auto', 'needs_approval', 'disabled']);
|
|
@@ -127,9 +133,25 @@ function readJson(path, parse) {
|
|
|
127
133
|
return error?.code === 'ENOENT' || error?.code === 'ENOTDIR' ? null : unreadable(path, error);
|
|
128
134
|
}
|
|
129
135
|
try {
|
|
130
|
-
|
|
136
|
+
if (holdsNoData(text)) throw NO_DATA;
|
|
137
|
+
return parse(parseJsonText(text));
|
|
131
138
|
} catch (error) {
|
|
132
|
-
return
|
|
139
|
+
return recoverDamaged(path, parse, error);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// A damaged policy (empty, NUL-filled, torn, invalid) must never fail open.
|
|
144
|
+
// With a valid last good copy `<file>.bak` the damaged file is kept aside and
|
|
145
|
+
// the copy restored, so the user's deny list survives. Without one the file
|
|
146
|
+
// stays where it is (`keepWithoutBackup`) and every connector stays off until
|
|
147
|
+
// the policy is saved again — moving it aside would make the next read "no
|
|
148
|
+
// policy" (all on). A busy recovery lock also answers deny-all.
|
|
149
|
+
function recoverDamaged(path, parse, error) {
|
|
150
|
+
try {
|
|
151
|
+
const value = readJsonStateSync(path, { validate: parse, fallback: DENY_ALL_CONNECTORS, keepWithoutBackup: true });
|
|
152
|
+
return value === DENY_ALL_CONNECTORS ? unreadable(path, error) : value;
|
|
153
|
+
} catch (recoveryError) {
|
|
154
|
+
return unreadable(path, recoveryError);
|
|
133
155
|
}
|
|
134
156
|
}
|
|
135
157
|
|
|
@@ -146,11 +168,22 @@ export function readConnectorsFile(path) {
|
|
|
146
168
|
return readJson(path, (manifest) => parseConnectorPolicy(manifest?.connectors));
|
|
147
169
|
}
|
|
148
170
|
|
|
149
|
-
/** The agent's saved policy under `dataDir`, or null when it has none.
|
|
171
|
+
/** The agent's saved policy under `dataDir`, or null when it has none. Mixy's
|
|
172
|
+
* runtime id is `lead`: her policy is in her identity folder (bots/mixy once
|
|
173
|
+
* the bot store is active, agents/lead before), the file her profile writes. */
|
|
150
174
|
export function agentConnectors(agentId, dataDir = resolvePluginData()) {
|
|
151
175
|
const id = clean(agentId);
|
|
152
176
|
if (!id || !AGENT_DIR_ID.test(id)) return null;
|
|
153
|
-
|
|
177
|
+
const dir = id === MIXY_AGENT_ID ? mixyIdentityDir(dataDir) : userDefinitionDir(dataDir, id);
|
|
178
|
+
return cachedRead(join(dir, 'agent.json'), readConnectorsFile);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/** The agent whose connector policy `session` runs under. A coding Lead shares
|
|
182
|
+
* Mixy's runtime id `lead` but is not her: the policy her profile edits is
|
|
183
|
+
* hers alone, so a coding Lead has none ('') and only its Project's applies. */
|
|
184
|
+
export function connectorPolicyAgentOf(session) {
|
|
185
|
+
const agent = clean(session?.agent);
|
|
186
|
+
return agent === MIXY_AGENT_ID && sessionModeOf(session) !== AGENT_MODE ? '' : agent;
|
|
154
187
|
}
|
|
155
188
|
|
|
156
189
|
/** Uncached: the project policy file → `[{ root, off }]`, null without one,
|
|
@@ -186,6 +219,65 @@ export function connectorToolPermission(policy, toolOrName) {
|
|
|
186
219
|
return policy?.tools?.[mcp.server]?.[mcp.tool] || connectorToolDefault(serverConnector(mcp.server), mcp.tool);
|
|
187
220
|
}
|
|
188
221
|
|
|
222
|
+
const PERMISSION_RANK = Object.freeze({ disabled: 0, needs_approval: 1, auto: 2 });
|
|
223
|
+
export const NO_WIDEN_MESSAGE = 'This change would widen what the bot may do without asking; make it on the desktop.';
|
|
224
|
+
|
|
225
|
+
/** Whether replacing the stored policy `before` (a stored value, a parsed
|
|
226
|
+
* policy or null) with `after` (`{ off, tools }` or null) could give any
|
|
227
|
+
* tool more authority, judged on EFFECTIVE permissions: a connector switched
|
|
228
|
+
* back on, a tool allowed, or an override removed (the tool then runs on its
|
|
229
|
+
* default, which is `needs_approval` for a catalog connector's high-risk
|
|
230
|
+
* tools and `auto` otherwise). A legacy allow list or an unreadable policy is
|
|
231
|
+
* not compared: it counts as widening. */
|
|
232
|
+
export function connectorPolicyWidens(before, after) {
|
|
233
|
+
let old;
|
|
234
|
+
try {
|
|
235
|
+
old = before == null ? { off: [], tools: {} } : parseConnectorPolicy(before);
|
|
236
|
+
} catch {
|
|
237
|
+
return true;
|
|
238
|
+
}
|
|
239
|
+
if (old.allow) return true;
|
|
240
|
+
const next = after == null ? { off: [], tools: {} } : after;
|
|
241
|
+
const offBefore = new Set(old.off);
|
|
242
|
+
const offAfter = new Set(next.off || []);
|
|
243
|
+
for (const server of offBefore) if (!offAfter.has(server)) return true;
|
|
244
|
+
const toolsBefore = old.tools || {};
|
|
245
|
+
const toolsAfter = next.tools || {};
|
|
246
|
+
for (const server of new Set([...Object.keys(toolsBefore), ...Object.keys(toolsAfter)])) {
|
|
247
|
+
const tools = new Set([...Object.keys(toolsBefore[server] || {}), ...Object.keys(toolsAfter[server] || {})]);
|
|
248
|
+
for (const tool of tools) {
|
|
249
|
+
// No override: at least asks / at most runs freely.
|
|
250
|
+
const was = offBefore.has(server) ? 0 : (PERMISSION_RANK[toolsBefore[server]?.[tool]] ?? 1);
|
|
251
|
+
const now = offAfter.has(server) ? 0 : (PERMISSION_RANK[toolsAfter[server]?.[tool]] ?? 2);
|
|
252
|
+
if (now > was) return true;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
return false;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/** Throws NO_WIDEN_MESSAGE when `after` could widen `before` (see connectorPolicyWidens). */
|
|
259
|
+
export function assertConnectorPolicyNotWidened(before, after) {
|
|
260
|
+
if (connectorPolicyWidens(before, after)) throw new Error(NO_WIDEN_MESSAGE);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
let toolPermissionWriter = null;
|
|
264
|
+
/** The host attaches the one writer of an agent's per-tool permission (the
|
|
265
|
+
* agent editor keeps every other agent.json key). */
|
|
266
|
+
export function setConnectorToolPermissionWriter(write) {
|
|
267
|
+
toolPermissionWriter = typeof write === 'function' ? write : null;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/** Save `permission` for the connector tool `toolOrName` under `agentId`'s own
|
|
271
|
+
* policy. False when it is no connector tool or no writer is attached. */
|
|
272
|
+
export async function writeAgentMcpToolPermission(agentId, toolOrName, permission) {
|
|
273
|
+
const mcp = parseMcpToolName(toolOrName);
|
|
274
|
+
if (!toolPermissionWriter || !mcp || isSelfMcpToolName(toolOrName) || !CONNECTOR_PERMISSIONS.includes(permission)) {
|
|
275
|
+
return false;
|
|
276
|
+
}
|
|
277
|
+
await toolPermissionWriter(agentId, mcp.server, mcp.tool, permission);
|
|
278
|
+
return true;
|
|
279
|
+
}
|
|
280
|
+
|
|
189
281
|
/** The permission `agentId` has for the MCP tool `toolOrName`. */
|
|
190
282
|
export function agentMcpToolPermission(agentId, toolOrName) {
|
|
191
283
|
if (!parseMcpToolName(toolOrName)) return 'auto';
|
|
@@ -117,14 +117,11 @@ async function enumerate({ strict = false } = {}) {
|
|
|
117
117
|
* names and (POSIX) process groups. Rejects instead of returning a partial
|
|
118
118
|
* table, so callers can tell "nothing found" from "could not look".
|
|
119
119
|
*
|
|
120
|
-
*
|
|
121
|
-
*
|
|
122
|
-
*
|
|
123
|
-
*
|
|
124
|
-
*
|
|
125
|
-
* confirmation by a whole re-check. Such a caller starts a read when none
|
|
126
|
-
* runs, else joins the one queued to begin when the running read settles —
|
|
127
|
-
* every observer asking meanwhile shares it. At most one enumerator runs at
|
|
120
|
+
* Every caller sees a table from a read begun after it asked: an earlier
|
|
121
|
+
* read may miss newly spawned descendants before a kill, or report a killed
|
|
122
|
+
* tree alive afterwards. A caller starts a read when none runs, else joins
|
|
123
|
+
* the one queued to begin when the running read settles — every caller
|
|
124
|
+
* asking meanwhile shares it. At most one enumerator runs at
|
|
128
125
|
* a time, however many shells settle or watch at once (each one is a
|
|
129
126
|
* PowerShell process on Windows; one per settle and watch pass compounded
|
|
130
127
|
* machine load into multi-second daemon stalls). */
|
|
@@ -137,8 +134,7 @@ function startTableRead() {
|
|
|
137
134
|
tableInFlight = read;
|
|
138
135
|
return read;
|
|
139
136
|
}
|
|
140
|
-
export function readProcessTable(
|
|
141
|
-
if (shared && tableInFlight) return tableInFlight;
|
|
137
|
+
export function readProcessTable() {
|
|
142
138
|
if (nextTable) return nextTable;
|
|
143
139
|
if (!tableInFlight) return startTableRead();
|
|
144
140
|
const settled = () => {};
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { setMaxListeners } from 'node:events';
|
|
2
|
-
import { mkdirSync
|
|
2
|
+
import { mkdirSync } from 'node:fs';
|
|
3
|
+
import { writeFileAtomicSync } from '../../../shared/atomic-file.mjs';
|
|
3
4
|
import { writeFile } from 'node:fs/promises';
|
|
4
5
|
import {
|
|
5
6
|
MAX_ATTACHMENT_BLOB_BYTES,
|
|
@@ -772,9 +773,11 @@ function spillMcpBinary(buffer, extension) {
|
|
|
772
773
|
const path = join(dir, `mcp-${hash}.${extension}`);
|
|
773
774
|
try {
|
|
774
775
|
mkdirSync(dir, { recursive: true });
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
776
|
+
// Named by its content hash: whole-file create-if-absent (a crash never
|
|
777
|
+
// leaves a torn file under the name that would be trusted as the content).
|
|
778
|
+
writeFileAtomicSync(path, buffer, { createOnly: true });
|
|
779
|
+
} catch {
|
|
780
|
+
return null;
|
|
778
781
|
}
|
|
779
782
|
return path;
|
|
780
783
|
}
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
* anthropic-turn-result.mjs); nothing here touches the socket.
|
|
11
11
|
*/
|
|
12
12
|
import { prepareAnthropicImages } from './lib/anthropic-image-input.mjs';
|
|
13
|
+
import { anthropicPrefixFingerprint, recordAnthropicPrefix } from './lib/anthropic-prefix-trace.mjs';
|
|
13
14
|
import { buildAnthropicBetaHeaders, supportsAnthropicFastMode } from './anthropic-betas.mjs';
|
|
14
15
|
import { applyAnthropicServerFallback } from './anthropic-server-fallback.mjs';
|
|
15
16
|
import { fastModeAvailable } from './anthropic-fast-mode.mjs';
|
|
@@ -17,6 +18,7 @@ import { applyAnthropicEffortToBody, shouldIncludeEffortBeta } from './anthropic
|
|
|
17
18
|
import { isCustomProviderName, resolveCustomReasoning } from './custom-reasoning.mjs';
|
|
18
19
|
import {
|
|
19
20
|
anthropicThinkingActive,
|
|
21
|
+
appendAnthropicTailReminder,
|
|
20
22
|
applyAnthropicCacheMarkers,
|
|
21
23
|
clampAnthropicThinkingBudget as clampThinkingBudgetTokens,
|
|
22
24
|
resolveAnthropicCacheTtls as resolveCacheTtls,
|
|
@@ -24,6 +26,7 @@ import {
|
|
|
24
26
|
sharedCachePrefixBoundary,
|
|
25
27
|
toAnthropicToolChoice,
|
|
26
28
|
} from './lib/anthropic-request-utils.mjs';
|
|
29
|
+
import { splitTailReminder } from './lib/tail-reminder-split.mjs';
|
|
27
30
|
import {
|
|
28
31
|
buildSystemBlocks,
|
|
29
32
|
resolveMaxTokens,
|
|
@@ -50,7 +53,7 @@ import {
|
|
|
50
53
|
export async function buildAnthropicApiRequest({
|
|
51
54
|
name,
|
|
52
55
|
config,
|
|
53
|
-
messages,
|
|
56
|
+
messages: requestMessages,
|
|
54
57
|
useModel,
|
|
55
58
|
tools,
|
|
56
59
|
opts,
|
|
@@ -67,6 +70,7 @@ export async function buildAnthropicApiRequest({
|
|
|
67
70
|
// the global by-model-id capability index and the Claude-id ladders are
|
|
68
71
|
// first-party knowledge and must not leak onto (or from) a custom endpoint.
|
|
69
72
|
const isCustom = isCustomProviderName(name);
|
|
73
|
+
const { messages, reminder } = splitTailReminder(requestMessages);
|
|
70
74
|
const maxTokens = resolveMaxTokens(capabilityModel, { modelMaxOutputTokens });
|
|
71
75
|
const effortOptions = { ...config, ...opts, disableBetaHeaders: config?.disableBetaHeaders };
|
|
72
76
|
const gatewayEffort = typeof config?.messagesEffort === 'function' ? config.messagesEffort(opts) : null;
|
|
@@ -108,7 +112,9 @@ export async function buildAnthropicApiRequest({
|
|
|
108
112
|
model: useModel,
|
|
109
113
|
max_tokens: maxTokens,
|
|
110
114
|
system: systemBlocks.length ? systemBlocks : undefined,
|
|
111
|
-
messages: await prepareAnthropicImages(anthropicMessages,
|
|
115
|
+
messages: await prepareAnthropicImages(appendAnthropicTailReminder(anthropicMessages, reminder), {
|
|
116
|
+
signal: opts.signal,
|
|
117
|
+
}),
|
|
112
118
|
};
|
|
113
119
|
// Server-side fallback and fast mode are decided from Claude model ids:
|
|
114
120
|
// first-party knowledge, never applied to a custom endpoint.
|
|
@@ -135,6 +141,55 @@ export async function buildAnthropicApiRequest({
|
|
|
135
141
|
.map((t) => (t && typeof t.name === 'string' ? t.name : null))
|
|
136
142
|
.filter(Boolean)
|
|
137
143
|
);
|
|
144
|
+
applyRequestReasoning(params, {
|
|
145
|
+
isCustom,
|
|
146
|
+
name,
|
|
147
|
+
config,
|
|
148
|
+
opts,
|
|
149
|
+
modelInfo,
|
|
150
|
+
capabilityModel,
|
|
151
|
+
maxTokens,
|
|
152
|
+
gatewayEffort,
|
|
153
|
+
effortProjection,
|
|
154
|
+
});
|
|
155
|
+
if (params.tools) resolveRequestToolChoice(params, opts, config);
|
|
156
|
+
// Fast mode → speed: "fast" on models Anthropic marks as speed-capable.
|
|
157
|
+
// Suppressed while the fast capacity pool is cooling down.
|
|
158
|
+
let fastLatched = fastModeLatched;
|
|
159
|
+
if (!isCustom && opts.fast === true && supportsAnthropicFastMode(useModel) && fastModeAvailable()) {
|
|
160
|
+
params.speed = 'fast';
|
|
161
|
+
fastLatched = true;
|
|
162
|
+
}
|
|
163
|
+
// NOTE: do NOT sanitize here. params.messages was already sanitized
|
|
164
|
+
// once inside toAnthropicMessages and then had cache markers applied.
|
|
165
|
+
// Re-sanitizing after marking could drop/reorder a marked block and
|
|
166
|
+
// move the provider-visible cache breakpoint off the cached one — the
|
|
167
|
+
// exact COLD-turn bug this change fixes. Order: build → sanitize
|
|
168
|
+
// (once) → mark → prepare image bytes → send.
|
|
169
|
+
params.stream = true;
|
|
170
|
+
// Diagnostic: per-block prefix hashes of exactly what is sent.
|
|
171
|
+
recordAnthropicPrefix(opts.sessionId || opts.session?.id || null, anthropicPrefixFingerprint(params));
|
|
172
|
+
|
|
173
|
+
const requestHeaders = requestHeadersFor(params, {
|
|
174
|
+
config,
|
|
175
|
+
opts,
|
|
176
|
+
isCustom,
|
|
177
|
+
fastLatched,
|
|
178
|
+
hasDeferredTools,
|
|
179
|
+
capabilityModel,
|
|
180
|
+
effortProjection,
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
return { params, requestHeaders, knownToolNames, fastModeLatched: fastLatched };
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/** The turn's thinking/effort fields on `params` (in place): a custom
|
|
187
|
+
* provider's from its own model row, else the turn effort (or a gateway's
|
|
188
|
+
* own `messagesEffort`; 'none' sends no thinking at all). */
|
|
189
|
+
function applyRequestReasoning(
|
|
190
|
+
params,
|
|
191
|
+
{ isCustom, name, config, opts, modelInfo, capabilityModel, maxTokens, gatewayEffort, effortProjection }
|
|
192
|
+
) {
|
|
138
193
|
if (isCustom) {
|
|
139
194
|
const { fields } = resolveCustomReasoning({
|
|
140
195
|
protocol: 'anthropic',
|
|
@@ -156,32 +211,29 @@ export async function buildAnthropicApiRequest({
|
|
|
156
211
|
progressUpdates: config?.disableBetaHeaders !== true,
|
|
157
212
|
});
|
|
158
213
|
}
|
|
159
|
-
|
|
160
|
-
const toolChoice = toAnthropicToolChoice(opts.toolChoice, {
|
|
161
|
-
thinking: anthropicThinkingActive(params),
|
|
162
|
-
disableParallelToolUse: opts.disableParallelToolUse === true,
|
|
163
|
-
// A gateway that cannot honour disable_parallel_tool_use declares so.
|
|
164
|
-
supportsDisableParallelToolUse: config?.supportsDisableParallelToolUse !== false,
|
|
165
|
-
onThinkingConflict: opts.toolChoiceThinkingConflict,
|
|
166
|
-
});
|
|
167
|
-
if (toolChoice) params.tool_choice = toolChoice;
|
|
168
|
-
else delete params.tool_choice;
|
|
169
|
-
}
|
|
170
|
-
// Fast mode → speed: "fast" on models Anthropic marks as speed-capable.
|
|
171
|
-
// Suppressed while the fast capacity pool is cooling down.
|
|
172
|
-
let fastLatched = fastModeLatched;
|
|
173
|
-
if (!isCustom && opts.fast === true && supportsAnthropicFastMode(useModel) && fastModeAvailable()) {
|
|
174
|
-
params.speed = 'fast';
|
|
175
|
-
fastLatched = true;
|
|
176
|
-
}
|
|
177
|
-
// NOTE: do NOT sanitize here. params.messages was already sanitized
|
|
178
|
-
// once inside toAnthropicMessages and then had cache markers applied.
|
|
179
|
-
// Re-sanitizing after marking could drop/reorder a marked block and
|
|
180
|
-
// move the provider-visible cache breakpoint off the cached one — the
|
|
181
|
-
// exact COLD-turn bug this change fixes. Order: build → sanitize
|
|
182
|
-
// (once) → mark → prepare image bytes → send.
|
|
183
|
-
params.stream = true;
|
|
214
|
+
}
|
|
184
215
|
|
|
216
|
+
/** The reserved `tool_choice` of a request with tools, resolved once
|
|
217
|
+
* thinking is known (forced any/tool is invalid while thinking is on);
|
|
218
|
+
* dropped when none applies. */
|
|
219
|
+
function resolveRequestToolChoice(params, opts, config) {
|
|
220
|
+
const toolChoice = toAnthropicToolChoice(opts.toolChoice, {
|
|
221
|
+
thinking: anthropicThinkingActive(params),
|
|
222
|
+
disableParallelToolUse: opts.disableParallelToolUse === true,
|
|
223
|
+
// A gateway that cannot honour disable_parallel_tool_use declares so.
|
|
224
|
+
supportsDisableParallelToolUse: config?.supportsDisableParallelToolUse !== false,
|
|
225
|
+
onThinkingConflict: opts.toolChoiceThinkingConflict,
|
|
226
|
+
});
|
|
227
|
+
if (toolChoice) params.tool_choice = toolChoice;
|
|
228
|
+
else delete params.tool_choice;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/** The per-request headers of the final body `params`: the anthropic-beta
|
|
232
|
+
* set it needs, then the caller's `opts.requestHeaders`; null when none. */
|
|
233
|
+
function requestHeadersFor(
|
|
234
|
+
params,
|
|
235
|
+
{ config, opts, isCustom, fastLatched, hasDeferredTools, capabilityModel, effortProjection }
|
|
236
|
+
) {
|
|
185
237
|
// Per-call headers override the client defaultHeaders, so the
|
|
186
238
|
// constructor-level disableBetaHeaders opt-out must be honoured here
|
|
187
239
|
// too — otherwise opencode-go's anthropic-compatible routing
|
|
@@ -205,8 +257,5 @@ export async function buildAnthropicApiRequest({
|
|
|
205
257
|
].filter(Boolean);
|
|
206
258
|
if (betas.length) betaHeaders = { 'anthropic-beta': betas.join(',') };
|
|
207
259
|
}
|
|
208
|
-
|
|
209
|
-
betaHeaders || opts.requestHeaders ? { ...(betaHeaders || {}), ...(opts.requestHeaders || {}) } : null;
|
|
210
|
-
|
|
211
|
-
return { params, requestHeaders, knownToolNames, fastModeLatched: fastLatched };
|
|
260
|
+
return betaHeaders || opts.requestHeaders ? { ...(betaHeaders || {}), ...(opts.requestHeaders || {}) } : null;
|
|
212
261
|
}
|
|
@@ -149,7 +149,7 @@ function _markLatestByFamily(models) {
|
|
|
149
149
|
for (const m of byFamily.values()) m.latest = true;
|
|
150
150
|
}
|
|
151
151
|
|
|
152
|
-
function _compareVersion(a, b) {
|
|
152
|
+
export function _compareVersion(a, b) {
|
|
153
153
|
const na = (a.match(/^claude-[a-z]+-(\d+)(?:-(\d+))?/i) || []).slice(1).map(Number);
|
|
154
154
|
const nb = (b.match(/^claude-[a-z]+-(\d+)(?:-(\d+))?/i) || []).slice(1).map(Number);
|
|
155
155
|
for (let i = 0; i < Math.max(na.length, nb.length); i++) {
|
|
@@ -10,7 +10,7 @@ import { createNpmVersionSource, maxSemver } from './npm-cli-version.mjs';
|
|
|
10
10
|
// source release just to advance the user-agent version.
|
|
11
11
|
// Claude Opus 5.5 requires 2.1.280 or newer. The release workflow raises this
|
|
12
12
|
// to the published CLI version (scripts/sync-client-version-floors.mjs).
|
|
13
|
-
export const DEFAULT_CLI_VERSION = '2.1.
|
|
13
|
+
export const DEFAULT_CLI_VERSION = '2.1.307';
|
|
14
14
|
export const CLAUDE_CLI_NPM_PACKAGE = '@anthropic-ai/claude-code';
|
|
15
15
|
|
|
16
16
|
const CACHE_SCHEMA_VERSION = 1;
|
|
@@ -6,6 +6,11 @@
|
|
|
6
6
|
* from anthropic.mjs. agent-trace instrumented.
|
|
7
7
|
*/
|
|
8
8
|
import { traceAgentSse, traceAgentUsage } from '../agent-trace.mjs';
|
|
9
|
+
import {
|
|
10
|
+
anthropicPrefixFingerprint,
|
|
11
|
+
latestAnthropicPrefix,
|
|
12
|
+
recordAnthropicPrefix,
|
|
13
|
+
} from './lib/anthropic-prefix-trace.mjs';
|
|
9
14
|
import { boundProviderAuthPath } from '../../../shared/provider-auth-binding.mjs';
|
|
10
15
|
import { resolveAnthropicMaxTokens } from './anthropic-max-tokens.mjs';
|
|
11
16
|
import { MODELS } from './lib/anthropic-models.mjs';
|
|
@@ -56,6 +61,7 @@ import { ANTHROPIC_EFFORT_LEVELS, withEffortRejectionFallback } from './effort-r
|
|
|
56
61
|
import { getLlmDispatcher, preconnect } from '../../../shared/llm/http-agent.mjs';
|
|
57
62
|
import {
|
|
58
63
|
anthropicThinkingActive,
|
|
64
|
+
appendAnthropicTailReminder,
|
|
59
65
|
applyAnthropicCacheMarkers,
|
|
60
66
|
clampAnthropicThinkingBudget as clampThinkingBudgetTokens,
|
|
61
67
|
deferredAnthropicTools as sharedDeferredAnthropicTools,
|
|
@@ -67,6 +73,7 @@ import {
|
|
|
67
73
|
toAnthropicMessages,
|
|
68
74
|
toAnthropicToolChoice,
|
|
69
75
|
} from './lib/anthropic-request-utils.mjs';
|
|
76
|
+
import { splitTailReminder } from './lib/tail-reminder-split.mjs';
|
|
70
77
|
import {
|
|
71
78
|
EFFORT_CONFIGURATION_BETA,
|
|
72
79
|
isEffortConfigurationRejection,
|
|
@@ -189,7 +196,8 @@ function requestAnthropicTools(tools, messages, opts) {
|
|
|
189
196
|
// the tier3 1h cache_control to that block; BP1/BP2 take the system TTL. No
|
|
190
197
|
// `<system-reminder>` user message / sentinel scan is involved anymore.
|
|
191
198
|
|
|
192
|
-
function buildRequestBody(
|
|
199
|
+
function buildRequestBody(requestMessages, model, tools, sendOpts) {
|
|
200
|
+
const { messages, reminder } = splitTailReminder(requestMessages);
|
|
193
201
|
const systemMsgs = messages.filter((m) => m.role === 'system');
|
|
194
202
|
const chatMsgs = messages.filter((m) => m.role !== 'system');
|
|
195
203
|
const opts = sendOpts || {};
|
|
@@ -229,7 +237,7 @@ function buildRequestBody(messages, model, tools, sendOpts) {
|
|
|
229
237
|
const body = {
|
|
230
238
|
model,
|
|
231
239
|
max_tokens: maxTokens,
|
|
232
|
-
messages: anthropicMessages,
|
|
240
|
+
messages: appendAnthropicTailReminder(anthropicMessages, reminder),
|
|
233
241
|
stream: true,
|
|
234
242
|
};
|
|
235
243
|
applyAnthropicServerFallback(body, model, {
|
|
@@ -463,6 +471,8 @@ export class AnthropicOAuthProvider {
|
|
|
463
471
|
const useModel = model || (await ensureLatestAnthropicModel(this));
|
|
464
472
|
const body = buildRequestBody(messages, useModel, tools, sendOpts);
|
|
465
473
|
body.messages = await prepareAnthropicImages(body.messages, { signal: externalSignal });
|
|
474
|
+
// Diagnostic: per-block prefix hashes of exactly what is sent.
|
|
475
|
+
recordAnthropicPrefix(opts.sessionId || null, anthropicPrefixFingerprint(body));
|
|
466
476
|
if (body.speed === 'fast') {
|
|
467
477
|
this.fastModeBetaHeaderLatched = true;
|
|
468
478
|
}
|
|
@@ -544,42 +554,15 @@ export class AnthropicOAuthProvider {
|
|
|
544
554
|
let maxTokensAdjusted = false;
|
|
545
555
|
try {
|
|
546
556
|
for (let attemptIndex = 0; attemptIndex <= ANTHROPIC_MAX_MIDSTREAM_RETRIES; attemptIndex++) {
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
if (isRefreshableAuthRejection(response.status, rejectedAuthBody)) {
|
|
557
|
-
process.stderr.write(`[anthropic-oauth] ${response.status} — forcing refresh and retrying once\n`);
|
|
558
|
-
cleanupCancelHandler(cancelHandler);
|
|
559
|
-
// Body was drained above; abort closes any remaining transport
|
|
560
|
-
// resources before the credential-refresh replay.
|
|
561
|
-
try {
|
|
562
|
-
controller?.abort?.();
|
|
563
|
-
} catch {}
|
|
564
|
-
auth.creds = await this.ensureAuth({ forceRefresh: true, reason: String(response.status) });
|
|
565
|
-
({ response, controller, cancelHandler } = await requestWithRetry(auth.creds.accessToken));
|
|
566
|
-
rejectedAuthBody = null;
|
|
567
|
-
}
|
|
568
|
-
|
|
569
|
-
// "input length and max_tokens exceed context limit": shrink max_tokens
|
|
570
|
-
// to what the context has room for and replay once.
|
|
571
|
-
if (response.status === 400 && !maxTokensAdjusted) {
|
|
572
|
-
rejectedAuthBody = await response.text().catch(() => '');
|
|
573
|
-
if (adjustMaxTokensForContextOverflow(body, 400, rejectedAuthBody)) {
|
|
574
|
-
maxTokensAdjusted = true;
|
|
575
|
-
cleanupCancelHandler(cancelHandler);
|
|
576
|
-
try {
|
|
577
|
-
controller?.abort?.();
|
|
578
|
-
} catch {}
|
|
579
|
-
({ response, controller, cancelHandler } = await requestWithRetry(auth.creds.accessToken));
|
|
580
|
-
rejectedAuthBody = null;
|
|
581
|
-
}
|
|
582
|
-
}
|
|
557
|
+
const opened = await this._openResponse({
|
|
558
|
+
requestWithRetry,
|
|
559
|
+
cleanupCancelHandler,
|
|
560
|
+
auth,
|
|
561
|
+
body,
|
|
562
|
+
maxTokensAdjusted,
|
|
563
|
+
});
|
|
564
|
+
const { response, controller, cancelHandler, rejectedAuthBody } = opened;
|
|
565
|
+
maxTokensAdjusted = opened.maxTokensAdjusted;
|
|
583
566
|
if (!response.ok) {
|
|
584
567
|
cleanupCancelHandler(cancelHandler);
|
|
585
568
|
return await this._resendAfterRejection({
|
|
@@ -657,6 +640,51 @@ export class AnthropicOAuthProvider {
|
|
|
657
640
|
}
|
|
658
641
|
}
|
|
659
642
|
|
|
643
|
+
// One attempt's initial response: a 401 (or Anthropic's exact revoked-token
|
|
644
|
+
// 403) refreshes the credentials in `auth` and replays once; a context
|
|
645
|
+
// overflow 400 shrinks `body.max_tokens` and replays once per send
|
|
646
|
+
// (`maxTokensAdjusted`). `rejectedAuthBody`: the drained text of a final
|
|
647
|
+
// rejected response, null when none was read.
|
|
648
|
+
async _openResponse({ requestWithRetry, cleanupCancelHandler, auth, body, maxTokensAdjusted }) {
|
|
649
|
+
let response, controller, cancelHandler;
|
|
650
|
+
({ response, controller, cancelHandler } = await requestWithRetry(auth.creds.accessToken));
|
|
651
|
+
|
|
652
|
+
// Refresh on 401, and only Anthropic's exact official revoked-token
|
|
653
|
+
// 403 signature. Ordinary permission/policy 403s are terminal.
|
|
654
|
+
let rejectedAuthBody = null;
|
|
655
|
+
if (response.status === 401 || response.status === 403) {
|
|
656
|
+
rejectedAuthBody = await response.text().catch(() => '');
|
|
657
|
+
}
|
|
658
|
+
if (isRefreshableAuthRejection(response.status, rejectedAuthBody)) {
|
|
659
|
+
process.stderr.write(`[anthropic-oauth] ${response.status} — forcing refresh and retrying once\n`);
|
|
660
|
+
cleanupCancelHandler(cancelHandler);
|
|
661
|
+
// Body was drained above; abort closes any remaining transport
|
|
662
|
+
// resources before the credential-refresh replay.
|
|
663
|
+
try {
|
|
664
|
+
controller?.abort?.();
|
|
665
|
+
} catch {}
|
|
666
|
+
auth.creds = await this.ensureAuth({ forceRefresh: true, reason: String(response.status) });
|
|
667
|
+
({ response, controller, cancelHandler } = await requestWithRetry(auth.creds.accessToken));
|
|
668
|
+
rejectedAuthBody = null;
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
// "input length and max_tokens exceed context limit": shrink max_tokens
|
|
672
|
+
// to what the context has room for and replay once.
|
|
673
|
+
if (response.status === 400 && !maxTokensAdjusted) {
|
|
674
|
+
rejectedAuthBody = await response.text().catch(() => '');
|
|
675
|
+
if (adjustMaxTokensForContextOverflow(body, 400, rejectedAuthBody)) {
|
|
676
|
+
maxTokensAdjusted = true;
|
|
677
|
+
cleanupCancelHandler(cancelHandler);
|
|
678
|
+
try {
|
|
679
|
+
controller?.abort?.();
|
|
680
|
+
} catch {}
|
|
681
|
+
({ response, controller, cancelHandler } = await requestWithRetry(auth.creds.accessToken));
|
|
682
|
+
rejectedAuthBody = null;
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
return { response, controller, cancelHandler, rejectedAuthBody, maxTokensAdjusted };
|
|
686
|
+
}
|
|
687
|
+
|
|
660
688
|
// A non-OK initial response: quota errors surface as-is; a CLI-version
|
|
661
689
|
// floor or an unknown/retired model replays the turn ONCE through send().
|
|
662
690
|
async _resendAfterRejection({ response, rejectedAuthBody, messages, model, useModel, tools, opts, onStageChange }) {
|
|
@@ -780,6 +808,7 @@ export class AnthropicOAuthProvider {
|
|
|
780
808
|
rawUsage: result.usage?.raw || null,
|
|
781
809
|
provider: 'anthropic-oauth',
|
|
782
810
|
requestKind,
|
|
811
|
+
prefixBlocks: latestAnthropicPrefix(sessionId),
|
|
783
812
|
});
|
|
784
813
|
|
|
785
814
|
// Phase I: if the live response surfaced a model id we don't know
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
*/
|
|
12
12
|
import { traceAgentUsage } from '../agent-trace.mjs';
|
|
13
13
|
import { reportedCostUsd } from './lib/usage-primitives.mjs';
|
|
14
|
+
import { latestAnthropicPrefix } from './lib/anthropic-prefix-trace.mjs';
|
|
14
15
|
|
|
15
16
|
/**
|
|
16
17
|
* @param {object} parseResult the assembled turn (streaming or normalized non-streaming)
|
|
@@ -47,6 +48,7 @@ export function buildAnthropicTurnResult(parseResult, { provider, useModel, opts
|
|
|
47
48
|
// Anthropic usage is additive at this inner transport
|
|
48
49
|
// boundary, even when OpenCode Go supplies the provider id.
|
|
49
50
|
inputTokensInclusive: false,
|
|
51
|
+
prefixBlocks: latestAnthropicPrefix(opts.sessionId || opts.session?.id || null),
|
|
50
52
|
});
|
|
51
53
|
}
|
|
52
54
|
|
|
@@ -3,6 +3,7 @@ import { createProviderReplay, providerReplayItems } from './lib/provider-replay
|
|
|
3
3
|
import { toGeminiContents, toGeminiRequestTools, toGeminiToolConfig } from './gemini-schema.mjs';
|
|
4
4
|
import { geminiThinkingConfig } from './gemini-thinking.mjs';
|
|
5
5
|
import { isGeminiFamilyModel } from './gemini-family.mjs';
|
|
6
|
+
import { splitTailReminder } from './lib/tail-reminder-split.mjs';
|
|
6
7
|
|
|
7
8
|
const THOUGHT_SIGNATURE_SENTINEL = 'skip_thought_signature_validator';
|
|
8
9
|
const CLAUDE_MAX_OUTPUT_TOKENS = 64000;
|
|
@@ -219,7 +220,10 @@ function antigravityGeminiThinkingConfig(model, opts) {
|
|
|
219
220
|
});
|
|
220
221
|
}
|
|
221
222
|
|
|
222
|
-
export function buildAntigravityRequest(
|
|
223
|
+
export function buildAntigravityRequest(requestMessages, model, tools, opts = {}, projectId) {
|
|
224
|
+
// The tail reminder is not history: the tool-loop closure below must see the
|
|
225
|
+
// real tail, so the reminder is appended to the wire contents last.
|
|
226
|
+
const { messages, reminder } = splitTailReminder(requestMessages);
|
|
223
227
|
const systemText = messages
|
|
224
228
|
.filter((message) => message.role === 'system')
|
|
225
229
|
.map((message) => message.content)
|
|
@@ -234,7 +238,7 @@ export function buildAntigravityRequest(messages, model, tools, opts = {}, proje
|
|
|
234
238
|
...(claude ? { maxOutputTokens: CLAUDE_MAX_OUTPUT_TOKENS } : {}),
|
|
235
239
|
...(thinkingConfig ? { thinkingConfig } : {}),
|
|
236
240
|
};
|
|
237
|
-
const request = { contents };
|
|
241
|
+
const request = { contents: reminder ? [...contents, { role: 'user', parts: [{ text: reminder }] }] : contents };
|
|
238
242
|
if (systemText) request.systemInstruction = { role: 'user', parts: [{ text: systemText }] };
|
|
239
243
|
const requestTools = claude
|
|
240
244
|
? claudeRequestTools(tools, opts.nativeTools, opts.onToolExcluded)
|
|
@@ -3,24 +3,43 @@
|
|
|
3
3
|
// Every turn starts again from the base, so a run of hard turns never
|
|
4
4
|
// ratchets the effort upward.
|
|
5
5
|
//
|
|
6
|
-
// The base is
|
|
7
|
-
//
|
|
6
|
+
// The base is the global "Auto reasoning base" setting (`autoEffortBase` in the
|
|
7
|
+
// agent config; low / medium / high, default `medium`), the same on every model
|
|
8
|
+
// and independent of the effort last chosen: raised only when the request needs
|
|
9
|
+
// it, lowered when it is easy. The ladder is the model's supported efforts from
|
|
8
10
|
// `low` up to the tier below the top: `none`/`minimal` switch thinking off and
|
|
9
|
-
// `max`/`ultra` are reserved for an explicit user choice, so
|
|
10
|
-
// them. A
|
|
11
|
+
// `max`/`ultra` are reserved for an explicit (fixed, Auto off) user choice, so
|
|
12
|
+
// auto never picks them. A base the model does not offer is clamped to the
|
|
13
|
+
// nearest ladder level.
|
|
11
14
|
|
|
12
15
|
const EFFORT_ORDER = Object.freeze(['none', 'minimal', 'low', 'medium', 'high', 'xhigh', 'max', 'ultra']);
|
|
13
16
|
const FLOOR_INDEX = EFFORT_ORDER.indexOf('low');
|
|
14
17
|
const RESERVED_TOP = new Set(['max', 'ultra']);
|
|
15
18
|
|
|
16
19
|
export const AUTO_EFFORT_BASE = 'medium';
|
|
20
|
+
/** The "Auto reasoning base" choices: the levels Auto can work around. */
|
|
21
|
+
export const AUTO_EFFORT_BASES = Object.freeze(['low', 'medium', 'high']);
|
|
17
22
|
|
|
18
|
-
/** The effort auto moves from:
|
|
19
|
-
|
|
20
|
-
|
|
23
|
+
/** The effort auto moves from: the global "Auto reasoning base" setting
|
|
24
|
+
* (`autoEffortBase` in the agent config); absent or invalid is `medium`. */
|
|
25
|
+
export function autoEffortBase(setting) {
|
|
26
|
+
const effort = String(setting ?? '')
|
|
21
27
|
.trim()
|
|
22
28
|
.toLowerCase();
|
|
23
|
-
return
|
|
29
|
+
return AUTO_EFFORT_BASES.includes(effort) ? effort : AUTO_EFFORT_BASE;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** The base on the model's ladder: the setting itself, else the nearest
|
|
33
|
+
* ladder level (the lower one on a tie); null when the ladder is empty. */
|
|
34
|
+
export function clampAutoEffortBase({ base, options }) {
|
|
35
|
+
const ladder = autoEffortLadder(options);
|
|
36
|
+
if (!ladder.length) return null;
|
|
37
|
+
const want = EFFORT_ORDER.indexOf(autoEffortBase(base));
|
|
38
|
+
let best = ladder[0];
|
|
39
|
+
for (const level of ladder) {
|
|
40
|
+
if (Math.abs(EFFORT_ORDER.indexOf(level) - want) < Math.abs(EFFORT_ORDER.indexOf(best) - want)) best = level;
|
|
41
|
+
}
|
|
42
|
+
return best;
|
|
24
43
|
}
|
|
25
44
|
|
|
26
45
|
export const AUTO_EFFORT_MODES = Object.freeze(['off', 'observe', 'on']);
|