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
|
@@ -20,6 +20,8 @@ process.env.MIXDOG_DAEMON_HOST = '1';
|
|
|
20
20
|
// Size the libuv threadpool before any async fs work spins it up (see
|
|
21
21
|
// uv-threadpool-boot.mjs) — imports below already touch fs.
|
|
22
22
|
await import('../runtime/shared/uv-threadpool-boot.mjs');
|
|
23
|
+
// Before anything can import sharp: Electron on Linux must use its wasm build.
|
|
24
|
+
await import('../runtime/shared/sharp-electron-linux.mjs');
|
|
23
25
|
|
|
24
26
|
// No V8 compile cache here, on purpose. Measured on this runtime: a cache hit
|
|
25
27
|
// saves nothing (spawn→ready 316-347ms with the cache vs 318-323ms without,
|
|
@@ -37,8 +39,17 @@ import { writeJsonAtomicSync } from '../runtime/shared/atomic-file.mjs';
|
|
|
37
39
|
import { ensurePrivateRuntimeRoot, resolveRuntimeRoot } from '../runtime/shared/runtime-root.mjs';
|
|
38
40
|
import { ensureProcessListenerHeadroom } from '../runtime/shared/process-listener-headroom.mjs';
|
|
39
41
|
import { listenerLeakWarningLine } from '../runtime/shared/listener-leak-warning.mjs';
|
|
40
|
-
import { claimSingletonOwner, releaseSingletonOwner } from '../runtime/shared/singleton-owner.mjs';
|
|
42
|
+
import { claimSingletonOwner, readSingletonOwner, releaseSingletonOwner } from '../runtime/shared/singleton-owner.mjs';
|
|
43
|
+
import {
|
|
44
|
+
createOwnerHeartbeat,
|
|
45
|
+
daemonOwnerIsStale,
|
|
46
|
+
daemonOwnerTimings,
|
|
47
|
+
discoveryPid,
|
|
48
|
+
proveDaemonOwnerRecord,
|
|
49
|
+
startOwnerLivenessListener,
|
|
50
|
+
} from './daemon-owner.mjs';
|
|
41
51
|
import { remoteIntentPath } from '../runtime/shared/remote-intent.mjs';
|
|
52
|
+
import { modelProxyClientMode } from '../runtime/shared/model-proxy-mode.mjs';
|
|
42
53
|
import { setChannelNotifySink } from '../runtime/channels/lib/parent-bridge.mjs';
|
|
43
54
|
import { setOwnerContext } from '../runtime/channels/lib/runtime-paths.mjs';
|
|
44
55
|
import { safeIpcSend } from '../runtime/shared/safe-ipc-send.mjs';
|
|
@@ -61,6 +72,7 @@ import { createSessionService } from './session-service.mjs';
|
|
|
61
72
|
import { createLocalSessionBridge } from './daemon-local-session-bridge.mjs';
|
|
62
73
|
import { createStoredSessionViews } from './daemon-stored-session-views.mjs';
|
|
63
74
|
import * as quotaResumeStore from '../runtime/shared/quota-resume-store.mjs';
|
|
75
|
+
import { registerWorktreeCacheCleanups } from '../runtime/shared/worktrees/cache-cleanups.mjs';
|
|
64
76
|
import { createInlineSessionRuntimeHost } from './session-runtime-inline-host.mjs';
|
|
65
77
|
import { getStandaloneMemoryRuntime } from '../session-runtime/services/memory-runtime-proxy.mjs';
|
|
66
78
|
import { createBootPhaseProfiler } from './boot-phase-profiler.mjs';
|
|
@@ -126,6 +138,18 @@ let remoteSessionState = { enabled: false, sessionId: null };
|
|
|
126
138
|
let shuttingDown = false;
|
|
127
139
|
let shutdownRecheckTimer = null;
|
|
128
140
|
let replacementRequested = null;
|
|
141
|
+
let ownerHeartbeat = null;
|
|
142
|
+
// Answers /health with this pid on the port the owner record names, from
|
|
143
|
+
// before the claim until exit (daemon-owner.mjs).
|
|
144
|
+
let ownerLiveness = null;
|
|
145
|
+
// The endpoints daemon.json carries once published; a heartbeat re-publishes
|
|
146
|
+
// them when the file goes missing (TEMP cleanup).
|
|
147
|
+
let publishedEndpoints = null;
|
|
148
|
+
// Remote mode only: the loopback relay that lets headless runs started from
|
|
149
|
+
// model shells (skill scripts) reach the PC behind this daemon's model proxy
|
|
150
|
+
// channel (providers/model-proxy-bridge.mjs), and its running-call count.
|
|
151
|
+
let modelProxyBridge = null;
|
|
152
|
+
let modelProxyBridgeActive = 0;
|
|
129
153
|
const eventLoopDelay = monitorEventLoopDelay({ resolution: 20 });
|
|
130
154
|
eventLoopDelay.enable();
|
|
131
155
|
let idleGc = null;
|
|
@@ -181,6 +205,7 @@ const SHUTDOWN_BUDGET_MS = 15_000;
|
|
|
181
205
|
async function shutdown(reason, code = 0) {
|
|
182
206
|
if (shuttingDown) return;
|
|
183
207
|
shuttingDown = true;
|
|
208
|
+
ownerHeartbeat?.stop();
|
|
184
209
|
exitRecorder.setReason(`shutdown: ${reason}`, code);
|
|
185
210
|
if (shutdownRecheckTimer) {
|
|
186
211
|
clearTimeout(shutdownRecheckTimer);
|
|
@@ -199,6 +224,13 @@ async function shutdown(reason, code = 0) {
|
|
|
199
224
|
try {
|
|
200
225
|
setChannelNotifySink(null);
|
|
201
226
|
} catch {}
|
|
227
|
+
// Abort every relayed model call, close the bridge sockets and wait for the
|
|
228
|
+
// calls to settle before anything they depend on goes away.
|
|
229
|
+
try {
|
|
230
|
+
await modelProxyBridge?.close(`daemon shutdown: ${reason}`);
|
|
231
|
+
} catch (e) {
|
|
232
|
+
log(`model proxy bridge stop failed: ${e?.message || e}`);
|
|
233
|
+
}
|
|
202
234
|
try {
|
|
203
235
|
await sessionService?.stop?.(reason);
|
|
204
236
|
} catch (e) {
|
|
@@ -258,7 +290,9 @@ async function shutdown(reason, code = 0) {
|
|
|
258
290
|
daemonTelemetry?.stop();
|
|
259
291
|
eventLoopDelay.disable();
|
|
260
292
|
try {
|
|
261
|
-
|
|
293
|
+
// Only our own: a successor that reclaimed a stale owner record may
|
|
294
|
+
// already have published its endpoint here.
|
|
295
|
+
if (discoveryPid(DAEMON_DISCOVERY_PATH) === process.pid) rmSync(DAEMON_DISCOVERY_PATH, { force: true });
|
|
262
296
|
} catch {}
|
|
263
297
|
try {
|
|
264
298
|
releaseSingletonOwner(OWNER_PATH, process.pid);
|
|
@@ -271,7 +305,7 @@ async function shutdown(reason, code = 0) {
|
|
|
271
305
|
* asks whether this process may exit; idle GC asks whether a pause is safe. */
|
|
272
306
|
function inFlightWork() {
|
|
273
307
|
return {
|
|
274
|
-
activeCalls: (transport?.activeCount ?? 0) + (sessionTransport?.activeCount ?? 0),
|
|
308
|
+
activeCalls: (transport?.activeCount ?? 0) + (sessionTransport?.activeCount ?? 0) + modelProxyBridgeActive,
|
|
275
309
|
queuedCalls: (transport?.queuedCount ?? 0) + (sessionTransport?.queuedCount ?? 0),
|
|
276
310
|
busySessions: sessionService?.busyCount ?? 0,
|
|
277
311
|
busyMemoryAgents: agentDispatchBroker?.snapshot?.().inFlight ?? 0,
|
|
@@ -421,7 +455,18 @@ function requestDaemonReplacement({ protocol, revision, version, force } = {}) {
|
|
|
421
455
|
/** Pid-verified singleton claim (claimSingletonOwner reclaims a dead-pid owner
|
|
422
456
|
* file and refuses only a LIVE peer). Loser exits so the spawner attaches to
|
|
423
457
|
* the winner instead of running a second daemon. */
|
|
424
|
-
function claimDaemonOwnership(bootPhases) {
|
|
458
|
+
async function claimDaemonOwnership(bootPhases) {
|
|
459
|
+
const timings = daemonOwnerTimings();
|
|
460
|
+
// A live-pid record is reclaimable only when its holder's endpoint does not
|
|
461
|
+
// answer with its pid (daemon-owner.mjs); probed before the sync claim.
|
|
462
|
+
const proof = await proveDaemonOwnerRecord(OWNER_PATH, {
|
|
463
|
+
discoveryPath: DAEMON_DISCOVERY_PATH,
|
|
464
|
+
staleMs: timings.staleMs,
|
|
465
|
+
});
|
|
466
|
+
// Up BEFORE the claim, so even the first record names a port that answers
|
|
467
|
+
// with this pid: a contender never reclaims a booting daemon on a stale-
|
|
468
|
+
// looking stamp (a clock jump) alone.
|
|
469
|
+
ownerLiveness = await startOwnerLivenessListener({ log });
|
|
425
470
|
const claim = claimSingletonOwner(OWNER_PATH, {
|
|
426
471
|
kind: 'mixdog-daemon',
|
|
427
472
|
pid: process.pid,
|
|
@@ -430,14 +475,47 @@ function claimDaemonOwnership(bootPhases) {
|
|
|
430
475
|
protocol: SESSION_PROTOCOL,
|
|
431
476
|
revision: SESSION_REVISION,
|
|
432
477
|
version: runtimeVersion(),
|
|
478
|
+
heartbeatAt: new Date().toISOString(),
|
|
479
|
+
...(ownerLiveness ? { livenessPort: ownerLiveness.port } : {}),
|
|
480
|
+
},
|
|
481
|
+
// A live pid that neither beats nor answers is a reused pid, not a peer.
|
|
482
|
+
isStale: (owner) => {
|
|
483
|
+
const stale = daemonOwnerIsStale(owner, { proof, staleMs: timings.staleMs });
|
|
484
|
+
if (stale) {
|
|
485
|
+
log(
|
|
486
|
+
`reclaiming stale owner record pid=${owner.pid} claimedAt=${owner.claimedAt}` +
|
|
487
|
+
` heartbeatAt=${owner.heartbeatAt ?? '-'} probe=${proof?.verdict ?? '-'}`
|
|
488
|
+
);
|
|
489
|
+
}
|
|
490
|
+
return stale;
|
|
433
491
|
},
|
|
434
492
|
});
|
|
435
493
|
if (!claim.owned) {
|
|
436
494
|
log(`live peer holds owner lock (pid=${claim.owner?.pid}) — exiting for attach`);
|
|
495
|
+
void ownerLiveness?.close();
|
|
496
|
+
ownerLiveness = null;
|
|
437
497
|
exitRecorder.setReason(`lost singleton claim to live peer pid=${claim.owner?.pid}`, 0);
|
|
438
498
|
process.exit(0);
|
|
439
499
|
}
|
|
440
500
|
bootPhases.mark('owner-claimed');
|
|
501
|
+
// Beats from here on, through boot: a slow boot is never mistaken for a
|
|
502
|
+
// reused pid. Losing the record to another pid, or the lease running out
|
|
503
|
+
// unrefreshed, means another daemon owns (or may now claim) this data dir —
|
|
504
|
+
// at most one may keep serving.
|
|
505
|
+
ownerHeartbeat = createOwnerHeartbeat({
|
|
506
|
+
ownerPath: OWNER_PATH,
|
|
507
|
+
owner: claim.owner,
|
|
508
|
+
heartbeatMs: timings.heartbeatMs,
|
|
509
|
+
fenceMs: timings.fenceMs,
|
|
510
|
+
tickMs: timings.tickMs,
|
|
511
|
+
stallMs: timings.stallMs,
|
|
512
|
+
log,
|
|
513
|
+
onBeat: republishMissingDiscovery,
|
|
514
|
+
onSuperseded: (record) => fenceDaemon(`owner claim superseded by pid=${record?.pid}`),
|
|
515
|
+
onFenced: ({ lastRefreshAt, sinceMs }) =>
|
|
516
|
+
fenceDaemon(`owner lease expired (no refresh for ${sinceMs}ms, last ${lastRefreshAt})`),
|
|
517
|
+
});
|
|
518
|
+
ownerHeartbeat.start();
|
|
441
519
|
process.on('exit', () => {
|
|
442
520
|
try {
|
|
443
521
|
releaseSingletonOwner(OWNER_PATH, process.pid);
|
|
@@ -497,7 +575,9 @@ function daemonStatusSnapshot() {
|
|
|
497
575
|
|
|
498
576
|
/** The file every attacher reads to find this daemon: wire identity plus both
|
|
499
577
|
* loopback endpoints and their privileged tokens. */
|
|
500
|
-
function publishDaemonDiscovery({ channel, session }) {
|
|
578
|
+
function publishDaemonDiscovery({ channel, session, modelProxy = null }) {
|
|
579
|
+
// A re-publish keeps the first publication's startedAt.
|
|
580
|
+
publishedEndpoints = { channel, session, modelProxy, startedAt: publishedEndpoints?.startedAt ?? Date.now() };
|
|
501
581
|
writeJsonAtomicSync(
|
|
502
582
|
DAEMON_DISCOVERY_PATH,
|
|
503
583
|
{
|
|
@@ -506,16 +586,75 @@ function publishDaemonDiscovery({ channel, session }) {
|
|
|
506
586
|
version: runtimeVersion(),
|
|
507
587
|
capabilityFingerprint: SESSION_CAPABILITY_FINGERPRINT,
|
|
508
588
|
pid: process.pid,
|
|
509
|
-
startedAt:
|
|
589
|
+
startedAt: publishedEndpoints.startedAt,
|
|
510
590
|
endpoints: {
|
|
511
591
|
channel: { port: channel.port, token: channel.token },
|
|
512
592
|
session: { port: session.port, token: session.token },
|
|
593
|
+
...(modelProxy ? { modelProxy: { port: modelProxy.port, token: modelProxy.token } } : {}),
|
|
513
594
|
},
|
|
514
595
|
},
|
|
515
596
|
{ compact: true, secret: true }
|
|
516
597
|
);
|
|
517
598
|
}
|
|
518
599
|
|
|
600
|
+
/** Remote mode only: the model proxy bridge endpoint to publish, or null. */
|
|
601
|
+
async function startModelProxyBridge(bootPhases) {
|
|
602
|
+
if (!modelProxyClientMode()) return null;
|
|
603
|
+
const [{ createModelProxyBridgeServer }, { getModelProxyChannel }] = await Promise.all([
|
|
604
|
+
import('../runtime/agent/orchestrator/providers/model-proxy-bridge.mjs'),
|
|
605
|
+
import('../runtime/agent/orchestrator/providers/model-proxy-channel.mjs'),
|
|
606
|
+
]);
|
|
607
|
+
modelProxyBridge = createModelProxyBridgeServer({
|
|
608
|
+
channel: getModelProxyChannel(),
|
|
609
|
+
// Fenced or shutting down: no new relayed call.
|
|
610
|
+
admit: () => !shuttingDown && admitOwnerWork(),
|
|
611
|
+
trackWork: (active) => {
|
|
612
|
+
modelProxyBridgeActive = active;
|
|
613
|
+
},
|
|
614
|
+
log,
|
|
615
|
+
});
|
|
616
|
+
const endpoint = await bootPhases.measure('model-proxy-bridge-start', () => modelProxyBridge.start());
|
|
617
|
+
log(`model proxy bridge on 127.0.0.1:${endpoint.port}`);
|
|
618
|
+
return endpoint;
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
/** Asked by both front doors before every register/call: an expired lease
|
|
622
|
+
* fences here (answered 503), and after an event-loop stall nothing is
|
|
623
|
+
* served until the record is re-verified as ours. */
|
|
624
|
+
function admitOwnerWork() {
|
|
625
|
+
return ownerHeartbeat ? ownerHeartbeat.admit() : true;
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
/** This process no longer holds (or is about to lose) the owner lease: refuse
|
|
629
|
+
* new work at once, unpublish its endpoint so launchers stop finding it, then
|
|
630
|
+
* shut down within the shutdown budget — before a contender may reclaim. */
|
|
631
|
+
function fenceDaemon(reason) {
|
|
632
|
+
if (shuttingDown) return;
|
|
633
|
+
log(`${reason} — refusing new work and stopping so only one daemon serves`);
|
|
634
|
+
sessionTransport?.commitDrain?.(reason);
|
|
635
|
+
transport?.commitDrain?.(reason);
|
|
636
|
+
modelProxyBridge?.abortAll(reason);
|
|
637
|
+
try {
|
|
638
|
+
if (discoveryPid(DAEMON_DISCOVERY_PATH) === process.pid) rmSync(DAEMON_DISCOVERY_PATH, { force: true });
|
|
639
|
+
} catch (e) {
|
|
640
|
+
log(`daemon.json unpublish failed: ${e?.message || e}`);
|
|
641
|
+
}
|
|
642
|
+
void shutdown(reason);
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
/** Heartbeat follow-up while this process still owns the lock: a daemon.json
|
|
646
|
+
* deleted (TEMP cleanup) or overwritten while it runs is written again, so
|
|
647
|
+
* new clients keep finding the one daemon that serves this data dir. */
|
|
648
|
+
function republishMissingDiscovery() {
|
|
649
|
+
if (!publishedEndpoints || shuttingDown || discoveryPid(DAEMON_DISCOVERY_PATH) === process.pid) return;
|
|
650
|
+
try {
|
|
651
|
+
publishDaemonDiscovery(publishedEndpoints);
|
|
652
|
+
log('daemon.json was missing or named another pid — re-published');
|
|
653
|
+
} catch (error) {
|
|
654
|
+
log(`daemon.json re-publish failed: ${error?.message || error}`);
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
|
|
519
658
|
/** Ready handshake for the spawner, plus the log-sink handoff pinned to the
|
|
520
659
|
* same boundary. Transport is already listening; signal ready before the heavy
|
|
521
660
|
* channel-worker connect so the spawner's ready wait never blocks on service
|
|
@@ -569,6 +708,7 @@ async function startChannelFrontDoor({ channelsRuntime, bootPhases }) {
|
|
|
569
708
|
transport = createChannelTransport({
|
|
570
709
|
handleCall: channelsRuntime.handleCall,
|
|
571
710
|
agentBroker: agentDispatchBroker,
|
|
711
|
+
admitWork: admitOwnerWork,
|
|
572
712
|
log,
|
|
573
713
|
// The durable channel link lives in this file. Without it a restart loses
|
|
574
714
|
// the pinned session (nothing to restore) and every catalog reports Remote
|
|
@@ -617,8 +757,27 @@ async function main() {
|
|
|
617
757
|
// are per-user and fail closed if another account owns the configured path.
|
|
618
758
|
ensurePrivateRuntimeRoot(RUNTIME_ROOT);
|
|
619
759
|
|
|
620
|
-
claimDaemonOwnership(bootPhases);
|
|
760
|
+
await claimDaemonOwnership(bootPhases);
|
|
621
761
|
registerMemoryRuntimeLazy();
|
|
762
|
+
// Every in-process session runtime removes managed worktrees through the
|
|
763
|
+
// shared manager; its root cleanups drop the path-keyed caches (turn
|
|
764
|
+
// snapshots, code graph) of a removed worktree. Modules load on first use.
|
|
765
|
+
registerWorktreeCacheCleanups();
|
|
766
|
+
// Managed worktrees, off the boot path: finish interrupted deletions and
|
|
767
|
+
// creations, release stale leases and, with autoCleanup on, remove clean,
|
|
768
|
+
// merged worktrees no stored session retains (never one a session keeps).
|
|
769
|
+
void import('../runtime/shared/worktrees/maintenance.mjs')
|
|
770
|
+
.then((maintenance) => maintenance.reconcileWorktreesAtStartup())
|
|
771
|
+
.then((report) => {
|
|
772
|
+
const changed = report.removed.length + report.recovered.length + report.pruned.length;
|
|
773
|
+
if (changed || report.errors.length) {
|
|
774
|
+
log(
|
|
775
|
+
`worktree reconcile: removed ${report.removed.length}, recovered ${report.recovered.length}, ` +
|
|
776
|
+
`pruned ${report.pruned.length}, errors ${report.errors.length}`
|
|
777
|
+
);
|
|
778
|
+
}
|
|
779
|
+
})
|
|
780
|
+
.catch((error) => log(`worktree reconcile failed: ${error?.message || error}`));
|
|
622
781
|
// The daemon owns prompt attachments; its census runs off the boot path
|
|
623
782
|
// (first attempt after the store's start delay, then per its interval).
|
|
624
783
|
void import('../runtime/attachments/store.mjs').then((store) => store.startAttachmentGc()).catch(() => {});
|
|
@@ -764,9 +923,21 @@ async function main() {
|
|
|
764
923
|
} catch {}
|
|
765
924
|
},
|
|
766
925
|
onUpgradeRequested: requestDaemonReplacement,
|
|
926
|
+
admitWork: admitOwnerWork,
|
|
767
927
|
});
|
|
768
928
|
const sessionEndpoint = await bootPhases.measure('session-transport-start', () => sessionTransport.start());
|
|
769
|
-
|
|
929
|
+
const modelProxyEndpoint = await startModelProxyBridge(bootPhases);
|
|
930
|
+
// A boot whose holder stopped answering may have been superseded by a
|
|
931
|
+
// contender between beats; never publish over the winner's endpoint. An
|
|
932
|
+
// unreadable record (an antivirus lock) is not a takeover.
|
|
933
|
+
const ownerPid = Number(readSingletonOwner(OWNER_PATH).owner?.pid) || 0;
|
|
934
|
+
if (shuttingDown) return;
|
|
935
|
+
if (ownerPid && ownerPid !== process.pid) {
|
|
936
|
+
log(`owner record now names pid=${ownerPid} — exiting for attach before publishing`);
|
|
937
|
+
exitRecorder.setReason(`owner claim superseded by pid=${ownerPid} before publish`, 0);
|
|
938
|
+
process.exit(0);
|
|
939
|
+
}
|
|
940
|
+
publishDaemonDiscovery({ channel: { port, token }, session: sessionEndpoint, modelProxy: modelProxyEndpoint });
|
|
770
941
|
bootPhases.mark('discovery-published');
|
|
771
942
|
log(`session front door on 127.0.0.1:${sessionEndpoint.port}`);
|
|
772
943
|
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { createRequire } from 'node:module';
|
|
2
2
|
import { fileURLToPath } from 'node:url';
|
|
3
3
|
import { sanitizeForWire } from './session-wire-values.mjs';
|
|
4
|
+
import { modelProxyClientMode } from '../runtime/shared/model-proxy-mode.mjs';
|
|
5
|
+
import { getModelProxyChannel } from '../runtime/agent/orchestrator/providers/model-proxy-channel.mjs';
|
|
4
6
|
|
|
5
7
|
const requireDesktopModule = createRequire(import.meta.url);
|
|
6
8
|
|
|
@@ -185,9 +187,24 @@ export class DesktopServiceRegistry {
|
|
|
185
187
|
if (this.#closed) throw new Error('session service is closed');
|
|
186
188
|
const token = subscriberToken(ctx);
|
|
187
189
|
if (token) service.subscribers.add(token);
|
|
190
|
+
this.#syncModelProxyPresence();
|
|
188
191
|
return { desktopId: service.desktopId };
|
|
189
192
|
}
|
|
190
193
|
|
|
194
|
+
// Proxied model calls need a live desktop subscriber; tell the channel the
|
|
195
|
+
// moment the last one leaves instead of waiting for its claim timeout.
|
|
196
|
+
#syncModelProxyPresence() {
|
|
197
|
+
if (!modelProxyClientMode()) return;
|
|
198
|
+
let present = false;
|
|
199
|
+
for (const service of this.#servicesById.values()) {
|
|
200
|
+
if (service.subscribers.size) {
|
|
201
|
+
present = true;
|
|
202
|
+
break;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
getModelProxyChannel().setClientPresent(present);
|
|
206
|
+
}
|
|
207
|
+
|
|
191
208
|
// Every addressed call also (re)subscribes its caller to the service lane.
|
|
192
209
|
#requireSubscribed(desktopId, ctx) {
|
|
193
210
|
const service = this.#require(desktopId);
|
|
@@ -211,6 +228,7 @@ export class DesktopServiceRegistry {
|
|
|
211
228
|
|
|
212
229
|
ready({ desktopId } = {}, ctx = null) {
|
|
213
230
|
const { service, token } = this.#requireSubscribed(desktopId, ctx);
|
|
231
|
+
this.#syncModelProxyPresence();
|
|
214
232
|
this.#onReady({ desktopId: service.desktopId, clientToken: token || null });
|
|
215
233
|
return { ok: true };
|
|
216
234
|
}
|
|
@@ -219,6 +237,7 @@ export class DesktopServiceRegistry {
|
|
|
219
237
|
const service = this.#require(desktopId);
|
|
220
238
|
const token = subscriberToken(ctx);
|
|
221
239
|
if (token) service.subscribers.delete(token);
|
|
240
|
+
this.#syncModelProxyPresence();
|
|
222
241
|
return { ok: true, unsubscribed: true };
|
|
223
242
|
}
|
|
224
243
|
|
|
@@ -228,6 +247,7 @@ export class DesktopServiceRegistry {
|
|
|
228
247
|
for (const service of this.#servicesById.values()) {
|
|
229
248
|
service.subscribers.delete(token);
|
|
230
249
|
}
|
|
250
|
+
this.#syncModelProxyPresence();
|
|
231
251
|
}
|
|
232
252
|
|
|
233
253
|
notifySessionRuntimeReleased(sessionId, reason) {
|
|
@@ -122,14 +122,17 @@ export async function createLocalSessionRuntime({
|
|
|
122
122
|
model,
|
|
123
123
|
effort,
|
|
124
124
|
fast,
|
|
125
|
+
autoEffort,
|
|
125
126
|
modelParameters,
|
|
126
127
|
contextPercent,
|
|
128
|
+
contextTokens,
|
|
127
129
|
selectedContextWindow,
|
|
128
130
|
toolMode = 'full',
|
|
129
131
|
remote = false,
|
|
130
132
|
cwd,
|
|
131
133
|
desktopSession,
|
|
132
134
|
sessionProfile,
|
|
135
|
+
worktree,
|
|
133
136
|
executeAgentControl,
|
|
134
137
|
} = {}) {
|
|
135
138
|
const startedAt = performance.now();
|
|
@@ -150,14 +153,18 @@ export async function createLocalSessionRuntime({
|
|
|
150
153
|
model,
|
|
151
154
|
effort,
|
|
152
155
|
fast,
|
|
156
|
+
autoEffort,
|
|
153
157
|
modelParameters,
|
|
154
158
|
contextPercent,
|
|
159
|
+
contextTokens,
|
|
155
160
|
selectedContextWindow,
|
|
156
161
|
toolMode,
|
|
157
162
|
remote,
|
|
158
163
|
...(cwd ? { cwd } : {}),
|
|
159
164
|
...(desktopSession ? { desktopSession } : {}),
|
|
160
165
|
...(sessionProfile && typeof sessionProfile === 'object' ? { sessionProfile } : {}),
|
|
166
|
+
// Session creation worktree intent; consumed by the workspace transition scope.
|
|
167
|
+
...(worktree && typeof worktree === 'object' ? { worktree } : {}),
|
|
161
168
|
...(typeof executeAgentControl === 'function' ? { executeAgentControl } : {}),
|
|
162
169
|
});
|
|
163
170
|
bootProfile('session:create:runtime-ready', { ms: (performance.now() - startedAt).toFixed(1) });
|
|
@@ -396,6 +403,10 @@ export async function createLocalSessionRuntime({
|
|
|
396
403
|
subscribeRuntimeNotifications,
|
|
397
404
|
});
|
|
398
405
|
Object.assign(bag, createSessionFlow(bag));
|
|
406
|
+
// Runtime-driven completion wakes (no attached surface acked the completion)
|
|
407
|
+
// get the same idle auto-clear as a user submit.
|
|
408
|
+
lifecycle.unsubscribeCompletionWakeGate =
|
|
409
|
+
runtime.setCompletionWakeGate?.(() => bag.autoClearBeforeWake()) || null;
|
|
399
410
|
Object.assign(
|
|
400
411
|
bag,
|
|
401
412
|
createGoalContinuation({
|
|
@@ -13,7 +13,7 @@ const URGENT_CALLS = new Set([
|
|
|
13
13
|
'desktop.unsubscribe',
|
|
14
14
|
]);
|
|
15
15
|
|
|
16
|
-
export function createAttachmentCalls({ request, port, serverToken, clientToken, stopStream, log }) {
|
|
16
|
+
export function createAttachmentCalls({ request, port, serverToken, clientToken, stopStream, stopHeartbeat, log }) {
|
|
17
17
|
let closed = false;
|
|
18
18
|
|
|
19
19
|
async function call(name, args = {}, { timeoutMs = 300_000, callId = null } = {}) {
|
|
@@ -38,6 +38,7 @@ export function createAttachmentCalls({ request, port, serverToken, clientToken,
|
|
|
38
38
|
// Preserve the daemon's machine-readable classification across the wire.
|
|
39
39
|
const err = new Error(out.error);
|
|
40
40
|
if (out.code) err.code = String(out.code);
|
|
41
|
+
if (out.details && typeof out.details === 'object') err.details = out.details;
|
|
41
42
|
throw err;
|
|
42
43
|
}
|
|
43
44
|
return out?.result;
|
|
@@ -47,6 +48,7 @@ export function createAttachmentCalls({ request, port, serverToken, clientToken,
|
|
|
47
48
|
if (closed) return;
|
|
48
49
|
closed = true;
|
|
49
50
|
stopStream();
|
|
51
|
+
stopHeartbeat?.();
|
|
50
52
|
try {
|
|
51
53
|
await request({
|
|
52
54
|
port,
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
// The error ensureDaemon throws when no session endpoint appeared before its
|
|
2
|
+
// deadline. The first line is the stable message callers and tests match; the
|
|
3
|
+
// lines below carry what the last pass observed — an unanswered discovery, an
|
|
4
|
+
// owner that never published, the newest candidate's exit and first stderr
|
|
5
|
+
// error — so the desktop error card's details name the cause, not only the
|
|
6
|
+
// symptom.
|
|
7
|
+
export const DAEMON_UNAVAILABLE_MESSAGE = 'daemon session endpoint is unavailable';
|
|
8
|
+
|
|
9
|
+
function exitCodeText(code) {
|
|
10
|
+
// Windows reports NTSTATUS crash codes as large unsigned values.
|
|
11
|
+
return Number.isSafeInteger(code) && code > 0x7fffffff ? `0x${code.toString(16).toUpperCase()}` : String(code);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function candidateLines(candidate, { attempt, maxAttempts, now }) {
|
|
15
|
+
const head = `last daemon candidate (attempt ${attempt} of ${maxAttempts})`;
|
|
16
|
+
if (!candidate.pid) return [`${head} failed to spawn: ${candidate.error || 'unknown error'}`];
|
|
17
|
+
let state;
|
|
18
|
+
if (candidate.exitedAt) {
|
|
19
|
+
state =
|
|
20
|
+
`exited code=${candidate.exitCode === null ? '-' : exitCodeText(candidate.exitCode)}` +
|
|
21
|
+
` signal=${candidate.exitSignal || '-'} after ${candidate.uptimeMs ?? '?'}ms` +
|
|
22
|
+
(candidate.exitReason ? ` (${candidate.exitReason})` : '');
|
|
23
|
+
} else if (candidate.ready) {
|
|
24
|
+
state = 'reported ready but its endpoint never answered';
|
|
25
|
+
} else {
|
|
26
|
+
const spawnedAt = Date.parse(candidate.spawnedAt || '');
|
|
27
|
+
state = `still running, not ready ${Number.isFinite(spawnedAt) ? now - spawnedAt : '?'}ms after spawn`;
|
|
28
|
+
}
|
|
29
|
+
const lines = [`${head}: pid=${candidate.pid} ${state}`];
|
|
30
|
+
if (candidate.stderrErrorLine) lines.push(`first stderr error: ${candidate.stderrErrorLine}`);
|
|
31
|
+
else if (candidate.stderrLastLine) lines.push(`last stderr line: ${candidate.stderrLastLine}`);
|
|
32
|
+
if (candidate.stderrPath) lines.push(`stderr capture: ${candidate.stderrPath}`);
|
|
33
|
+
return lines;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function daemonUnavailableError({
|
|
37
|
+
discovery = null,
|
|
38
|
+
owner = null,
|
|
39
|
+
candidate = null,
|
|
40
|
+
spawnAttempts = 0,
|
|
41
|
+
maxSpawnAttempts = 0,
|
|
42
|
+
captureDir,
|
|
43
|
+
now = Date.now(),
|
|
44
|
+
}) {
|
|
45
|
+
const lines = [DAEMON_UNAVAILABLE_MESSAGE];
|
|
46
|
+
if (discovery) lines.push(`discovery: pid=${discovery.pid} port=${discovery.port} did not answer its health check`);
|
|
47
|
+
if (owner) {
|
|
48
|
+
lines.push(
|
|
49
|
+
`daemon owner: pid=${owner.pid} (claimed ${owner.claimedAt || 'at an unknown time'}) held the lock` +
|
|
50
|
+
' but never published an endpoint'
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
if (candidate) {
|
|
54
|
+
lines.push(...candidateLines(candidate, { attempt: spawnAttempts, maxAttempts: maxSpawnAttempts, now }));
|
|
55
|
+
} else {
|
|
56
|
+
lines.push(`no daemon candidate was spawned (spawn attempts allowed: ${maxSpawnAttempts})`);
|
|
57
|
+
}
|
|
58
|
+
lines.push(`daemon crash captures: ${captureDir}`);
|
|
59
|
+
const error = new Error(lines.join('\n'));
|
|
60
|
+
error.daemonUnavailable = { discovery, owner, candidate, spawnAttempts, captureDir };
|
|
61
|
+
return error;
|
|
62
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
// session-client/lease-heartbeat.mjs
|
|
2
|
+
// A remote attachment has no local pid the daemon can probe, so its liveness
|
|
3
|
+
// is a lease the client renews: POST /client/heartbeat every `heartbeatMs`,
|
|
4
|
+
// one request in flight at a time, each bounded by heartbeatMs / 2. A failed
|
|
5
|
+
// beat is only logged — the next tick retries, and the stream's own recovery
|
|
6
|
+
// decides when the attachment is truly lost.
|
|
7
|
+
import { clampLeaseMs } from '../session-transport/client-registry/client-grace.mjs';
|
|
8
|
+
|
|
9
|
+
export function createLeaseHeartbeat({ request, port, serverToken, clientToken, heartbeatMs = 10_000, leaseMs, log }) {
|
|
10
|
+
// Always beat at least three times per (server-clamped) lease.
|
|
11
|
+
heartbeatMs = Math.min(heartbeatMs, Math.floor(clampLeaseMs(leaseMs) / 3));
|
|
12
|
+
let timer = null;
|
|
13
|
+
let inFlight = false;
|
|
14
|
+
|
|
15
|
+
async function beat() {
|
|
16
|
+
if (inFlight) return;
|
|
17
|
+
inFlight = true;
|
|
18
|
+
try {
|
|
19
|
+
await request({
|
|
20
|
+
port,
|
|
21
|
+
token: serverToken,
|
|
22
|
+
method: 'POST',
|
|
23
|
+
path: '/client/heartbeat',
|
|
24
|
+
body: { token: clientToken },
|
|
25
|
+
timeoutMs: heartbeatMs / 2,
|
|
26
|
+
control: true,
|
|
27
|
+
});
|
|
28
|
+
} catch (error) {
|
|
29
|
+
log(`heartbeat failed: ${error?.message || error}`);
|
|
30
|
+
} finally {
|
|
31
|
+
inFlight = false;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function start() {
|
|
36
|
+
if (timer) return;
|
|
37
|
+
timer = setInterval(beat, heartbeatMs);
|
|
38
|
+
timer.unref?.();
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function stop() {
|
|
42
|
+
if (timer) clearInterval(timer);
|
|
43
|
+
timer = null;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return { start, stop };
|
|
47
|
+
}
|