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
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import { mkdir, writeFile } from 'node:fs/promises';
|
|
7
7
|
import { dirname, resolve } from 'node:path';
|
|
8
|
+
import { noteOfficeWrite } from '../core/office-write-ledger.mjs';
|
|
8
9
|
import {
|
|
9
10
|
PDFDocument,
|
|
10
11
|
PDFArray,
|
|
@@ -130,6 +131,7 @@ export async function writeSibling(path, target, bytes) {
|
|
|
130
131
|
if (output.toLowerCase() === resolve(path).toLowerCase())
|
|
131
132
|
throw new Error('output must differ from the document being edited');
|
|
132
133
|
await mkdir(dirname(output), { recursive: true });
|
|
134
|
+
noteOfficeWrite(output);
|
|
133
135
|
await writeFile(output, bytes);
|
|
134
136
|
return output;
|
|
135
137
|
}
|
|
@@ -5,6 +5,7 @@ import { createCanvas, loadImage } from '@napi-rs/canvas';
|
|
|
5
5
|
import { pdfjsStandardFontDataUrl, resolvedPdfJs } from '../../attachments/pdfjs-runtime.mjs';
|
|
6
6
|
import { installPdfGlobals } from './pdf-document.mjs';
|
|
7
7
|
import { workerExecArgv } from '../../shared/worker-exec-argv.mjs';
|
|
8
|
+
import { noteOfficeWrite } from '../core/office-write-ledger.mjs';
|
|
8
9
|
|
|
9
10
|
const PAGE_NUMBER_PATTERN = /^\s*(?:(?:page\s*)?\d+\s*(?:of|\/)\s*\d+|페이지\s*\d+|\d+\s*페이지)\s*$/i;
|
|
10
11
|
|
|
@@ -144,8 +145,8 @@ async function runPdfRenderWorker(data, signal = null) {
|
|
|
144
145
|
}
|
|
145
146
|
|
|
146
147
|
// Many pages are reviewed as contact sheets: one labelled grid image per group
|
|
147
|
-
// of pages,
|
|
148
|
-
async function
|
|
148
|
+
// of pages, for a file next to the PDF.
|
|
149
|
+
async function contactSheet(group, width, imagePath, renderPage) {
|
|
149
150
|
const columns = group.length <= 2 ? 1 : Math.max(2, Math.ceil(Math.sqrt(group.length * 0.75)));
|
|
150
151
|
const rows = Math.ceil(group.length / columns);
|
|
151
152
|
const gap = 12;
|
|
@@ -182,7 +183,6 @@ async function writeContactSheet(group, width, imagePath, renderPage) {
|
|
|
182
183
|
context.strokeRect(x, y + labelHeight, image.width, image.height);
|
|
183
184
|
}
|
|
184
185
|
const data = sheet.toBuffer('image/png');
|
|
185
|
-
await writeFile(imagePath, data);
|
|
186
186
|
return {
|
|
187
187
|
page: group[0],
|
|
188
188
|
pages: group,
|
|
@@ -204,7 +204,18 @@ async function writeContactSheet(group, width, imagePath, renderPage) {
|
|
|
204
204
|
};
|
|
205
205
|
}
|
|
206
206
|
|
|
207
|
-
|
|
207
|
+
// The page images land beside the PDF, each reported as an Office write first.
|
|
208
|
+
async function writePageImages(images) {
|
|
209
|
+
for (const image of images) {
|
|
210
|
+
noteOfficeWrite(image.path);
|
|
211
|
+
await writeFile(image.path, Buffer.from(image.data, 'base64'));
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
async function renderPdfPagesDirect(
|
|
216
|
+
path,
|
|
217
|
+
{ pages = null, maxWidth = 1400, maximumScale = 2, signal = null, writeImages = true } = {}
|
|
218
|
+
) {
|
|
208
219
|
if (signal?.aborted) throw new Error('PDF rendering was cancelled');
|
|
209
220
|
installPdfGlobals();
|
|
210
221
|
const { getDocument, VerbosityLevel } = await resolvedPdfJs();
|
|
@@ -248,7 +259,6 @@ async function renderPdfPagesDirect(path, { pages = null, maxWidth = 1400, maxim
|
|
|
248
259
|
for (const pageNumber of selected) {
|
|
249
260
|
const { canvas, data } = await renderPage(pageNumber, maxWidth);
|
|
250
261
|
const imagePath = join(dirname(path), `${stem}-page-${pageNumber}.png`);
|
|
251
|
-
await writeFile(imagePath, data);
|
|
252
262
|
output.push({
|
|
253
263
|
page: pageNumber,
|
|
254
264
|
path: imagePath,
|
|
@@ -264,9 +274,10 @@ async function renderPdfPagesDirect(path, { pages = null, maxWidth = 1400, maxim
|
|
|
264
274
|
for (let offset = 0; offset < selected.length; offset += pagesPerSheet) {
|
|
265
275
|
const group = selected.slice(offset, offset + pagesPerSheet);
|
|
266
276
|
const imagePath = join(dirname(path), `${stem}-pages-${group[0]}-${group.at(-1)}.png`);
|
|
267
|
-
output.push(await
|
|
277
|
+
output.push(await contactSheet(group, width, imagePath, renderPage));
|
|
268
278
|
}
|
|
269
279
|
}
|
|
280
|
+
if (writeImages) await writePageImages(output);
|
|
270
281
|
const reviewedPages = output.flatMap((image) => image.pages || [image.page]);
|
|
271
282
|
const reviewedPageSet = new Set(reviewedPages);
|
|
272
283
|
return {
|
|
@@ -293,7 +304,9 @@ export async function renderPdfPages(path, options = {}) {
|
|
|
293
304
|
const signal = options.signal || null;
|
|
294
305
|
if (signal?.aborted) throw new Error('PDF rendering was cancelled');
|
|
295
306
|
if (!isMainThread) return await renderPdfPagesDirect(path, options);
|
|
296
|
-
|
|
307
|
+
// The worker rasterizes; this thread writes the images, where the call's
|
|
308
|
+
// write ledger can see each one before it lands.
|
|
309
|
+
const rendered = await runPdfRenderWorker(
|
|
297
310
|
{
|
|
298
311
|
kind: PDF_RENDER_WORKER_KIND,
|
|
299
312
|
path,
|
|
@@ -301,10 +314,13 @@ export async function renderPdfPages(path, options = {}) {
|
|
|
301
314
|
pages: options.pages ?? null,
|
|
302
315
|
maxWidth: options.maxWidth ?? 1400,
|
|
303
316
|
maximumScale: options.maximumScale ?? 2,
|
|
317
|
+
writeImages: false,
|
|
304
318
|
},
|
|
305
319
|
},
|
|
306
320
|
signal
|
|
307
321
|
);
|
|
322
|
+
await writePageImages(rendered.images);
|
|
323
|
+
return rendered;
|
|
308
324
|
}
|
|
309
325
|
|
|
310
326
|
if (!isMainThread && [PDF_RENDER_WORKER_KIND, PDF_RENDER_PAGE_WORKER_KIND].includes(workerData?.kind)) {
|
|
@@ -2,6 +2,7 @@ import { spawn } from 'node:child_process';
|
|
|
2
2
|
import { randomUUID } from 'node:crypto';
|
|
3
3
|
import { writeFile, rm, rename } from 'node:fs/promises';
|
|
4
4
|
import { dirname, join } from 'node:path';
|
|
5
|
+
import { noteOfficeWrite } from '../core/office-write-ledger.mjs';
|
|
5
6
|
|
|
6
7
|
// qpdf on PATH, or wherever MIXDOG_QPDF_PATH points (the Windows installer
|
|
7
8
|
// does not add itself to PATH).
|
|
@@ -89,6 +90,7 @@ export async function securePdf({ input, output, mode, password = '', ownerPassw
|
|
|
89
90
|
? ['--encrypt', password, ownerPassword || password, '256', '--', input, target]
|
|
90
91
|
: [`--password=${password}`, '--decrypt', input, target];
|
|
91
92
|
await writeFile(responsePath, `${args.join('\n')}\n`, { encoding: 'utf8', mode: 0o600 });
|
|
93
|
+
noteOfficeWrite(output);
|
|
92
94
|
try {
|
|
93
95
|
await run(QPDF, [`@${responsePath}`]);
|
|
94
96
|
if (samePath) await rename(target, output);
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { posix } from 'node:path';
|
|
2
2
|
import { createHash } from 'node:crypto';
|
|
3
3
|
import { readFile, writeFile } from 'node:fs/promises';
|
|
4
|
+
import { noteOfficeWrite } from '../core/office-write-ledger.mjs';
|
|
4
5
|
import { koreanParticleReplacements } from '../shared/korean-particles.mjs';
|
|
5
6
|
import { presetLabels } from '../shared/labels.mjs';
|
|
6
7
|
import {
|
|
@@ -137,7 +138,9 @@ export async function packageBuffer(zip) {
|
|
|
137
138
|
}
|
|
138
139
|
|
|
139
140
|
export async function savePackage(zip, path) {
|
|
140
|
-
|
|
141
|
+
const bytes = await packageBuffer(zip);
|
|
142
|
+
noteOfficeWrite(path);
|
|
143
|
+
await writeFile(path, bytes);
|
|
141
144
|
}
|
|
142
145
|
|
|
143
146
|
export function relationshipMap(xml) {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { writeFile } from 'node:fs/promises';
|
|
2
2
|
import { dirname, extname, basename, join } from 'node:path';
|
|
3
3
|
import { createCanvas, loadImage } from '@napi-rs/canvas';
|
|
4
|
+
import { noteOfficeWrite } from '../core/office-write-ledger.mjs';
|
|
4
5
|
|
|
5
6
|
function imageByPage(images) {
|
|
6
7
|
return new Map((images || []).map((image) => [Number(image.page), image]));
|
|
@@ -61,6 +62,7 @@ export async function compareRenderedPages(beforeImages, afterImages, outputPdf)
|
|
|
61
62
|
diffContext.putImageData(diff, 0, 0);
|
|
62
63
|
const data = diffCanvas.toBuffer('image/png');
|
|
63
64
|
const path = join(dirname(outputPdf), `${stem}-visual-diff-page-${page}.png`);
|
|
65
|
+
noteOfficeWrite(path);
|
|
64
66
|
await writeFile(path, data);
|
|
65
67
|
const total = width * height;
|
|
66
68
|
metrics.push({
|
|
@@ -156,6 +156,19 @@ export function readBoundedFile(
|
|
|
156
156
|
}
|
|
157
157
|
}
|
|
158
158
|
|
|
159
|
+
/**
|
|
160
|
+
* A file's state for the turn review of a tool write: null when absent, its
|
|
161
|
+
* bytes (binary included) up to PATCH_EDIT_MAX_BYTES and within `budget`,
|
|
162
|
+
* otherwise an omitted marker — counted as changed, never diffed or restored.
|
|
163
|
+
* Throws ENOTREGULAR for anything but a regular file.
|
|
164
|
+
*/
|
|
165
|
+
export function reviewFileContent(path, { budget = null } = {}) {
|
|
166
|
+
const read = readBoundedFile(path, { maxBytes: PATCH_EDIT_MAX_BYTES, budget, detectBinary: false });
|
|
167
|
+
if (read.state === 'absent') return null;
|
|
168
|
+
if (read.state === 'omitted') return { omitted: true, size: read.size, reason: read.reason };
|
|
169
|
+
return read.content;
|
|
170
|
+
}
|
|
171
|
+
|
|
159
172
|
/** Human phrase for an omitted read: "binary (N bytes)", "larger than 8 MiB (N bytes)". */
|
|
160
173
|
export function describeOmittedFile({ reason, size, limit } = {}) {
|
|
161
174
|
const bytes = `${Number(size) || 0} bytes`;
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
// In-process line diffs for the turn-change paths, bounded the way codex
|
|
2
|
+
// bounds its own: every file gets a short timeout, every operation (one tool
|
|
3
|
+
// call, one review or seal pass) a total budget. A file over the size limits,
|
|
4
|
+
// past its timeout or past the spent budget gets one replacement hunk between
|
|
5
|
+
// its common line prefix and suffix instead — still an exact patch that
|
|
6
|
+
// applies, only not a minimal one. Results are cached by content, so the same
|
|
7
|
+
// pair is not diffed again across calls and reviews.
|
|
8
|
+
import { createHash } from 'node:crypto';
|
|
9
|
+
import { performance } from 'node:perf_hooks';
|
|
10
|
+
import { formatPatch, structuredPatch } from 'diff';
|
|
11
|
+
|
|
12
|
+
/** Longest a single file's Myers diff may run (codex's per-file value). */
|
|
13
|
+
export const LINE_DIFF_FILE_TIMEOUT_MS = 100;
|
|
14
|
+
/** Total Myers diff time one operation may spend across its files. */
|
|
15
|
+
// Kept well under the lag target: an operation also renders, hashes and
|
|
16
|
+
// bounds its patches outside the diff itself.
|
|
17
|
+
export const LINE_DIFF_OPERATION_BUDGET_MS = 150;
|
|
18
|
+
// A side over either limit is never Myers-diffed.
|
|
19
|
+
const LINE_DIFF_MAX_CHARS = 1024 * 1024;
|
|
20
|
+
const LINE_DIFF_MAX_LINES = 20_000;
|
|
21
|
+
const CONTEXT_LINES = 3;
|
|
22
|
+
const CACHE_MAX_ENTRIES = 512;
|
|
23
|
+
// Cached hunks reference their texts: bound the cache by their length.
|
|
24
|
+
const CACHE_MAX_CHARS = 32 * 1024 * 1024;
|
|
25
|
+
|
|
26
|
+
/** One operation's diff budget; pass it to every lineDiffHunks call of it. */
|
|
27
|
+
export function createLineDiffBudget(totalMs = LINE_DIFF_OPERATION_BUDGET_MS) {
|
|
28
|
+
return { remainingMs: totalMs };
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const _cache = new Map();
|
|
32
|
+
let _cachedChars = 0;
|
|
33
|
+
const _hashByBytes = new WeakMap();
|
|
34
|
+
|
|
35
|
+
const sha1 = (value) => createHash('sha1').update(value).digest('hex');
|
|
36
|
+
|
|
37
|
+
/** Content hash of one side: a byte array is hashed once per object. A text
|
|
38
|
+
* hashes to the same id as its UTF-8 bytes; one that has no exact UTF-8 form
|
|
39
|
+
* (a lone surrogate) gets no id. */
|
|
40
|
+
function sideHash(source, text) {
|
|
41
|
+
if (source && typeof source === 'object') {
|
|
42
|
+
let hash = _hashByBytes.get(source);
|
|
43
|
+
if (!hash) {
|
|
44
|
+
hash = sha1(source);
|
|
45
|
+
_hashByBytes.set(source, hash);
|
|
46
|
+
}
|
|
47
|
+
return hash;
|
|
48
|
+
}
|
|
49
|
+
return text.isWellFormed() ? sha1(text) : null;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function cacheKey(sources, oldText, newText) {
|
|
53
|
+
const before = sideHash(sources[0], oldText);
|
|
54
|
+
const after = before && sideHash(sources[1], newText);
|
|
55
|
+
return after ? `${before}:${after}` : null;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function cacheGet(key) {
|
|
59
|
+
const entry = _cache.get(key);
|
|
60
|
+
if (!entry) return null;
|
|
61
|
+
_cache.delete(key);
|
|
62
|
+
_cache.set(key, entry);
|
|
63
|
+
return entry.result;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function cacheSet(key, result, chars) {
|
|
67
|
+
if (chars > CACHE_MAX_CHARS) return;
|
|
68
|
+
_cachedChars -= _cache.get(key)?.chars || 0;
|
|
69
|
+
_cache.delete(key);
|
|
70
|
+
_cache.set(key, { result, chars });
|
|
71
|
+
_cachedChars += chars;
|
|
72
|
+
while (_cache.size > CACHE_MAX_ENTRIES || _cachedChars > CACHE_MAX_CHARS) {
|
|
73
|
+
const [oldest, entry] = _cache.entries().next().value;
|
|
74
|
+
_cache.delete(oldest);
|
|
75
|
+
_cachedChars -= entry.chars;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function overLimits(text) {
|
|
80
|
+
if (text.length > LINE_DIFF_MAX_CHARS) return true;
|
|
81
|
+
let lines = 0;
|
|
82
|
+
for (let index = text.indexOf('\n'); index >= 0; index = text.indexOf('\n', index + 1)) {
|
|
83
|
+
lines += 1;
|
|
84
|
+
if (lines > LINE_DIFF_MAX_LINES) return true;
|
|
85
|
+
}
|
|
86
|
+
return false;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/** Lines with their `\n` (the last one may lack it), as jsdiff tokenizes. */
|
|
90
|
+
function lineTokens(text) {
|
|
91
|
+
if (!text) return [];
|
|
92
|
+
const lines = text.split('\n');
|
|
93
|
+
const tokens = lines.map((line, index) => (index < lines.length - 1 ? `${line}\n` : line));
|
|
94
|
+
if (tokens.at(-1) === '') tokens.pop();
|
|
95
|
+
return tokens;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/** The one hunk replacing everything between the texts' common line prefix
|
|
99
|
+
* and suffix, with up to three context lines each side, in jsdiff's hunk
|
|
100
|
+
* shape (`` included). */
|
|
101
|
+
function replacementHunks(oldText, newText) {
|
|
102
|
+
const before = lineTokens(oldText);
|
|
103
|
+
const after = lineTokens(newText);
|
|
104
|
+
const shortest = Math.min(before.length, after.length);
|
|
105
|
+
let start = 0;
|
|
106
|
+
while (start < shortest && before[start] === after[start]) start += 1;
|
|
107
|
+
let end = 0;
|
|
108
|
+
while (end < shortest - start && before[before.length - 1 - end] === after[after.length - 1 - end]) end += 1;
|
|
109
|
+
const lead = Math.min(CONTEXT_LINES, start);
|
|
110
|
+
const trail = Math.min(CONTEXT_LINES, end);
|
|
111
|
+
const lines = [];
|
|
112
|
+
const push = (mark, tokens) => {
|
|
113
|
+
for (const token of tokens) {
|
|
114
|
+
if (token.endsWith('\n')) lines.push(mark + token.slice(0, -1));
|
|
115
|
+
else lines.push(mark + token, '\');
|
|
116
|
+
}
|
|
117
|
+
};
|
|
118
|
+
push(' ', before.slice(start - lead, start));
|
|
119
|
+
push('-', before.slice(start, before.length - end));
|
|
120
|
+
push('+', after.slice(start, after.length - end));
|
|
121
|
+
push(' ', before.slice(before.length - end, before.length - end + trail));
|
|
122
|
+
const first = start - lead;
|
|
123
|
+
return [
|
|
124
|
+
{
|
|
125
|
+
oldStart: first + 1,
|
|
126
|
+
oldLines: before.length - end + trail - first,
|
|
127
|
+
newStart: first + 1,
|
|
128
|
+
newLines: after.length - end + trail - first,
|
|
129
|
+
lines,
|
|
130
|
+
},
|
|
131
|
+
];
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Hunks (context 3, jsdiff's structuredPatch shape) turning `oldText` into
|
|
136
|
+
* `newText`: jsdiff's own when its diff finishes within the file timeout and
|
|
137
|
+
* what is left of `budget` (`exact`), else the replacement hunk. `sources`
|
|
138
|
+
* are the raw sides the texts came from (bytes or the texts), used only as
|
|
139
|
+
* the cache key. The returned value is shared: never mutate it.
|
|
140
|
+
*/
|
|
141
|
+
export function lineDiffHunks(oldText, newText, budget, sources = [oldText, newText]) {
|
|
142
|
+
if (oldText === newText) return { hunks: [], exact: true };
|
|
143
|
+
const key = cacheKey(sources, oldText, newText);
|
|
144
|
+
const cached = key ? cacheGet(key) || budget.fallbacks?.get(key) : null;
|
|
145
|
+
if (cached) return cached;
|
|
146
|
+
let result = null;
|
|
147
|
+
// A fallback forced by the operation's spent budget is not this pair's own
|
|
148
|
+
// outcome: it is not cached, so a later operation can still diff it.
|
|
149
|
+
let repeatable = true;
|
|
150
|
+
if (!overLimits(oldText) && !overLimits(newText)) {
|
|
151
|
+
if (budget.remainingMs > 0) {
|
|
152
|
+
const timeout = Math.min(LINE_DIFF_FILE_TIMEOUT_MS, budget.remainingMs);
|
|
153
|
+
const startedAt = performance.now();
|
|
154
|
+
const patch = structuredPatch('', '', oldText, newText, '', '', { context: CONTEXT_LINES, timeout });
|
|
155
|
+
budget.remainingMs -= performance.now() - startedAt;
|
|
156
|
+
if (patch) result = { hunks: patch.hunks, exact: true };
|
|
157
|
+
else repeatable = timeout >= LINE_DIFF_FILE_TIMEOUT_MS;
|
|
158
|
+
} else {
|
|
159
|
+
repeatable = false;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
result ||= { hunks: replacementHunks(oldText, newText), exact: false };
|
|
163
|
+
if (key && repeatable) cacheSet(key, result, oldText.length + newText.length);
|
|
164
|
+
// Within its own operation it is final: the budget never grows back.
|
|
165
|
+
else if (key) (budget.fallbacks ||= new Map()).set(key, result);
|
|
166
|
+
return result;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/** The `--- `/`+++ ` headers and hunks of `hunks`, as jsdiff formats them,
|
|
170
|
+
* ending in exactly one newline. */
|
|
171
|
+
export function unifiedHunksBody(oldHeader, newHeader, hunks) {
|
|
172
|
+
const generated = formatPatch({ oldFileName: oldHeader, newFileName: newHeader, oldHeader: '', newHeader: '', hunks });
|
|
173
|
+
let end = generated.length;
|
|
174
|
+
// Linear trim: a /\n*$/ regex rescans from every position of a large patch.
|
|
175
|
+
while (end > 0 && generated.charCodeAt(end - 1) === 10) end -= 1;
|
|
176
|
+
return `${generated.slice(generated.search(/^--- /m), end)}\n`;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
export function _resetLineDiffCacheForTest() {
|
|
180
|
+
_cache.clear();
|
|
181
|
+
_cachedChars = 0;
|
|
182
|
+
}
|
|
@@ -175,6 +175,24 @@ export function appendBuffered(path, text) {
|
|
|
175
175
|
}
|
|
176
176
|
}
|
|
177
177
|
|
|
178
|
+
/**
|
|
179
|
+
* Drop every not-yet-written chunk for `path` (its file is being deleted, so a
|
|
180
|
+
* later flush must not recreate it). A write already handed to the OS cannot
|
|
181
|
+
* be recalled; callers unlink after this and it lands before or is removed.
|
|
182
|
+
*/
|
|
183
|
+
export function discardBuffered(path) {
|
|
184
|
+
const q = queues.get(path);
|
|
185
|
+
if (!q) return;
|
|
186
|
+
if (q.timer) {
|
|
187
|
+
clearTimeout(q.timer);
|
|
188
|
+
q.timer = null;
|
|
189
|
+
}
|
|
190
|
+
q.chunks = [];
|
|
191
|
+
q.bytes = 0;
|
|
192
|
+
q.failCount = 0;
|
|
193
|
+
deleteIfIdle(path, q);
|
|
194
|
+
}
|
|
195
|
+
|
|
178
196
|
/** Observable overflow counters for buffered append queues. */
|
|
179
197
|
export function getBufferedAppenderStats(path) {
|
|
180
198
|
const q = queues.get(path);
|
|
@@ -18,8 +18,10 @@ import {
|
|
|
18
18
|
backupUserData,
|
|
19
19
|
backupUserDataAsync,
|
|
20
20
|
markUserDataInitialized,
|
|
21
|
+
ensureUserDataInitialized,
|
|
21
22
|
loadLatestMixdogConfigFromBackup,
|
|
22
|
-
|
|
23
|
+
isUserDataRecoveryExpected,
|
|
24
|
+
findLegacyUserDataEvidence,
|
|
23
25
|
} from './user-data-guard.mjs';
|
|
24
26
|
import { AGENT_PROVIDER_ENV, AGENT_PROVIDER_ENV_ALIASES, getAgentApiKey } from './provider-api-key.mjs';
|
|
25
27
|
|
|
@@ -236,7 +238,7 @@ function readConfigFile() {
|
|
|
236
238
|
}
|
|
237
239
|
}
|
|
238
240
|
|
|
239
|
-
function writeConfigFile(data) {
|
|
241
|
+
function writeConfigFile(data, postBackupReason = 'post-config-write') {
|
|
240
242
|
// R4 data-at-rest: mixdog-config.json holds provider apiKeys; clamp to
|
|
241
243
|
// owner-only on POSIX via 0o600/0o700 mode bits, AND fail-closed on
|
|
242
244
|
// Windows where those bits are advisory — `secret: true` makes the
|
|
@@ -261,29 +263,76 @@ function writeConfigFile(data) {
|
|
|
261
263
|
markUserDataInitialized(resolvePluginData());
|
|
262
264
|
} catch {}
|
|
263
265
|
try {
|
|
264
|
-
backupUserData(resolvePluginData(),
|
|
266
|
+
backupUserData(resolvePluginData(), postBackupReason);
|
|
265
267
|
} catch {}
|
|
266
268
|
}
|
|
267
269
|
|
|
270
|
+
// Config paths whose restore could not be persisted: the in-memory restore is
|
|
271
|
+
// still served, but the failure is logged (and the write retried) only once
|
|
272
|
+
// per process instead of on every read.
|
|
273
|
+
const _restorePersistFailed = new Set();
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* Write a config restored on a plain read back to disk through the normal save
|
|
277
|
+
* path (atomic write + backups), so later reads hit the file instead of
|
|
278
|
+
* restoring again. Try-once lock: a contended lock means a writer (possibly
|
|
279
|
+
* this process's own outer RMW) is about to persist, so the next read retries.
|
|
280
|
+
*/
|
|
281
|
+
function persistRestoredConfig(restored) {
|
|
282
|
+
const path = configPath();
|
|
283
|
+
if (_restorePersistFailed.has(path)) return;
|
|
284
|
+
try {
|
|
285
|
+
const wrote = withFileLockSync(
|
|
286
|
+
`${path}.lock`,
|
|
287
|
+
() => {
|
|
288
|
+
if (existsSync(path)) return false;
|
|
289
|
+
writeAll(restored, 'post-restore');
|
|
290
|
+
return true;
|
|
291
|
+
},
|
|
292
|
+
{ timeoutMs: 0 }
|
|
293
|
+
);
|
|
294
|
+
if (wrote) {
|
|
295
|
+
process.stderr.write(
|
|
296
|
+
'[config] read: restored mixdog-config.json from latest user-data backup (missing after init) and saved it\n'
|
|
297
|
+
);
|
|
298
|
+
}
|
|
299
|
+
} catch (err) {
|
|
300
|
+
if (err?.code === 'ELOCKCONTENDED') return;
|
|
301
|
+
_restorePersistFailed.add(path);
|
|
302
|
+
process.stderr.write(
|
|
303
|
+
`[config] read: restored mixdog-config.json from latest user-data backup (missing after init); saving it failed, serving it from memory: ${err?.message || err}\n`
|
|
304
|
+
);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
268
308
|
function readAll() {
|
|
269
309
|
const parsed = readConfigFile();
|
|
270
|
-
if (parsed != null)
|
|
310
|
+
if (parsed != null) {
|
|
311
|
+
// A data dir from before the in-dir init marker gets one (plus a first
|
|
312
|
+
// scoped snapshot) once per process, so its config stays recoverable.
|
|
313
|
+
try {
|
|
314
|
+
ensureUserDataInitialized(resolvePluginData());
|
|
315
|
+
} catch {}
|
|
316
|
+
return canonicalizeUnifiedConfig(parsed);
|
|
317
|
+
}
|
|
271
318
|
// Symmetric with readAllForRmW(): a missing/unreadable config on a data
|
|
272
319
|
// dir that was previously initialized means the file was LOST (deleted,
|
|
273
|
-
// failed write), not a fresh install.
|
|
320
|
+
// failed write), not a fresh install. A recreated data dir has no marker
|
|
321
|
+
// and starts fresh. Self-heal from this data dir's newest
|
|
274
322
|
// structurally-complete backup instead of silently collapsing to {} —
|
|
275
323
|
// which callers (readSection/getCapabilities) merge with in-memory
|
|
276
|
-
// defaults, dropping the user's presets/agent routes/sections.
|
|
277
|
-
//
|
|
278
|
-
//
|
|
279
|
-
//
|
|
280
|
-
|
|
324
|
+
// defaults, dropping the user's presets/agent routes/sections. The restore
|
|
325
|
+
// is persisted under the config lock (only if the file is still missing) so
|
|
326
|
+
// later reads hit the file instead of restoring and logging again.
|
|
327
|
+
// An upgraded data dir (legacy marker / matching legacy backup, no in-dir
|
|
328
|
+
// marker yet) is a recovery case too; without a verifiable backup reads stay
|
|
329
|
+
// empty and RMW writes refuse (see readAllForRmW).
|
|
330
|
+
if (isUserDataRecoveryExpected(resolvePluginData())) {
|
|
281
331
|
const restored = loadLatestMixdogConfigFromBackup(resolvePluginData());
|
|
282
332
|
if (restored && isPlainObject(restored)) {
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
return canonicalizeUnifiedConfig(restored);
|
|
333
|
+
const config = canonicalizeUnifiedConfig(restored);
|
|
334
|
+
persistRestoredConfig(config);
|
|
335
|
+
return config;
|
|
287
336
|
}
|
|
288
337
|
}
|
|
289
338
|
return {};
|
|
@@ -300,11 +349,22 @@ function quarantineMalformedConfig(parseErr) {
|
|
|
300
349
|
}
|
|
301
350
|
|
|
302
351
|
function restoreAllForRmWOrThrow(reason) {
|
|
303
|
-
const restored = loadLatestMixdogConfigFromBackup(resolvePluginData());
|
|
352
|
+
const restored = loadLatestMixdogConfigFromBackup(resolvePluginData(), { fresh: true });
|
|
304
353
|
if (restored && isPlainObject(restored)) {
|
|
305
354
|
process.stderr.write(`[config] RMW read: restored mixdog-config.json from latest user-data backup (${reason})\n`);
|
|
306
355
|
return restored;
|
|
307
356
|
}
|
|
357
|
+
const legacy = findLegacyUserDataEvidence(resolvePluginData(), { fresh: true });
|
|
358
|
+
if (legacy) {
|
|
359
|
+
const err = new Error(
|
|
360
|
+
`[config] mixdog-config.json is missing but this data dir (${resolvePluginData()}) was initialized by an older ` +
|
|
361
|
+
`Mixdog build and no backup verified to belong to it was found; refusing to write a fresh config over a ` +
|
|
362
|
+
`recoverable state. Restore mixdog-config.json from a backup under ${legacy.roots.join(' or ')}, or delete ` +
|
|
363
|
+
`the legacy marker(s) to start fresh: ${legacy.markers.join(', ') || '(none; matching legacy backups are incomplete)'}`
|
|
364
|
+
);
|
|
365
|
+
err.code = 'EUSERDATARECOVERY';
|
|
366
|
+
throw err;
|
|
367
|
+
}
|
|
308
368
|
throw new Error(
|
|
309
369
|
`[config] mixdog-config.json is unreadable and no valid backup was found (${reason}); refusing section write that would wipe other sections`
|
|
310
370
|
);
|
|
@@ -321,7 +381,7 @@ function readAllForRmW() {
|
|
|
321
381
|
raw = readFileSync(configPath(), 'utf8');
|
|
322
382
|
} catch (err) {
|
|
323
383
|
if (err.code === 'ENOENT') {
|
|
324
|
-
if (
|
|
384
|
+
if (isUserDataRecoveryExpected(resolvePluginData(), { fresh: true })) {
|
|
325
385
|
return restoreAllForRmWOrThrow('config file missing after user-data was initialized');
|
|
326
386
|
}
|
|
327
387
|
return {};
|
|
@@ -402,8 +462,8 @@ export function subscribeConfigChange(listener) {
|
|
|
402
462
|
};
|
|
403
463
|
}
|
|
404
464
|
|
|
405
|
-
function writeAll(data) {
|
|
406
|
-
writeConfigFile(canonicalizeUnifiedConfig(data));
|
|
465
|
+
function writeAll(data, postBackupReason) {
|
|
466
|
+
writeConfigFile(canonicalizeUnifiedConfig(data), postBackupReason);
|
|
407
467
|
notifyConfigChanged();
|
|
408
468
|
}
|
|
409
469
|
|
|
@@ -215,6 +215,12 @@ export function presentErrorText(error, options = {}) {
|
|
|
215
215
|
const name = error && typeof error === 'object' ? String(error.name || '') : '';
|
|
216
216
|
const code = error && typeof error === 'object' ? String(error.code || '') : '';
|
|
217
217
|
const status = errorHttpStatus(error);
|
|
218
|
+
// The fixed request overhead error is actionable as written (sizes,
|
|
219
|
+
// contributors, remedy): keep its lines instead of a generic "Context too
|
|
220
|
+
// large." that would suggest compacting.
|
|
221
|
+
if (code === 'TOOL_SURFACE_EXCEEDS_CONTEXT' || /\(TOOL_SURFACE_EXCEEDS_CONTEXT\)/.test(text)) {
|
|
222
|
+
return stripErrorPrefix(text);
|
|
223
|
+
}
|
|
218
224
|
const jsonError = extractJsonError(text);
|
|
219
225
|
if (jsonError) text = jsonError;
|
|
220
226
|
const embeddedError = extractEmbeddedError(text);
|
|
@@ -1,3 +1,8 @@
|
|
|
1
1
|
export function enforceOwnerOnlyAclWin32(targetPath: string, options?: { fresh?: boolean }): void;
|
|
2
2
|
|
|
3
|
-
export function enforceOwnerOnlyAclWin32Async(
|
|
3
|
+
export function enforceOwnerOnlyAclWin32Async(
|
|
4
|
+
targetPath: string,
|
|
5
|
+
options?: { fresh?: boolean }
|
|
6
|
+
): Promise<void>;
|
|
7
|
+
|
|
8
|
+
export function enforceOwnerOnlyDirectoryWin32Async(targetPath: string): Promise<void>;
|
|
@@ -73,6 +73,16 @@ function icaclsPath() {
|
|
|
73
73
|
return path;
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
+
const quoteForPowerShell = (value) => `'${String(value).replaceAll("'", "''")}'`;
|
|
77
|
+
|
|
78
|
+
function powerShellCommand(icacls, script) {
|
|
79
|
+
return {
|
|
80
|
+
executable: join(dirname(icacls), 'WindowsPowerShell', 'v1.0', 'powershell.exe'),
|
|
81
|
+
label: 'Windows ACL update',
|
|
82
|
+
args: ['-NoProfile', '-NonInteractive', '-EncodedCommand', Buffer.from(script, 'utf16le').toString('base64')],
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
|
|
76
86
|
function ownerOnlyAclCommand(targetPath, sid, fresh, icacls) {
|
|
77
87
|
if (fresh) {
|
|
78
88
|
return {
|
|
@@ -84,7 +94,7 @@ function ownerOnlyAclCommand(targetPath, sid, fresh, icacls) {
|
|
|
84
94
|
// Never /reset a published secret: that temporarily restores broad parent
|
|
85
95
|
// grants, and a later failure leaves them in place. Build the protected DACL
|
|
86
96
|
// in memory and publish it in one native ACL update instead.
|
|
87
|
-
const quote =
|
|
97
|
+
const quote = quoteForPowerShell;
|
|
88
98
|
const script = [
|
|
89
99
|
"$ErrorActionPreference = 'Stop';",
|
|
90
100
|
`$path = ${quote(targetPath)};`,
|
|
@@ -100,11 +110,37 @@ function ownerOnlyAclCommand(targetPath, sid, fresh, icacls) {
|
|
|
100
110
|
'if ($directory) { [System.IO.Directory]::SetAccessControl($path, $acl) }',
|
|
101
111
|
'else { [System.IO.File]::SetAccessControl($path, $acl) };',
|
|
102
112
|
].join(' ');
|
|
103
|
-
return
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
113
|
+
return powerShellCommand(icacls, script);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Replaces a directory's whole DACL (explicit grants to anyone else included)
|
|
117
|
+
// with one protected, inheritable owner-only grant, in one native update, so
|
|
118
|
+
// files created inside need no ACL call of their own. Files already inside
|
|
119
|
+
// that carry an explicit grant or any other principal's grant are reset to
|
|
120
|
+
// inherit only that owner grant.
|
|
121
|
+
function ownerOnlyDirectoryCommand(targetPath, sid, icacls) {
|
|
122
|
+
const script = [
|
|
123
|
+
"$ErrorActionPreference = 'Stop';",
|
|
124
|
+
`$path = ${quoteForPowerShell(targetPath)};`,
|
|
125
|
+
`$sid = [System.Security.Principal.SecurityIdentifier]::new(${quoteForPowerShell(sid)});`,
|
|
126
|
+
'$acl = [System.Security.AccessControl.DirectorySecurity]::new();',
|
|
127
|
+
'$acl.SetAccessRuleProtection($true, $false);',
|
|
128
|
+
'$acl.AddAccessRule([System.Security.AccessControl.FileSystemAccessRule]::new(',
|
|
129
|
+
'$sid, [System.Security.AccessControl.FileSystemRights]::FullControl,',
|
|
130
|
+
"[System.Security.AccessControl.InheritanceFlags]'ContainerInherit, ObjectInherit',",
|
|
131
|
+
'[System.Security.AccessControl.PropagationFlags]::None,',
|
|
132
|
+
'[System.Security.AccessControl.AccessControlType]::Allow));',
|
|
133
|
+
'[System.IO.Directory]::SetAccessControl($path, $acl);',
|
|
134
|
+
'foreach ($file in [System.IO.Directory]::GetFiles($path)) {',
|
|
135
|
+
'$current = [System.IO.File]::GetAccessControl($file);',
|
|
136
|
+
'$rules = @($current.GetAccessRules($true, $true, [System.Security.Principal.SecurityIdentifier]));',
|
|
137
|
+
'$foreign = @($rules | Where-Object { -not $_.IsInherited -or $_.IdentityReference -ne $sid });',
|
|
138
|
+
'if ($current.AreAccessRulesProtected -or $foreign.Count -gt 0) {',
|
|
139
|
+
'$reset = [System.Security.AccessControl.FileSecurity]::new();',
|
|
140
|
+
'$reset.SetAccessRuleProtection($false, $false);',
|
|
141
|
+
'[System.IO.File]::SetAccessControl($file, $reset) } };',
|
|
142
|
+
].join(' ');
|
|
143
|
+
return powerShellCommand(icacls, script);
|
|
108
144
|
}
|
|
109
145
|
|
|
110
146
|
function aclFailure(targetPath, cause, label = 'icacls') {
|
|
@@ -143,3 +179,15 @@ export async function enforceOwnerOnlyAclWin32Async(targetPath, { fresh = false
|
|
|
143
179
|
throw aclFailure(targetPath, error, command.label);
|
|
144
180
|
}
|
|
145
181
|
}
|
|
182
|
+
|
|
183
|
+
export async function enforceOwnerOnlyDirectoryWin32Async(targetPath) {
|
|
184
|
+
if (process.platform !== 'win32') return;
|
|
185
|
+
const icacls = icaclsPath();
|
|
186
|
+
if (cachedUserSid === null) cachedUserSid = await resolveCurrentUserPrincipalAsync();
|
|
187
|
+
const command = ownerOnlyDirectoryCommand(targetPath, cachedUserSid, icacls);
|
|
188
|
+
try {
|
|
189
|
+
await execFileAsync(command.executable, command.args, { windowsHide: true, timeout: 15_000 });
|
|
190
|
+
} catch (error) {
|
|
191
|
+
throw aclFailure(targetPath, error, command.label);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
@@ -291,6 +291,12 @@ export function applyAnthropicEffortToBody(
|
|
|
291
291
|
) {
|
|
292
292
|
if (!body || typeof body !== 'object') return;
|
|
293
293
|
|
|
294
|
+
// A gateway that declares its own effort contract (DeepSeek ignores
|
|
295
|
+
// budget_tokens) projects effort itself; returning true skips the Claude rules.
|
|
296
|
+
if (typeof opts.messagesEffortProjection === 'function') {
|
|
297
|
+
if (opts.messagesEffortProjection(body, { model, effort: opts.effort }) === true) return;
|
|
298
|
+
}
|
|
299
|
+
|
|
294
300
|
const thinkingBudgetTokens = Number(opts.thinkingBudgetTokens);
|
|
295
301
|
if (Number.isFinite(thinkingBudgetTokens) && thinkingBudgetTokens > 0) {
|
|
296
302
|
assertAnthropicManualBudgetSupported(model);
|