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
|
@@ -21,14 +21,15 @@ export function createTurnSegments({ messages, opts, suppressMidTurnText }) {
|
|
|
21
21
|
// live. A suppressed sub-agent session neither surfaces nor accumulates
|
|
22
22
|
// text — except on the max-output ladder, whose parts must still rebuild
|
|
23
23
|
// the complete answer the caller receives (keepWhenSuppressed).
|
|
24
|
-
|
|
24
|
+
// `committed: true` marks text that is the just-committed message's own.
|
|
25
|
+
record(text, { keepWhenSuppressed = false, committed = false } = {}) {
|
|
25
26
|
if (suppressMidTurnText) {
|
|
26
27
|
if (keepWhenSuppressed) parts.push(text);
|
|
27
28
|
return;
|
|
28
29
|
}
|
|
29
30
|
parts.push(text);
|
|
30
31
|
try {
|
|
31
|
-
opts.onAssistantText?.(text);
|
|
32
|
+
opts.onAssistantText?.(text, { committed });
|
|
32
33
|
} catch {
|
|
33
34
|
/* best-effort */
|
|
34
35
|
}
|
|
@@ -108,7 +108,8 @@ export function createNoToolTurnResolver({
|
|
|
108
108
|
drainSteering('terminal', {
|
|
109
109
|
maxPriority: 'next',
|
|
110
110
|
beforeAppend: () => {
|
|
111
|
-
if (segments.commitIntermediate(response) && hasContent)
|
|
111
|
+
if (segments.commitIntermediate(response) && hasContent)
|
|
112
|
+
segments.record(response.content, { committed: true });
|
|
112
113
|
},
|
|
113
114
|
})
|
|
114
115
|
) {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// Steering-message normalization/merge helpers.
|
|
2
2
|
// Merges queued steering entries into a single content payload + display text.
|
|
3
|
+
import { stableMessageId } from '../../../../shared/transcript-metadata.mjs';
|
|
3
4
|
|
|
4
5
|
function steeringContentText(content) {
|
|
5
6
|
if (typeof content === 'string') return content;
|
|
@@ -135,10 +136,14 @@ function injectSteeringEntry(merged, { messages, opts, stage, onSkillPrompt }) {
|
|
|
135
136
|
const rawSubmittedAt = Number(merged.submittedAt);
|
|
136
137
|
const submittedAt = Number.isFinite(rawSubmittedAt) && rawSubmittedAt > 0 ? rawSubmittedAt : undefined;
|
|
137
138
|
const injectedAt = Date.now();
|
|
139
|
+
// Every steered message carries a stable id from creation; the surface
|
|
140
|
+
// gets it back (onSteerMessage) for the row it draws. A steered prompt
|
|
141
|
+
// keeps the id the surface gave it, else gets one.
|
|
142
|
+
const messageId = stableMessageId(merged.transcriptMeta?.messageId);
|
|
138
143
|
const steeringTranscriptMeta =
|
|
139
144
|
merged.transcriptMeta && typeof merged.transcriptMeta === 'object'
|
|
140
|
-
? { at: submittedAt ?? injectedAt, ...merged.transcriptMeta }
|
|
141
|
-
:
|
|
145
|
+
? { at: submittedAt ?? injectedAt, ...merged.transcriptMeta, messageId }
|
|
146
|
+
: { messageId };
|
|
142
147
|
// Tag steering-origin user messages so provider lowering keeps them
|
|
143
148
|
// distinct from preceding tool results. Keep each queued command as
|
|
144
149
|
// its own user turn instead of collapsing priority/mode buckets
|
|
@@ -158,7 +163,7 @@ function injectSteeringEntry(merged, { messages, opts, stage, onSkillPrompt }) {
|
|
|
158
163
|
source: notification ? 'task-notification' : 'steering',
|
|
159
164
|
...(execution ? { execution } : {}),
|
|
160
165
|
...(submissionIds.length ? { submissionIds } : {}),
|
|
161
|
-
|
|
166
|
+
transcript: steeringTranscriptMeta,
|
|
162
167
|
},
|
|
163
168
|
});
|
|
164
169
|
if (!notification) onSkillPrompt(merged.content);
|
|
@@ -172,9 +177,12 @@ function injectSteeringEntry(merged, { messages, opts, stage, onSkillPrompt }) {
|
|
|
172
177
|
...(notification ? { mode: 'task-notification' } : {}),
|
|
173
178
|
...(execution ? { execution } : {}),
|
|
174
179
|
...(Array.isArray(merged.images) && merged.images.length ? { images: merged.images } : {}),
|
|
175
|
-
|
|
180
|
+
transcriptMeta: steeringTranscriptMeta,
|
|
181
|
+
messageId,
|
|
176
182
|
});
|
|
177
|
-
} catch {
|
|
183
|
+
} catch {
|
|
184
|
+
/* best-effort host callback */
|
|
185
|
+
}
|
|
178
186
|
return {
|
|
179
187
|
count: Number(merged.count) || 1,
|
|
180
188
|
textLength: String(text || '').length,
|
|
@@ -224,9 +232,11 @@ export function createSteeringDrain({ messages, opts, sessionId, onSkillPrompt }
|
|
|
224
232
|
try {
|
|
225
233
|
process.stderr.write(
|
|
226
234
|
`[steer] sess=${sessionId} injected ${stage} user message(s)` +
|
|
227
|
-
` (merged=${totalCount} len=${totalTextLen} waitMs=${
|
|
235
|
+
` (merged=${totalCount} len=${totalTextLen} waitMs=${maxQueueWaitMs})\n`
|
|
228
236
|
);
|
|
229
|
-
} catch {
|
|
237
|
+
} catch {
|
|
238
|
+
/* best-effort diagnostics */
|
|
239
|
+
}
|
|
230
240
|
}
|
|
231
241
|
return true;
|
|
232
242
|
};
|
|
@@ -40,6 +40,7 @@ export function usageDeltaEvent({
|
|
|
40
40
|
model,
|
|
41
41
|
usage,
|
|
42
42
|
sendTools,
|
|
43
|
+
toolCalls = 0,
|
|
43
44
|
}) {
|
|
44
45
|
return {
|
|
45
46
|
sessionId,
|
|
@@ -51,6 +52,8 @@ export function usageDeltaEvent({
|
|
|
51
52
|
usageMetricsEpoch,
|
|
52
53
|
deltaInput: usage.inputTokens || 0,
|
|
53
54
|
deltaOutput: usage.outputTokens || 0,
|
|
55
|
+
// Tool calls this response dispatched; the session's cumulative count.
|
|
56
|
+
deltaToolCalls: toolCalls,
|
|
54
57
|
...reasoningUsage(usage),
|
|
55
58
|
deltaPrompt: usage.promptTokens || 0,
|
|
56
59
|
// Cache delta carried alongside input/output so live metrics reflect
|
|
@@ -40,13 +40,23 @@ const traceAskStart = (sessionId, explicitPrefetch) => {
|
|
|
40
40
|
|
|
41
41
|
/** The transcript timestamps the caller supplied for this ask: the persisted
|
|
42
42
|
* user metadata plus a one-shot reader for the assistant metadata. */
|
|
43
|
-
function createAskTranscriptMeta(rawTranscriptMeta) {
|
|
43
|
+
export function createAskTranscriptMeta(rawTranscriptMeta) {
|
|
44
44
|
return {
|
|
45
45
|
raw: rawTranscriptMeta,
|
|
46
46
|
user: persistedUserTranscriptMetadata(rawTranscriptMeta),
|
|
47
47
|
takeAssistant: () => {
|
|
48
|
+
const live = rawTranscriptMeta && typeof rawTranscriptMeta === 'object';
|
|
49
|
+
// An id the live row already minted is the one persisted; otherwise the
|
|
50
|
+
// row has not been drawn yet and takes this message's id and time.
|
|
51
|
+
const rowKnowsId =
|
|
52
|
+
live && typeof rawTranscriptMeta.assistantMessageId === 'string' && rawTranscriptMeta.assistantMessageId;
|
|
48
53
|
const metadata = persistedAssistantTranscriptMetadata(rawTranscriptMeta);
|
|
49
|
-
if (
|
|
54
|
+
if (live) {
|
|
55
|
+
// One-shot: the next reply gets its own time and stable id.
|
|
56
|
+
delete rawTranscriptMeta.assistantAt;
|
|
57
|
+
delete rawTranscriptMeta.assistantMessageId;
|
|
58
|
+
rawTranscriptMeta.persistedAssistant = rowKnowsId ? null : { messageId: metadata.messageId, at: metadata.at };
|
|
59
|
+
}
|
|
50
60
|
return metadata;
|
|
51
61
|
},
|
|
52
62
|
};
|
|
@@ -70,6 +70,8 @@ export async function resolveTurnAutoEffort({ sessionId, session, provider, inpu
|
|
|
70
70
|
// Agent sessions are judged like any other: their spawn brief and later Lead
|
|
71
71
|
// `send` messages arrive as plain prompts.
|
|
72
72
|
if (mode === 'off' || input?.promptSource) return null;
|
|
73
|
+
// The session's route turned Auto effort off: its chosen effort stands.
|
|
74
|
+
if (session?.autoEffort === false) return null;
|
|
73
75
|
const request = promptContentText(input?.prompt).trim();
|
|
74
76
|
// Skill bodies and runtime blocks arrive as tagged text, not as a request.
|
|
75
77
|
if (!request || request.startsWith('<')) return null;
|
|
@@ -13,7 +13,9 @@ const optionalFn = (value) => (typeof value === 'function' ? value : undefined);
|
|
|
13
13
|
const relay = (fn, ...args) => {
|
|
14
14
|
try {
|
|
15
15
|
fn?.(...args);
|
|
16
|
-
} catch {
|
|
16
|
+
} catch {
|
|
17
|
+
/* a host callback must never break the turn */
|
|
18
|
+
}
|
|
17
19
|
};
|
|
18
20
|
|
|
19
21
|
/**
|
|
@@ -70,10 +72,10 @@ export function buildAgentLoopOptions({
|
|
|
70
72
|
checkpoint.schedule();
|
|
71
73
|
if (typeof askOpts?.onReasoningDelta === 'function') askOpts.onReasoningDelta(chunk);
|
|
72
74
|
},
|
|
73
|
-
onAssistantText: (text) => {
|
|
75
|
+
onAssistantText: (text, detail) => {
|
|
74
76
|
interruption.recordAssistantText(text);
|
|
75
77
|
checkpoint.schedule(true);
|
|
76
|
-
if (typeof askOpts?.onAssistantText === 'function') askOpts.onAssistantText(text);
|
|
78
|
+
if (typeof askOpts?.onAssistantText === 'function') askOpts.onAssistantText(text, detail);
|
|
77
79
|
},
|
|
78
80
|
takeAssistantTranscriptMetadata,
|
|
79
81
|
// Send-opt only. Never copy onto `session` — a transient
|
|
@@ -22,11 +22,13 @@ import { recordProviderContextBaseline } from '../loop/compact-policy.mjs';
|
|
|
22
22
|
import { throwIfAborted } from '../../../../shared/abort-race.mjs';
|
|
23
23
|
import { appendAssistantTurnMessage, persistProviderState, recordAskUsage } from './ask-turn-commit.mjs';
|
|
24
24
|
|
|
25
|
-
function attachAssistantTranscriptCompletion(messages, completion, turnStartedAt = 0) {
|
|
25
|
+
export function attachAssistantTranscriptCompletion(messages, completion, turnStartedAt = 0) {
|
|
26
26
|
if (!Array.isArray(messages) || !completion || typeof completion !== 'object') return false;
|
|
27
27
|
const elapsedMs = Math.max(0, Number(completion.elapsedMs || 0));
|
|
28
28
|
const status = typeof completion.status === 'string' && completion.status ? completion.status : 'done';
|
|
29
29
|
const verb = typeof completion.verb === 'string' && completion.verb ? completion.verb : 'Thought';
|
|
30
|
+
const checkpointId =
|
|
31
|
+
typeof completion.checkpointId === 'string' && completion.checkpointId ? completion.checkpointId : '';
|
|
30
32
|
let turnStart = -1;
|
|
31
33
|
const expectedAt = Number(turnStartedAt || 0);
|
|
32
34
|
if (expectedAt > 0) {
|
|
@@ -60,7 +62,7 @@ function attachAssistantTranscriptCompletion(messages, completion, turnStartedAt
|
|
|
60
62
|
...meta,
|
|
61
63
|
transcript: {
|
|
62
64
|
...transcript,
|
|
63
|
-
completion: { status, verb, elapsedMs },
|
|
65
|
+
completion: { status, verb, elapsedMs, ...(checkpointId ? { checkpointId } : {}) },
|
|
64
66
|
},
|
|
65
67
|
},
|
|
66
68
|
};
|
|
@@ -145,6 +147,7 @@ export function publishAskTurn({ sessionId, opened, terminalResultPreview, askOp
|
|
|
145
147
|
status: 'done',
|
|
146
148
|
verb: rawTranscriptMeta?.completionVerb,
|
|
147
149
|
elapsedMs: Date.now() - turnStartedAt,
|
|
150
|
+
checkpointId: rawTranscriptMeta?.checkpointId,
|
|
148
151
|
},
|
|
149
152
|
turnStartedAt
|
|
150
153
|
);
|
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
} from '../context-utils.mjs';
|
|
16
16
|
import { previewFreshContextCompaction, runFreshContextCompact } from '../loop/fresh-context.mjs';
|
|
17
17
|
import { positiveInt } from '../../../../shared/numbers.mjs';
|
|
18
|
+
import { writeStderrBestEffort } from '../../../../shared/best-effort-stderr.mjs';
|
|
18
19
|
import { traceAgentCompact, messagePrefixHash } from '../../agent-trace.mjs';
|
|
19
20
|
import { uncachedInputTokensForProvider } from './usage-metrics.mjs';
|
|
20
21
|
import { pruneOffloadSession } from '../tool-result-offload.mjs';
|
|
@@ -33,6 +34,7 @@ import {
|
|
|
33
34
|
shouldCompactForRequestMedia,
|
|
34
35
|
} from '../loop/compact-policy.mjs';
|
|
35
36
|
import { snapshotProviderRequestTools } from '../../runtime-core/tool-catalog.mjs';
|
|
37
|
+
import { scheduleCompactionArchive, stampCompactionMessageIds } from '../compaction-archive-capture.mjs';
|
|
36
38
|
|
|
37
39
|
// 'compacting' is a transient in-flight stage written just before Compact
|
|
38
40
|
// runs. If the process crashes or only partially
|
|
@@ -126,7 +128,7 @@ function handoffSummaryTimeoutMs(session, messageTokens) {
|
|
|
126
128
|
* mutated and nothing is persisted — so a caller may compact a COPY of a
|
|
127
129
|
* conversation. Session inheritance relies on exactly that: an oversized
|
|
128
130
|
* transcript is compacted for the heir while the session that owns it keeps
|
|
129
|
-
* every message
|
|
131
|
+
* every message.
|
|
130
132
|
*/
|
|
131
133
|
export async function runHandoffCompaction({
|
|
132
134
|
session,
|
|
@@ -190,9 +192,8 @@ async function sessionCompactionPlan(session, opts) {
|
|
|
190
192
|
}
|
|
191
193
|
// Reserve must mirror loop.mjs (buildCompactPolicy): request reserve (tool
|
|
192
194
|
// schema) PLUS the configured reserve (session.compaction.reservedTokens or
|
|
193
|
-
// MIXDOG_AGENT_COMPACT_RESERVED_TOKENS env).
|
|
194
|
-
// the
|
|
195
|
-
// loop path reserves, so a compacted transcript could overflow on next send.
|
|
195
|
+
// MIXDOG_AGENT_COMPACT_RESERVED_TOKENS env). Without the configured headroom
|
|
196
|
+
// the loop path reserves, a compacted transcript could overflow on next send.
|
|
196
197
|
// A cold (just loaded) transcript is metered in slices first; the plan
|
|
197
198
|
// numbers below then read the per-message memos.
|
|
198
199
|
await primeContextEstimates(messages, session);
|
|
@@ -202,6 +203,7 @@ async function sessionCompactionPlan(session, opts) {
|
|
|
202
203
|
resolvedSessionId,
|
|
203
204
|
mode,
|
|
204
205
|
force,
|
|
206
|
+
archive: opts.archive !== false,
|
|
205
207
|
messages,
|
|
206
208
|
boundary,
|
|
207
209
|
alignedPolicy,
|
|
@@ -292,8 +294,8 @@ function compactionRecord(plan, { stage, after, now, reserve, outcome }) {
|
|
|
292
294
|
}
|
|
293
295
|
|
|
294
296
|
// compact_meta parity with the loop's pre-send pass: the out-of-loop
|
|
295
|
-
// (post-turn/manual) compaction — and its failure —
|
|
296
|
-
//
|
|
297
|
+
// (post-turn/manual) compaction — and its failure — is visible to trace
|
|
298
|
+
// analytics.
|
|
297
299
|
function traceSessionCompaction(plan, compactStartedAt, { counts, failure }) {
|
|
298
300
|
const { session, mode } = plan;
|
|
299
301
|
traceAgentCompact({
|
|
@@ -342,13 +344,9 @@ async function runSessionHandoff(plan, opts) {
|
|
|
342
344
|
} catch (err) {
|
|
343
345
|
run.freshContextError = err;
|
|
344
346
|
run.compactError = err;
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
);
|
|
349
|
-
} catch {
|
|
350
|
-
/* best-effort */
|
|
351
|
-
}
|
|
347
|
+
writeStderrBestEffort(
|
|
348
|
+
`[session] fresh-context ${mode} compact failed (sess=${session.id || 'unknown'}): ${err?.message || err}\n`
|
|
349
|
+
);
|
|
352
350
|
}
|
|
353
351
|
if (!run.compacted && !run.compactError) {
|
|
354
352
|
run.compactError = new Error('fresh-context compact produced no messages');
|
|
@@ -515,8 +513,8 @@ function compactionOutcome(session, freshContextResult, changed, now) {
|
|
|
515
513
|
}
|
|
516
514
|
|
|
517
515
|
// Observability parity with the loop's pre-send pass: record the
|
|
518
|
-
// out-of-loop mutation as compact_meta
|
|
519
|
-
//
|
|
516
|
+
// out-of-loop mutation as compact_meta, so a large post-turn compact is not
|
|
517
|
+
// traced as intentional_transition: null.
|
|
520
518
|
function traceCommittedCompaction(plan, compactStartedAt, { messages, compacted, beforeBytes, afterBytes, changed }) {
|
|
521
519
|
let beforePrefixHash = null;
|
|
522
520
|
try {
|
|
@@ -645,6 +643,19 @@ async function commitSessionCompaction(plan, run, compactStartedAt) {
|
|
|
645
643
|
},
|
|
646
644
|
outcome: compactionOutcome(session, freshContextResult, changed, now),
|
|
647
645
|
});
|
|
646
|
+
// Display-only: the dropped conversation stays readable above the divider.
|
|
647
|
+
// Only the selection and the segment number happen here; the projection and
|
|
648
|
+
// the write run later, off this commit.
|
|
649
|
+
const archived =
|
|
650
|
+
changed && plan.archive
|
|
651
|
+
? scheduleCompactionArchive({
|
|
652
|
+
session,
|
|
653
|
+
sessionId: resolvedSessionId,
|
|
654
|
+
before: messages,
|
|
655
|
+
after: transcript,
|
|
656
|
+
at: now,
|
|
657
|
+
})
|
|
658
|
+
: null;
|
|
648
659
|
if (changed) {
|
|
649
660
|
invalidateProviderContextBaseline(session);
|
|
650
661
|
if (postCompactPolicy) {
|
|
@@ -678,6 +689,10 @@ async function commitSessionCompaction(plan, run, compactStartedAt) {
|
|
|
678
689
|
freshContextError: null,
|
|
679
690
|
handoffSource: freshContextResult?.handoffSource || 'session-local',
|
|
680
691
|
usage: freshContextResult?.usage || null,
|
|
692
|
+
// Messages that left the model context (0 for a rewrite-only pass):
|
|
693
|
+
// surfaces archive only those rows.
|
|
694
|
+
droppedMessages: archived?.dropped ?? 0,
|
|
695
|
+
droppedIdentities: archived?.identities ?? null,
|
|
681
696
|
}
|
|
682
697
|
);
|
|
683
698
|
}
|
|
@@ -692,7 +707,8 @@ export async function runSessionCompaction(session, opts = {}) {
|
|
|
692
707
|
});
|
|
693
708
|
}
|
|
694
709
|
plan.requireReduction =
|
|
695
|
-
(plan.mode === 'auto' || opts.requireReduction === true) &&
|
|
710
|
+
(plan.mode === 'auto' || opts.requireReduction === true) &&
|
|
711
|
+
!shouldCompactForRequestMedia(plan.messages, session.provider);
|
|
696
712
|
const skipped = () =>
|
|
697
713
|
compactionResult(plan, { changed: false, skipped: true, reason: 'no_token_reduction' }, unchangedAfter(plan), {
|
|
698
714
|
freshContext: false,
|
|
@@ -713,6 +729,11 @@ export async function runSessionCompaction(session, opts = {}) {
|
|
|
713
729
|
}).reducesTokens
|
|
714
730
|
)
|
|
715
731
|
return skipped();
|
|
732
|
+
// Every message enters the compaction with a stable id (legacy ones are
|
|
733
|
+
// stamped now and persisted): the result's rewrites carry it, so what was
|
|
734
|
+
// dropped is exactly the ids the result no longer holds. The snapshot is a
|
|
735
|
+
// prefix of the live transcript holding the same message objects.
|
|
736
|
+
stampCompactionMessageIds(plan.messages, plan.resolvedSessionId);
|
|
716
737
|
const compactStartedAt = Date.now();
|
|
717
738
|
try {
|
|
718
739
|
await opts.onStageChange?.('compacting');
|
|
@@ -117,8 +117,12 @@ export function resolveSessionContextMeta(provider, model, seed = {}, { wholeWin
|
|
|
117
117
|
// (a session older than selectedContextWindow, a route saved before the
|
|
118
118
|
// catalog warmed) is still the user's choice and keeps the served window.
|
|
119
119
|
const recordsSelection = wholeWindow || boundedPercent(seed.contextPercent) !== null;
|
|
120
|
+
// An exact token selection without its resolved window still names it,
|
|
121
|
+
// bounded by what the model serves.
|
|
122
|
+
const selectedTokens = positiveInt(seed.contextTokens);
|
|
120
123
|
const requestedContextWindow =
|
|
121
124
|
positiveInt(seed.selectedContextWindow) ||
|
|
125
|
+
(selectedTokens ? Math.min(selectedTokens, servedContextWindow) : 0) ||
|
|
122
126
|
(recordsSelection
|
|
123
127
|
? servedContextWindow
|
|
124
128
|
: contextWindowRange({ provider: providerNameOf(provider), contextWindow: servedContextWindow }).defaultWindow);
|
|
@@ -185,6 +185,96 @@ export function refreshSessionBp3Environment(session, cwd) {
|
|
|
185
185
|
return true;
|
|
186
186
|
}
|
|
187
187
|
|
|
188
|
+
const CORE_MEMORY_HEADING = '# Core Memory\n';
|
|
189
|
+
const INSTRUCTION_FILES_HEADING = '# Instruction Files';
|
|
190
|
+
|
|
191
|
+
function trimmedSlice(value) {
|
|
192
|
+
return typeof value === 'string' ? value.trim() : '';
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// Where a heading-led slice starts in a BP3 join: `cut` ends the text before
|
|
196
|
+
// it (separator included), `start` is the slice's first character.
|
|
197
|
+
function bp3SliceAt(text, heading) {
|
|
198
|
+
if (text.startsWith(heading)) return { cut: 0, start: 0 };
|
|
199
|
+
const at = text.indexOf(`${BP3_PART_SEPARATOR}${heading}`);
|
|
200
|
+
return at < 0 ? null : { cut: at, start: at + BP3_PART_SEPARATOR.length };
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Replace the workspace-scoped tail of a BP3 core (composeSystemPrompt order:
|
|
205
|
+
* …, `# Core Memory`, `# Instruction Files`). The instruction-files slice is
|
|
206
|
+
* always last and may itself contain separators, so it is cut by position;
|
|
207
|
+
* the core-memory slice is one separator-free part. `undefined` keeps a slice,
|
|
208
|
+
* an empty string drops it.
|
|
209
|
+
* @param {string} core
|
|
210
|
+
* @param {{ coreMemoryContext?: string, instructionFilesContext?: string }} slices
|
|
211
|
+
*/
|
|
212
|
+
export function replaceWorkspaceCoreSlices(core, { coreMemoryContext, instructionFilesContext } = {}) {
|
|
213
|
+
let head = String(core || '');
|
|
214
|
+
let instructionFiles = '';
|
|
215
|
+
const instructionAt = bp3SliceAt(head, INSTRUCTION_FILES_HEADING);
|
|
216
|
+
if (instructionAt) {
|
|
217
|
+
instructionFiles = head.slice(instructionAt.start);
|
|
218
|
+
head = head.slice(0, instructionAt.cut);
|
|
219
|
+
}
|
|
220
|
+
let parts = head ? head.split(BP3_PART_SEPARATOR) : [];
|
|
221
|
+
if (coreMemoryContext !== undefined) {
|
|
222
|
+
parts = parts.filter((part) => !part.startsWith(CORE_MEMORY_HEADING));
|
|
223
|
+
const memory = trimmedSlice(coreMemoryContext);
|
|
224
|
+
if (memory) parts.push(`${CORE_MEMORY_HEADING}${memory}`);
|
|
225
|
+
}
|
|
226
|
+
if (instructionFilesContext !== undefined) instructionFiles = trimmedSlice(instructionFilesContext);
|
|
227
|
+
return [...parts, instructionFiles].filter((part) => part && part.trim()).join(BP3_PART_SEPARATOR);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Replace the shell/project facts that lead the persisted environment tail
|
|
232
|
+
* (`- Cwd …`, shell tools, git state, cwd listing); the trailing response
|
|
233
|
+
* language block is kept. `undefined` keeps the facts.
|
|
234
|
+
*/
|
|
235
|
+
export function replaceWorkspaceEnvironment(environmentTail, environmentContext) {
|
|
236
|
+
if (environmentContext === undefined) return environmentTail;
|
|
237
|
+
const parts = typeof environmentTail === 'string' && environmentTail ? environmentTail.split(BP3_PART_SEPARATOR) : [];
|
|
238
|
+
const facts = trimmedSlice(environmentContext);
|
|
239
|
+
if (parts[0]?.startsWith('- ')) parts[0] = facts;
|
|
240
|
+
else parts.unshift(facts);
|
|
241
|
+
return parts.filter((part) => part && part.trim()).join(BP3_PART_SEPARATOR);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Re-root an existing session's prompt at a new workspace without touching
|
|
246
|
+
* the conversation: environment facts, project memory and instruction files
|
|
247
|
+
* are replaced in place (never appended), then the `# Session` block is
|
|
248
|
+
* rebuilt for `cwd`. Messages are replaced, not mutated, so delta saves write
|
|
249
|
+
* a full snapshot.
|
|
250
|
+
* @param {object} session
|
|
251
|
+
* @param {{ cwd: string, environmentContext?: string, coreMemoryContext?: string, instructionFilesContext?: string }} context
|
|
252
|
+
* @returns {boolean}
|
|
253
|
+
*/
|
|
254
|
+
export function refreshSessionWorkspaceContext(
|
|
255
|
+
session,
|
|
256
|
+
{ cwd, environmentContext, coreMemoryContext, instructionFilesContext } = {}
|
|
257
|
+
) {
|
|
258
|
+
if (!session) return false;
|
|
259
|
+
if (typeof session.bp3EnvironmentContext === 'string') {
|
|
260
|
+
session.bp3EnvironmentContext = replaceWorkspaceEnvironment(session.bp3EnvironmentContext, environmentContext);
|
|
261
|
+
}
|
|
262
|
+
const slices = { coreMemoryContext, instructionFilesContext };
|
|
263
|
+
if (session.bp3EnvSplit === true) {
|
|
264
|
+
const target = bp3SystemMessage(session);
|
|
265
|
+
if (target && typeof target.content === 'string') {
|
|
266
|
+
const nextCore = replaceWorkspaceCoreSlices(target.content, slices);
|
|
267
|
+
replaceSystemMessageContent(session, target, nextCore);
|
|
268
|
+
session.bp3CoreContext = nextCore;
|
|
269
|
+
}
|
|
270
|
+
} else if (typeof session.bp3CoreContext === 'string') {
|
|
271
|
+
// Legacy combined layout: refreshSessionBp3Environment rebuilds the tier3
|
|
272
|
+
// block from the core plus the environment below.
|
|
273
|
+
session.bp3CoreContext = replaceWorkspaceCoreSlices(session.bp3CoreContext, slices);
|
|
274
|
+
}
|
|
275
|
+
return refreshSessionBp3Environment(session, cwd);
|
|
276
|
+
}
|
|
277
|
+
|
|
188
278
|
export function resetSessionBp3Environment(session) {
|
|
189
279
|
if (session?.bp3EnvSplit === true) {
|
|
190
280
|
const target = bpEnvSystemMessage(session);
|
|
@@ -25,6 +25,8 @@ import {
|
|
|
25
25
|
import { getProvider } from '../../providers/registry.mjs';
|
|
26
26
|
import { isSessionCompactionBlocked, getSessionAbortSignal, _runtimeEntries } from './runtime-liveness.mjs';
|
|
27
27
|
import { mintSessionId } from './session-id.mjs';
|
|
28
|
+
import { hideCompactionArchive } from '../compaction-archive-capture.mjs';
|
|
29
|
+
import { writeStderrBestEffort } from '../../../../shared/best-effort-stderr.mjs';
|
|
28
30
|
|
|
29
31
|
/** Force-flush session metrics to disk. Used by watchdog terminal-reap (fix B). */
|
|
30
32
|
export async function flushSessionMetrics(sessionId) {
|
|
@@ -97,14 +99,6 @@ export async function updateSessionManualTitle(id, title) {
|
|
|
97
99
|
return true;
|
|
98
100
|
}
|
|
99
101
|
// --- Clear messages (keep system prompt + provider/model/cwd) ---
|
|
100
|
-
function bestEffortStderr(line) {
|
|
101
|
-
try {
|
|
102
|
-
process.stderr.write(line);
|
|
103
|
-
} catch {
|
|
104
|
-
/* best-effort */
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
|
|
108
102
|
// Token/provider accounting a cleared transcript starts from; shared by the
|
|
109
103
|
// live session and the cold fork of its outgoing transcript.
|
|
110
104
|
function clearedTokenAccounting(now) {
|
|
@@ -112,6 +106,7 @@ function clearedTokenAccounting(now) {
|
|
|
112
106
|
providerState: undefined,
|
|
113
107
|
totalInputTokens: 0,
|
|
114
108
|
totalOutputTokens: 0,
|
|
109
|
+
totalToolCalls: 0,
|
|
115
110
|
reasoningUsage: null,
|
|
116
111
|
totalCachedReadTokens: 0,
|
|
117
112
|
totalCacheWriteTokens: 0,
|
|
@@ -133,11 +128,18 @@ async function compactBeforeClear(session, sessionId, clearOptions) {
|
|
|
133
128
|
let clearCompactError = null;
|
|
134
129
|
if (messages.length >= 3) {
|
|
135
130
|
try {
|
|
136
|
-
|
|
131
|
+
// The clear right after discards this compaction's display too, so it
|
|
132
|
+
// archives nothing (clear semantics are unchanged).
|
|
133
|
+
const compactResult = await runSessionCompaction(session, {
|
|
134
|
+
mode: 'manual',
|
|
135
|
+
force: true,
|
|
136
|
+
sessionId,
|
|
137
|
+
archive: false,
|
|
138
|
+
});
|
|
137
139
|
if (compactResult?.error) clearCompactError = new Error(compactResult.error);
|
|
138
140
|
} catch (err) {
|
|
139
141
|
clearCompactError = err;
|
|
140
|
-
|
|
142
|
+
writeStderrBestEffort(`[session] auto-clear pre-compact failed (sess=${sessionId}): ${err?.message || err}\n`);
|
|
141
143
|
}
|
|
142
144
|
messages = Array.isArray(session.messages) ? session.messages : [];
|
|
143
145
|
}
|
|
@@ -208,6 +210,11 @@ function forkOutgoingTranscript(session, messages, now) {
|
|
|
208
210
|
...clearedTokenAccounting(now),
|
|
209
211
|
};
|
|
210
212
|
delete fork.liveTurnMessages;
|
|
213
|
+
// The archive sidecar belongs to the source id; the fork has none.
|
|
214
|
+
if (fork.compaction?.archive) {
|
|
215
|
+
fork.compaction = { ...fork.compaction };
|
|
216
|
+
delete fork.compaction.archive;
|
|
217
|
+
}
|
|
211
218
|
setLiveSession(fork);
|
|
212
219
|
void saveSessionAsync(fork)
|
|
213
220
|
.then(() => {
|
|
@@ -219,10 +226,10 @@ function forkOutgoingTranscript(session, messages, now) {
|
|
|
219
226
|
evictLiveSession(forkId);
|
|
220
227
|
})
|
|
221
228
|
.catch((err) => {
|
|
222
|
-
|
|
229
|
+
writeStderrBestEffort(`[session] clear-fork save failed (sess=${forkId}): ${err?.message || err}\n`);
|
|
223
230
|
});
|
|
224
231
|
} catch (err) {
|
|
225
|
-
|
|
232
|
+
writeStderrBestEffort(`[session] clear-fork failed (sess=${session.id}): ${err?.message || err}\n`);
|
|
226
233
|
}
|
|
227
234
|
}
|
|
228
235
|
|
|
@@ -277,6 +284,9 @@ export async function clearSessionMessages(sessionId, options = {}) {
|
|
|
277
284
|
lastClearAfterMessageTokens: tokens.afterMessageTokens,
|
|
278
285
|
lastClearCompactError: clearCompactError?.message || null,
|
|
279
286
|
};
|
|
287
|
+
// A clear wipes the visible conversation: earlier compactions' archived
|
|
288
|
+
// display goes with it (nothing new is archived).
|
|
289
|
+
hideCompactionArchive(session);
|
|
280
290
|
if (summaryCarriedForward && tokens.postClearPolicy) {
|
|
281
291
|
recordContextUsageSnapshot(session, tokens.postClearPolicy, {
|
|
282
292
|
messages: keep,
|
|
@@ -32,11 +32,13 @@ import {
|
|
|
32
32
|
unusedModelEditToolName,
|
|
33
33
|
} from '../../../../shared/edit-tool-dialect.mjs';
|
|
34
34
|
import { resolveSessionContextMeta } from './context-meta.mjs';
|
|
35
|
+
import { normalizeContextTokens } from '../../../../shared/llm/default-context-window.mjs';
|
|
35
36
|
import { getAgentRuntimeSync, warnAgentRuntimeResolveFailureOnce } from './agent-runtime-singleton.mjs';
|
|
36
37
|
import { ensureCodexWireSessionId, mintSessionId } from './session-id.mjs';
|
|
37
38
|
import { providerCacheKey } from './provider-cache-key.mjs';
|
|
38
39
|
import { buildSessionRecord, normalizeDesktopSessionMetadata, sessionOriginFields } from './session-record.mjs';
|
|
39
40
|
import { refreshSessionBp3Environment } from './prompt-utils.mjs';
|
|
41
|
+
import { isWorktreeBound } from '../../../../shared/worktrees/contract.mjs';
|
|
40
42
|
|
|
41
43
|
// Owner liveness (attach-on-resume guard) and the durable turn-checkpoint
|
|
42
44
|
// recovery it gates; re-exported so prior importers of this module stay
|
|
@@ -153,6 +155,8 @@ function resolveSessionRoute(opts, presetObj, profile) {
|
|
|
153
155
|
const toolPreset = opts.tools || presetObj?.tools || (typeof opts.preset === 'string' ? opts.preset : null) || 'full';
|
|
154
156
|
const effort = Object.hasOwn(opts, 'effort') ? opts.effort || null : presetObj?.effort || null;
|
|
155
157
|
const fast = presetObj?.fast === true || opts.fast === true;
|
|
158
|
+
// Absent means on: an explicit request wins, else the preset's own flag.
|
|
159
|
+
const autoEffort = opts.autoEffort !== undefined ? opts.autoEffort !== false : presetObj?.autoEffort !== false;
|
|
156
160
|
let modelParameters = {};
|
|
157
161
|
if (opts.modelParameters && typeof opts.modelParameters === 'object') {
|
|
158
162
|
modelParameters = { ...opts.modelParameters };
|
|
@@ -160,6 +164,7 @@ function resolveSessionRoute(opts, presetObj, profile) {
|
|
|
160
164
|
modelParameters = { ...presetObj.modelParameters };
|
|
161
165
|
}
|
|
162
166
|
const contextPercent = normalizeContextPercent(opts.contextPercent);
|
|
167
|
+
const contextTokens = normalizeContextTokens(opts.contextTokens) || null;
|
|
163
168
|
if (!providerName) throw new Error('createSession: provider is required');
|
|
164
169
|
if (!modelName) throw new Error('createSession: model is required');
|
|
165
170
|
const provider = getProvider(providerName);
|
|
@@ -172,7 +177,19 @@ function resolveSessionRoute(opts, presetObj, profile) {
|
|
|
172
177
|
// materialization. Supplying that reservation here keeps the address
|
|
173
178
|
// stable across intake -> queued turn -> provider execution.
|
|
174
179
|
const id = requestedId || mintSessionId();
|
|
175
|
-
return {
|
|
180
|
+
return {
|
|
181
|
+
providerName,
|
|
182
|
+
modelName,
|
|
183
|
+
provider,
|
|
184
|
+
toolPreset,
|
|
185
|
+
effort,
|
|
186
|
+
fast,
|
|
187
|
+
autoEffort,
|
|
188
|
+
modelParameters,
|
|
189
|
+
contextPercent,
|
|
190
|
+
contextTokens,
|
|
191
|
+
id,
|
|
192
|
+
};
|
|
176
193
|
}
|
|
177
194
|
|
|
178
195
|
// Unified-shard policy — no broad role-specific schema filter. Keep
|
|
@@ -213,6 +230,7 @@ export function createSession(opts) {
|
|
|
213
230
|
const contextMeta = resolveSessionContextMeta(provider, modelName, {
|
|
214
231
|
selectedContextWindow: opts.selectedContextWindow,
|
|
215
232
|
contextPercent: route.contextPercent,
|
|
233
|
+
contextTokens: route.contextTokens,
|
|
216
234
|
});
|
|
217
235
|
const session = buildSessionRecord({
|
|
218
236
|
opts,
|
|
@@ -287,6 +305,10 @@ function applyRouteFields(session, route) {
|
|
|
287
305
|
if (route.provider) session.provider = route.provider;
|
|
288
306
|
if (route.model) session.model = route.model;
|
|
289
307
|
if (Object.hasOwn(route, 'fast')) session.fast = route.fast === true;
|
|
308
|
+
if (Object.hasOwn(route, 'autoEffort')) {
|
|
309
|
+
if (route.autoEffort === false) session.autoEffort = false;
|
|
310
|
+
else delete session.autoEffort;
|
|
311
|
+
}
|
|
290
312
|
if (Object.hasOwn(route, 'effort')) session.effort = route.effort || null;
|
|
291
313
|
ensureCodexWireSessionId(session);
|
|
292
314
|
if (Object.hasOwn(route, 'modelParameters')) {
|
|
@@ -294,6 +316,8 @@ function applyRouteFields(session, route) {
|
|
|
294
316
|
route.modelParameters && typeof route.modelParameters === 'object' ? { ...route.modelParameters } : {};
|
|
295
317
|
}
|
|
296
318
|
if (Object.hasOwn(route, 'contextPercent')) session.contextPercent = normalizeContextPercent(route.contextPercent);
|
|
319
|
+
if (Object.hasOwn(route, 'contextTokens'))
|
|
320
|
+
session.contextTokens = normalizeContextTokens(route.contextTokens) || null;
|
|
297
321
|
if (Object.hasOwn(route, 'selectedContextWindow')) {
|
|
298
322
|
session.selectedContextWindow = Number(route.selectedContextWindow) || null;
|
|
299
323
|
}
|
|
@@ -441,13 +465,18 @@ function _applyDesktopResumeScope(session, options) {
|
|
|
441
465
|
}
|
|
442
466
|
// The host's summary may predate a cwd change. It supplies the
|
|
443
467
|
// classification check, not permission to overwrite the session's
|
|
444
|
-
// newer execution Project. Task metadata always remains pathless.
|
|
468
|
+
// newer execution Project. Task metadata always remains pathless. A Lead
|
|
469
|
+
// in its worktree executes outside the Project: its registered Project
|
|
470
|
+
// stays the workspace record's projectPath, never the worktree cwd.
|
|
471
|
+
const workspace = session.workspace;
|
|
472
|
+
const executionProject =
|
|
473
|
+
isWorktreeBound(workspace) && typeof workspace.projectPath === 'string' ? workspace.projectPath : session.cwd;
|
|
445
474
|
session.desktopSession =
|
|
446
475
|
expectedDesktop.classification === 'project'
|
|
447
476
|
? normalizeDesktopSessionMetadata(
|
|
448
477
|
{
|
|
449
478
|
...expectedDesktop,
|
|
450
|
-
projectPath:
|
|
479
|
+
projectPath: executionProject || expectedDesktop.projectPath,
|
|
451
480
|
},
|
|
452
481
|
session.cwd
|
|
453
482
|
)
|