mixdog 1.1.1 → 1.2.0
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 +1 -0
- package/package.json +2 -2
- package/scripts/lib/run-node-tests.mjs +9 -0
- package/scripts/prune-desktop-runtime.mjs +3 -0
- package/scripts/release-paths.mjs +39 -2
- package/scripts/test-direct.mjs +4 -2
- package/src/defaults/shipped-history.json +6 -0
- package/src/defaults/skills/setup/references/actions.md +5 -2
- package/src/defaults/skills/visualize/SKILL.md +157 -0
- package/src/defaults/skills/visualize/references/craft.md +88 -0
- package/src/headless-command.mjs +56 -3
- package/src/headless-exec.mjs +17 -1
- package/src/headless-role.mjs +2 -2
- package/src/help.mjs +2 -0
- package/src/lib/rules-builder.cjs +0 -23
- package/src/rules/lead/LEAD.md +2 -2
- package/src/rules/shared/70-delivery.md +2 -2
- package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch/preset.mjs +1 -0
- package/src/runtime/agent/orchestrator/agent-runtime/maintenance-route.mjs +1 -0
- package/src/runtime/agent/orchestrator/agent-runtime/session-builder.mjs +2 -0
- package/src/runtime/agent/orchestrator/config-presets.mjs +2 -0
- package/src/runtime/agent/orchestrator/config-providers-merge.mjs +17 -11
- package/src/runtime/agent/orchestrator/config-storage.mjs +1 -0
- package/src/runtime/agent/orchestrator/config.mjs +9 -1
- package/src/runtime/agent/orchestrator/mcp/client-config.mjs +47 -7
- package/src/runtime/agent/orchestrator/mcp/client-handshake.mjs +51 -8
- package/src/runtime/agent/orchestrator/mcp/client-transport.mjs +25 -5
- package/src/runtime/agent/orchestrator/mcp/client.mjs +348 -122
- package/src/runtime/agent/orchestrator/mcp/reconnect-singleflight.mjs +4 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-credentials.mjs +3 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +15 -1
- package/src/runtime/agent/orchestrator/providers/anthropic.mjs +13 -1
- package/src/runtime/agent/orchestrator/providers/antigravity-oauth-tokens.mjs +2 -0
- package/src/runtime/agent/orchestrator/providers/cursor-auth.mjs +2 -0
- package/src/runtime/agent/orchestrator/providers/cursor-wire-normalization.mjs +2 -1
- package/src/runtime/agent/orchestrator/providers/effort-configuration.mjs +12 -4
- package/src/runtime/agent/orchestrator/providers/effort-rejection.mjs +244 -0
- package/src/runtime/agent/orchestrator/providers/grok-client-version.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/grok-oauth-tokens.mjs +3 -0
- package/src/runtime/agent/orchestrator/providers/mimo-oauth-profile.mjs +8 -40
- package/src/runtime/agent/orchestrator/providers/mimo-oauth-tokens.mjs +2 -0
- package/src/runtime/agent/orchestrator/providers/mimo-oauth.mjs +51 -12
- package/src/runtime/agent/orchestrator/providers/model-cache.mjs +37 -2
- package/src/runtime/agent/orchestrator/providers/model-proxy-channel.d.mts +67 -0
- package/src/runtime/agent/orchestrator/providers/model-proxy-channel.mjs +521 -0
- package/src/runtime/agent/orchestrator/providers/model-proxy-provider.mjs +251 -0
- package/src/runtime/agent/orchestrator/providers/muse-code-oauth-tokens.mjs +2 -0
- package/src/runtime/agent/orchestrator/providers/muse-code-oauth.mjs +69 -102
- package/src/runtime/agent/orchestrator/providers/oauth-credential-probes.mjs +3 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +6 -1
- package/src/runtime/agent/orchestrator/providers/openai-oauth-catalog-store.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/openai-oauth-catalog.mjs +5 -2
- package/src/runtime/agent/orchestrator/providers/openai-oauth-tokens.mjs +3 -0
- package/src/runtime/agent/orchestrator/providers/registry.mjs +15 -1
- package/src/runtime/agent/orchestrator/runtime-core/builtin-features.mjs +10 -0
- package/src/runtime/agent/orchestrator/runtime-core/config-helpers.mjs +36 -3
- package/src/runtime/agent/orchestrator/runtime-core/effort.mjs +12 -11
- package/src/runtime/agent/orchestrator/runtime-core/model-capabilities.mjs +16 -1
- package/src/runtime/agent/orchestrator/runtime-core/plugin-mcp.mjs +11 -0
- package/src/runtime/agent/orchestrator/runtime-core/tool-defs.mjs +11 -8
- package/src/runtime/agent/orchestrator/runtime-core/workflow.mjs +10 -0
- package/src/runtime/agent/orchestrator/session/cache/prefetch-cache.mjs +9 -5
- package/src/runtime/agent/orchestrator/session/cache/read-cache.mjs +18 -8
- package/src/runtime/agent/orchestrator/session/compact/execution-tail.mjs +10 -3
- package/src/runtime/agent/orchestrator/session/compact/messages.mjs +18 -11
- package/src/runtime/agent/orchestrator/session/compact/runner.mjs +13 -5
- package/src/runtime/agent/orchestrator/session/compaction-archive-capture.mjs +362 -0
- package/src/runtime/agent/orchestrator/session/compaction-archive.mjs +253 -0
- package/src/runtime/agent/orchestrator/session/context-utils.mjs +12 -14
- package/src/runtime/agent/orchestrator/session/loop/no-tool-turn/continuation.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/loop/no-tool-turn/diagnostic.mjs +1 -7
- package/src/runtime/agent/orchestrator/session/loop/no-tool-turn/refusal.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/loop/no-tool-turn/segments.mjs +3 -2
- package/src/runtime/agent/orchestrator/session/loop/no-tool-turn.mjs +2 -1
- package/src/runtime/agent/orchestrator/session/loop/send-phase.mjs +1 -0
- package/src/runtime/agent/orchestrator/session/loop/steering.mjs +17 -7
- package/src/runtime/agent/orchestrator/session/loop/usage.mjs +3 -0
- package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +12 -2
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-auto-effort.mjs +2 -0
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-loop-options.mjs +5 -3
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-settle.mjs +5 -2
- package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +37 -16
- package/src/runtime/agent/orchestrator/session/manager/context-meta.mjs +4 -0
- package/src/runtime/agent/orchestrator/session/manager/prompt-utils.mjs +90 -0
- package/src/runtime/agent/orchestrator/session/manager/session-crud.mjs +22 -12
- package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +32 -3
- package/src/runtime/agent/orchestrator/session/manager/session-prompt-composition.mjs +27 -0
- package/src/runtime/agent/orchestrator/session/manager/session-record.mjs +16 -1
- package/src/runtime/agent/orchestrator/session/manager/status-telemetry.mjs +1 -0
- package/src/runtime/agent/orchestrator/session/manager/usage-metrics.mjs +2 -0
- package/src/runtime/agent/orchestrator/session/manager.mjs +1 -5
- package/src/runtime/agent/orchestrator/session/message-ids.mjs +123 -0
- package/src/runtime/agent/orchestrator/session/message-ids.test-support.mjs +29 -0
- package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +29 -19
- package/src/runtime/agent/orchestrator/session/read-dedup.mjs +0 -1
- package/src/runtime/agent/orchestrator/session/store/session-delete.mjs +3 -0
- package/src/runtime/agent/orchestrator/session/store/sweep/sweep-finish.mjs +10 -3
- package/src/runtime/agent/orchestrator/session/store-agent-worker-rows.mjs +11 -0
- package/src/runtime/agent/orchestrator/session/store-summary-fields.mjs +29 -0
- package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +5 -1
- package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +23 -4
- package/src/runtime/agent/orchestrator/session/store-transcript-projection.mjs +20 -3
- package/src/runtime/agent/orchestrator/session/transcript-restore/restore.mjs +28 -8
- package/src/runtime/agent/orchestrator/session/transcript-restore/user-text.mjs +140 -0
- package/src/runtime/agent/orchestrator/tools/builtin/cache-layers.mjs +52 -2
- package/src/runtime/agent/orchestrator/tools/builtin/git-command-tool.mjs +32 -22
- package/src/runtime/agent/orchestrator/tools/builtin/git-repo-rw-lock.mjs +10 -3
- package/src/runtime/agent/orchestrator/tools/builtin/read-cache-policy.mjs +8 -0
- package/src/runtime/agent/orchestrator/tools/builtin/read-range-index.mjs +31 -12
- package/src/runtime/agent/orchestrator/tools/builtin/read-single-fast-paths.mjs +32 -35
- package/src/runtime/agent/orchestrator/tools/builtin/read-single-render.mjs +11 -12
- package/src/runtime/agent/orchestrator/tools/builtin/read-single-stream.mjs +9 -6
- package/src/runtime/agent/orchestrator/tools/builtin/read-single-tool.mjs +80 -66
- package/src/runtime/agent/orchestrator/tools/builtin/read-snapshot-runtime.mjs +8 -3
- package/src/runtime/agent/orchestrator/tools/builtin/read-source-windows.mjs +5 -1
- package/src/runtime/agent/orchestrator/tools/builtin/read-streaming.mjs +16 -7
- package/src/runtime/agent/orchestrator/tools/builtin/read-tool/batch-dispatch.mjs +2 -1
- package/src/runtime/agent/orchestrator/tools/builtin/read-tool/batch-publication.mjs +137 -0
- package/src/runtime/agent/orchestrator/tools/builtin/read-tool/batch-snapshot.mjs +11 -10
- package/src/runtime/agent/orchestrator/tools/builtin/read-tool.mjs +21 -11
- package/src/runtime/agent/orchestrator/tools/builtin/runtime-capabilities.mjs +12 -1
- package/src/runtime/agent/orchestrator/tools/builtin/task-tool.mjs +56 -13
- package/src/runtime/agent/orchestrator/tools/code-graph/disk-cache.mjs +58 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/trusted-roots.mjs +25 -1
- package/src/runtime/agent/orchestrator/tools/patch/ui-diff.mjs +78 -5
- package/src/runtime/agent/orchestrator/tools/progress-message.mjs +2 -0
- package/src/runtime/agent/orchestrator/tools/shell-command.mjs +0 -10
- package/src/runtime/attachments/store.mjs +38 -0
- package/src/runtime/channels/index.mjs +1 -5
- package/src/runtime/channels/lib/runtime-paths.mjs +8 -23
- package/src/runtime/channels/lib/tool-dispatch.mjs +1 -13
- package/src/runtime/channels/lib/worker-ipc.mjs +2 -2
- package/src/runtime/channels/lib/worker-main.mjs +4 -34
- package/src/runtime/shared/atomic-file.mjs +8 -2
- package/src/runtime/shared/best-effort-stderr.mjs +8 -0
- package/src/runtime/shared/code-graph-state.mjs +12 -0
- package/src/runtime/shared/defaults-separation.mjs +2 -0
- package/src/runtime/shared/git-runner-worker.mjs +3 -2
- package/src/runtime/shared/git-runner.mjs +4 -3
- package/src/runtime/shared/llm/default-context-window.mjs +6 -0
- package/src/runtime/shared/llm/model-catalog-projection.mjs +5 -4
- package/src/runtime/shared/llm/model-catalog.mjs +240 -118
- package/src/runtime/shared/llm/provider-catalog-cache.mjs +36 -28
- package/src/runtime/shared/llm/provider-catalog-registry.mjs +156 -0
- package/src/runtime/shared/llm/provider-seed-rosters.mjs +130 -0
- package/src/runtime/shared/model-proxy-mode.d.mts +3 -0
- package/src/runtime/shared/model-proxy-mode.mjs +19 -0
- package/src/runtime/shared/provider-accounts.mjs +2 -2
- package/src/runtime/shared/provider-api-key.mjs +21 -1
- package/src/runtime/shared/schedule-model-ref.mjs +11 -2
- package/src/runtime/shared/semver.d.mts +1 -0
- package/src/runtime/shared/semver.mjs +57 -0
- package/src/runtime/shared/session-usage-totals.mjs +19 -0
- package/src/runtime/shared/sharp-electron-linux.mjs +25 -0
- package/src/runtime/shared/singleton-owner.mjs +36 -6
- package/src/runtime/shared/statusline/statusline-segments.mjs +68 -14
- package/src/runtime/shared/tool-surface.mjs +9 -2
- package/src/runtime/shared/tool-work-units.mjs +14 -0
- package/src/runtime/shared/transcript-metadata.mjs +17 -1
- package/src/runtime/shared/turn-changes.mjs +791 -0
- package/src/runtime/shared/turn-snapshot-store.mjs +346 -12
- package/src/runtime/shared/turn-snapshot.mjs +690 -81
- package/src/runtime/shared/turn-worktree-snapshot.mjs +161 -11
- package/src/runtime/shared/update-checker.mjs +2 -31
- package/src/runtime/shared/worktrees/cache-cleanups.mjs +52 -0
- package/src/runtime/shared/worktrees/contract.mjs +551 -0
- package/src/runtime/shared/worktrees/disk.mjs +57 -0
- package/src/runtime/shared/worktrees/git.mjs +247 -0
- package/src/runtime/shared/worktrees/include.mjs +480 -0
- package/src/runtime/shared/worktrees/lock.mjs +46 -0
- package/src/runtime/shared/worktrees/maintenance.mjs +96 -0
- package/src/runtime/shared/worktrees/manager.mjs +1215 -0
- package/src/runtime/shared/worktrees/merge.mjs +182 -0
- package/src/runtime/shared/worktrees/owner.mjs +46 -0
- package/src/runtime/shared/worktrees/paths.mjs +62 -0
- package/src/runtime/shared/worktrees/registry.mjs +110 -0
- package/src/runtime/shared/worktrees/settings.mjs +84 -0
- package/src/runtime/shared/worktrees/slug.mjs +62 -0
- package/src/runtime/shared/worktrees/test-support.mjs +96 -0
- package/src/runtime/visualize-bridge/client.mjs +181 -0
- package/src/runtime/visualize-bridge/tool-defs.mjs +97 -0
- package/src/session-runtime/boot/apis.mjs +35 -0
- package/src/session-runtime/boot/begin.mjs +0 -2
- package/src/session-runtime/boot/config.mjs +2 -0
- package/src/session-runtime/boot/facade.mjs +20 -0
- package/src/session-runtime/boot/infrastructure.mjs +1 -0
- package/src/session-runtime/boot/session.mjs +1 -0
- package/src/session-runtime/boot/tools.mjs +9 -4
- package/src/session-runtime/config-lifecycle.mjs +10 -1
- package/src/session-runtime/cwd-plugins/cwd-apply.mjs +11 -1
- package/src/session-runtime/global-extensions.mjs +2 -2
- package/src/session-runtime/internal-tool-executor/bridge-tools.mjs +27 -2
- package/src/session-runtime/internal-tool-executor/workspace-tools.mjs +32 -1
- package/src/session-runtime/internal-tool-executor.mjs +9 -1
- package/src/session-runtime/lifecycle/session-catalog.mjs +8 -1
- package/src/session-runtime/lifecycle/session-switch/context-switch.mjs +5 -0
- package/src/session-runtime/lifecycle/session-switch/resume.mjs +21 -3
- package/src/session-runtime/lifecycle/session-switch/session-delete.mjs +16 -0
- package/src/session-runtime/mcp-glue/connect.mjs +11 -6
- package/src/session-runtime/mcp-glue/server-catalog.mjs +7 -3
- package/src/session-runtime/mcp-glue/server-input.mjs +29 -0
- package/src/session-runtime/model-route/route-persist.mjs +3 -0
- package/src/session-runtime/model-route/set-route.mjs +15 -2
- package/src/session-runtime/notification-bus.mjs +9 -1
- package/src/session-runtime/provider-models/row-hydration.mjs +2 -0
- package/src/session-runtime/resource-extension-sync.mjs +30 -7
- package/src/session-runtime/resource-mcp-api.mjs +27 -10
- package/src/session-runtime/route-resolution.mjs +10 -1
- package/src/session-runtime/route-state.mjs +16 -2
- package/src/session-runtime/runtime-bootstrap.mjs +0 -4
- package/src/session-runtime/runtime-core.mjs +5 -0
- package/src/session-runtime/runtime-facade.mjs +8 -0
- package/src/session-runtime/runtime-paths.mjs +0 -1
- package/src/session-runtime/runtime-review-api.mjs +9 -0
- package/src/session-runtime/services/agent-tool/helpers.mjs +5 -0
- package/src/session-runtime/services/agent-tool/job-views/agent-list.mjs +3 -0
- package/src/session-runtime/services/agent-tool/job-views/job-lookup.mjs +1 -0
- package/src/session-runtime/services/agent-tool/job-views/spawn-meta.mjs +1 -0
- package/src/session-runtime/services/agent-tool/lead-worker-index.mjs +15 -0
- package/src/session-runtime/services/agent-tool/send-flow.mjs +1 -0
- package/src/session-runtime/services/agent-tool/spawn-flow/spawn-plan.mjs +10 -2
- package/src/session-runtime/services/agent-tool/spawn-flow/spawn-prep.mjs +19 -7
- package/src/session-runtime/services/agent-tool/spawn-preset.mjs +3 -0
- package/src/session-runtime/services/agent-tool/tag-registry.mjs +2 -1
- package/src/session-runtime/services/agent-tool/worker-index/row-shape.mjs +5 -0
- package/src/session-runtime/services/agent-tool/worker-rows.mjs +1 -0
- package/src/session-runtime/services/agent-tool.mjs +1 -0
- package/src/session-runtime/services/channel-worker.mjs +1 -1
- package/src/session-runtime/services/daemon-candidate.mjs +3 -2
- package/src/session-runtime/services/daemon-crash-capture/spawn-capture.mjs +55 -0
- package/src/session-runtime/session-create/session-options.mjs +10 -1
- package/src/session-runtime/session-turn-api.mjs +1 -0
- package/src/session-runtime/settings-api.mjs +4 -2
- package/src/session-runtime/settings-builtin-tools-api.mjs +9 -2
- package/src/session-runtime/settings-profile-api.mjs +27 -2
- package/src/session-runtime/settings-worktree-api.mjs +17 -0
- package/src/session-runtime/setup-tool/executor.mjs +11 -4
- package/src/session-runtime/setup-tool/settings-contract.mjs +3 -1
- package/src/session-runtime/setup-tool/tool-defs.mjs +31 -3
- package/src/session-runtime/starter-guide-state.mjs +62 -0
- package/src/session-runtime/turn/agent-ops.mjs +12 -1
- package/src/session-runtime/turn/turn-run/ask-callbacks.mjs +13 -3
- package/src/session-runtime/turn/turn-run/snapshot-tracker.mjs +20 -15
- package/src/session-runtime/turn/turn-run/turn-open.mjs +4 -5
- package/src/session-runtime/turn/turn-run/turn-prepare.mjs +29 -1
- package/src/session-runtime/turn/turn-run/turn-settle.mjs +6 -1
- package/src/session-runtime/turn/turn-run.mjs +6 -7
- package/src/session-runtime/workflow-agents-api/agent-route.mjs +15 -3
- package/src/session-runtime/workflow-agents-api/onboarding.mjs +3 -2
- package/src/session-runtime/workspace/admission-gate.mjs +197 -0
- package/src/session-runtime/workspace/project-branches.mjs +136 -0
- package/src/session-runtime/workspace/transition-steps.mjs +364 -0
- package/src/session-runtime/workspace/transitions.mjs +651 -0
- package/src/session-runtime/workspace/workspace-state.mjs +112 -0
- package/src/standalone/channel-binding.mjs +1 -2
- package/src/standalone/channel-transport/call-dispatch.mjs +4 -10
- package/src/standalone/channel-transport/http-routes.mjs +17 -0
- package/src/standalone/channel-transport.mjs +13 -1
- package/src/standalone/daemon-channels-loader.mjs +2 -3
- package/src/standalone/daemon-desktop-runtime.mjs +1 -0
- package/src/standalone/daemon-owner.mjs +372 -0
- package/src/standalone/daemon-stored-session-views.mjs +4 -1
- package/src/standalone/daemon.mjs +139 -5
- package/src/standalone/desktop-service-registry.mjs +20 -0
- package/src/standalone/local-session-runtime.mjs +13 -78
- package/src/standalone/session-client/attachment-calls.mjs +3 -1
- package/src/standalone/session-client/daemon-unavailable.mjs +62 -0
- package/src/standalone/session-client/lease-heartbeat.mjs +47 -0
- package/src/standalone/session-client.mjs +104 -13
- package/src/standalone/session-protocol.mjs +17 -0
- package/src/standalone/session-proxy.mjs +3 -0
- package/src/standalone/session-service/agent-tree/agent-child.mjs +4 -0
- package/src/standalone/session-service/agent-tree/agent-rehydrate.mjs +1 -0
- package/src/standalone/session-service/entries/lifecycle.mjs +6 -0
- package/src/standalone/session-service/projection/revisions.mjs +2 -0
- package/src/standalone/session-service/projection/transcript-window.mjs +149 -19
- package/src/standalone/session-service/projection/workspace.mjs +14 -0
- package/src/standalone/session-service/session-calls/session-action.mjs +11 -3
- package/src/standalone/session-transport/call-route.mjs +13 -0
- package/src/standalone/session-transport/client-registry/client-grace.mjs +45 -3
- package/src/standalone/session-transport/client-registry/registration-replays.mjs +1 -0
- package/src/standalone/session-transport/client-registry.mjs +20 -3
- package/src/standalone/session-transport/routes.mjs +79 -2
- package/src/standalone/session-transport/stack.mjs +2 -0
- package/src/standalone/session-transport.mjs +24 -1
- package/src/tui/app/message-selector.mjs +2 -1
- package/src/tui/app/model-options.mjs +35 -5
- package/src/tui/app/model-picker/provider-models-list.mjs +2 -2
- package/src/tui/app/model-picker.mjs +4 -2
- package/src/tui/dist/index.mjs +332 -36
- package/src/tui/index.jsx +10 -2
- package/src/tui/session/compaction-view.mjs +147 -0
- package/src/tui/session/context-state.mjs +6 -0
- package/src/tui/session/initial-state.mjs +3 -0
- package/src/tui/session/quota-resume.mjs +2 -1
- package/src/tui/session/session-api/commands.mjs +10 -2
- package/src/tui/session/session-api/controls.mjs +4 -0
- package/src/tui/session/session-api/extensions.mjs +12 -8
- package/src/tui/session/session-api/intake/rewind.mjs +2 -1
- package/src/tui/session/session-api/integrations.mjs +18 -0
- package/src/tui/session/session-api/lifecycle/dispose.mjs +6 -1
- package/src/tui/session/session-api/lifecycle/inheritance.mjs +2 -0
- package/src/tui/session/session-api/lifecycle/resume.mjs +3 -0
- package/src/tui/session/session-api/routes.mjs +10 -1
- package/src/tui/session/session-api/settings.mjs +2 -0
- package/src/tui/session/session-api/shared.mjs +10 -6
- package/src/tui/session/session-api/worktree.mjs +60 -0
- package/src/tui/session/session-api.mjs +2 -0
- package/src/tui/session/session-flow/auto-clear.mjs +31 -12
- package/src/tui/session/session-flow/drain.mjs +15 -1
- package/src/tui/session/session-flow/session-reset.mjs +4 -3
- package/src/tui/session/session-flow.mjs +2 -0
- package/src/tui/session/transcript-intake.mjs +14 -5
- package/src/tui/session/turn-stream.mjs +32 -4
- package/src/tui/session/turn-tool-cards.mjs +10 -2
- package/src/tui/session/turn.mjs +39 -10
- package/src/runtime/channels/tool-defs.mjs +0 -17
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import { clean, hasOwn } from './session-text.mjs';
|
|
4
4
|
import { normalizeEffortInput, normalizeSavedEffort } from './effort.mjs';
|
|
5
5
|
import { routeFastKey, fastPreferenceFor } from './model-capabilities.mjs';
|
|
6
|
+
import { normalizeContextTokens } from '../../../shared/llm/default-context-window.mjs';
|
|
6
7
|
|
|
7
8
|
const DEFAULT_PROVIDER = 'anthropic-oauth';
|
|
8
9
|
export const DEFAULT_MODEL = '';
|
|
@@ -53,6 +54,14 @@ function cleanContextPercent(value) {
|
|
|
53
54
|
return Math.max(10, Math.min(100, Math.round(percent / 10) * 10));
|
|
54
55
|
}
|
|
55
56
|
|
|
57
|
+
/** The route's exact window in tokens: an explicit request, else none when a
|
|
58
|
+
* percent is chosen instead, else the saved or preset selection. */
|
|
59
|
+
function resolveContextTokens({ contextTokens, hasExplicitContextPercent, saved, preset = null }) {
|
|
60
|
+
if (contextTokens !== undefined) return normalizeContextTokens(contextTokens);
|
|
61
|
+
if (hasExplicitContextPercent) return undefined;
|
|
62
|
+
return normalizeContextTokens(saved.contextTokens ?? preset?.contextTokens);
|
|
63
|
+
}
|
|
64
|
+
|
|
56
65
|
/** The route's fast flag: an explicit request, else the saved choice, else the preset or the provider preference. */
|
|
57
66
|
function resolveFast({ hasExplicitFast, explicitFast, saved, presetFast = false, preference }) {
|
|
58
67
|
if (hasExplicitFast) return explicitFast;
|
|
@@ -60,8 +69,19 @@ function resolveFast({ hasExplicitFast, explicitFast, saved, presetFast = false,
|
|
|
60
69
|
return presetFast || preference();
|
|
61
70
|
}
|
|
62
71
|
|
|
72
|
+
/** The route's Auto effort flag: an explicit request, else the saved choice,
|
|
73
|
+
* else the preset. Absent means on; only `false` turns it off. */
|
|
74
|
+
function resolveAutoEffort({ autoEffort, saved, preset = null }) {
|
|
75
|
+
if (autoEffort !== undefined) return autoEffort !== false;
|
|
76
|
+
if (hasOwn(saved, 'autoEffort')) return saved.autoEffort !== false;
|
|
77
|
+
return preset?.autoEffort !== false;
|
|
78
|
+
}
|
|
79
|
+
|
|
63
80
|
export function makeResolveRoute(resolveDefaultProvider) {
|
|
64
|
-
return function resolveRoute(
|
|
81
|
+
return function resolveRoute(
|
|
82
|
+
config,
|
|
83
|
+
{ provider, model, effort, fast, autoEffort, modelParameters, contextPercent, contextTokens } = {}
|
|
84
|
+
) {
|
|
65
85
|
const explicitProvider = clean(provider);
|
|
66
86
|
const explicitModel = clean(model);
|
|
67
87
|
const hasExplicitEffort = effort !== undefined;
|
|
@@ -70,6 +90,9 @@ export function makeResolveRoute(resolveDefaultProvider) {
|
|
|
70
90
|
const explicitFast = fast === true;
|
|
71
91
|
const hasExplicitModelParameters = modelParameters !== undefined;
|
|
72
92
|
const hasExplicitContextPercent = contextPercent !== undefined;
|
|
93
|
+
// An exact token selection replaces a saved percent unless a percent is
|
|
94
|
+
// requested alongside it.
|
|
95
|
+
const keepsSavedPercent = contextTokens === undefined;
|
|
73
96
|
|
|
74
97
|
if (!explicitProvider) {
|
|
75
98
|
const preset = findPreset(config, explicitModel || config?.default);
|
|
@@ -78,6 +101,7 @@ export function makeResolveRoute(resolveDefaultProvider) {
|
|
|
78
101
|
const m = clean(preset.model) || DEFAULT_MODEL;
|
|
79
102
|
if (p && m) {
|
|
80
103
|
const saved = modelSettingsFor(config, p, m);
|
|
104
|
+
const tokens = resolveContextTokens({ contextTokens, hasExplicitContextPercent, saved, preset });
|
|
81
105
|
return {
|
|
82
106
|
provider: p,
|
|
83
107
|
model: m,
|
|
@@ -90,12 +114,16 @@ export function makeResolveRoute(resolveDefaultProvider) {
|
|
|
90
114
|
presetFast: preset.fast === true,
|
|
91
115
|
preference: () => fastPreferenceFor(config, p, m),
|
|
92
116
|
}),
|
|
117
|
+
autoEffort: resolveAutoEffort({ autoEffort, saved, preset }),
|
|
93
118
|
modelParameters: hasExplicitModelParameters
|
|
94
119
|
? cleanModelParameters(modelParameters)
|
|
95
120
|
: cleanModelParameters(saved.modelParameters ?? preset.modelParameters),
|
|
96
121
|
contextPercent: hasExplicitContextPercent
|
|
97
122
|
? cleanContextPercent(contextPercent)
|
|
98
|
-
:
|
|
123
|
+
: keepsSavedPercent
|
|
124
|
+
? cleanContextPercent(saved.contextPercent ?? preset.contextPercent)
|
|
125
|
+
: undefined,
|
|
126
|
+
...(tokens ? { contextTokens: tokens } : {}),
|
|
99
127
|
};
|
|
100
128
|
}
|
|
101
129
|
}
|
|
@@ -104,18 +132,23 @@ export function makeResolveRoute(resolveDefaultProvider) {
|
|
|
104
132
|
const p = explicitProvider || resolveDefaultProvider(config);
|
|
105
133
|
const m = explicitModel || DEFAULT_MODEL;
|
|
106
134
|
const saved = modelSettingsFor(config, p, m);
|
|
135
|
+
const tokens = resolveContextTokens({ contextTokens, hasExplicitContextPercent, saved });
|
|
107
136
|
return {
|
|
108
137
|
provider: p,
|
|
109
138
|
model: m,
|
|
110
139
|
preset: null,
|
|
111
140
|
effort: hasExplicitEffort ? explicitEffort : normalizeSavedEffort(saved.effort),
|
|
112
141
|
fast: resolveFast({ hasExplicitFast, explicitFast, saved, preference: () => fastPreferenceFor(config, p, m) }),
|
|
142
|
+
autoEffort: resolveAutoEffort({ autoEffort, saved }),
|
|
113
143
|
modelParameters: hasExplicitModelParameters
|
|
114
144
|
? cleanModelParameters(modelParameters)
|
|
115
145
|
: cleanModelParameters(saved.modelParameters),
|
|
116
146
|
contextPercent: hasExplicitContextPercent
|
|
117
147
|
? cleanContextPercent(contextPercent)
|
|
118
|
-
:
|
|
148
|
+
: keepsSavedPercent
|
|
149
|
+
? cleanContextPercent(saved.contextPercent)
|
|
150
|
+
: undefined,
|
|
151
|
+
...(tokens ? { contextTokens: tokens } : {}),
|
|
119
152
|
};
|
|
120
153
|
};
|
|
121
154
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// Reasoning-effort catalogs and coercion. Pure helpers.
|
|
2
2
|
import { clean } from './session-text.mjs';
|
|
3
3
|
import { customEffortLevels, snapCustomEffort } from '../providers/custom-reasoning.mjs';
|
|
4
|
+
import { rejectedEffortLevelsSync } from '../providers/effort-rejection.mjs';
|
|
4
5
|
|
|
5
6
|
const TOOL_MODES = new Set(['full', 'readonly', 'lead']);
|
|
6
7
|
const ALL_EFFORT_LEVELS = new Set(['none', 'minimal', 'low', 'medium', 'high', 'xhigh', 'max', 'ultra']);
|
|
@@ -15,22 +16,15 @@ const EFFORT_LABELS = {
|
|
|
15
16
|
ultra: 'Ultra',
|
|
16
17
|
};
|
|
17
18
|
|
|
19
|
+
// Last-resort ladders for routes whose endpoints publish no effort metadata
|
|
20
|
+
// (no per-model levels on /models, no same-endpoint catalog row). Every other
|
|
21
|
+
// provider's ladder comes from its own catalog rows (live roster, models.dev
|
|
22
|
+
// via provider-catalog-registry, seeds); a route with none offers no effort.
|
|
18
23
|
const EFFORT_OPTIONS_BY_PROVIDER = {
|
|
19
24
|
openai: ['none', 'low', 'medium', 'high', 'xhigh'],
|
|
20
|
-
// gpt-5.6+ catalogs declare max/ultra; the openai-oauth transport folds
|
|
21
|
-
// ultra -> max on the wire (openai-oauth.mjs _normalizeReasoningEffort).
|
|
22
|
-
'openai-oauth': ['none', 'low', 'medium', 'high', 'xhigh', 'max', 'ultra'],
|
|
23
|
-
anthropic: ['low', 'medium', 'high', 'xhigh', 'max'],
|
|
24
|
-
'anthropic-oauth': ['low', 'medium', 'high', 'xhigh', 'max'],
|
|
25
25
|
// xAI Grok 4.6+: low/medium/high/xhigh. Reasoning cannot be disabled.
|
|
26
26
|
xai: ['low', 'medium', 'high', 'xhigh'],
|
|
27
|
-
'grok-oauth': ['low', 'medium', 'high', 'xhigh'],
|
|
28
|
-
'muse-code-oauth': ['minimal', 'low', 'medium', 'high', 'xhigh'],
|
|
29
27
|
'mimo-oauth': ['minimal', 'low', 'medium', 'high'],
|
|
30
|
-
// Antigravity carries the effort tier inside the wire model id
|
|
31
|
-
// (gemini-3-pro-high / -low, claude-*-thinking), so the route exposes none.
|
|
32
|
-
'antigravity-oauth': [],
|
|
33
|
-
'opencode-go': ['high', 'max'],
|
|
34
28
|
};
|
|
35
29
|
const EFFORT_BY_FAMILY = {
|
|
36
30
|
opus: ['low', 'medium', 'high', 'xhigh', 'max'],
|
|
@@ -73,7 +67,14 @@ export function normalizeEffortInput(value) {
|
|
|
73
67
|
return v;
|
|
74
68
|
}
|
|
75
69
|
|
|
70
|
+
// Levels the endpoint has rejected for this route stop being offered.
|
|
76
71
|
export function effortOptionsFor(provider, model) {
|
|
72
|
+
const options = catalogEffortOptions(provider, model);
|
|
73
|
+
const rejected = model?.id ? rejectedEffortLevelsSync(provider, model.id) : null;
|
|
74
|
+
return rejected?.size ? options.filter((level) => !rejected.has(level)) : options;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function catalogEffortOptions(provider, model) {
|
|
77
78
|
// Custom-provider rows declare their levels authoritatively: an explicit []
|
|
78
79
|
// (or no declaration) means none, with no family/provider fallback.
|
|
79
80
|
if (model?.customReasoning) return customEffortLevels(model) || [];
|
|
@@ -6,6 +6,7 @@ import { clean, hasOwn } from './session-text.mjs';
|
|
|
6
6
|
import { modelSupportsServiceTier } from '../providers/model-service-tiers.mjs';
|
|
7
7
|
import { openAiDirectSupportsFast, openAiDirectSupportsUltrafast } from '../providers/openai-direct-request.mjs';
|
|
8
8
|
import { supportsAnthropicFastMode } from '../../../shared/llm/anthropic-betas.mjs';
|
|
9
|
+
import { normalizeContextTokens } from '../../../shared/llm/default-context-window.mjs';
|
|
9
10
|
|
|
10
11
|
const FAST_CAPABLE_PROVIDERS = new Set([
|
|
11
12
|
'anthropic',
|
|
@@ -123,6 +124,11 @@ export function saveModelSettings(cfgMod, route, { fastCapable = true, baseConfi
|
|
|
123
124
|
else delete nextSetting.effort;
|
|
124
125
|
if (fastCapable) nextSetting.fast = route.fast === true;
|
|
125
126
|
else nextSetting.fast = false;
|
|
127
|
+
// Auto effort is on unless turned off: only the off state is stored.
|
|
128
|
+
if (hasOwn(route, 'autoEffort')) {
|
|
129
|
+
if (route.autoEffort === false) nextSetting.autoEffort = false;
|
|
130
|
+
else delete nextSetting.autoEffort;
|
|
131
|
+
}
|
|
126
132
|
if (route.modelParameters && typeof route.modelParameters === 'object') {
|
|
127
133
|
nextSetting.modelParameters = Object.fromEntries(
|
|
128
134
|
Object.entries(route.modelParameters)
|
|
@@ -136,13 +142,22 @@ export function saveModelSettings(cfgMod, route, { fastCapable = true, baseConfi
|
|
|
136
142
|
// pickers send the default stop with every selection; storing it froze each
|
|
137
143
|
// model at whatever its default was that day, so a later default never
|
|
138
144
|
// reached anyone who had already selected the model.
|
|
145
|
+
// An exact window in tokens replaces the percentage, and a percentage
|
|
146
|
+
// replaces it. A token selection at the default window reaches here already
|
|
147
|
+
// dropped (resolveRouteContextState answers no contextTokens for it).
|
|
148
|
+
const contextTokens = normalizeContextTokens(route.contextTokens);
|
|
139
149
|
const requestedPercent = Number(route.contextPercent);
|
|
140
150
|
const contextPercent = Math.round(requestedPercent / 10) * 10;
|
|
141
151
|
const isDefault = contextPercent === Number(route.contextDefaultPercent);
|
|
142
|
-
if (
|
|
152
|
+
if (contextTokens) {
|
|
153
|
+
nextSetting.contextTokens = contextTokens;
|
|
154
|
+
delete nextSetting.contextPercent;
|
|
155
|
+
} else if (requestedPercent >= 10 && requestedPercent <= 100 && !isDefault) {
|
|
143
156
|
nextSetting.contextPercent = contextPercent;
|
|
157
|
+
delete nextSetting.contextTokens;
|
|
144
158
|
} else {
|
|
145
159
|
delete nextSetting.contextPercent;
|
|
160
|
+
delete nextSetting.contextTokens;
|
|
146
161
|
}
|
|
147
162
|
modelSettings[key] = nextSetting;
|
|
148
163
|
|
|
@@ -33,9 +33,20 @@ const MCP_TRANSPORT_FIELDS = new Set([
|
|
|
33
33
|
'autoDetect',
|
|
34
34
|
]);
|
|
35
35
|
|
|
36
|
+
// Every spelling resolveMcpStartupTimeoutMs / resolveMcpCallTimeoutMs reads.
|
|
37
|
+
// A newly supplied value must replace the whole group, or a stale alias that
|
|
38
|
+
// the resolver prefers would keep winning.
|
|
39
|
+
export const MCP_TIMEOUT_FIELD_GROUPS = [
|
|
40
|
+
['startupTimeoutMs', 'startup_timeout_ms', 'startupTimeoutSec', 'startup_timeout_sec'],
|
|
41
|
+
['timeoutMs', 'timeout_ms', 'callTimeoutMs', 'call_timeout_ms'],
|
|
42
|
+
];
|
|
43
|
+
|
|
36
44
|
export function mergeMcpServerConfig(existing, next) {
|
|
37
45
|
const merged = isPlainObject(existing) ? { ...existing } : {};
|
|
38
46
|
for (const field of MCP_TRANSPORT_FIELDS) delete merged[field];
|
|
47
|
+
for (const group of MCP_TIMEOUT_FIELD_GROUPS) {
|
|
48
|
+
if (group.some((field) => Object.hasOwn(next, field))) for (const field of group) delete merged[field];
|
|
49
|
+
}
|
|
39
50
|
return { ...merged, ...next };
|
|
40
51
|
}
|
|
41
52
|
|
|
@@ -40,17 +40,21 @@ export const CWD_TOOL = {
|
|
|
40
40
|
},
|
|
41
41
|
trigger: 'Show or switch the active Project; not a shell-local cd.',
|
|
42
42
|
description:
|
|
43
|
-
'Show or switch the active Project. list returns the registered projects (name, path); a project name instead of a path → list first, then set the matching path, ask only if several match. path must be an existing directory. A shell-local cd does not change the Project.',
|
|
43
|
+
'Show or switch the active Project. list returns the registered projects (name, path); a project name instead of a path → list first, then set the matching path, ask only if several match. path must be an existing directory. A shell-local cd does not change the Project. merge-worktree / remove-worktree act on this session’s git worktree only when the user explicitly asks.',
|
|
44
44
|
inputSchema: {
|
|
45
45
|
type: 'object',
|
|
46
46
|
properties: {
|
|
47
47
|
action: {
|
|
48
48
|
type: 'string',
|
|
49
|
-
enum: ['get', 'set', 'list'],
|
|
49
|
+
enum: ['get', 'set', 'list', 'merge-worktree', 'remove-worktree', 'list-worktrees', 'prune-worktrees'],
|
|
50
50
|
description:
|
|
51
|
-
'get shows the active Project (default without path), set switches to path, list returns registered projects.',
|
|
51
|
+
'get shows the active Project (default without path), set switches to path, list returns registered projects; merge-worktree merges this session’s worktree into the project folder’s checked-out branch, remove-worktree deletes it (returning to the project folder; call again if it reports WORKTREE_REMOVAL_INCOMPLETE); list-worktrees lists every managed worktree and who retains it; prune-worktrees removes clean, merged ones no session retains.',
|
|
52
52
|
},
|
|
53
53
|
path: { type: 'string', description: 'Existing directory to switch to (implies action=set).' },
|
|
54
|
+
force: {
|
|
55
|
+
type: 'boolean',
|
|
56
|
+
description: 'remove-worktree: delete even with uncommitted or unmerged work (only when the user says so).',
|
|
57
|
+
},
|
|
54
58
|
},
|
|
55
59
|
additionalProperties: false,
|
|
56
60
|
},
|
|
@@ -93,13 +97,13 @@ const TERMINAL_RUNTIME_TOOLS = ['terminal'];
|
|
|
93
97
|
const OFFICE_RUNTIME_TOOLS = ['office'];
|
|
94
98
|
const MEDIA_RUNTIME_TOOLS = ['media'];
|
|
95
99
|
const TIDY_RUNTIME_TOOLS = ['tidy'];
|
|
100
|
+
const VISUALIZE_RUNTIME_TOOLS = ['visualize'];
|
|
96
101
|
|
|
97
102
|
const admitted = (tools, names) => (tools || []).filter((tool) => names.includes(tool?.name));
|
|
98
103
|
|
|
99
104
|
/**
|
|
100
105
|
* Assemble this runtime's tool definitions from the loaded tool-def modules.
|
|
101
|
-
* Pure: callers resolve their own availability flags (skills env gate
|
|
102
|
-
* ownership) and pass the already-built goal/agent tool arrays.
|
|
106
|
+
* Pure: callers resolve their own availability flags (skills env gate) and pass the already-built goal/agent tool arrays.
|
|
103
107
|
*
|
|
104
108
|
* - `standaloneTools` is the model-facing set (also the deferred catalog base).
|
|
105
109
|
* - `internalToolDefs` adds the `public: false` web-search tools: reachable
|
|
@@ -108,7 +112,6 @@ const admitted = (tools, names) => (tools || []).filter((tool) => names.includes
|
|
|
108
112
|
export function collectStandaloneToolDefs({
|
|
109
113
|
webSearchToolDefs,
|
|
110
114
|
memoryToolDefs,
|
|
111
|
-
channelToolDefs,
|
|
112
115
|
codeGraphToolDefs,
|
|
113
116
|
browserToolDefs = [],
|
|
114
117
|
computerToolDefs = [],
|
|
@@ -116,10 +119,10 @@ export function collectStandaloneToolDefs({
|
|
|
116
119
|
officeToolDefs = [],
|
|
117
120
|
mediaToolDefs = [],
|
|
118
121
|
tidyToolDefs = [],
|
|
122
|
+
visualizeToolDefs = [],
|
|
119
123
|
setupToolDefs = [],
|
|
120
124
|
goalTools = [],
|
|
121
125
|
agentTools = [],
|
|
122
|
-
isChannelTool = () => false,
|
|
123
126
|
skillToolEnabled = true,
|
|
124
127
|
}) {
|
|
125
128
|
const webSearchRuntimeTools = admitted(webSearchToolDefs?.TOOL_DEFS, WEB_SEARCH_RUNTIME_TOOLS);
|
|
@@ -129,7 +132,6 @@ export function collectStandaloneToolDefs({
|
|
|
129
132
|
CWD_TOOL,
|
|
130
133
|
...webSearchRuntimeTools.filter((tool) => tool?.public !== false),
|
|
131
134
|
...admitted(memoryToolDefs?.TOOL_DEFS, MEMORY_RUNTIME_TOOLS),
|
|
132
|
-
...(channelToolDefs?.TOOL_DEFS || []).filter((tool) => isChannelTool(tool?.name)),
|
|
133
135
|
...admitted(codeGraphToolDefs?.CODE_GRAPH_TOOL_DEFS, CODE_GRAPH_RUNTIME_TOOLS),
|
|
134
136
|
...admitted(browserToolDefs, BROWSER_RUNTIME_TOOLS),
|
|
135
137
|
...admitted(computerToolDefs, COMPUTER_RUNTIME_TOOLS),
|
|
@@ -137,6 +139,7 @@ export function collectStandaloneToolDefs({
|
|
|
137
139
|
...admitted(officeToolDefs, OFFICE_RUNTIME_TOOLS),
|
|
138
140
|
...admitted(mediaToolDefs, MEDIA_RUNTIME_TOOLS),
|
|
139
141
|
...admitted(tidyToolDefs, TIDY_RUNTIME_TOOLS),
|
|
142
|
+
...admitted(visualizeToolDefs, VISUALIZE_RUNTIME_TOOLS),
|
|
140
143
|
...setupToolDefs,
|
|
141
144
|
...goalTools,
|
|
142
145
|
...agentTools,
|
|
@@ -6,6 +6,7 @@ import { clean } from './session-text.mjs';
|
|
|
6
6
|
import { normalizeEffortInput } from './effort.mjs';
|
|
7
7
|
import { isLikelyRawModelId } from './config-helpers.mjs';
|
|
8
8
|
import { configuredAgentRouteCandidates } from '../../../shared/agent-route-config.mjs';
|
|
9
|
+
import { normalizeContextTokens } from '../../../shared/llm/default-context-window.mjs';
|
|
9
10
|
import { normalizeAgentId, normalizeWorkflowId } from './workflow-ids.mjs';
|
|
10
11
|
import { createWorkflowPacks } from './workflow/packs.mjs';
|
|
11
12
|
import { createWorkflowAgents } from './workflow/agents.mjs';
|
|
@@ -186,19 +187,27 @@ export function normalizeWorkflowRoute(routeLike, fallback = {}) {
|
|
|
186
187
|
if (!isLikelyRawModelId(model)) return null;
|
|
187
188
|
const effort = normalizeEffortInput(routeLike?.effort ?? fallback.effort);
|
|
188
189
|
const fast = routeLike?.fast ?? fallback.fast;
|
|
190
|
+
// Absent means Auto effort is on; only the off state is carried.
|
|
191
|
+
const autoEffort = routeLike?.autoEffort ?? fallback.autoEffort;
|
|
189
192
|
const modelParameters = routeLike?.modelParameters ?? fallback.modelParameters;
|
|
190
193
|
const requestedPercent = Number(routeLike?.contextPercent ?? fallback.contextPercent);
|
|
191
194
|
const contextPercent =
|
|
192
195
|
Number.isFinite(requestedPercent) && requestedPercent >= 10 && requestedPercent <= 100
|
|
193
196
|
? Math.round(requestedPercent / 10) * 10
|
|
194
197
|
: null;
|
|
198
|
+
// A percent on the route replaces the fallback's exact window.
|
|
199
|
+
const contextTokens = normalizeContextTokens(
|
|
200
|
+
routeLike?.contextTokens ?? (routeLike?.contextPercent == null ? fallback.contextTokens : undefined)
|
|
201
|
+
);
|
|
195
202
|
return {
|
|
196
203
|
provider,
|
|
197
204
|
model,
|
|
198
205
|
...(effort ? { effort } : {}),
|
|
199
206
|
...(typeof fast === 'boolean' ? { fast } : {}),
|
|
207
|
+
...(autoEffort === false ? { autoEffort: false } : {}),
|
|
200
208
|
...(modelParameters && typeof modelParameters === 'object' ? { modelParameters: { ...modelParameters } } : {}),
|
|
201
209
|
...(contextPercent ? { contextPercent } : {}),
|
|
210
|
+
...(contextTokens ? { contextTokens } : {}),
|
|
202
211
|
};
|
|
203
212
|
}
|
|
204
213
|
|
|
@@ -214,6 +223,7 @@ export function upsertWorkflowPreset(presets, slot, routeLike) {
|
|
|
214
223
|
model: route.model,
|
|
215
224
|
...(route.effort ? { effort: route.effort } : {}),
|
|
216
225
|
...(route.fast === true ? { fast: true } : {}),
|
|
226
|
+
...(route.autoEffort === false ? { autoEffort: false } : {}),
|
|
217
227
|
...(route.modelParameters ? { modelParameters: route.modelParameters } : {}),
|
|
218
228
|
tools: 'full',
|
|
219
229
|
};
|
|
@@ -13,6 +13,7 @@ import { createHash } from 'node:crypto';
|
|
|
13
13
|
import { writeJsonAtomicSync } from '../../../../shared/atomic-file.mjs';
|
|
14
14
|
import { resolvePluginData } from '../../../../shared/plugin-paths.mjs';
|
|
15
15
|
import { setBoundedTextCacheEntry } from './text-cache-budget.mjs';
|
|
16
|
+
import { canCacheReadFile } from '../../tools/builtin/read-cache-policy.mjs';
|
|
16
17
|
|
|
17
18
|
// Entry cap: MIXDOG_PREFETCH_CACHE_MAX env var, default 200.
|
|
18
19
|
const _envCap = Number(process.env.MIXDOG_PREFETCH_CACHE_MAX);
|
|
@@ -110,12 +111,14 @@ function rememberPrefetch(absPath, entry) {
|
|
|
110
111
|
}
|
|
111
112
|
|
|
112
113
|
export function capturePrefetchCacheState(absPath) {
|
|
113
|
-
|
|
114
|
+
const stat = _statTuple(_normalizeCacheKey(absPath));
|
|
115
|
+
return canCacheReadFile(stat) ? stat : null;
|
|
114
116
|
}
|
|
115
117
|
|
|
116
118
|
/**
|
|
117
119
|
* Look up a cached prefetch result for `absPath`. Stat-validates the file on
|
|
118
|
-
* every hit. Returns null on miss, TTL expiry,
|
|
120
|
+
* every hit. Returns null on miss, TTL expiry, stat mismatch, or a file
|
|
121
|
+
* outside the prefix budget, including a persisted legacy row. Returns
|
|
119
122
|
* `{ content, ts }` on a fresh hit.
|
|
120
123
|
*/
|
|
121
124
|
export function tryPrefetchCached(absPath) {
|
|
@@ -135,7 +138,7 @@ export function tryPrefetchCached(absPath) {
|
|
|
135
138
|
return null;
|
|
136
139
|
}
|
|
137
140
|
const fresh = _statTuple(absPath);
|
|
138
|
-
if (!_statEqual(entry.stat, fresh)) {
|
|
141
|
+
if (!_statEqual(entry.stat, fresh) || !canCacheReadFile(fresh)) {
|
|
139
142
|
_prefetchCache.delete(absPath);
|
|
140
143
|
_deleteDiskEntry(absPath);
|
|
141
144
|
return null;
|
|
@@ -152,7 +155,8 @@ export function tryPrefetchCached(absPath) {
|
|
|
152
155
|
|
|
153
156
|
/**
|
|
154
157
|
* Store a per-file prefetch result. Silently skips empty or error-prefixed
|
|
155
|
-
* content so only clean read
|
|
158
|
+
* content, and files outside the prefix budget, so only clean bounded read
|
|
159
|
+
* output enters the cache.
|
|
156
160
|
* Async callers must pass the state captured before reading; null disables
|
|
157
161
|
* insertion. Omission is reserved for synchronous current-body insertion.
|
|
158
162
|
*/
|
|
@@ -162,7 +166,7 @@ export function setPrefetchCached(absPath, content, readState) {
|
|
|
162
166
|
if (classifyResultKind(content) === 'error') return;
|
|
163
167
|
absPath = _normalizeCacheKey(absPath);
|
|
164
168
|
const stat = _statTuple(absPath);
|
|
165
|
-
if (!stat) return;
|
|
169
|
+
if (!stat || !canCacheReadFile(stat)) return;
|
|
166
170
|
// Async readers supply the version observed before execution. Never label
|
|
167
171
|
// an older body with the version that happens to exist at insertion time.
|
|
168
172
|
if (readState !== undefined && !_statEqual(readState, stat)) return;
|
|
@@ -5,6 +5,7 @@ import { clearScopedToolsForSession } from './scoped-cache.mjs';
|
|
|
5
5
|
import { registerSessionPurgeHook } from '../store.mjs';
|
|
6
6
|
import { releaseReadSnapshotScope } from '../../tools/builtin/snapshot-store.mjs';
|
|
7
7
|
import { setBoundedTextCacheEntry } from './text-cache-budget.mjs';
|
|
8
|
+
import { canCacheReadFile } from '../../tools/builtin/read-cache-policy.mjs';
|
|
8
9
|
|
|
9
10
|
const MAX_PER_SESSION = 100;
|
|
10
11
|
|
|
@@ -114,6 +115,12 @@ function _arrayStatsValid(statsByAbs) {
|
|
|
114
115
|
return true;
|
|
115
116
|
}
|
|
116
117
|
|
|
118
|
+
// Equal stat tuples do not prove bytes past the hashed prefix. Files outside
|
|
119
|
+
// that budget must not be captured, reused, or inserted.
|
|
120
|
+
function _cacheableStats(statsByAbs) {
|
|
121
|
+
return !!statsByAbs && Object.values(statsByAbs).every((stat) => canCacheReadFile(stat));
|
|
122
|
+
}
|
|
123
|
+
|
|
117
124
|
function _getOrCreate(sessionId) {
|
|
118
125
|
let m = _bySession.get(sessionId);
|
|
119
126
|
if (!m) {
|
|
@@ -157,20 +164,22 @@ export function captureReadCacheState({ args, cwd }) {
|
|
|
157
164
|
const key = _keyFor(args, cwd);
|
|
158
165
|
if (key === null && Array.isArray(args?.path ?? args?.file_path)) {
|
|
159
166
|
const parsed = _arrayKeyAndStats(args, cwd);
|
|
160
|
-
if (!parsed ||
|
|
167
|
+
if (!parsed || !_cacheableStats(parsed.statsByAbs)) return null;
|
|
161
168
|
return { ...parsed, kind: 'array' };
|
|
162
169
|
}
|
|
163
170
|
if (!key) return null;
|
|
164
171
|
const abs = _absFromKey(key);
|
|
165
172
|
const stat = _statTuple(abs);
|
|
166
|
-
|
|
173
|
+
if (!canCacheReadFile(stat)) return null;
|
|
174
|
+
return { key, statsByAbs: { [abs]: stat }, kind: 'scalar' };
|
|
167
175
|
}
|
|
168
176
|
|
|
169
177
|
/**
|
|
170
178
|
* Look up a cached read result for this session+args. Stats the file at
|
|
171
179
|
* lookup time and only returns when the stat tuple still matches. Returns
|
|
172
|
-
* null on miss, on path-form mismatch,
|
|
173
|
-
* the
|
|
180
|
+
* null on miss, on path-form mismatch, on stat failure, or when any file
|
|
181
|
+
* exceeds the prefix budget. On hit returns the full entry
|
|
182
|
+
* { content, firstToolUseId, ts }.
|
|
174
183
|
*/
|
|
175
184
|
export function tryReadCached({ sessionId, args, cwd }) {
|
|
176
185
|
if (!sessionId) return null;
|
|
@@ -182,7 +191,7 @@ export function tryReadCached({ sessionId, args, cwd }) {
|
|
|
182
191
|
if (!map) return null;
|
|
183
192
|
const entry = map.get(parsed.key);
|
|
184
193
|
if (entry?.kind !== 'array') return null;
|
|
185
|
-
if (!_arrayStatsValid(entry.statsByAbs)) {
|
|
194
|
+
if (!_arrayStatsValid(entry.statsByAbs) || !_cacheableStats(entry.statsByAbs)) {
|
|
186
195
|
map.delete(parsed.key);
|
|
187
196
|
_ridxPruneKey(sessionId, parsed.key);
|
|
188
197
|
return null;
|
|
@@ -197,7 +206,7 @@ export function tryReadCached({ sessionId, args, cwd }) {
|
|
|
197
206
|
const entry = map.get(key);
|
|
198
207
|
if (!entry) return null;
|
|
199
208
|
const fresh = _statTuple(_absFromKey(key));
|
|
200
|
-
if (!_statEqual(entry.stat, fresh)) {
|
|
209
|
+
if (!_statEqual(entry.stat, fresh) || !canCacheReadFile(fresh)) {
|
|
201
210
|
map.delete(key);
|
|
202
211
|
_ridxPruneKey(sessionId, key);
|
|
203
212
|
return null;
|
|
@@ -208,7 +217,8 @@ export function tryReadCached({ sessionId, args, cwd }) {
|
|
|
208
217
|
}
|
|
209
218
|
|
|
210
219
|
/**
|
|
211
|
-
* Cache a successful read result. Skip caching if
|
|
220
|
+
* Cache a successful read result. Skip caching if a file is missing or
|
|
221
|
+
* outside the prefix budget.
|
|
212
222
|
* `toolUseId` is the tool_use id of the FIRST call that populated the entry.
|
|
213
223
|
* Async executors pass their pre-execution `readState`; null disables caching
|
|
214
224
|
* when the source version could not be established. Omission retains direct
|
|
@@ -218,7 +228,7 @@ export function setReadCached({ sessionId, args, cwd, content, toolUseId, readSt
|
|
|
218
228
|
if (!sessionId) return;
|
|
219
229
|
if (typeof content !== 'string' || content.length === 0) return;
|
|
220
230
|
const parsed = captureReadCacheState({ args, cwd });
|
|
221
|
-
if (!parsed) return;
|
|
231
|
+
if (!parsed || !_cacheableStats(parsed.statsByAbs)) return;
|
|
222
232
|
if (
|
|
223
233
|
readState !== undefined &&
|
|
224
234
|
(!readState ||
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
latestActualUserInstructionIndex,
|
|
8
8
|
} from './messages.mjs';
|
|
9
9
|
import { staleObservations, supersedeObservation } from './observation-supersession.mjs';
|
|
10
|
+
import { carryMessageId, withoutMessageIdsReplacer } from '../message-ids.mjs';
|
|
10
11
|
|
|
11
12
|
export const EXECUTION_RECOVERY_SOURCE = 'compact-execution-recovery';
|
|
12
13
|
const TOOL_HISTORY_CONTEXT_RATIO = 0.05;
|
|
@@ -34,7 +35,11 @@ export function executionTokens(messages) {
|
|
|
34
35
|
const { uiDiff: _uiDiff, ...rest } = message;
|
|
35
36
|
return rest;
|
|
36
37
|
});
|
|
37
|
-
|
|
38
|
+
// Display ids never reach the model either.
|
|
39
|
+
return Math.max(
|
|
40
|
+
estimateMessagesTokens(budgetMessages),
|
|
41
|
+
estimateTokens(JSON.stringify(budgetMessages, withoutMessageIdsReplacer))
|
|
42
|
+
);
|
|
38
43
|
}
|
|
39
44
|
|
|
40
45
|
function requestStart(messages, index) {
|
|
@@ -123,7 +128,8 @@ function assembleTail(messages, kept, { anchor, firstKept, preserveConversation
|
|
|
123
128
|
// Omitted execution stays in the archive; its conversational text
|
|
124
129
|
// remains verbatim without replaying an orphaned call or reasoning.
|
|
125
130
|
if (message.toolCalls?.length) {
|
|
126
|
-
|
|
131
|
+
// The text-only copy is this message's rewrite: it keeps its id.
|
|
132
|
+
if (message.content) tail.push(carryMessageId({ role: 'assistant', content: message.content }, message));
|
|
127
133
|
} else {
|
|
128
134
|
tail.push(message);
|
|
129
135
|
}
|
|
@@ -158,7 +164,8 @@ export function buildExecutionTail(
|
|
|
158
164
|
sessionId,
|
|
159
165
|
toolCallId: 'compact-execution',
|
|
160
166
|
channel: 'compact-execution',
|
|
161
|
-
|
|
167
|
+
// Model-readable evidence: display ids stay out of it.
|
|
168
|
+
content: JSON.stringify({ version: 1, messages: transcript }, withoutMessageIdsReplacer, 2),
|
|
162
169
|
});
|
|
163
170
|
if (!archive) throw new Error('compact: execution history could not be archived; original context preserved');
|
|
164
171
|
recovery = [
|
|
@@ -2,13 +2,16 @@
|
|
|
2
2
|
// construction helpers.
|
|
3
3
|
import { SUMMARY_PREFIX, SUMMARY_PREFIX_ANCHOR } from './constants.mjs';
|
|
4
4
|
import { sha16, roleCounts } from './text-utils.mjs';
|
|
5
|
+
import { carryMessageId, withoutMessageIdsReplacer } from '../message-ids.mjs';
|
|
5
6
|
import {
|
|
6
7
|
isInternalRuntimeNotificationText,
|
|
7
8
|
isModelVisibleToolCompletionWrapper,
|
|
8
9
|
} from '../../../../shared/tool-execution-contract.mjs';
|
|
9
10
|
|
|
10
11
|
export function compactHeader(oldHistory) {
|
|
11
|
-
|
|
12
|
+
// Stable display ids are bookkeeping, not conversation: the header names
|
|
13
|
+
// the same history whether or not its messages were stamped.
|
|
14
|
+
const encoded = JSON.stringify(oldHistory ?? [], withoutMessageIdsReplacer);
|
|
12
15
|
return [
|
|
13
16
|
SUMMARY_PREFIX,
|
|
14
17
|
`messages=${oldHistory.length} sha256=${sha16(encoded)} roles=${roleCounts(oldHistory) || 'none'}`,
|
|
@@ -130,16 +133,20 @@ function referenceFilesManifestMessage(message) {
|
|
|
130
133
|
const paths = [...text.matchAll(/^###\s+(.+?)\s*$/gm)].map((match) => String(match[1] || '').trim()).filter(Boolean);
|
|
131
134
|
if (paths.length === 0) return null;
|
|
132
135
|
const uniquePaths = [...new Set(paths)];
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
'
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
136
|
+
// The manifest rewrites the reference-files message: it keeps that id.
|
|
137
|
+
return carryMessageId(
|
|
138
|
+
{
|
|
139
|
+
role: 'user',
|
|
140
|
+
content: [
|
|
141
|
+
'<system-reminder>',
|
|
142
|
+
'# Reference files',
|
|
143
|
+
'These files were attached when the session started. Re-read a path when its current contents are needed; the original bodies are not re-injected after Compact.',
|
|
144
|
+
...uniquePaths.map((path) => `- ${path}`),
|
|
145
|
+
'</system-reminder>',
|
|
146
|
+
].join('\n'),
|
|
147
|
+
},
|
|
148
|
+
message
|
|
149
|
+
);
|
|
143
150
|
}
|
|
144
151
|
|
|
145
152
|
export function splitProtectedContext(messages) {
|
|
@@ -20,6 +20,7 @@ import { buildExecutionTail } from './execution-tail.mjs';
|
|
|
20
20
|
import { latestSkillBodies } from '../../context/skill-state.mjs';
|
|
21
21
|
import { effectiveBudget } from './budget.mjs';
|
|
22
22
|
import { stripRuntimeUserContext, withRuntimeUserContext } from '../runtime-user-context.mjs';
|
|
23
|
+
import { carryMessageId } from '../message-ids.mjs';
|
|
23
24
|
import { normalizeIngestRole, shouldExcludeIngestMessage } from '../../../../memory/lib/session-ingest.mjs';
|
|
24
25
|
import { codexWireSendOpts } from '../manager/session-id.mjs';
|
|
25
26
|
import { rebaseCompactedEffortConfiguration } from '../../providers/effort-configuration.mjs';
|
|
@@ -380,7 +381,8 @@ function skillLoaderPair(live, bodyIndex, name, retainedCallIds) {
|
|
|
380
381
|
const call = owner?.toolCalls.find((entry) => entry.id === result.toolCallId);
|
|
381
382
|
if (call?.name !== 'Skill' || call.arguments?.name !== name) continue;
|
|
382
383
|
if (!result.nativeToolSearch || retainedCallIds.has(result.toolCallId)) return [];
|
|
383
|
-
|
|
384
|
+
// The re-attached call is part of its owner message: it keeps that id.
|
|
385
|
+
return [carryMessageId({ role: 'assistant', content: '', toolCalls: [call] }, owner), result];
|
|
384
386
|
}
|
|
385
387
|
return [];
|
|
386
388
|
}
|
|
@@ -549,11 +551,17 @@ function assertHandoffFits(summaryMessage, room) {
|
|
|
549
551
|
export function freshContextCompactMessages(messages, budgetTokens, opts = {}) {
|
|
550
552
|
const startedAt = Date.now();
|
|
551
553
|
const targetBudget = effectiveBudget(budgetTokens, opts);
|
|
552
|
-
let budget = targetBudget;
|
|
553
554
|
const baseSanitized = reconcileDedupStubs(sanitizeToolPairs(messages));
|
|
554
555
|
const baseTokens = safeEstimateMessagesTokens(baseSanitized);
|
|
555
|
-
if (baseTokens != null && baseTokens <=
|
|
556
|
-
return fitsBudgetResult({
|
|
556
|
+
if (baseTokens != null && baseTokens <= targetBudget && opts.force !== true) {
|
|
557
|
+
return fitsBudgetResult({
|
|
558
|
+
startedAt,
|
|
559
|
+
messages,
|
|
560
|
+
baseSanitized,
|
|
561
|
+
baseTokens,
|
|
562
|
+
budget: targetBudget,
|
|
563
|
+
query: opts.query || '',
|
|
564
|
+
});
|
|
557
565
|
}
|
|
558
566
|
|
|
559
567
|
const source = splitFreshSource(baseSanitized);
|
|
@@ -562,7 +570,7 @@ export function freshContextCompactMessages(messages, budgetTokens, opts = {}) {
|
|
|
562
570
|
budgetTokens,
|
|
563
571
|
opts
|
|
564
572
|
);
|
|
565
|
-
budget = freshContextBudget({ targetBudget, budgetTokens, mandatoryCost, summaryMessage, opts });
|
|
573
|
+
const budget = freshContextBudget({ targetBudget, budgetTokens, mandatoryCost, summaryMessage, opts });
|
|
566
574
|
assertMandatoryFits(budget, mandatoryCost, summaryMessage, opts.contextWindow);
|
|
567
575
|
const handoff = handoffRoomFor(budget, mandatoryCost, opts);
|
|
568
576
|
const summaryContent = String(summaryMessage?.content || '');
|