mixdog 1.1.2 → 1.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -5
- package/package.json +2 -1
- package/scripts/lib/run-node-tests.mjs +9 -0
- package/scripts/prune-desktop-runtime.mjs +3 -0
- package/scripts/test-direct.mjs +4 -2
- package/src/defaults/shipped-history.json +10 -0
- package/src/defaults/skills/pptx/SKILL.md +6 -6
- package/src/defaults/skills/pptx/references/review.md +6 -4
- package/src/defaults/skills/pptx/scripts/source-extract.mjs +1 -1
- package/src/defaults/skills/setup/references/actions.md +18 -5
- package/src/defaults/skills/visualize/SKILL.md +160 -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/help.mjs +2 -0
- package/src/rules/shared/60-verification.md +3 -1
- package/src/rules/shared/70-delivery.md +4 -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/cache-mtime.mjs +26 -5
- 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 +19 -5
- package/src/runtime/agent/orchestrator/context/collect.mjs +2 -1
- package/src/runtime/agent/orchestrator/context/deferred-tools.mjs +11 -5
- package/src/runtime/agent/orchestrator/context/skill-catalog.mjs +74 -21
- package/src/runtime/agent/orchestrator/mcp/client-config.mjs +133 -15
- package/src/runtime/agent/orchestrator/mcp/client-handshake.mjs +55 -8
- package/src/runtime/agent/orchestrator/mcp/client-transport.mjs +86 -8
- package/src/runtime/agent/orchestrator/mcp/client.mjs +474 -164
- package/src/runtime/agent/orchestrator/mcp/lazy-output-validator.mjs +25 -0
- package/src/runtime/agent/orchestrator/mcp/plugin-placeholders.mjs +54 -0
- package/src/runtime/agent/orchestrator/mcp/reconnect-singleflight.mjs +4 -0
- package/src/runtime/agent/orchestrator/providers/account-pool.mjs +108 -17
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-client-version.mjs +1 -1
- 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 +32 -12
- package/src/runtime/agent/orchestrator/providers/antigravity-oauth-tokens.mjs +2 -0
- package/src/runtime/agent/orchestrator/providers/auto-effort.mjs +9 -0
- package/src/runtime/agent/orchestrator/providers/command-code-oauth-login.mjs +337 -0
- package/src/runtime/agent/orchestrator/providers/command-code-oauth-tokens.mjs +159 -0
- package/src/runtime/agent/orchestrator/providers/command-code-oauth.mjs +444 -0
- package/src/runtime/agent/orchestrator/providers/cursor-auth.mjs +2 -0
- package/src/runtime/agent/orchestrator/providers/cursor-client-version.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/cursor-wire-normalization.mjs +2 -1
- package/src/runtime/agent/orchestrator/providers/custom-provider.mjs +7 -4
- package/src/runtime/agent/orchestrator/providers/effort-configuration.mjs +21 -6
- package/src/runtime/agent/orchestrator/providers/effort-rejection.mjs +244 -0
- package/src/runtime/agent/orchestrator/providers/gemini.mjs +41 -28
- 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/lib/gemini-model-catalog.mjs +7 -1
- 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-bridge.mjs +1080 -0
- package/src/runtime/agent/orchestrator/providers/model-proxy-channel.d.mts +82 -0
- package/src/runtime/agent/orchestrator/providers/model-proxy-channel.mjs +556 -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 +15 -0
- package/src/runtime/agent/orchestrator/providers/oauth-usage.mjs +6 -5
- package/src/runtime/agent/orchestrator/providers/openai-compat-chat-send.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/openai-compat-models.mjs +6 -2
- package/src/runtime/agent/orchestrator/providers/openai-compat-options.mjs +11 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-xai-send.mjs +2 -1
- package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +15 -4
- 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/openai-ws.mjs +4 -4
- package/src/runtime/agent/orchestrator/providers/registry.mjs +47 -7
- 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 +15 -11
- package/src/runtime/agent/orchestrator/runtime-core/extension-scopes.mjs +15 -55
- package/src/runtime/agent/orchestrator/runtime-core/goal-control.mjs +15 -2
- package/src/runtime/agent/orchestrator/runtime-core/model-capabilities.mjs +16 -1
- package/src/runtime/agent/orchestrator/runtime-core/plugin-mcp.mjs +278 -84
- package/src/runtime/agent/orchestrator/runtime-core/skill-tool-loading.mjs +4 -1
- package/src/runtime/agent/orchestrator/runtime-core/tool-catalog-schema.mjs +21 -0
- package/src/runtime/agent/orchestrator/runtime-core/tool-catalog.mjs +54 -3
- package/src/runtime/agent/orchestrator/runtime-core/tool-defs.mjs +10 -3
- 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/catalog-db/index.mjs +284 -0
- package/src/runtime/agent/orchestrator/session/catalog-db/schema.mjs +268 -0
- package/src/runtime/agent/orchestrator/session/catalog-db/sqlite-gate.mjs +27 -0
- package/src/runtime/agent/orchestrator/session/catalog-db/worker.mjs +628 -0
- package/src/runtime/agent/orchestrator/session/compact/execution-tail.mjs +10 -3
- package/src/runtime/agent/orchestrator/session/compact/messages.mjs +24 -13
- 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/conversation-import/claude.mjs +184 -0
- package/src/runtime/agent/orchestrator/session/conversation-import/codex.mjs +136 -0
- package/src/runtime/agent/orchestrator/session/conversation-import/convert.mjs +128 -0
- package/src/runtime/agent/orchestrator/session/conversation-import/imported-session.mjs +127 -0
- package/src/runtime/agent/orchestrator/session/conversation-import/turns.mjs +101 -0
- package/src/runtime/agent/orchestrator/session/explicit-skills.mjs +83 -5
- package/src/runtime/agent/orchestrator/session/imported-message.mjs +11 -0
- package/src/runtime/agent/orchestrator/session/loop/completion-guards.mjs +2 -1
- package/src/runtime/agent/orchestrator/session/loop/loop-state.mjs +1 -1
- 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 +5 -3
- package/src/runtime/agent/orchestrator/session/loop/send-phase.mjs +7 -4
- package/src/runtime/agent/orchestrator/session/loop/steering.mjs +21 -10
- package/src/runtime/agent/orchestrator/session/loop/tool-helpers.mjs +3 -0
- package/src/runtime/agent/orchestrator/session/loop/usage.mjs +3 -0
- package/src/runtime/agent/orchestrator/session/manager/ask-queue.mjs +12 -3
- package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +15 -3
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-auto-effort.mjs +22 -12
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-execute.mjs +9 -2
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-loop-options.mjs +9 -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/pending-message-entry.mjs +17 -4
- 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 +0 -1
- 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 +33 -22
- package/src/runtime/agent/orchestrator/session/store/listing.mjs +6 -2
- package/src/runtime/agent/orchestrator/session/store/session-delete.mjs +3 -0
- package/src/runtime/agent/orchestrator/session/store/summary-cache.mjs +6 -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-catalog.mjs +817 -0
- package/src/runtime/agent/orchestrator/session/store-summary-fields.mjs +36 -0
- package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +23 -12
- package/src/runtime/agent/orchestrator/session/store-summary-reader-catalog-fixture.mjs +158 -0
- package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +202 -374
- package/src/runtime/agent/orchestrator/session/store-transcript-projection.mjs +20 -3
- package/src/runtime/agent/orchestrator/session/synthetic-user-envelope.mjs +3 -1
- package/src/runtime/agent/orchestrator/session/tool-batch/pre-dispatch.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/transcript-restore/restore.mjs +48 -9
- package/src/runtime/agent/orchestrator/session/transcript-restore/user-text.mjs +161 -0
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/invocation-plan.mjs +17 -1
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/spawn-env.mjs +4 -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/lib/list-helpers.mjs +2 -2
- package/src/runtime/agent/orchestrator/tools/builtin/path-diagnostics.mjs +6 -6
- package/src/runtime/agent/orchestrator/tools/builtin/read-cache-policy.mjs +8 -0
- package/src/runtime/agent/orchestrator/tools/builtin/read-range-index.mjs +203 -33
- package/src/runtime/agent/orchestrator/tools/builtin/read-single-fast-paths.mjs +32 -35
- package/src/runtime/agent/orchestrator/tools/builtin/read-single-guards.mjs +8 -4
- 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 +83 -68
- package/src/runtime/agent/orchestrator/tools/builtin/read-snapshot-runtime.mjs +8 -3
- package/src/runtime/agent/orchestrator/tools/builtin/read-source-windows.mjs +20 -3
- 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/search-glob-tool.mjs +13 -10
- package/src/runtime/agent/orchestrator/tools/builtin/search-grep-tool.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/builtin/search-path-diagnostics.mjs +17 -12
- 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/dispatch/root-resolution.mjs +2 -2
- package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/code-graph/trusted-roots.mjs +25 -1
- package/src/runtime/agent/orchestrator/tools/patch/apply-patch/codex-batch.mjs +4 -4
- package/src/runtime/agent/orchestrator/tools/patch/apply-patch/route.mjs +2 -2
- package/src/runtime/agent/orchestrator/tools/patch/orchestrator.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/patch/read-redirects.mjs +34 -30
- package/src/runtime/agent/orchestrator/tools/patch/sequence/units.mjs +7 -7
- package/src/runtime/agent/orchestrator/tools/patch/sequence.mjs +1 -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-snapshot.mjs +4 -0
- package/src/runtime/attachments/store.d.mts +3 -0
- package/src/runtime/attachments/store.mjs +38 -0
- package/src/runtime/channels/lib/owned-runtime/owned-start.mjs +1 -1
- package/src/runtime/channels/lib/webhook/session-dispatch.mjs +16 -4
- package/src/runtime/channels/lib/worker-main.mjs +17 -14
- package/src/runtime/computer-bridge/linux-readiness.mjs +0 -2
- package/src/runtime/effort-judge/judge-client.mjs +56 -18
- package/src/runtime/memory/lib/memory.mjs +1 -1
- package/src/runtime/memory/lib/session-ingest.mjs +2 -2
- package/src/runtime/shared/atomic-file.mjs +33 -6
- 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/extension-scopes.mjs +5 -3
- package/src/runtime/shared/file-lock.mjs +37 -11
- package/src/runtime/shared/git-runner-worker.mjs +3 -2
- package/src/runtime/shared/git-runner.mjs +4 -3
- package/src/runtime/shared/llm/anthropic-effort.mjs +14 -0
- 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 -130
- package/src/runtime/shared/llm/provider-catalog-cache.mjs +36 -28
- package/src/runtime/shared/llm/provider-catalog-registry.mjs +162 -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/plugin-manifest.mjs +18 -9
- package/src/runtime/shared/pristine-execution-contract.json +9 -2
- package/src/runtime/shared/pristine-execution.mjs +28 -4
- package/src/runtime/shared/provider-accounts.mjs +125 -21
- package/src/runtime/shared/provider-api-key.mjs +33 -1
- package/src/runtime/shared/schedule-model-ref.mjs +11 -2
- package/src/runtime/shared/session-usage-totals.mjs +19 -0
- package/src/runtime/shared/sharp-electron-linux.mjs +25 -0
- package/src/runtime/shared/shell-display.mjs +9 -3
- package/src/runtime/shared/singleton-owner.mjs +36 -6
- package/src/runtime/shared/skill-mentions.d.mts +10 -0
- package/src/runtime/shared/skill-mentions.mjs +133 -0
- package/src/runtime/shared/skill-script-runtime.mjs +264 -0
- package/src/runtime/shared/statusline/statusline-segments.mjs +68 -14
- package/src/runtime/shared/tool-primitives.mjs +35 -2
- package/src/runtime/shared/tool-surface.mjs +7 -0
- 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 +185 -15
- package/src/runtime/shared/worker-requests.mjs +15 -2
- 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/automation-agents.mjs +8 -2
- package/src/session-runtime/boot/apis.mjs +90 -0
- package/src/session-runtime/boot/config.mjs +7 -2
- package/src/session-runtime/boot/facade.mjs +21 -0
- package/src/session-runtime/boot/infrastructure.mjs +30 -18
- package/src/session-runtime/boot/session.mjs +1 -0
- package/src/session-runtime/boot/tools.mjs +4 -0
- package/src/session-runtime/config-lifecycle.mjs +10 -1
- package/src/session-runtime/conversation-import/discovery.mjs +698 -0
- package/src/session-runtime/conversation-import/service.mjs +369 -0
- package/src/session-runtime/cwd-plugins/cwd-apply.mjs +21 -6
- package/src/session-runtime/cwd-plugins/plugin-status.mjs +47 -42
- 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 +68 -16
- package/src/session-runtime/mcp-glue/plugin-servers.mjs +499 -0
- package/src/session-runtime/mcp-glue/server-catalog.mjs +122 -33
- package/src/session-runtime/mcp-glue/server-input.mjs +29 -0
- package/src/session-runtime/mcp-glue.mjs +22 -2
- 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/new-session-config.mjs +3 -2
- package/src/session-runtime/notification-bus.mjs +18 -3
- package/src/session-runtime/provider-auth/accounts.mjs +33 -15
- package/src/session-runtime/provider-models/row-hydration.mjs +2 -0
- package/src/session-runtime/provider-models/web-search-catalog.mjs +5 -1
- package/src/session-runtime/provider-readiness.mjs +16 -10
- package/src/session-runtime/resource-extension-sync.mjs +45 -7
- package/src/session-runtime/resource-mcp-api.mjs +121 -84
- package/src/session-runtime/resource-plugins-api.mjs +29 -41
- package/src/session-runtime/resource-scope-decorate.mjs +6 -8
- package/src/session-runtime/resource-skills-api.mjs +23 -2
- package/src/session-runtime/route-resolution.mjs +10 -1
- package/src/session-runtime/route-state.mjs +16 -2
- package/src/session-runtime/runtime-core.mjs +5 -0
- package/src/session-runtime/runtime-facade.mjs +8 -0
- package/src/session-runtime/runtime-review-api.mjs +9 -0
- package/src/session-runtime/schedule-session-run.mjs +1 -1
- 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-session-router.mjs +4 -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/services/hook-bus/command-handler.mjs +4 -1
- package/src/session-runtime/services/hook-bus/handlers.mjs +7 -3
- package/src/session-runtime/services/provider-admin.mjs +64 -18
- package/src/session-runtime/services/usage-dashboard-model.mjs +3 -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-worktree-api.mjs +17 -0
- package/src/session-runtime/setup-tool/executor.mjs +23 -5
- package/src/session-runtime/setup-tool/extended-actions.mjs +1 -0
- package/src/session-runtime/setup-tool/settings-contract.mjs +4 -1
- package/src/session-runtime/setup-tool/tool-defs.mjs +38 -4
- package/src/session-runtime/skills-api.mjs +7 -3
- package/src/session-runtime/turn/agent-ops.mjs +12 -1
- package/src/session-runtime/turn/turn-run/ask-callbacks.mjs +14 -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/webhook-session-run.mjs +16 -4
- package/src/session-runtime/workflow-agents-api/agent-route.mjs +15 -3
- 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/skill-script-entry.mjs +36 -0
- package/src/standalone/channel-transport/http-routes.mjs +17 -0
- package/src/standalone/channel-transport.mjs +13 -1
- 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 +15 -4
- package/src/standalone/daemon.mjs +179 -8
- package/src/standalone/desktop-service-registry.mjs +20 -0
- package/src/standalone/local-session-runtime.mjs +11 -0
- 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 +21 -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 +17 -4
- 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.jsx +9 -1
- package/src/tui/app/app-view/floating-panel.jsx +9 -1
- package/src/tui/app/app-view.jsx +6 -0
- package/src/tui/app/extension-pickers/plugins-pickers.mjs +35 -16
- package/src/tui/app/message-selector.mjs +2 -1
- package/src/tui/app/model-options.mjs +37 -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/app/skill-mention-palette.mjs +88 -0
- package/src/tui/app/slash-palette.mjs +23 -7
- package/src/tui/app/transcript-row-estimate.mjs +11 -3
- package/src/tui/app/use-prompt-draft-flow.mjs +7 -2
- package/src/tui/app/use-prompt-state.mjs +5 -0
- package/src/tui/components/Message.jsx +22 -1
- package/src/tui/components/PromptInput.jsx +21 -3
- package/src/tui/components/SlashCommandPalette.jsx +12 -3
- package/src/tui/components/TranscriptItem.jsx +9 -1
- package/src/tui/components/prompt-input/key-handler.mjs +21 -8
- package/src/tui/components/user-message-skills.mjs +38 -0
- package/src/tui/dist/index.mjs +839 -207
- 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/queue-helpers.mjs +7 -2
- 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 +4 -2
- package/src/tui/session/session-api/intake/rewind.mjs +2 -1
- package/src/tui/session/session-api/integrations.mjs +17 -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 +9 -0
- 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 +27 -3
- 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/tui-steering-persist.mjs +25 -9
- 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 +56 -10
- package/src/runtime/effort-judge/previous-human-request.mjs +0 -162
- package/src/runtime/effort-judge/verified-context-input.mjs +0 -10
|
@@ -2,13 +2,17 @@
|
|
|
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';
|
|
6
|
+
import { isImportedMessage } from '../imported-message.mjs';
|
|
5
7
|
import {
|
|
6
8
|
isInternalRuntimeNotificationText,
|
|
7
9
|
isModelVisibleToolCompletionWrapper,
|
|
8
10
|
} from '../../../../shared/tool-execution-contract.mjs';
|
|
9
11
|
|
|
10
12
|
export function compactHeader(oldHistory) {
|
|
11
|
-
|
|
13
|
+
// Stable display ids are bookkeeping, not conversation: the header names
|
|
14
|
+
// the same history whether or not its messages were stamped.
|
|
15
|
+
const encoded = JSON.stringify(oldHistory ?? [], withoutMessageIdsReplacer);
|
|
12
16
|
return [
|
|
13
17
|
SUMMARY_PREFIX,
|
|
14
18
|
`messages=${oldHistory.length} sha256=${sha16(encoded)} roles=${roleCounts(oldHistory) || 'none'}`,
|
|
@@ -27,6 +31,7 @@ export function makeSummaryMessage(content) {
|
|
|
27
31
|
export function isSummaryMessage(m) {
|
|
28
32
|
return (
|
|
29
33
|
m?.role === 'user' &&
|
|
34
|
+
!isImportedMessage(m) &&
|
|
30
35
|
(String(m?.meta?.source || '') === 'compact-summary' ||
|
|
31
36
|
(typeof m.content === 'string' &&
|
|
32
37
|
m.content.startsWith(SUMMARY_PREFIX_ANCHOR) &&
|
|
@@ -35,7 +40,7 @@ export function isSummaryMessage(m) {
|
|
|
35
40
|
}
|
|
36
41
|
|
|
37
42
|
export function isProtectedContextUserMessage(m) {
|
|
38
|
-
if (m?.role !== 'user' || typeof m.content !== 'string') return false;
|
|
43
|
+
if (m?.role !== 'user' || typeof m.content !== 'string' || isImportedMessage(m)) return false;
|
|
39
44
|
let content = m.content.trim();
|
|
40
45
|
if (!content.toLowerCase().startsWith('<system-reminder>')) return false;
|
|
41
46
|
const closingTag = '</system-reminder>';
|
|
@@ -59,7 +64,7 @@ export function isProtectedContextUserMessage(m) {
|
|
|
59
64
|
// "latest human request" selection (deriveCurrentRequest). The `meta:'skill'`
|
|
60
65
|
// marker is also honoured.
|
|
61
66
|
export function isInjectedSkillBodyMessage(m) {
|
|
62
|
-
if (m?.role !== 'user') return false;
|
|
67
|
+
if (m?.role !== 'user' || isImportedMessage(m)) return false;
|
|
63
68
|
if (m.meta === 'skill') return true;
|
|
64
69
|
return typeof m.content === 'string' && m.content.trimStart().startsWith('<skill>');
|
|
65
70
|
}
|
|
@@ -93,6 +98,8 @@ export function isActualUserInstructionMessage(message) {
|
|
|
93
98
|
return false;
|
|
94
99
|
}
|
|
95
100
|
const text = userMessageText(message);
|
|
101
|
+
// Imported prose is the user's own words whatever it looks like.
|
|
102
|
+
if (isImportedMessage(message)) return text.trim().length > 0;
|
|
96
103
|
if (/^Reference files:\s*/i.test(text.trimStart())) return false;
|
|
97
104
|
if (
|
|
98
105
|
SYNTHETIC_USER_CONTROL_RE.test(text.trimStart()) ||
|
|
@@ -130,16 +137,20 @@ function referenceFilesManifestMessage(message) {
|
|
|
130
137
|
const paths = [...text.matchAll(/^###\s+(.+?)\s*$/gm)].map((match) => String(match[1] || '').trim()).filter(Boolean);
|
|
131
138
|
if (paths.length === 0) return null;
|
|
132
139
|
const uniquePaths = [...new Set(paths)];
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
'
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
140
|
+
// The manifest rewrites the reference-files message: it keeps that id.
|
|
141
|
+
return carryMessageId(
|
|
142
|
+
{
|
|
143
|
+
role: 'user',
|
|
144
|
+
content: [
|
|
145
|
+
'<system-reminder>',
|
|
146
|
+
'# Reference files',
|
|
147
|
+
'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.',
|
|
148
|
+
...uniquePaths.map((path) => `- ${path}`),
|
|
149
|
+
'</system-reminder>',
|
|
150
|
+
].join('\n'),
|
|
151
|
+
},
|
|
152
|
+
message
|
|
153
|
+
);
|
|
143
154
|
}
|
|
144
155
|
|
|
145
156
|
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 || '');
|
|
@@ -0,0 +1,362 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Capture side of the display-only compaction archive (compaction-archive.mjs).
|
|
3
|
+
*
|
|
4
|
+
* A committed compaction hands over the transcript it replaced and the one
|
|
5
|
+
* that replaced it. What left the model context — the messages whose stable
|
|
6
|
+
* id (message-ids.mjs) the result no longer holds, and the lost parts of kept
|
|
7
|
+
* messages (compactionArchiveProjection) — is projected to transcript items
|
|
8
|
+
* exactly like stored history
|
|
9
|
+
* (restoreTranscriptItems over media-sanitized messages, so tool cards and
|
|
10
|
+
* diffs survive), namespaced as `arch<seq>_…` with `archived: true`, and
|
|
11
|
+
* appended as ONE segment to the session's sidecar, followed by the
|
|
12
|
+
* compaction divider.
|
|
13
|
+
*
|
|
14
|
+
* The commit only selects the dropped messages and allocates the segment
|
|
15
|
+
* number (`session.compaction.archive.nextSeq`, saved with the commit); the
|
|
16
|
+
* projection and the write run on a later event-loop turn, serialized per
|
|
17
|
+
* session and fenced against a delete. Nothing here touches
|
|
18
|
+
* `session.messages`.
|
|
19
|
+
*/
|
|
20
|
+
import { randomBytes } from 'node:crypto';
|
|
21
|
+
import { existsSync, unlinkSync } from 'node:fs';
|
|
22
|
+
import { open, readFile } from 'node:fs/promises';
|
|
23
|
+
import { basename, join } from 'node:path';
|
|
24
|
+
import { renameWithRetrySync } from '../../../shared/atomic-file.mjs';
|
|
25
|
+
import { writeStderrBestEffort } from '../../../shared/best-effort-stderr.mjs';
|
|
26
|
+
|
|
27
|
+
import { sanitizeContentForStoredHistory } from '../providers/media-normalization.mjs';
|
|
28
|
+
import { getStoreDir } from './store/paths-heartbeat.mjs';
|
|
29
|
+
import { forgetSessionSaveBaseline } from './store/save-worker.mjs';
|
|
30
|
+
import { messageIdOf, stampMessageIds } from './message-ids.mjs';
|
|
31
|
+
import { restoreTranscriptItems } from './transcript-restore/restore.mjs';
|
|
32
|
+
import {
|
|
33
|
+
COMPACTION_ARCHIVE_MAX_BYTES,
|
|
34
|
+
COMPACTION_DIVIDER_LABEL,
|
|
35
|
+
compactionArchiveDeleteEpoch,
|
|
36
|
+
compactionArchivePath,
|
|
37
|
+
compactionSegmentHead,
|
|
38
|
+
compactionSegmentLine,
|
|
39
|
+
} from './compaction-archive.mjs';
|
|
40
|
+
|
|
41
|
+
export { COMPACTION_DIVIDER_LABEL };
|
|
42
|
+
|
|
43
|
+
const pendingWrites = new Map();
|
|
44
|
+
|
|
45
|
+
// ── Dropped messages: by stable id only ──────────────────────────────────────
|
|
46
|
+
|
|
47
|
+
function toolCallsOf(message) {
|
|
48
|
+
const calls = Array.isArray(message.toolCalls) ? message.toolCalls : message.tool_calls;
|
|
49
|
+
return Array.isArray(calls) ? calls : [];
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function callIdOf(call) {
|
|
53
|
+
return String(call?.id || '');
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function resultCallIdOf(message) {
|
|
57
|
+
return message.role === 'tool' ? String(message.tool_call_id || message.toolCallId || '') : '';
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function hasText(content) {
|
|
61
|
+
if (typeof content === 'string') return content.trim().length > 0;
|
|
62
|
+
return Array.isArray(content) && content.some((part) => typeof part?.text === 'string' && part.text.trim());
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** The stable ids `messages` carries (stampMessageIds / carryMessageId). */
|
|
66
|
+
function idsOf(messages) {
|
|
67
|
+
const ids = new Set();
|
|
68
|
+
for (const message of messages) {
|
|
69
|
+
const id = messageIdOf(message);
|
|
70
|
+
if (id) ids.add(id);
|
|
71
|
+
}
|
|
72
|
+
return ids;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Messages of `before` whose stable id the compacted `after` no longer holds,
|
|
77
|
+
* in their original order. Identity is the id alone — never an object
|
|
78
|
+
* reference, a position or content. A message without an id (a string-meta
|
|
79
|
+
* context marker, see message-ids.mjs) has no display identity and is never
|
|
80
|
+
* reported.
|
|
81
|
+
*/
|
|
82
|
+
export function droppedByCompaction(before, after) {
|
|
83
|
+
const kept = idsOf(Array.isArray(after) ? after : []);
|
|
84
|
+
return (Array.isArray(before) ? before : []).filter((message) => {
|
|
85
|
+
const id = messageIdOf(message);
|
|
86
|
+
return id && !kept.has(id);
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* What a compaction took out of the model context, in `before` order, for the
|
|
92
|
+
* display archive: every dropped message, plus — for a kept message whose
|
|
93
|
+
* rewrite lost part of it — the lost part (tool calls no copy still carries,
|
|
94
|
+
* text when no copy has any) as a message of the same id. The tool results of
|
|
95
|
+
* those calls are dropped messages, so their cards restore whole.
|
|
96
|
+
* `textIds` are the ids whose text rows left the context: the dropped
|
|
97
|
+
* messages and the kept ones that lost all their text.
|
|
98
|
+
*/
|
|
99
|
+
export function compactionArchiveProjection(before, after) {
|
|
100
|
+
const copiesById = new Map();
|
|
101
|
+
for (const message of Array.isArray(after) ? after : []) {
|
|
102
|
+
const id = messageIdOf(message);
|
|
103
|
+
if (!id) continue;
|
|
104
|
+
if (!copiesById.has(id)) copiesById.set(id, []);
|
|
105
|
+
copiesById.get(id).push(message);
|
|
106
|
+
}
|
|
107
|
+
const messages = [];
|
|
108
|
+
const textIds = [];
|
|
109
|
+
for (const message of Array.isArray(before) ? before : []) {
|
|
110
|
+
const id = messageIdOf(message);
|
|
111
|
+
if (!id) continue;
|
|
112
|
+
const copies = copiesById.get(id);
|
|
113
|
+
if (!copies) {
|
|
114
|
+
messages.push(message);
|
|
115
|
+
textIds.push(id);
|
|
116
|
+
continue;
|
|
117
|
+
}
|
|
118
|
+
const keptCalls = new Set(copies.flatMap((copy) => toolCallsOf(copy).map(callIdOf)));
|
|
119
|
+
const lostCalls = toolCallsOf(message).filter((call) => callIdOf(call) && !keptCalls.has(callIdOf(call)));
|
|
120
|
+
const lostText = hasText(message.content) && !copies.some((copy) => hasText(copy.content));
|
|
121
|
+
if (!lostCalls.length && !lostText) continue;
|
|
122
|
+
if (lostText) textIds.push(id);
|
|
123
|
+
const { toolCalls: _calls, tool_calls: _wireCalls, ...rest } = message;
|
|
124
|
+
messages.push({
|
|
125
|
+
...rest,
|
|
126
|
+
content: lostText ? message.content : '',
|
|
127
|
+
...(lostCalls.length ? { toolCalls: lostCalls } : {}),
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
return { messages, textIds };
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/** What a live view needs to find the rows of what left the context: the
|
|
134
|
+
* stable ids whose text rows went (projection `textIds`) and the tool calls
|
|
135
|
+
* that went (dropped results and calls no kept copy carries). */
|
|
136
|
+
export function droppedIdentitySummary(projection) {
|
|
137
|
+
const callIds = new Set();
|
|
138
|
+
for (const message of projection.messages) {
|
|
139
|
+
for (const call of toolCallsOf(message)) if (callIdOf(call)) callIds.add(callIdOf(call));
|
|
140
|
+
const resultId = resultCallIdOf(message);
|
|
141
|
+
if (resultId) callIds.add(resultId);
|
|
142
|
+
}
|
|
143
|
+
return { messageIds: [...new Set(projection.textIds)], callIds: [...callIds] };
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/** Stamp stable ids on the transcript a compaction is about to run on (or a
|
|
147
|
+
* resumed session restores rows from) and make sure they are persisted: the
|
|
148
|
+
* next save goes out whole instead of as a delta that would leave the
|
|
149
|
+
* stamped, already-saved messages unchanged on disk. Returns the count. */
|
|
150
|
+
export function stampCompactionMessageIds(messages, sessionId) {
|
|
151
|
+
const stamped = stampMessageIds(messages, { sessionId });
|
|
152
|
+
if (stamped && sessionId) forgetSessionSaveBaseline(String(sessionId));
|
|
153
|
+
return stamped;
|
|
154
|
+
}
|
|
155
|
+
// ── Segment projection and publication ───────────────────────────────────────
|
|
156
|
+
|
|
157
|
+
function storedHistoryMessage(message) {
|
|
158
|
+
const content = sanitizeContentForStoredHistory(message.content);
|
|
159
|
+
return content === message.content ? message : { ...message, content };
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/** The display items of one segment: what left the context
|
|
163
|
+
* (compactionArchiveProjection messages) as stored history restores it,
|
|
164
|
+
* namespaced and marked archived, then the divider. Empty when nothing
|
|
165
|
+
* visible left. */
|
|
166
|
+
export function compactionSegmentItems(messages, { sessionId, seq, at }) {
|
|
167
|
+
const restored = restoreTranscriptItems(messages.map(storedHistoryMessage), { sessionId });
|
|
168
|
+
if (!restored.length) return [];
|
|
169
|
+
const prefix = `hist_${sessionId}_`;
|
|
170
|
+
const items = restored.map((item) => {
|
|
171
|
+
const id = String(item?.id ?? '');
|
|
172
|
+
return { ...item, id: `arch${seq}_${id.startsWith(prefix) ? id.slice(prefix.length) : id}`, archived: true };
|
|
173
|
+
});
|
|
174
|
+
items.push({
|
|
175
|
+
kind: 'statusdone',
|
|
176
|
+
id: `arch${seq}_divider`,
|
|
177
|
+
status: 'compacted',
|
|
178
|
+
label: COMPACTION_DIVIDER_LABEL,
|
|
179
|
+
at,
|
|
180
|
+
});
|
|
181
|
+
return items;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
function itemBytes(item) {
|
|
185
|
+
return Buffer.byteLength(JSON.stringify(item));
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/** A segment line whose bytes plus its trailing newline fit `maxBytes`: a
|
|
189
|
+
* segment that alone exceeds the cap keeps its newest items (and the
|
|
190
|
+
* divider). `bytes` includes the newline. */
|
|
191
|
+
function cappedSegmentLine({ seq, prev, at, items }, maxBytes) {
|
|
192
|
+
let line = compactionSegmentLine({ seq, prev, at, items });
|
|
193
|
+
let bytes = Buffer.byteLength(line) + 1;
|
|
194
|
+
if (bytes <= maxBytes) return { line, bytes };
|
|
195
|
+
const sizes = items.map(itemBytes);
|
|
196
|
+
let start = 0;
|
|
197
|
+
while (bytes > maxBytes && start < items.length - 1) {
|
|
198
|
+
bytes -= sizes[start] + 1;
|
|
199
|
+
start += 1;
|
|
200
|
+
}
|
|
201
|
+
line = compactionSegmentLine({ seq, prev, at, items: items.slice(start) });
|
|
202
|
+
return { line, bytes: Buffer.byteLength(line) + 1 };
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
let publishHook = null;
|
|
206
|
+
/** Test seam: runs after a segment's temp file is written, right before the
|
|
207
|
+
* fenced publication. */
|
|
208
|
+
export function _setCompactionArchivePublishHook(hook) {
|
|
209
|
+
publishHook = typeof hook === 'function' ? hook : null;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
let syncFile = (handle) => handle.sync();
|
|
213
|
+
/** Test seam: replaces the temp file's fsync (null restores it). */
|
|
214
|
+
export function _setCompactionArchiveFsync(sync) {
|
|
215
|
+
syncFile = typeof sync === 'function' ? sync : (handle) => handle.sync();
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
const recordExists = (dir, sessionId) => existsSync(join(dir, `${sessionId}.json`));
|
|
219
|
+
|
|
220
|
+
/** Write `text` to a private temp file and fsync it — a failed write or
|
|
221
|
+
* fsync fails the publication — then publish it with a synchronous fence
|
|
222
|
+
* check + rename: a delete that bumped the epoch (or removed the record)
|
|
223
|
+
* since `epoch` was captured wins. Any outcome but a rename removes the temp
|
|
224
|
+
* file. */
|
|
225
|
+
async function publishArchive({ dir, path, sessionId, text, epoch }) {
|
|
226
|
+
const tmp = join(dir, `.${basename(path)}.${randomBytes(8).toString('hex')}.tmp`);
|
|
227
|
+
const removeTmp = () => {
|
|
228
|
+
try {
|
|
229
|
+
unlinkSync(tmp);
|
|
230
|
+
} catch {
|
|
231
|
+
/* already gone */
|
|
232
|
+
}
|
|
233
|
+
};
|
|
234
|
+
try {
|
|
235
|
+
const handle = await open(tmp, 'wx', 0o600);
|
|
236
|
+
try {
|
|
237
|
+
await handle.writeFile(text, 'utf8');
|
|
238
|
+
await syncFile(handle);
|
|
239
|
+
} finally {
|
|
240
|
+
await handle.close();
|
|
241
|
+
}
|
|
242
|
+
if (publishHook) await publishHook({ sessionId, path, tmp });
|
|
243
|
+
} catch (error) {
|
|
244
|
+
removeTmp();
|
|
245
|
+
throw error;
|
|
246
|
+
}
|
|
247
|
+
// No await between the fence and the rename.
|
|
248
|
+
if (compactionArchiveDeleteEpoch(sessionId) !== epoch || !recordExists(dir, sessionId)) {
|
|
249
|
+
removeTmp();
|
|
250
|
+
return false;
|
|
251
|
+
}
|
|
252
|
+
try {
|
|
253
|
+
renameWithRetrySync(tmp, path);
|
|
254
|
+
} catch (error) {
|
|
255
|
+
removeTmp();
|
|
256
|
+
throw error;
|
|
257
|
+
}
|
|
258
|
+
return true;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
async function appendSegment({ sessionId, seq, at, items, clearedThrough, maxBytes, epoch }) {
|
|
262
|
+
const dir = getStoreDir();
|
|
263
|
+
const path = compactionArchivePath(dir, sessionId);
|
|
264
|
+
// A deleted session must not regrow its archive.
|
|
265
|
+
if (!path || compactionArchiveDeleteEpoch(sessionId) !== epoch || !recordExists(dir, sessionId)) return null;
|
|
266
|
+
let text = '';
|
|
267
|
+
try {
|
|
268
|
+
text = await readFile(path, 'utf8');
|
|
269
|
+
} catch {
|
|
270
|
+
text = '';
|
|
271
|
+
}
|
|
272
|
+
const existing = [];
|
|
273
|
+
for (const line of text.split('\n')) {
|
|
274
|
+
const head = compactionSegmentHead(line);
|
|
275
|
+
// Segments a clear hid are dropped on the next write.
|
|
276
|
+
if (head && head.seq > clearedThrough && head.seq !== seq) existing.push({ line, head });
|
|
277
|
+
}
|
|
278
|
+
const prev = existing.length ? existing.at(-1).head.seq : null;
|
|
279
|
+
const segment = cappedSegmentLine({ seq, prev, at, items }, maxBytes);
|
|
280
|
+
let total = segment.bytes;
|
|
281
|
+
for (const entry of existing) total += Buffer.byteLength(entry.line) + 1;
|
|
282
|
+
while (existing.length && total > maxBytes) {
|
|
283
|
+
total -= Buffer.byteLength(existing.shift().line) + 1;
|
|
284
|
+
}
|
|
285
|
+
const lines = [...existing.map((entry) => entry.line), segment.line];
|
|
286
|
+
if (!(await publishArchive({ dir, path, sessionId, text: `${lines.join('\n')}\n`, epoch }))) return null;
|
|
287
|
+
return lines.map((line) => compactionSegmentHead(line)).filter(Boolean);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/**
|
|
291
|
+
* Archive what a committed compaction dropped. Synchronous part: select the
|
|
292
|
+
* dropped messages, capture the delete fence and allocate the segment number
|
|
293
|
+
* on `session.compaction.archive`; the rest runs later. Returns
|
|
294
|
+
* `{ dropped, identities, written }` — the dropped message count, their
|
|
295
|
+
* identities for a live view (droppedIdentitySummary) and the pending write
|
|
296
|
+
* (resolves to the retained segment list) — or null when nothing visible was
|
|
297
|
+
* dropped (a rewrite-only compaction).
|
|
298
|
+
*/
|
|
299
|
+
export function scheduleCompactionArchive({
|
|
300
|
+
session,
|
|
301
|
+
sessionId = null,
|
|
302
|
+
before,
|
|
303
|
+
after,
|
|
304
|
+
at = Date.now(),
|
|
305
|
+
maxBytes = COMPACTION_ARCHIVE_MAX_BYTES,
|
|
306
|
+
}) {
|
|
307
|
+
const id = String(sessionId || session?.id || '');
|
|
308
|
+
if (!session || typeof session !== 'object' || !compactionArchivePath('', id)) return null;
|
|
309
|
+
const projection = compactionArchiveProjection(before, after);
|
|
310
|
+
// Only what the transcript shows is archived; a pass that took out nothing
|
|
311
|
+
// visible (a rewrite, control rows, hidden context) leaves the view live.
|
|
312
|
+
// Short-circuits on the first visible message.
|
|
313
|
+
if (!projection.messages.some((message) => restoreTranscriptItems([message], { sessionId: id }).length > 0)) {
|
|
314
|
+
return null;
|
|
315
|
+
}
|
|
316
|
+
const dropped = droppedByCompaction(before, after);
|
|
317
|
+
if (!session.compaction || typeof session.compaction !== 'object') session.compaction = {};
|
|
318
|
+
const previous =
|
|
319
|
+
session.compaction.archive && typeof session.compaction.archive === 'object' ? session.compaction.archive : {};
|
|
320
|
+
const seq = Math.max(1, Math.floor(Number(previous.nextSeq) || 1));
|
|
321
|
+
const clearedThrough = Math.max(0, Math.floor(Number(previous.clearedThrough) || 0));
|
|
322
|
+
const epoch = compactionArchiveDeleteEpoch(id);
|
|
323
|
+
session.compaction.archive = { ...previous, nextSeq: seq + 1 };
|
|
324
|
+
const run = async () => {
|
|
325
|
+
await new Promise((resolve) => setImmediate(resolve));
|
|
326
|
+
const items = compactionSegmentItems(projection.messages, { sessionId: id, seq, at });
|
|
327
|
+
if (!items.length) return null;
|
|
328
|
+
const segments = await appendSegment({ sessionId: id, seq, at, items, clearedThrough, maxBytes, epoch });
|
|
329
|
+
const archive = session.compaction?.archive;
|
|
330
|
+
// Small metadata only; a later clear may have hidden this segment.
|
|
331
|
+
if (segments && archive && typeof archive === 'object' && !(Number(archive.clearedThrough) >= seq)) {
|
|
332
|
+
archive.segments = segments;
|
|
333
|
+
}
|
|
334
|
+
return segments;
|
|
335
|
+
};
|
|
336
|
+
const task = (pendingWrites.get(id) || Promise.resolve()).then(run).catch((error) => {
|
|
337
|
+
writeStderrBestEffort(`[session] compaction archive write failed (sess=${id}): ${error?.message || error}\n`);
|
|
338
|
+
return null;
|
|
339
|
+
});
|
|
340
|
+
pendingWrites.set(id, task);
|
|
341
|
+
void task.finally(() => {
|
|
342
|
+
if (pendingWrites.get(id) === task) pendingWrites.delete(id);
|
|
343
|
+
});
|
|
344
|
+
return { dropped: dropped.length, identities: droppedIdentitySummary(projection), written: task };
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
/** Hide everything archived so far (a clear): later reads and the next write
|
|
348
|
+
* skip those segments. */
|
|
349
|
+
export function hideCompactionArchive(session) {
|
|
350
|
+
const archive = session?.compaction?.archive;
|
|
351
|
+
if (!archive || typeof archive !== 'object') return;
|
|
352
|
+
session.compaction.archive = {
|
|
353
|
+
...archive,
|
|
354
|
+
clearedThrough: Math.max(0, Math.floor(Number(archive.nextSeq) || 1) - 1),
|
|
355
|
+
segments: [],
|
|
356
|
+
};
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
/** Settles once every archive write scheduled so far has finished. */
|
|
360
|
+
export async function flushCompactionArchiveWrites() {
|
|
361
|
+
await Promise.all([...pendingWrites.values()]);
|
|
362
|
+
}
|