mixdog 1.2.2 → 1.2.3
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 +18 -0
- package/package.json +2 -1
- package/scripts/provider-pingpong-live.mjs +509 -0
- package/src/agents/advisor/AGENT.md +1 -1
- package/src/agents/advisor/agent.json +1 -1
- package/src/defaults/shipped-history.json +5 -0
- package/src/defaults/skills/setup/SKILL.md +6 -1
- package/src/defaults/skills/setup/references/actions.md +44 -22
- package/src/defaults/skills/setup/references/surfaces.md +10 -8
- package/src/headless-exec.mjs +15 -8
- package/src/lib/keychain-cjs.cjs +7 -4
- package/src/output-styles/common.md +11 -10
- package/src/rules/agent/AGENT.md +5 -5
- package/src/rules/lead/LEAD.md +5 -5
- package/src/runtime/agent/orchestrator/agent-trace-io.mjs +92 -61
- package/src/runtime/agent/orchestrator/cache-mtime.mjs +10 -1
- package/src/runtime/agent/orchestrator/config-providers-merge.mjs +7 -0
- package/src/runtime/agent/orchestrator/config.mjs +5 -0
- package/src/runtime/agent/orchestrator/context/collect.mjs +1 -0
- package/src/runtime/agent/orchestrator/context/deferred-tools.mjs +26 -7
- package/src/runtime/agent/orchestrator/context/skill-catalog.mjs +112 -7
- package/src/runtime/agent/orchestrator/providers/anthropic-api-request.mjs +48 -20
- package/src/runtime/agent/orchestrator/providers/anthropic-error-contract.mjs +2 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-max-tokens.mjs +24 -17
- package/src/runtime/agent/orchestrator/providers/anthropic-messages.mjs +7 -6
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-client-version.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +29 -10
- package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-blocks.mjs +20 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-events/content-block-delta.mjs +15 -2
- package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-events/content-block-start.mjs +11 -4
- package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-events/message-events.mjs +3 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-events.mjs +12 -5
- package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-outcome.mjs +1 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-state.mjs +14 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +2 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-turn-result.mjs +9 -0
- package/src/runtime/agent/orchestrator/providers/anthropic.mjs +6 -0
- package/src/runtime/agent/orchestrator/providers/antigravity-request.mjs +58 -7
- package/src/runtime/agent/orchestrator/providers/antigravity-response.mjs +29 -10
- package/src/runtime/agent/orchestrator/providers/antigravity-retry-policy.mjs +16 -3
- package/src/runtime/agent/orchestrator/providers/antigravity-stream.mjs +10 -61
- package/src/runtime/agent/orchestrator/providers/antigravity-transport.mjs +12 -6
- package/src/runtime/agent/orchestrator/providers/codex-client-meta.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/command-code-oauth.mjs +114 -72
- package/src/runtime/agent/orchestrator/providers/compat-request-policy.mjs +78 -2
- package/src/runtime/agent/orchestrator/providers/cursor-auth.mjs +37 -5
- package/src/runtime/agent/orchestrator/providers/cursor-client-version.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/cursor-wire-stream-bridge.mjs +1 -0
- package/src/runtime/agent/orchestrator/providers/cursor-wire-stream-messages.mjs +43 -2
- package/src/runtime/agent/orchestrator/providers/cursor-wire.mjs +3 -0
- package/src/runtime/agent/orchestrator/providers/cursor.mjs +9 -2
- package/src/runtime/agent/orchestrator/providers/custom-provider-config.mjs +64 -1
- package/src/runtime/agent/orchestrator/providers/custom-provider.mjs +70 -6
- package/src/runtime/agent/orchestrator/providers/custom-reasoning.mjs +34 -7
- package/src/runtime/agent/orchestrator/providers/custom-tool-wire.mjs +32 -6
- package/src/runtime/agent/orchestrator/providers/deepseek.mjs +67 -0
- package/src/runtime/agent/orchestrator/providers/effort-configuration.mjs +3 -1
- package/src/runtime/agent/orchestrator/providers/gemini-cache-policy.mjs +11 -4
- package/src/runtime/agent/orchestrator/providers/gemini-cache.mjs +25 -8
- package/src/runtime/agent/orchestrator/providers/gemini-error-contract.mjs +87 -5
- package/src/runtime/agent/orchestrator/providers/gemini-request-body.mjs +41 -5
- package/src/runtime/agent/orchestrator/providers/gemini-retry-policy.mjs +13 -13
- package/src/runtime/agent/orchestrator/providers/gemini-schema.mjs +355 -37
- package/src/runtime/agent/orchestrator/providers/gemini-sdk-request.mjs +1 -2
- package/src/runtime/agent/orchestrator/providers/gemini-stream.mjs +93 -47
- package/src/runtime/agent/orchestrator/providers/gemini-thinking.mjs +55 -10
- package/src/runtime/agent/orchestrator/providers/gemini.mjs +26 -8
- package/src/runtime/agent/orchestrator/providers/grok-client-version.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/grok-oauth.mjs +1 -0
- package/src/runtime/agent/orchestrator/providers/lib/action-input-contract.mjs +60 -0
- package/src/runtime/agent/orchestrator/providers/lib/anthropic-native-blocks.mjs +1 -0
- package/src/runtime/agent/orchestrator/providers/lib/anthropic-request-utils.mjs +246 -42
- package/src/runtime/agent/orchestrator/providers/lib/anthropic-server-tool-usage.mjs +18 -0
- package/src/runtime/agent/orchestrator/providers/lib/anthropic-system-blocks.mjs +27 -6
- package/src/runtime/agent/orchestrator/providers/lib/claude-bridge-schema.mjs +388 -0
- package/src/runtime/agent/orchestrator/providers/lib/inclusive-usage.mjs +54 -0
- package/src/runtime/agent/orchestrator/providers/lib/origin-bound-fetch.mjs +16 -0
- package/src/runtime/agent/orchestrator/providers/lib/responses-terminal-fields.mjs +11 -0
- package/src/runtime/agent/orchestrator/providers/lib/usage-primitives.mjs +8 -0
- package/src/runtime/agent/orchestrator/providers/lib/wire-pairing.mjs +48 -22
- package/src/runtime/agent/orchestrator/providers/media-normalization.mjs +119 -18
- package/src/runtime/agent/orchestrator/providers/mimo-oauth-profile.mjs +13 -15
- package/src/runtime/agent/orchestrator/providers/model-proxy-channel.d.mts +1 -1
- package/src/runtime/agent/orchestrator/providers/model-proxy-channel.mjs +1 -0
- package/src/runtime/agent/orchestrator/providers/model-proxy-provider.mjs +33 -1
- package/src/runtime/agent/orchestrator/providers/oauth-credential-probes.mjs +2 -0
- package/src/runtime/agent/orchestrator/providers/openai-codex-metadata.mjs +2 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-chat-send.mjs +121 -61
- package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/chunk-events.mjs +35 -14
- package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/stream-result.mjs +5 -2
- package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/stream-state.mjs +37 -7
- package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/tool-call-acc.mjs +2 -9
- package/src/runtime/agent/orchestrator/providers/openai-compat-error-contract.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/openai-compat-models.mjs +47 -2
- package/src/runtime/agent/orchestrator/providers/openai-compat-options.mjs +47 -25
- package/src/runtime/agent/orchestrator/providers/openai-compat-presets.mjs +5 -1
- package/src/runtime/agent/orchestrator/providers/openai-compat-reasoning-field.mjs +151 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-response-normalization.mjs +42 -24
- package/src/runtime/agent/orchestrator/providers/openai-compat-responses-events.mjs +63 -25
- package/src/runtime/agent/orchestrator/providers/openai-compat-responses-head-parity.json +2901 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-responses-parity.fixtures.mjs +172 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-responses.mjs +53 -5
- package/src/runtime/agent/orchestrator/providers/openai-compat-stream.mjs +7 -1
- package/src/runtime/agent/orchestrator/providers/openai-compat-trace.mjs +2 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-wire.mjs +103 -26
- package/src/runtime/agent/orchestrator/providers/openai-compat-xai-send.mjs +23 -4
- package/src/runtime/agent/orchestrator/providers/openai-compat-xai.mjs +11 -6
- package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +6 -1
- package/src/runtime/agent/orchestrator/providers/openai-direct-request.mjs +15 -23
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-events/output-items.mjs +23 -10
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-events/terminal-frames.mjs +25 -4
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-events.mjs +10 -0
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-items.mjs +18 -7
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-outcome.mjs +4 -0
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-state.mjs +5 -0
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/text-relay.mjs +9 -1
- package/src/runtime/agent/orchestrator/providers/openai-model-capabilities.mjs +80 -0
- package/src/runtime/agent/orchestrator/providers/openai-oauth-dispatch.mjs +3 -1
- package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +7 -0
- package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +7 -1
- package/src/runtime/agent/orchestrator/providers/openai-responses-input.mjs +71 -24
- package/src/runtime/agent/orchestrator/providers/openai-responses-payload-parity.fixture.json +323 -0
- package/src/runtime/agent/orchestrator/providers/openai-responses-payload.mjs +44 -16
- package/src/runtime/agent/orchestrator/providers/openai-ws-delta.mjs +7 -16
- package/src/runtime/agent/orchestrator/providers/openai-ws-response-state/refusals.mjs +60 -0
- package/src/runtime/agent/orchestrator/providers/openai-ws-response-state/tool-calls.mjs +51 -35
- package/src/runtime/agent/orchestrator/providers/openai-ws-response-state.mjs +30 -0
- package/src/runtime/agent/orchestrator/providers/openai-ws-stream/event-switch.mjs +12 -0
- package/src/runtime/agent/orchestrator/providers/openai-ws-stream/terminal-frames.mjs +25 -6
- package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +11 -3
- package/src/runtime/agent/orchestrator/providers/opencode-go-gemini.mjs +170 -0
- package/src/runtime/agent/orchestrator/providers/opencode-go.mjs +75 -70
- package/src/runtime/agent/orchestrator/providers/openrouter.mjs +75 -0
- package/src/runtime/agent/orchestrator/providers/provider-error-kind.mjs +147 -8
- package/src/runtime/agent/orchestrator/providers/registry.mjs +4 -1
- package/src/runtime/agent/orchestrator/providers/retry-classification.mjs +5 -0
- package/src/runtime/agent/orchestrator/providers/transport-scope.d.mts +6 -0
- package/src/runtime/agent/orchestrator/providers/transport-scope.mjs +28 -0
- package/src/runtime/agent/orchestrator/providers/wire-gateway.mjs +157 -0
- package/src/runtime/agent/orchestrator/providers/wire-route.mjs +512 -0
- package/src/runtime/agent/orchestrator/providers/xai-tool-search-head-parity.json +1131 -0
- package/src/runtime/agent/orchestrator/providers/xai-tool-search.fixtures.mjs +183 -0
- package/src/runtime/agent/orchestrator/runtime-core/ask-user-tool-defs.mjs +100 -0
- package/src/runtime/agent/orchestrator/runtime-core/deferred-tool-availability.mjs +21 -0
- package/src/runtime/agent/orchestrator/runtime-core/effort.mjs +15 -0
- package/src/runtime/agent/orchestrator/runtime-core/provider-request-snapshot.mjs +7 -3
- package/src/runtime/agent/orchestrator/runtime-core/session-title.mjs +21 -1
- package/src/runtime/agent/orchestrator/runtime-core/skill-tool-loading.mjs +14 -3
- package/src/runtime/agent/orchestrator/runtime-core/tool-bridge.mjs +526 -0
- package/src/runtime/agent/orchestrator/runtime-core/tool-catalog-data.mjs +3 -0
- package/src/runtime/agent/orchestrator/runtime-core/tool-catalog-schema.mjs +10 -7
- package/src/runtime/agent/orchestrator/runtime-core/tool-catalog.mjs +176 -71
- package/src/runtime/agent/orchestrator/runtime-core/tool-defs.mjs +62 -10
- package/src/runtime/agent/orchestrator/runtime-core/tool-exposure.mjs +96 -0
- package/src/runtime/agent/orchestrator/session/catalog-db/schema.mjs +3 -1
- package/src/runtime/agent/orchestrator/session/compact/constants.mjs +3 -2
- package/src/runtime/agent/orchestrator/session/compact/messages.mjs +14 -93
- package/src/runtime/agent/orchestrator/session/compact/summary-anchor.mjs +5 -0
- package/src/runtime/agent/orchestrator/session/compact/user-message-kind.mjs +120 -0
- package/src/runtime/agent/orchestrator/session/context-tool-schema.mjs +17 -2
- package/src/runtime/agent/orchestrator/session/context-utils.mjs +8 -4
- package/src/runtime/agent/orchestrator/session/eager-dispatch/admission.mjs +2 -1
- package/src/runtime/agent/orchestrator/session/eager-dispatch.mjs +6 -1
- package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +91 -1
- package/src/runtime/agent/orchestrator/session/loop/completion-guards.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/loop/context-overflow.mjs +56 -0
- package/src/runtime/agent/orchestrator/session/loop/deferred-call-through.mjs +18 -8
- package/src/runtime/agent/orchestrator/session/loop/loop-state.mjs +1 -0
- package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +21 -10
- package/src/runtime/agent/orchestrator/session/loop/provider-tool-exclusions.mjs +132 -0
- package/src/runtime/agent/orchestrator/session/loop/request-boundary.mjs +30 -0
- package/src/runtime/agent/orchestrator/session/loop/send-phase.mjs +63 -20
- package/src/runtime/agent/orchestrator/session/loop/step-auto-effort.mjs +2 -1
- package/src/runtime/agent/orchestrator/session/loop/tool-call-resolution.mjs +95 -0
- package/src/runtime/agent/orchestrator/session/loop/tool-exec/before-hook.mjs +30 -3
- package/src/runtime/agent/orchestrator/session/loop/tool-exec/dispatch.mjs +15 -4
- package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +15 -1
- package/src/runtime/agent/orchestrator/session/loop/tool-exposure-migration.mjs +150 -0
- package/src/runtime/agent/orchestrator/session/loop/tool-phase.mjs +4 -1
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-auto-effort.mjs +2 -1
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-commit.mjs +34 -12
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-execute.mjs +16 -0
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-loop-options.mjs +57 -25
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-prepare.mjs +6 -0
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-settle.mjs +4 -0
- package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +8 -0
- package/src/runtime/agent/orchestrator/session/manager/prompt-utils.mjs +208 -14
- package/src/runtime/agent/orchestrator/session/manager/session-close.mjs +15 -3
- package/src/runtime/agent/orchestrator/session/manager/session-crud.mjs +25 -3
- package/src/runtime/agent/orchestrator/session/manager/session-fallback-title.mjs +82 -0
- package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +19 -2
- package/src/runtime/agent/orchestrator/session/manager/session-record.mjs +3 -0
- package/src/runtime/agent/orchestrator/session/manager/tool-resolution.mjs +4 -1
- package/src/runtime/agent/orchestrator/session/manager.mjs +2 -1
- package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +6 -0
- package/src/runtime/agent/orchestrator/session/send-with-recovery.mjs +13 -0
- package/src/runtime/agent/orchestrator/session/side-question-store.mjs +96 -0
- package/src/runtime/agent/orchestrator/session/side-question.mjs +192 -0
- package/src/runtime/agent/orchestrator/session/store/lifecycle-barriers.mjs +17 -3
- package/src/runtime/agent/orchestrator/session/store/live-state.mjs +23 -0
- package/src/runtime/agent/orchestrator/session/store/paths-heartbeat.mjs +58 -2
- package/src/runtime/agent/orchestrator/session/store/session-delete.mjs +41 -28
- package/src/runtime/agent/orchestrator/session/store/session-observers.mjs +4 -2
- package/src/runtime/agent/orchestrator/session/store/sweep/sweep-finish.mjs +12 -1
- package/src/runtime/agent/orchestrator/session/store/write-admission.mjs +3 -1
- package/src/runtime/agent/orchestrator/session/store-summary-archived-agent.mjs +34 -15
- package/src/runtime/agent/orchestrator/session/store-summary-catalog.mjs +4 -0
- package/src/runtime/agent/orchestrator/session/store-summary-fields.mjs +8 -0
- package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +10 -1
- package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +10 -2
- package/src/runtime/agent/orchestrator/session/store.mjs +19 -3
- package/src/runtime/agent/orchestrator/session/tool-batch/execute-call.mjs +1 -0
- package/src/runtime/agent/orchestrator/session/tool-batch/finalize.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/tool-batch/record-outcome.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/tool-batch/state.mjs +7 -1
- package/src/runtime/agent/orchestrator/session/tool-result-offload.mjs +4 -0
- package/src/runtime/agent/orchestrator/session/tool-schema-rejection.mjs +77 -0
- package/src/runtime/agent/orchestrator/tools/bash-policy-scan.mjs +8 -2
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/command-preflight.mjs +3 -3
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +4 -1
- package/src/runtime/agent/orchestrator/tools/builtin/external-tool-adapters.mjs +14 -10
- package/src/runtime/agent/orchestrator/tools/builtin/lib/shell-job-records.mjs +49 -34
- package/src/runtime/agent/orchestrator/tools/builtin/runtime-capabilities.mjs +23 -1
- package/src/runtime/agent/orchestrator/tools/code-graph/dispatch/root-resolution.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/code-graph/search.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/patch/ui-diff.mjs +73 -39
- package/src/runtime/agent/orchestrator/tools/progress-message.mjs +18 -1
- package/src/runtime/agent/orchestrator/tools/shell-command.mjs +7 -4
- package/src/runtime/agent/orchestrator/tools/shell-exec-policy.mjs +88 -12
- package/src/runtime/agent/orchestrator/tools/shell-policy.mjs +2 -0
- package/src/runtime/computer-bridge/action-schema.mjs +44 -21
- package/src/runtime/github/tool.mjs +92 -2
- package/src/runtime/memory/index.mjs +1 -0
- package/src/runtime/memory/lib/http-router/ingest-routes.mjs +22 -1
- package/src/runtime/memory/lib/ingest/transcript-offsets.mjs +2 -0
- package/src/runtime/memory/lib/pg/adapter.mjs +98 -1
- package/src/runtime/memory/lib/pg/process.mjs +31 -26
- package/src/runtime/memory/lib/pg/run-child.mjs +49 -0
- package/src/runtime/memory/lib/pg/supervisor.mjs +108 -3
- package/src/runtime/memory/lib/runtime-fetcher.mjs +12 -13
- package/src/runtime/memory/lib/transcript-ingest.mjs +14 -0
- package/src/runtime/office/README.md +1 -1
- package/src/runtime/office/authoring/html-document-author.mjs +2 -0
- package/src/runtime/office/authoring/pptx-contact-sheet.mjs +2 -0
- package/src/runtime/office/authoring/pptx-html-measure.mjs +2 -0
- package/src/runtime/office/authoring/pptx-html-runner.mjs +2 -0
- package/src/runtime/office/authoring/xlsx-html-runner.mjs +2 -0
- package/src/runtime/office/core/office-actions-read.mjs +3 -0
- package/src/runtime/office/core/office-render-preview.mjs +2 -0
- package/src/runtime/office/core/office-session-dispatch.mjs +7 -0
- package/src/runtime/office/core/office-sessionless-actions.mjs +8 -0
- package/src/runtime/office/core/office-sessions.mjs +5 -0
- package/src/runtime/office/core/office-transactions.mjs +3 -0
- package/src/runtime/office/core/office-write-ledger.mjs +98 -0
- package/src/runtime/office/design/library/templates/mixdog-executive.mixdog-edit.pptx +0 -0
- package/src/runtime/office/index.mjs +30 -0
- package/src/runtime/office/pdf/pdf-edit-document.mjs +2 -0
- package/src/runtime/office/pdf/pdf-render.mjs +23 -7
- package/src/runtime/office/pdf/pdf-security.mjs +2 -0
- package/src/runtime/office/portable/portable-opc.mjs +4 -1
- package/src/runtime/office/quality/visual-diff.mjs +2 -0
- package/src/runtime/shared/bounded-file-read.mjs +13 -0
- package/src/runtime/shared/bounded-line-diff.mjs +182 -0
- package/src/runtime/shared/buffered-appender.mjs +18 -0
- package/src/runtime/shared/config.mjs +78 -18
- package/src/runtime/shared/err-text.mjs +6 -0
- package/src/runtime/shared/file-permissions.d.mts +6 -1
- package/src/runtime/shared/file-permissions.mjs +54 -6
- package/src/runtime/shared/llm/anthropic-effort.mjs +6 -0
- package/src/runtime/shared/llm/usage-accounting.mjs +5 -2
- package/src/runtime/shared/llm/usage-ledger-rollup.mjs +3 -0
- package/src/runtime/shared/loopback-listener.mjs +9 -0
- package/src/runtime/shared/provider-api-key.mjs +2 -0
- package/src/runtime/shared/review-diff.mjs +52 -0
- package/src/runtime/shared/session-sidebar-host.d.mts +4 -0
- package/src/runtime/shared/session-sidebar-host.mjs +38 -0
- package/src/runtime/shared/slow-span.mjs +114 -0
- package/src/runtime/shared/statusline/statusline-segments.mjs +28 -4
- package/src/runtime/shared/tool-card-model/card-header.mjs +6 -1
- package/src/runtime/shared/tool-card-model/generic-detail.mjs +39 -5
- package/src/runtime/shared/tool-card-model.mjs +5 -2
- package/src/runtime/shared/tool-output-limit.mjs +4 -0
- package/src/runtime/shared/tool-primitives.mjs +13 -0
- package/src/runtime/shared/tool-result-summary.mjs +8 -1
- package/src/runtime/shared/tool-surface.mjs +18 -3
- package/src/runtime/shared/tool-work-units.mjs +40 -4
- package/src/runtime/shared/transcript-writer/purge.mjs +74 -0
- package/src/runtime/shared/transcript-writer.mjs +10 -2
- package/src/runtime/shared/turn-changes.mjs +268 -100
- package/src/runtime/shared/turn-review-reply.mjs +8 -39
- package/src/runtime/shared/turn-snapshot-store.mjs +19 -108
- package/src/runtime/shared/turn-snapshot.mjs +857 -683
- package/src/runtime/shared/turn-worktree-snapshot.mjs +183 -255
- package/src/runtime/shared/user-data-guard.mjs +262 -49
- package/src/runtime/shared/worktrees/carry.mjs +531 -0
- package/src/runtime/shared/worktrees/contract.mjs +34 -0
- package/src/runtime/shared/worktrees/file-recovery.mjs +68 -0
- package/src/runtime/shared/worktrees/git-head.mjs +205 -0
- package/src/runtime/shared/worktrees/git.mjs +8 -3
- package/src/runtime/shared/worktrees/maintenance.mjs +1 -1
- package/src/runtime/shared/worktrees/manager.mjs +156 -8
- package/src/runtime/shared/worktrees/merge.mjs +31 -21
- package/src/runtime/shared/worktrees/paths.d.mts +6 -0
- package/src/runtime/terminal-bridge/client.mjs +44 -11
- package/src/runtime/terminal-bridge/tool-defs.mjs +13 -6
- package/src/runtime/tidy/apply.mjs +47 -2
- package/src/runtime/tidy/run-engines.mjs +11 -5
- package/src/session-runtime/agent-tool-routing.mjs +19 -13
- package/src/session-runtime/boot/apis.mjs +13 -5
- package/src/session-runtime/boot/facade.mjs +1 -0
- package/src/session-runtime/boot/infrastructure.mjs +6 -1
- package/src/session-runtime/boot/session.mjs +0 -1
- package/src/session-runtime/boot/tools.mjs +4 -1
- package/src/session-runtime/cwd-plugins/cwd-apply.mjs +12 -5
- package/src/session-runtime/internal-tool-executor/ask-user-tool.mjs +65 -0
- package/src/session-runtime/internal-tool-executor/bridge-tools.mjs +3 -1
- package/src/session-runtime/internal-tool-executor/knowledge-tools.mjs +11 -4
- package/src/session-runtime/internal-tool-executor/workspace-tools.mjs +115 -18
- package/src/session-runtime/internal-tool-executor.mjs +6 -2
- package/src/session-runtime/lifecycle/cold-heir.mjs +188 -0
- package/src/session-runtime/lifecycle/inheritance.mjs +71 -27
- package/src/session-runtime/lifecycle/session-catalog.mjs +12 -3
- package/src/session-runtime/lifecycle/session-switch/resume.mjs +6 -0
- package/src/session-runtime/lifecycle/session-switch/session-delete.mjs +29 -6
- package/src/session-runtime/lifecycle/teardown.mjs +72 -7
- package/src/session-runtime/lifecycle-api.mjs +1 -0
- package/src/session-runtime/mcp-glue/connect.mjs +17 -5
- package/src/session-runtime/model-route/set-route.mjs +2 -2
- package/src/session-runtime/native-web-search-usage.mjs +51 -0
- package/src/session-runtime/native-web-search.mjs +10 -1
- package/src/session-runtime/new-session-config.mjs +4 -2
- package/src/session-runtime/provider-auth/login.mjs +4 -0
- package/src/session-runtime/resource-scope-decorate.mjs +10 -3
- package/src/session-runtime/runtime-review-api.mjs +8 -2
- package/src/session-runtime/services/agent-tool/index-write-queue.mjs +81 -20
- package/src/session-runtime/services/agent-tool/lead-worker-index/forgotten-leads.mjs +29 -0
- package/src/session-runtime/services/agent-tool/lead-worker-index/index-file.mjs +1 -1
- package/src/session-runtime/services/agent-tool/lead-worker-index.mjs +73 -7
- package/src/session-runtime/services/agent-tool/tag-registry.mjs +15 -1
- package/src/session-runtime/services/agent-tool/worker-index/row-store.mjs +10 -1
- package/src/session-runtime/services/agent-tool/worker-index.mjs +46 -7
- package/src/session-runtime/services/agent-tool.mjs +3 -0
- package/src/session-runtime/services/channel-admin.mjs +19 -3
- package/src/session-runtime/services/memory-runtime-proxy/calls.mjs +4 -0
- package/src/session-runtime/services/memory-runtime-proxy.mjs +1 -0
- package/src/session-runtime/services/provider-admin.mjs +81 -15
- package/src/session-runtime/session-transcript-purge.mjs +32 -0
- package/src/session-runtime/session-turn-api.mjs +14 -5
- package/src/session-runtime/settings-builtin-tools-api.mjs +9 -0
- package/src/session-runtime/setup-tool/executor.mjs +36 -6
- package/src/session-runtime/setup-tool/extended-actions.mjs +86 -6
- package/src/session-runtime/setup-tool/settings-contract.mjs +13 -3
- package/src/session-runtime/setup-tool/tool-defs.mjs +13 -4
- package/src/session-runtime/skills-api.mjs +3 -5
- package/src/session-runtime/tool-surface.mjs +5 -1
- package/src/session-runtime/turn/compaction-gate.mjs +25 -0
- package/src/session-runtime/turn/session-ops.mjs +7 -5
- package/src/session-runtime/turn/side-question-ops.mjs +22 -0
- package/src/session-runtime/turn/turn-run/ask-callbacks.mjs +5 -1
- package/src/session-runtime/turn/turn-run/turn-prepare.mjs +8 -3
- package/src/session-runtime/turn/turn-run/turn-settle.mjs +10 -0
- package/src/session-runtime/turn/turn-run/turn-timing.mjs +4 -3
- package/src/session-runtime/turn/turn-run.mjs +25 -9
- package/src/session-runtime/usage-stats-api.mjs +43 -11
- package/src/session-runtime/workspace/project-branches.mjs +7 -5
- package/src/session-runtime/workspace/transition-steps.mjs +129 -31
- package/src/session-runtime/workspace/transitions.mjs +190 -15
- package/src/standalone/daemon-agent-control.mjs +9 -1
- package/src/standalone/daemon-local-session-bridge.mjs +14 -1
- package/src/standalone/daemon-owner.mjs +17 -4
- package/src/standalone/daemon-pg-stop.mjs +51 -0
- package/src/standalone/daemon-stop-budget.d.mts +2 -0
- package/src/standalone/daemon-stop-budget.mjs +18 -0
- package/src/standalone/daemon-stored-session-views.mjs +2 -0
- package/src/standalone/daemon-telemetry.mjs +19 -0
- package/src/standalone/daemon.mjs +61 -1
- package/src/standalone/desktop-service-registry.mjs +5 -0
- package/src/standalone/local-session-runtime.mjs +70 -19
- package/src/standalone/session-client.mjs +42 -0
- package/src/standalone/session-protocol.mjs +7 -0
- package/src/standalone/session-proxy/projection.mjs +41 -4
- package/src/standalone/session-runtime-inline-host/boot-loaders.mjs +15 -1
- package/src/standalone/session-runtime-inline-host.mjs +1 -0
- package/src/standalone/session-service/session-calls/session-action.mjs +18 -3
- package/src/standalone/session-service-api.mjs +6 -1
- package/src/standalone/session-state-patch.d.mts +2 -1
- package/src/standalone/session-state-patch.mjs +41 -1
- package/src/tui/App.jsx +25 -0
- package/src/tui/app/app-view/floating-panel.jsx +26 -1
- package/src/tui/app/btw-controller.mjs +118 -0
- package/src/tui/app/create-app-pickers.mjs +2 -0
- package/src/tui/app/shell-layout/row-budgets.mjs +11 -1
- package/src/tui/app/shell-layout/surfaces.mjs +3 -1
- package/src/tui/app/slash-commands.mjs +13 -3
- package/src/tui/app/slash-dispatch/session-commands.mjs +19 -6
- package/src/tui/app/use-global-key-input/approval-shortcut.mjs +12 -0
- package/src/tui/app/use-global-key-input.mjs +5 -12
- package/src/tui/app/use-prompt-handlers.mjs +11 -0
- package/src/tui/components/BtwPanel.jsx +53 -0
- package/src/tui/components/QuestionPanel.jsx +275 -0
- package/src/tui/components/question-panel-model.mjs +156 -0
- package/src/tui/dist/index.mjs +2413 -1465
- package/src/tui/session/context-state.mjs +4 -2
- package/src/tui/session/session-api/commands.mjs +10 -0
- package/src/tui/session/session-api/controls.mjs +2 -2
- package/src/tui/session/session-api/integrations.mjs +9 -2
- package/src/tui/session/session-api/lifecycle/dispose.mjs +1 -0
- package/src/tui/session/session-api/lifecycle.mjs +2 -1
- package/src/tui/session/session-api/settings.mjs +6 -0
- package/src/tui/session/session-api.mjs +1 -0
- package/src/tui/session/tool-approval.mjs +10 -7
- package/src/tui/session/transcript-spill.mjs +14 -7
- package/src/tui/session/turn-stream.mjs +174 -31
- package/src/tui/session/turn.mjs +12 -5
- package/src/workflows/default/WORKFLOW.md +3 -1
|
@@ -12,10 +12,18 @@
|
|
|
12
12
|
* The owner marker disappears when the job settles and a `.done` marker keeps
|
|
13
13
|
* the terminal JSON out of live counters. Terminal metadata remains available
|
|
14
14
|
* for a bounded manual-recovery window.
|
|
15
|
+
*
|
|
16
|
+
* Ordering: a promoted job keeps its foreground id, so the foreground marker's
|
|
17
|
+
* retire and the promoted job's publish address the same files. Every publish,
|
|
18
|
+
* complete and retire for one jobId therefore runs on a per-job chain, strictly
|
|
19
|
+
* in call order and never overlapping. A record is written atomically (temp
|
|
20
|
+
* file + rename) and always before its owner marker, so a reader never sees a
|
|
21
|
+
* partial `.json`, and a marker never exists without its `.json`.
|
|
15
22
|
*/
|
|
16
23
|
import { access, mkdir, readFile, readdir, rm, stat, writeFile } from 'node:fs/promises';
|
|
17
24
|
import { homedir } from 'node:os';
|
|
18
25
|
import { join } from 'node:path';
|
|
26
|
+
import { writeJsonAtomicAsync } from '../../../../../shared/atomic-file.mjs';
|
|
19
27
|
import { isPidAlive } from '../../../../../shared/pid-liveness.mjs';
|
|
20
28
|
import { displayShellCommand } from '../../../../../shared/shell-display.mjs';
|
|
21
29
|
|
|
@@ -24,12 +32,27 @@ import { displayShellCommand } from '../../../../../shared/shell-display.mjs';
|
|
|
24
32
|
const ORPHAN_GRACE_MS = 60 * 60 * 1000;
|
|
25
33
|
const COMPLETED_SHELL_JOB_TTL_MS = 30 * 60 * 1000;
|
|
26
34
|
|
|
27
|
-
const
|
|
35
|
+
const chains = new Map();
|
|
28
36
|
const paths = new Map();
|
|
29
37
|
const records = new Map();
|
|
30
|
-
|
|
38
|
+
// jobId -> token of the live terminal record. Every publish/retire clears it and
|
|
39
|
+
// every completion replaces it, so a TTL timer armed for an older incarnation
|
|
40
|
+
// can tell its files are no longer the ones it was armed for.
|
|
41
|
+
const incarnations = new Map();
|
|
42
|
+
let incarnationSeq = 0;
|
|
31
43
|
let sweepStarted = false;
|
|
32
44
|
|
|
45
|
+
/** Run `operation` after every earlier operation for `jobId` has finished. */
|
|
46
|
+
function enqueue(jobId, operation) {
|
|
47
|
+
const run = (chains.get(jobId) || Promise.resolve()).then(operation);
|
|
48
|
+
const tail = run.catch(() => {});
|
|
49
|
+
chains.set(jobId, tail);
|
|
50
|
+
void tail.then(() => {
|
|
51
|
+
if (chains.get(jobId) === tail) chains.delete(jobId);
|
|
52
|
+
});
|
|
53
|
+
return run;
|
|
54
|
+
}
|
|
55
|
+
|
|
33
56
|
function dataDir() {
|
|
34
57
|
return process.env.MIXDOG_DATA_DIR || join(process.env.MIXDOG_HOME || join(homedir(), '.mixdog'), 'data');
|
|
35
58
|
}
|
|
@@ -140,9 +163,8 @@ export function publishShellJobRecord(
|
|
|
140
163
|
const dir = jobsDir();
|
|
141
164
|
const jsonPath = join(dir, `${jobId}.json`);
|
|
142
165
|
const ownerPath = join(dir, `${jobId}.owner-${ownerPid}`);
|
|
143
|
-
retired.delete(jobId);
|
|
144
166
|
paths.set(jobId, [jsonPath, ownerPath]);
|
|
145
|
-
|
|
167
|
+
void enqueue(jobId, async () => {
|
|
146
168
|
try {
|
|
147
169
|
await mkdir(dir, { recursive: true });
|
|
148
170
|
if (!sweepStarted) {
|
|
@@ -172,18 +194,13 @@ export function publishShellJobRecord(
|
|
|
172
194
|
...(foreground ? { foreground: true } : {}),
|
|
173
195
|
};
|
|
174
196
|
records.set(jobId, record);
|
|
175
|
-
|
|
197
|
+
incarnations.delete(jobId);
|
|
198
|
+
// The record lands whole before the marker that advertises it.
|
|
199
|
+
await writeJsonAtomicAsync(jsonPath, record, { fsync: false });
|
|
176
200
|
await writeFile(ownerPath, '', 'utf8');
|
|
177
201
|
} catch {
|
|
178
202
|
/* the record is presentation only; never fail a job for it */
|
|
179
203
|
}
|
|
180
|
-
// A job that settled while the write was in flight must not leave a
|
|
181
|
-
// record behind.
|
|
182
|
-
if (retired.has(jobId)) await removePaths([jsonPath, ownerPath]);
|
|
183
|
-
})();
|
|
184
|
-
publishing.set(jobId, work);
|
|
185
|
-
void work.finally(() => {
|
|
186
|
-
if (publishing.get(jobId) === work) publishing.delete(jobId);
|
|
187
204
|
});
|
|
188
205
|
}
|
|
189
206
|
|
|
@@ -196,11 +213,7 @@ export function completeShellJobRecord(jobId, detail) {
|
|
|
196
213
|
const donePath = join(dir, `${key}.done`);
|
|
197
214
|
const list = paths.get(key) || [jsonPath];
|
|
198
215
|
const ownerPaths = list.filter((path) => path.includes('.owner-'));
|
|
199
|
-
|
|
200
|
-
const work = (async () => {
|
|
201
|
-
try {
|
|
202
|
-
await pending;
|
|
203
|
-
} catch {}
|
|
216
|
+
return enqueue(key, async () => {
|
|
204
217
|
await mkdir(dir, { recursive: true });
|
|
205
218
|
let base = records.get(key) || null;
|
|
206
219
|
if (!base) {
|
|
@@ -210,19 +223,26 @@ export function completeShellJobRecord(jobId, detail) {
|
|
|
210
223
|
}
|
|
211
224
|
const record = terminalRecord(base, { ...detail, jobId: key });
|
|
212
225
|
records.set(key, record);
|
|
213
|
-
await
|
|
226
|
+
await writeJsonAtomicAsync(jsonPath, record, { fsync: false });
|
|
214
227
|
await writeFile(donePath, '', 'utf8');
|
|
215
228
|
await removePaths(ownerPaths);
|
|
216
229
|
paths.set(key, [jsonPath, donePath]);
|
|
230
|
+
const incarnation = ++incarnationSeq;
|
|
231
|
+
incarnations.set(key, incarnation);
|
|
217
232
|
const timer = setTimeout(() => {
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
void
|
|
233
|
+
// Expire through the job's queue and only if no publish, retire or newer
|
|
234
|
+
// completion has replaced the files this timer was armed for.
|
|
235
|
+
void enqueue(key, async () => {
|
|
236
|
+
if (incarnations.get(key) !== incarnation) return;
|
|
237
|
+
incarnations.delete(key);
|
|
238
|
+
records.delete(key);
|
|
239
|
+
paths.delete(key);
|
|
240
|
+
await removePaths([jsonPath, donePath]);
|
|
241
|
+
});
|
|
221
242
|
}, COMPLETED_SHELL_JOB_TTL_MS);
|
|
222
243
|
timer.unref?.();
|
|
223
244
|
return true;
|
|
224
|
-
})
|
|
225
|
-
return work;
|
|
245
|
+
}).catch(() => false);
|
|
226
246
|
}
|
|
227
247
|
|
|
228
248
|
export async function readShellJobRecord(jobId) {
|
|
@@ -266,18 +286,13 @@ export async function listShellJobRecords() {
|
|
|
266
286
|
export function retireShellJobRecord(jobId) {
|
|
267
287
|
const key = String(jobId || '').trim();
|
|
268
288
|
if (!key) return;
|
|
269
|
-
retired.add(key);
|
|
270
|
-
records.delete(key);
|
|
271
289
|
const list = paths.get(key);
|
|
272
290
|
paths.delete(key);
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
/* the publish path owns its failure */
|
|
279
|
-
}
|
|
291
|
+
// Queued behind every earlier publish and ahead of any later one: a promoted
|
|
292
|
+
// job publishing after this call is written only once this removal is done.
|
|
293
|
+
void enqueue(key, async () => {
|
|
294
|
+
records.delete(key);
|
|
295
|
+
incarnations.delete(key);
|
|
280
296
|
await removePaths(list);
|
|
281
|
-
|
|
282
|
-
})();
|
|
297
|
+
});
|
|
283
298
|
}
|
|
@@ -41,7 +41,29 @@ function _posixStartupShell(candidates = ['/bin/bash', '/usr/bin/bash', '/bin/sh
|
|
|
41
41
|
return null;
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
-
|
|
44
|
+
// Every session composes this line. Probing it means up to names×PATHEXT×PATH
|
|
45
|
+
// stats on Windows (~40ms) or a login-shell spawn on POSIX, both on the event
|
|
46
|
+
// loop, so one answer per PATH/shell is reused for a short window — the same
|
|
47
|
+
// rule as gitExecutablePresent: a session opened after an install still picks
|
|
48
|
+
// the tool up once the window passes.
|
|
49
|
+
const SHELL_TOOLS_STARTUP_TTL_MS = 30_000;
|
|
50
|
+
let _shellToolsStartup = null;
|
|
51
|
+
|
|
52
|
+
export function describeShellToolsStartupState(options = {}) {
|
|
53
|
+
if (options._spawnSync) return _probeShellToolsStartupState(options);
|
|
54
|
+
const { platform = process.platform, names = SHELL_TOOL_STARTUP_NAMES, shellPath = null } = options;
|
|
55
|
+
const pathValue = options.pathValue ?? (process.env.PATH || '');
|
|
56
|
+
const key = JSON.stringify([platform, names, shellPath, pathValue]);
|
|
57
|
+
const now = Date.now();
|
|
58
|
+
if (_shellToolsStartup?.key === key && now - _shellToolsStartup.at < SHELL_TOOLS_STARTUP_TTL_MS) {
|
|
59
|
+
return _shellToolsStartup.value;
|
|
60
|
+
}
|
|
61
|
+
const value = _probeShellToolsStartupState(options);
|
|
62
|
+
_shellToolsStartup = { key, at: now, value };
|
|
63
|
+
return value;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function _probeShellToolsStartupState({
|
|
45
67
|
platform = process.platform,
|
|
46
68
|
names = SHELL_TOOL_STARTUP_NAMES,
|
|
47
69
|
shellPath = null,
|
|
@@ -136,7 +136,7 @@ export function resolveDirectoryRoot(name, effectiveCwd, { filesystemRootCwd })
|
|
|
136
136
|
`${_PROJECT_ROOT_SENTINELS.join('/')} at it or any ancestor)` +
|
|
137
137
|
`${childRoots.length > 1 ? ` and holds ${childRoots.length} separate project roots (${listed})` : ''}. ` +
|
|
138
138
|
`${boundaryRoot ? `The nearest sentinel is at '${boundaryRoot}' (home/temp), which is never auto-adopted. ` : ''}` +
|
|
139
|
-
`Refusing to index an arbitrary tree.
|
|
139
|
+
`Refusing to index an arbitrary tree. Switch Project with the session tool (set-project), or pass an explicit ` +
|
|
140
140
|
`'cwd' (repo root) or a 'file' anchor.`
|
|
141
141
|
);
|
|
142
142
|
}
|
|
@@ -854,6 +854,6 @@ export function _augmentNoHitDiagnostic(result, emptyToken, graph, cwd, symbol)
|
|
|
854
854
|
}
|
|
855
855
|
return (
|
|
856
856
|
`${emptyToken}\n# '${symbol}' not present in graph rooted at ${cwd} (${n} files indexed${trunc}). ` +
|
|
857
|
-
`If it should exist, the target is likely outside this cwd — pass an explicit 'cwd' (repo root) or 'file' anchor, or
|
|
857
|
+
`If it should exist, the target is likely outside this cwd — pass an explicit 'cwd' (repo root) or 'file' anchor, or switch Project with the session tool (set-project).`
|
|
858
858
|
);
|
|
859
859
|
}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
// UI-diff side channel: a committed apply_patch (or edit-tool write) records
|
|
2
2
|
// a turn-review change entry and a per-call diff the desktop/TUI tool cards
|
|
3
3
|
// pick up. Display only — never affects edit semantics.
|
|
4
|
-
import {
|
|
4
|
+
import { createLineDiffBudget, lineDiffHunks, unifiedHunksBody } from '../../../../shared/bounded-line-diff.mjs';
|
|
5
5
|
import { boundReviewPatch } from '../../../../shared/review-diff.mjs';
|
|
6
|
+
import { decodeText } from '../../../../shared/turn-changes.mjs';
|
|
6
7
|
import { recordTurnDiffChanges } from '../../../../shared/turn-snapshot.mjs';
|
|
7
8
|
import { pathKey, resolveV4AEntryPath } from './paths.mjs';
|
|
8
9
|
import { patchHeaderPathForResolved } from './read-redirects.mjs';
|
|
@@ -38,31 +39,21 @@ function headerPath(value) {
|
|
|
38
39
|
.replace(/\\/g, '/');
|
|
39
40
|
}
|
|
40
41
|
|
|
41
|
-
const DIFF_TIMEOUT_MS = 100;
|
|
42
|
-
|
|
43
|
-
const utf8 = new TextDecoder('utf-8', { fatal: true, ignoreBOM: true });
|
|
44
|
-
|
|
45
42
|
function isOmitted(value) {
|
|
46
43
|
return Boolean(value) && typeof value === 'object' && !(value instanceof Uint8Array) && value.omitted === true;
|
|
47
44
|
}
|
|
48
45
|
|
|
49
|
-
// Text of one side: '' for an absent file, null when it is not UTF-8
|
|
46
|
+
// Text of one side: '' for an absent file, null when it is binary (not UTF-8
|
|
47
|
+
// or holding a NUL byte) — the same rule as the turn review's tracker diffs.
|
|
50
48
|
function sideText(value) {
|
|
51
|
-
if (value == null) return
|
|
52
|
-
if (typeof value === 'string') return value;
|
|
53
|
-
if (value instanceof Uint8Array) {
|
|
54
|
-
try {
|
|
55
|
-
return utf8.decode(value);
|
|
56
|
-
} catch {
|
|
57
|
-
return null;
|
|
58
|
-
}
|
|
59
|
-
}
|
|
49
|
+
if (value == null || typeof value === 'string' || value instanceof Uint8Array) return decodeText(value);
|
|
60
50
|
return null;
|
|
61
51
|
}
|
|
62
52
|
|
|
63
53
|
// One file's change, from the call's own before and after states: absent
|
|
64
54
|
// (null), bytes or text, or an omitted marker when the bytes were not kept.
|
|
65
|
-
|
|
55
|
+
// `budget` is the call's line-diff budget.
|
|
56
|
+
function renderCallChange({ displayPath, newDisplayPath, before, after }, budget) {
|
|
66
57
|
const oldName = headerPath(displayPath) || 'unknown file';
|
|
67
58
|
const newName = headerPath(newDisplayPath) || oldName;
|
|
68
59
|
const oldHeader = before == null ? '/dev/null' : `a/${oldName}`;
|
|
@@ -82,23 +73,46 @@ function renderCallChange({ displayPath, newDisplayPath, before, after }) {
|
|
|
82
73
|
return oldName === newName ? '' : `${head}similarity index 100%\nrename from ${oldName}\nrename to ${newName}\n`;
|
|
83
74
|
}
|
|
84
75
|
const rename = oldName === newName ? '' : `rename from ${oldName}\nrename to ${newName}\n`;
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
76
|
+
const { hunks } = lineDiffHunks(oldText, newText, budget, [before ?? null, after ?? null]);
|
|
77
|
+
return `${head}${rename}${unifiedHunksBody(oldHeader, newHeader, hunks)}`;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Added/deleted lines of one rendered change, counted in its hunks only.
|
|
81
|
+
function hunkLineCounts(patch) {
|
|
82
|
+
let additions = 0;
|
|
83
|
+
let deletions = 0;
|
|
84
|
+
let inHunk = false;
|
|
85
|
+
for (const line of patch.split('\n')) {
|
|
86
|
+
if (line.startsWith('@@')) inHunk = true;
|
|
87
|
+
else if (inHunk && line.startsWith('+')) additions += 1;
|
|
88
|
+
else if (inHunk && line.startsWith('-')) deletions += 1;
|
|
94
89
|
}
|
|
95
|
-
return
|
|
90
|
+
return { additions, deletions };
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Each change with the line counts of its own unbounded diff (the turn review
|
|
94
|
+
// sums them per file), plus the call's whole diff, within the call's budget.
|
|
95
|
+
export function measureCallChanges(changes, budget = createLineDiffBudget()) {
|
|
96
|
+
const patches = (changes || []).map((change) => renderCallChange(change, budget));
|
|
97
|
+
return {
|
|
98
|
+
diff: patches.join(''),
|
|
99
|
+
changes: (changes || []).map((change, index) => ({ ...change, ...hunkLineCounts(patches[index]) })),
|
|
100
|
+
};
|
|
96
101
|
}
|
|
97
102
|
|
|
98
103
|
// A call's card shows the change that call made — its own before against its
|
|
99
104
|
// own after — never the turn's running total for the same files.
|
|
100
105
|
export function callUiDiff(changes) {
|
|
101
|
-
return (changes
|
|
106
|
+
return measureCallChanges(changes).diff;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Fold the call's measured changes into the turn review and publish its card
|
|
110
|
+
// diff. Both share one line-diff budget: the call is one operation.
|
|
111
|
+
function recordCallChanges(callId, sessionId, changes) {
|
|
112
|
+
const budget = createLineDiffBudget();
|
|
113
|
+
const measured = measureCallChanges(changes, budget);
|
|
114
|
+
recordTurnDiffChanges(sessionId, measured.changes, budget);
|
|
115
|
+
registerApplyPatchUiDiff(callId, measured.diff);
|
|
102
116
|
}
|
|
103
117
|
|
|
104
118
|
// Edit-tool UI diff: a successful single-file str-replace edit records the
|
|
@@ -108,21 +122,42 @@ export function callUiDiff(changes) {
|
|
|
108
122
|
export function registerEditToolUiDiff({ callId, sessionId, basePath, fullPath, before, after }) {
|
|
109
123
|
if (!callId || !sessionId || typeof fullPath !== 'string') return;
|
|
110
124
|
try {
|
|
111
|
-
|
|
112
|
-
{
|
|
113
|
-
path: fullPath,
|
|
114
|
-
displayPath: patchHeaderPathForResolved(basePath || '', fullPath),
|
|
115
|
-
before: before ?? null,
|
|
116
|
-
after: after ?? null,
|
|
117
|
-
},
|
|
118
|
-
];
|
|
119
|
-
recordTurnDiffChanges(sessionId, changes);
|
|
120
|
-
registerApplyPatchUiDiff(callId, callUiDiff(changes));
|
|
125
|
+
recordCallChanges(callId, sessionId, [toolFileChange(basePath, { fullPath, before, after })]);
|
|
121
126
|
} catch {
|
|
122
127
|
// best-effort display channel
|
|
123
128
|
}
|
|
124
129
|
}
|
|
125
130
|
|
|
131
|
+
function toolFileChange(basePath, { fullPath, before, after }) {
|
|
132
|
+
return {
|
|
133
|
+
path: fullPath,
|
|
134
|
+
displayPath: patchHeaderPathForResolved(basePath || '', fullPath),
|
|
135
|
+
before: before ?? null,
|
|
136
|
+
after: after ?? null,
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Writes of a tool without a per-call card (tidy, office): each change
|
|
141
|
+
// `{ fullPath, before, after }` (null = absent, bytes, or an omitted marker)
|
|
142
|
+
// joins the turn review with its own line counts, like an edit-tool write.
|
|
143
|
+
// Side channel only — never affects the tool result.
|
|
144
|
+
export function recordToolFileChanges({ sessionId, basePath, changes }) {
|
|
145
|
+
if (!sessionId || !changes?.length) return;
|
|
146
|
+
try {
|
|
147
|
+
const budget = createLineDiffBudget();
|
|
148
|
+
recordTurnDiffChanges(
|
|
149
|
+
sessionId,
|
|
150
|
+
measureCallChanges(
|
|
151
|
+
changes.map((change) => toolFileChange(basePath, change)),
|
|
152
|
+
budget
|
|
153
|
+
).changes,
|
|
154
|
+
budget
|
|
155
|
+
);
|
|
156
|
+
} catch {
|
|
157
|
+
// best-effort review channel
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
126
161
|
// Review content of one snapshot: null when absent, the bytes when captured,
|
|
127
162
|
// and an explicit omitted marker (never empty bytes) when they were not.
|
|
128
163
|
function snapshotContent(snapshot) {
|
|
@@ -184,8 +219,7 @@ export function registerCommittedPatchUiDiff({
|
|
|
184
219
|
after: snapshotContent(after),
|
|
185
220
|
});
|
|
186
221
|
}
|
|
187
|
-
|
|
188
|
-
registerApplyPatchUiDiff(callId, callUiDiff(changes));
|
|
222
|
+
recordCallChanges(callId, sessionId, changes);
|
|
189
223
|
} catch {
|
|
190
224
|
// Review collection is a side channel and must never affect committed
|
|
191
225
|
// apply_patch success/failure semantics.
|
|
@@ -105,8 +105,25 @@ export function formatToolStartProgress(name, args = {}) {
|
|
|
105
105
|
// ── host_input / cwd ─────────────────────────────────────────────
|
|
106
106
|
case 'inject_input':
|
|
107
107
|
return 'injecting input';
|
|
108
|
-
case 'cwd':
|
|
108
|
+
case 'cwd': // legacy: old transcripts
|
|
109
109
|
return a.action === 'set' ? 'setting cwd' : 'resolving cwd';
|
|
110
|
+
case 'session':
|
|
111
|
+
switch (a.action) {
|
|
112
|
+
case 'set-project':
|
|
113
|
+
return 'setting project';
|
|
114
|
+
case 'rename':
|
|
115
|
+
return 'renaming session';
|
|
116
|
+
case 'compact':
|
|
117
|
+
return 'scheduling compaction';
|
|
118
|
+
case 'fork':
|
|
119
|
+
return 'forking into a new session';
|
|
120
|
+
case 'favorite':
|
|
121
|
+
return a.value === false ? 'unfavoriting session' : 'favoriting session';
|
|
122
|
+
case 'archive':
|
|
123
|
+
return a.value === false ? 'unarchiving session' : 'archiving session';
|
|
124
|
+
default:
|
|
125
|
+
return 'checking session';
|
|
126
|
+
}
|
|
110
127
|
|
|
111
128
|
default:
|
|
112
129
|
return `running ${name}`;
|
|
@@ -27,9 +27,9 @@ export {
|
|
|
27
27
|
} from './shell-powershell.mjs';
|
|
28
28
|
export { stripAnsi, ExecResult } from './shell-exec-output.mjs';
|
|
29
29
|
|
|
30
|
-
async function _execPolicyBlockMessage(command) {
|
|
30
|
+
async function _execPolicyBlockMessage(command, elevationApproved = false) {
|
|
31
31
|
const { checkExecPolicyMessage } = await import('./bash-policy-scan.mjs');
|
|
32
|
-
return checkExecPolicyMessage(command);
|
|
32
|
+
return checkExecPolicyMessage(command, { elevationApproved });
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
// Admission-wait ceiling. Without it a saturated shell lane (all leases held
|
|
@@ -233,7 +233,7 @@ const failedResult = (run, stderr, failureReason) =>
|
|
|
233
233
|
// Admission lease, policy preflight, then the spawn. Resolves the run with a
|
|
234
234
|
// tool-phase failure and returns false when the command never reaches a shell.
|
|
235
235
|
async function admitAndSpawn(run, params, { admissionWaitMs, onSaturated }) {
|
|
236
|
-
const { admission, command, ownerSessionId, abortSignal } = params;
|
|
236
|
+
const { admission, command, ownerSessionId, abortSignal, elevationApproved } = params;
|
|
237
237
|
try {
|
|
238
238
|
run.resourceLease = await acquireShellLeaseBounded(admission, {
|
|
239
239
|
abortSignal,
|
|
@@ -241,7 +241,7 @@ async function admitAndSpawn(run, params, { admissionWaitMs, onSaturated }) {
|
|
|
241
241
|
label: `[task_id: ${run.foregroundRecordId}] ${String(command || '').slice(0, 120)}`,
|
|
242
242
|
ownerKey: ownerSessionId,
|
|
243
243
|
});
|
|
244
|
-
const policyErr = await _execPolicyBlockMessage(command);
|
|
244
|
+
const policyErr = await _execPolicyBlockMessage(command, elevationApproved === true);
|
|
245
245
|
if (policyErr) {
|
|
246
246
|
await releaseResourceLease(run);
|
|
247
247
|
run.resolveResult(failedResult(run, policyErr, 'preflight failed'));
|
|
@@ -544,6 +544,8 @@ export function execShellCommand(options) {
|
|
|
544
544
|
// hands the script to the shell out-of-band — see the POSIX env+eval path in
|
|
545
545
|
// bash-tool.mjs, which keeps the command body out of the child's argv.
|
|
546
546
|
execScript = null,
|
|
547
|
+
// The user approved this call's elevation 'ask' decision (runtime-set).
|
|
548
|
+
elevationApproved = false,
|
|
547
549
|
} = options;
|
|
548
550
|
return new Promise(async (resolve) => {
|
|
549
551
|
const run = createShellRun({ abortSignal, resolve, jobId });
|
|
@@ -565,6 +567,7 @@ export function execShellCommand(options) {
|
|
|
565
567
|
abortSignal,
|
|
566
568
|
directArgv,
|
|
567
569
|
execScript,
|
|
570
|
+
elevationApproved,
|
|
568
571
|
ownerSessionId,
|
|
569
572
|
clientHostPid,
|
|
570
573
|
},
|
|
@@ -2,7 +2,7 @@ import { extractHeredocBodies, extractShellCInner, stripQuotedAndHeredoc } from
|
|
|
2
2
|
import { extractPowerShellCommandInner } from './shell-powershell.mjs';
|
|
3
3
|
import { decodePowerShellEncodedCommand, isBlockedCommand, WRAPPER_NAMES } from './shell-policy.mjs';
|
|
4
4
|
|
|
5
|
-
/** @typedef {'allow'|'deny'} ExecPolicyDecision */
|
|
5
|
+
/** @typedef {'allow'|'ask'|'deny'} ExecPolicyDecision */
|
|
6
6
|
|
|
7
7
|
const EXEC_POLICY_DENY_PATTERNS = [
|
|
8
8
|
/\b(curl|wget|fetch|Invoke-WebRequest|iwr)\b[^\n|&;]*\|[^\n|&;]*\b(sh|bash|zsh|dash|pwsh|powershell)(?:\.exe)?\b/i,
|
|
@@ -16,9 +16,28 @@ const EXEC_POLICY_DENY_PATTERNS = [
|
|
|
16
16
|
// lookahead instead of the previous bare `iex\s+`.
|
|
17
17
|
/\|\s*iex\b/i,
|
|
18
18
|
/\biex\s+[$("'@]/i,
|
|
19
|
-
/\bStart-Process\b[^\n]*\s-Verb\s+RunAs\b/i,
|
|
20
19
|
];
|
|
21
20
|
|
|
21
|
+
// Elevated launch: `Start-Process` (or its aliases `saps` / `start`) with
|
|
22
|
+
// `-Verb RunAs`. Not a hard deny — the user is asked (parity with Claude Code's
|
|
23
|
+
// "Command requests elevated privileges"). `-Verb` may be abbreviated to any
|
|
24
|
+
// prefix (`-v`, `-ve`, `-ver`) and take its value after a space or a colon.
|
|
25
|
+
// `Start-Process` matches anywhere (as the former deny rule did); the short
|
|
26
|
+
// aliases only at a command position so `npm start -v RunAs` stays allowed.
|
|
27
|
+
const ELEVATION_ASK_PATTERN =
|
|
28
|
+
/(?:\bStart-Process\b|(?:^|[;&|({=\n])\s*(?:&\s*)?(?:saps|start)(?![\w-]))[^\n]*?\s-v(?:e(?:rb?)?)?(?:\s*:\s*|\s+)RunAs\b/i;
|
|
29
|
+
const ELEVATION_ASK_REASON = 'Command requests elevated privileges';
|
|
30
|
+
|
|
31
|
+
// PowerShell spellings of the same token that the quote-stripping pass would
|
|
32
|
+
// otherwise hide or split: backtick line continuation, backtick-escaped
|
|
33
|
+
// letters (`-V`erb`), and a quoted `-Verb` value (`-Verb:'RunAs'`).
|
|
34
|
+
function _normalizeElevationSyntax(text) {
|
|
35
|
+
return String(text || '')
|
|
36
|
+
.replace(/`\r?\n/g, ' ')
|
|
37
|
+
.replace(/`([A-Za-z])/g, '$1')
|
|
38
|
+
.replace(/(\s-v\w*(?:\s*:\s*|\s+))(['"])\s*RunAs\s*\2/gi, '$1RunAs');
|
|
39
|
+
}
|
|
40
|
+
|
|
22
41
|
// Verbs with no non-destructive invocation. `dd` is deliberately ABSENT: the
|
|
23
42
|
// disk-destroying form (`of=/dev/<disk>`) is hard-blocked at every command
|
|
24
43
|
// position by shell-policy, while `dd if=/dev/zero of=file` is the ordinary
|
|
@@ -39,12 +58,44 @@ const EXEC_POLICY_DENY_COMMANDS = new Set([
|
|
|
39
58
|
'parted',
|
|
40
59
|
]);
|
|
41
60
|
|
|
42
|
-
const _POLICY_RANK = { allow: 0,
|
|
61
|
+
const _POLICY_RANK = { allow: 0, ask: 1, deny: 2 };
|
|
43
62
|
|
|
44
63
|
// EVERY command position, not just the first: a deny-listed verb after `&&`,
|
|
45
64
|
// `;`, `|` or a newline is still an execution request, and reading only
|
|
46
65
|
// segment 0 let `true && shutdown /s` through. Callers pass quote-stripped
|
|
47
66
|
// text, so prose inside a string ("graceful shutdown") never reaches here.
|
|
67
|
+
const _ELEVATION_WRAPPERS = new Set(['sudo', 'doas', 'pkexec', 'gsudo']);
|
|
68
|
+
const _bareName = (token) =>
|
|
69
|
+
token
|
|
70
|
+
.replace(/^.*[\\/]/, '')
|
|
71
|
+
.toLowerCase()
|
|
72
|
+
.replace(/\.(exe|cmd|bat|com)$/i, '');
|
|
73
|
+
|
|
74
|
+
// Returns the first privilege-escalation wrapper (sudo, doas, pkexec, gsudo,
|
|
75
|
+
// with or without path/extension) found at any command position, else null.
|
|
76
|
+
function _elevationWrapperAtPositions(command) {
|
|
77
|
+
for (const seg of String(command || '').split(/[;&|\n]+/)) {
|
|
78
|
+
const tokens = seg.trim().split(/\s+/).filter(Boolean);
|
|
79
|
+
let i = 0;
|
|
80
|
+
while (i < tokens.length) {
|
|
81
|
+
const t = tokens[i];
|
|
82
|
+
if (/^[A-Za-z_][A-Za-z0-9_]*=/.test(t)) {
|
|
83
|
+
i++;
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
const base = _bareName(t);
|
|
87
|
+
if (_ELEVATION_WRAPPERS.has(base)) return base;
|
|
88
|
+
if (WRAPPER_NAMES.has(base)) {
|
|
89
|
+
i++;
|
|
90
|
+
while (i < tokens.length && (/^[-+]/.test(tokens[i]) || /^\d+[smhd]?$/.test(tokens[i]))) i++;
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
break;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
|
|
48
99
|
function _commandNamesAtPositions(command) {
|
|
49
100
|
const names = [];
|
|
50
101
|
for (const seg of String(command || '').split(/[;&|\n]+/)) {
|
|
@@ -56,7 +107,7 @@ function _commandNamesAtPositions(command) {
|
|
|
56
107
|
i++;
|
|
57
108
|
continue;
|
|
58
109
|
}
|
|
59
|
-
if (WRAPPER_NAMES.has(t
|
|
110
|
+
if (WRAPPER_NAMES.has(_bareName(t))) {
|
|
60
111
|
i++;
|
|
61
112
|
while (i < tokens.length && (/^[-+]/.test(tokens[i]) || /^\d+[smhd]?$/.test(tokens[i]))) i++;
|
|
62
113
|
continue;
|
|
@@ -74,14 +125,15 @@ function classifyExecPolicy(command) {
|
|
|
74
125
|
if (!text.trim()) return { decision: 'allow', reason: '' };
|
|
75
126
|
// Scan executable syntax, not quoted log/search text. Recursively preserve
|
|
76
127
|
// actual shell, PowerShell, heredoc, and encoded-command payload coverage.
|
|
77
|
-
const
|
|
78
|
-
|
|
79
|
-
...extractShellCInner(text)
|
|
80
|
-
...extractPowerShellCommandInner(text)
|
|
81
|
-
...extractHeredocBodies(text)
|
|
128
|
+
const rawTargets = [
|
|
129
|
+
text,
|
|
130
|
+
...extractShellCInner(text),
|
|
131
|
+
...extractPowerShellCommandInner(text),
|
|
132
|
+
...extractHeredocBodies(text),
|
|
82
133
|
];
|
|
83
134
|
const decodedPowerShell = decodePowerShellEncodedCommand(text);
|
|
84
|
-
if (decodedPowerShell)
|
|
135
|
+
if (decodedPowerShell) rawTargets.push(decodedPowerShell);
|
|
136
|
+
const executableTargets = rawTargets.map(stripQuotedAndHeredoc);
|
|
85
137
|
if (executableTargets.some((target) => isBlockedCommand(target))) {
|
|
86
138
|
return {
|
|
87
139
|
decision: 'deny',
|
|
@@ -115,6 +167,24 @@ function classifyExecPolicy(command) {
|
|
|
115
167
|
};
|
|
116
168
|
}
|
|
117
169
|
}
|
|
170
|
+
// Elevation asks only after every deny rule above has had its say.
|
|
171
|
+
for (const target of rawTargets) {
|
|
172
|
+
const hit = ELEVATION_ASK_PATTERN.exec(stripQuotedAndHeredoc(_normalizeElevationSyntax(target)));
|
|
173
|
+
if (hit) {
|
|
174
|
+
return {
|
|
175
|
+
decision: 'ask',
|
|
176
|
+
reason: ELEVATION_ASK_REASON,
|
|
177
|
+
rule: 'elevation',
|
|
178
|
+
match: hit[0].trim().slice(0, 80),
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
for (const target of executableTargets) {
|
|
183
|
+
const wrapper = _elevationWrapperAtPositions(target);
|
|
184
|
+
if (wrapper) {
|
|
185
|
+
return { decision: 'ask', reason: ELEVATION_ASK_REASON, rule: 'elevation', match: wrapper };
|
|
186
|
+
}
|
|
187
|
+
}
|
|
118
188
|
// Destructive-but-legitimate commands are NOT judged here. The caller
|
|
119
189
|
// computes that non-blocking warning once (getDedupedDestructiveWarnings)
|
|
120
190
|
// and prepends it to the result; deriving it again per scan target only
|
|
@@ -140,9 +210,15 @@ export function evaluateExecPolicyFromTargets(targets) {
|
|
|
140
210
|
return worst;
|
|
141
211
|
}
|
|
142
212
|
|
|
143
|
-
/** Pre-spawn block message
|
|
144
|
-
|
|
213
|
+
/** Pre-spawn block message. An 'ask' decision blocks unless the runtime
|
|
214
|
+
* already obtained the user's approval for this call (`elevationApproved`). */
|
|
215
|
+
export function formatExecPolicyBlockMessage(policyResult, { elevationApproved = false } = {}) {
|
|
145
216
|
const r = policyResult || { decision: 'allow' };
|
|
217
|
+
if (r.decision === 'ask') {
|
|
218
|
+
if (elevationApproved === true) return null;
|
|
219
|
+
const rule = r.rule ? ` [rule ${r.rule}${r.match ? `: "${r.match}"` : ''}]` : '';
|
|
220
|
+
return `Error: command requires user approval for elevation — ${r.reason || ELEVATION_ASK_REASON}${rule}; no approval was granted for this call`;
|
|
221
|
+
}
|
|
146
222
|
if (r.decision === 'deny') {
|
|
147
223
|
const rule = r.rule ? ` [rule ${r.rule}${r.match ? `: "${r.match}"` : ''}]` : '';
|
|
148
224
|
const alt = r.alternative ? `; instead: ${r.alternative}` : '';
|