mixdog 0.9.62 → 0.9.64
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 +3 -0
- package/package.json +9 -2
- package/scripts/code-graph-description-contract.mjs +3 -3
- package/scripts/verify-release-assets.mjs +2 -2
- package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch.mjs +11 -14
- package/src/runtime/agent/orchestrator/agent-runtime/agent-progress-watchdog.mjs +6 -6
- package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +2 -2
- package/src/runtime/agent/orchestrator/agent-trace-format.mjs +1 -0
- package/src/runtime/agent/orchestrator/config.mjs +19 -124
- package/src/runtime/agent/orchestrator/context/collect.mjs +6 -6
- package/src/runtime/agent/orchestrator/dispatch-persist.mjs +2 -2
- package/src/runtime/agent/orchestrator/internal-agents.mjs +2 -2
- package/src/runtime/agent/orchestrator/mcp/child-tree.mjs +1 -1
- package/src/runtime/agent/orchestrator/mcp/client.mjs +4 -4
- package/src/runtime/agent/orchestrator/providers/admission-scheduler.mjs +2 -2
- package/src/runtime/agent/orchestrator/providers/anthropic-betas.mjs +4 -4
- package/src/runtime/agent/orchestrator/providers/anthropic-effort.mjs +2 -2
- package/src/runtime/agent/orchestrator/providers/anthropic-max-tokens.mjs +4 -4
- package/src/runtime/agent/orchestrator/providers/anthropic-messages.mjs +333 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-model-resolve.mjs +2 -2
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-credentials.mjs +4 -4
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/anthropic.mjs +3 -277
- package/src/runtime/agent/orchestrator/providers/codex-client-meta.mjs +2 -2
- package/src/runtime/agent/orchestrator/providers/custom-tool-wire.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/gemini-cache.mjs +2 -2
- package/src/runtime/agent/orchestrator/providers/gemini-stream.mjs +6 -6
- package/src/runtime/agent/orchestrator/providers/grok-oauth-login.mjs +196 -0
- package/src/runtime/agent/orchestrator/providers/grok-oauth-tokens.mjs +460 -0
- package/src/runtime/agent/orchestrator/providers/grok-oauth.mjs +14 -593
- package/src/runtime/agent/orchestrator/providers/lib/anthropic-request-utils.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/media-normalization.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/openai-compat-wire.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/openai-compat-xai.mjs +4 -4
- package/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs +2 -2
- package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +5 -280
- package/src/runtime/agent/orchestrator/providers/openai-responses-payload.mjs +357 -0
- package/src/runtime/agent/orchestrator/providers/openai-ws-headers.mjs +216 -0
- package/src/runtime/agent/orchestrator/providers/openai-ws-pool.mjs +2 -188
- package/src/runtime/agent/orchestrator/providers/openai-ws-stream.mjs +2 -2
- package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +2 -2
- package/src/runtime/agent/orchestrator/providers/opencode-go.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/provider-catalog-cache.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/registry.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/retry-classifier.mjs +2 -2
- package/src/runtime/agent/orchestrator/providers/statusline-route-meta.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/cache/prefetch-cache.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/cache/scoped-cache.mjs +3 -3
- package/src/runtime/agent/orchestrator/session/compact/budget.mjs +2 -2
- package/src/runtime/agent/orchestrator/session/compact/file-reattach.mjs +2 -2
- package/src/runtime/agent/orchestrator/session/compact/summary-schema.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/compact/summary.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/compact/text-utils.mjs +2 -2
- package/src/runtime/agent/orchestrator/session/context-utils.mjs +4 -4
- package/src/runtime/agent/orchestrator/session/loop/deferred-call-through.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +17 -0
- package/src/runtime/agent/orchestrator/session/loop/steering.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/loop/stored-tool-args.mjs +18 -26
- package/src/runtime/agent/orchestrator/session/loop/termination.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/loop/tool-classify.mjs +3 -3
- package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +1 -0
- package/src/runtime/agent/orchestrator/session/manager/context-meta.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/manager/delivered-completions.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/manager/status-telemetry.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/store/live-state.mjs +49 -0
- package/src/runtime/agent/orchestrator/session/store/load-cache.mjs +49 -0
- package/src/runtime/agent/orchestrator/session/store/save-worker.mjs +260 -0
- package/src/runtime/agent/orchestrator/session/store/serialize.mjs +72 -0
- package/src/runtime/agent/orchestrator/session/store/summary-cache.mjs +163 -0
- package/src/runtime/agent/orchestrator/session/store/write-guards.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +4 -1
- package/src/runtime/agent/orchestrator/session/store.mjs +9 -561
- package/src/runtime/agent/orchestrator/session/tool-envelope.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/tool-result-offload.mjs +2 -2
- package/src/runtime/agent/orchestrator/stall-policy.mjs +11 -11
- package/src/runtime/agent/orchestrator/tools/bash-policy-scan.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/bash-session.mjs +2 -2
- package/src/runtime/agent/orchestrator/tools/builtin/advisory-lock.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +2 -2
- package/src/runtime/agent/orchestrator/tools/builtin/atomic-write.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +2 -154
- package/src/runtime/agent/orchestrator/tools/builtin/cache-layers.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/builtin/cwd-utils.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/builtin/fs-reachability.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/builtin/fuzzy-match.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/builtin/glob-walk.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/builtin/path-locks.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/builtin/path-utils.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/builtin/read-args.mjs +2 -2
- package/src/runtime/agent/orchestrator/tools/builtin/read-formatting.mjs +2 -3
- package/src/runtime/agent/orchestrator/tools/builtin/read-image-resize.mjs +4 -4
- package/src/runtime/agent/orchestrator/tools/builtin/read-open.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/builtin/read-streaming.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/builtin/rg-runner.mjs +2 -2
- package/src/runtime/agent/orchestrator/tools/builtin/search-path-diagnostics.mjs +5 -5
- package/src/runtime/agent/orchestrator/tools/builtin/shell-analysis.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/builtin/shell-job-paths.mjs +2 -2
- package/src/runtime/agent/orchestrator/tools/builtin/shell-job-process.mjs +2 -2
- package/src/runtime/agent/orchestrator/tools/builtin/shell-job-spawn.mjs +519 -0
- package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +5 -449
- package/src/runtime/agent/orchestrator/tools/builtin/snapshot-helpers.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/builtin/task-tool.mjs +212 -0
- package/src/runtime/agent/orchestrator/tools/builtin.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/code-graph/build.mjs +1 -7
- package/src/runtime/agent/orchestrator/tools/code-graph/disk-cache.mjs +9 -36
- package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.mjs +15 -3
- package/src/runtime/agent/orchestrator/tools/code-graph/search-references.mjs +429 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/search.mjs +2 -395
- package/src/runtime/agent/orchestrator/tools/code-graph/symbol-index.mjs +2 -2
- package/src/runtime/agent/orchestrator/tools/code-graph/text-mask.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/code-graph/trusted-roots.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/code-graph-prewarm-worker.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/code-graph-state.mjs +3 -3
- package/src/runtime/agent/orchestrator/tools/mutation-planner.mjs +5 -5
- package/src/runtime/agent/orchestrator/tools/next-call-utils.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/patch/dispatch.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/patch/matcher.mjs +7 -7
- package/src/runtime/agent/orchestrator/tools/patch/orchestrator.mjs +72 -9
- package/src/runtime/agent/orchestrator/tools/patch/parsing.mjs +4 -4
- package/src/runtime/agent/orchestrator/tools/patch/paths.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/patch/v4a-convert.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/patch-tool-defs.mjs +4 -4
- package/src/runtime/agent/orchestrator/tools/patch.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/result-compression.mjs +3 -3
- package/src/runtime/agent/orchestrator/tools/shell-command.mjs +2 -444
- package/src/runtime/agent/orchestrator/tools/shell-exec-output.mjs +505 -0
- package/src/runtime/agent/orchestrator/tools/shell-exec-policy.mjs +2 -2
- package/src/runtime/agent/orchestrator/tools/shell-snapshot.mjs +1 -1
- package/src/runtime/channels/lib/backend-dispatch.mjs +20 -5
- package/src/runtime/channels/lib/scheduler.mjs +1 -1
- package/src/runtime/channels/lib/telegram-format.mjs +1 -1
- package/src/runtime/channels/lib/voice-runtime-fetcher.mjs +4 -4
- package/src/runtime/channels/tool-defs.mjs +2 -3
- package/src/runtime/memory/index.mjs +1 -5
- package/src/runtime/memory/lib/core-memory-candidates.mjs +357 -0
- package/src/runtime/memory/lib/core-memory-store.mjs +7 -357
- package/src/runtime/memory/lib/cycle-signatures.mjs +1 -1
- package/src/runtime/memory/lib/embedding-model-config.mjs +1 -1
- package/src/runtime/memory/lib/embedding-provider.mjs +1 -5
- package/src/runtime/memory/lib/embedding-worker.mjs +1 -5
- package/src/runtime/memory/lib/ko-morph.mjs +2 -2
- package/src/runtime/memory/lib/memory-config-flags.mjs +2 -5
- package/src/runtime/memory/lib/memory-cycle-requests.mjs +3 -7
- package/src/runtime/memory/lib/memory-cycle1.mjs +4 -8
- package/src/runtime/memory/lib/memory-cycle2-gate.mjs +1 -1
- package/src/runtime/memory/lib/memory-cycle2-mutations.mjs +1 -1
- package/src/runtime/memory/lib/memory-cycle2-shared.mjs +2 -5
- package/src/runtime/memory/lib/memory-cycle3.mjs +1 -5
- package/src/runtime/memory/lib/memory-embed.mjs +2 -6
- package/src/runtime/memory/lib/memory-log.mjs +8 -0
- package/src/runtime/memory/lib/memory-ops-policy.mjs +4 -8
- package/src/runtime/memory/lib/memory-recall-scope-filter.mjs +1 -1
- package/src/runtime/memory/lib/memory-recall-store.mjs +2 -193
- package/src/runtime/memory/lib/memory-score.mjs +1 -1
- package/src/runtime/memory/lib/memory-text-utils.mjs +2 -2
- package/src/runtime/memory/lib/memory.mjs +2 -6
- package/src/runtime/memory/lib/pg/adapter.mjs +2 -6
- package/src/runtime/memory/lib/pg/process.mjs +1 -5
- package/src/runtime/memory/lib/pg/supervisor.mjs +1 -5
- package/src/runtime/memory/lib/recall-format.mjs +2 -2
- package/src/runtime/memory/lib/recall-scoring.mjs +195 -0
- package/src/runtime/memory/lib/runtime-fetcher.mjs +1 -5
- package/src/runtime/memory/lib/trace-store.mjs +2 -6
- package/src/runtime/search/lib/http-fetch.mjs +6 -6
- package/src/runtime/search/lib/web-tools.mjs +2 -2
- package/src/runtime/shared/buffered-appender.mjs +1 -1
- package/src/runtime/shared/child-guardian.mjs +1 -1
- package/src/runtime/shared/child-spawn-gate.mjs +1 -1
- package/src/runtime/shared/config.mjs +4 -4
- package/src/runtime/shared/launcher-control.mjs +9 -10
- package/src/runtime/shared/llm/index.mjs +5 -20
- package/src/runtime/shared/markdown-frontmatter.mjs +1 -1
- package/src/runtime/shared/memory-snapshot.mjs +4 -4
- package/src/runtime/shared/pristine-execution.mjs +1 -1
- package/src/runtime/shared/process-lifecycle.mjs +0 -1
- package/src/runtime/shared/process-shutdown.mjs +1 -1
- package/src/runtime/shared/resource-admission.mjs +2 -2
- package/src/runtime/shared/schedule-session-run.mjs +1 -1
- package/src/runtime/shared/schedules-db.mjs +1 -1
- package/src/runtime/shared/service-discovery.mjs +2 -2
- package/src/runtime/shared/staged-update.mjs +6 -6
- package/src/runtime/shared/tool-execution-contract.mjs +1 -1
- package/src/runtime/shared/tool-primitives.mjs +1 -1
- package/src/runtime/shared/tool-surface.mjs +2 -2
- package/src/runtime/shared/update-checker.mjs +1 -1
- package/src/runtime/shared/user-cwd.mjs +3 -3
- package/src/runtime/shared/user-data-guard.mjs +4 -4
- package/src/runtime/shared/workspace-router.mjs +5 -5
- package/src/session-runtime/config-helpers.mjs +2 -3
- package/src/session-runtime/config-lifecycle.mjs +1 -1
- package/src/session-runtime/hitch-profile.mjs +1 -1
- package/src/session-runtime/lifecycle-api.mjs +6 -4
- package/src/session-runtime/model-recency.mjs +5 -5
- package/src/session-runtime/plugin-mcp.mjs +1 -1
- package/src/session-runtime/provider-request-snapshot.mjs +338 -0
- package/src/session-runtime/session-text.mjs +4 -4
- package/src/session-runtime/tool-catalog-schema.mjs +197 -0
- package/src/session-runtime/tool-catalog.mjs +6 -483
- package/src/session-runtime/tool-defs.mjs +1 -1
- package/src/standalone/agent-task-status.mjs +1 -1
- package/src/standalone/agent-tool/helpers.mjs +2 -2
- package/src/standalone/channel-admin.mjs +4 -4
- package/src/standalone/explore-tool.mjs +3 -3
- package/src/standalone/hook-bus/config.mjs +1 -1
- package/src/standalone/hook-bus/constants.mjs +1 -1
- package/src/standalone/hook-bus/rules.mjs +1 -1
- package/src/standalone/projects.mjs +1 -1
- package/src/standalone/provider-admin.mjs +2 -2
- package/src/tui/app/app-format.mjs +2 -2
- package/src/tui/app/clipboard.mjs +3 -3
- package/src/tui/app/input-parsers.mjs +1 -1
- package/src/tui/app/text-layout.mjs +1 -1
- package/src/tui/app/transcript-row-estimate.mjs +360 -0
- package/src/tui/app/transcript-window.mjs +5 -347
- package/src/tui/app/use-mouse-input.mjs +1 -1
- package/src/tui/app/use-prompt-handlers.mjs +1 -1
- package/src/tui/components/tool-execution/surface-detail.mjs +9 -9
- package/src/tui/components/tool-execution/text-format.mjs +2 -2
- package/src/tui/components/tool-output-format.mjs +7 -7
- package/src/tui/display-width.mjs +4 -4
- package/src/tui/dist/index.mjs +63 -97
- package/src/tui/engine/agent-envelope.mjs +5 -5
- package/src/tui/engine/notification-plan.mjs +1 -1
- package/src/tui/engine/queue-helpers.mjs +3 -3
- package/src/tui/engine/render-timing.mjs +4 -5
- package/src/tui/engine/session-api-ext.mjs +3 -3
- package/src/tui/engine/tool-result-status.mjs +5 -5
- package/src/tui/engine/tool-result-text.mjs +1 -1
- package/src/tui/engine/transcript-spill.mjs +585 -0
- package/src/tui/engine/tui-steering-persist.mjs +1 -1
- package/src/tui/engine.mjs +2 -474
- package/src/tui/lib/voice-setup.mjs +1 -1
- package/src/tui/markdown/format-token.mjs +1 -1
- package/src/tui/markdown/render-ansi.mjs +1 -1
- package/src/tui/markdown/table-layout.mjs +4 -4
- package/src/tui/paste-attachments.mjs +1 -1
- package/src/tui/prompt-history-store.mjs +3 -3
- package/src/tui/statusline-ansi-bridge.mjs +2 -2
- package/src/tui/theme.mjs +1 -1
- package/src/tui/transcript-tool-failures.mjs +2 -2
- package/src/ui/ansi.mjs +0 -9
- package/src/ui/statusline-format.mjs +2 -4
- package/scripts/_bench-cwc.json +0 -20
- package/scripts/_jitter-fuzz.jsx +0 -42
- package/scripts/_jitter-fuzz2.jsx +0 -30
- package/scripts/_jitter-probe.jsx +0 -35
- package/scripts/_jp2.jsx +0 -16
- package/scripts/_smoke_wd.mjs +0 -7
- package/scripts/abort-recovery-test.mjs +0 -175
- package/scripts/agent-dispatch-abort-compose-test.mjs +0 -31
- package/scripts/agent-live-arg-guard-smoke.mjs +0 -20
- package/scripts/agent-live-path-suffix-smoke.mjs +0 -34
- package/scripts/agent-live-toolcall-args-smoke.mjs +0 -45
- package/scripts/agent-loop-policy-test.mjs +0 -37
- package/scripts/agent-model-liveness-test.mjs +0 -763
- package/scripts/agent-parallel-smoke.mjs +0 -435
- package/scripts/agent-route-batch-test.mjs +0 -40
- package/scripts/agent-tag-reuse-smoke.mjs +0 -441
- package/scripts/agent-terminal-reap-test.mjs +0 -252
- package/scripts/agent-trace-io-test.mjs +0 -133
- package/scripts/ansi-color-capability-test.mjs +0 -90
- package/scripts/anthropic-admission-retry-integration-test.mjs +0 -119
- package/scripts/anthropic-maxtokens-test.mjs +0 -119
- package/scripts/anthropic-oauth-refresh-race-test.mjs +0 -397
- package/scripts/anthropic-transport-policy-test.mjs +0 -726
- package/scripts/apply-patch-edit-smoke.mjs +0 -71
- package/scripts/arg-guard-test.mjs +0 -93
- package/scripts/async-notify-settlement-test.mjs +0 -99
- package/scripts/atomic-lock-tryonce-test.mjs +0 -125
- package/scripts/background-task-meta-smoke.mjs +0 -38
- package/scripts/bench-run.mjs +0 -508
- package/scripts/boot-smoke.mjs +0 -137
- package/scripts/build-runtime-windows.ps1 +0 -242
- package/scripts/channel-daemon-smoke.mjs +0 -1103
- package/scripts/code-graph-aggregate-cwd-test.mjs +0 -158
- package/scripts/code-graph-disk-hit-test.mjs +0 -290
- package/scripts/code-graph-dispatch-test.mjs +0 -96
- package/scripts/code-graph-root-federation-test.mjs +0 -273
- package/scripts/compact-active-turn-test.mjs +0 -68
- package/scripts/compact-file-reattach-test.mjs +0 -88
- package/scripts/compact-pressure-test.mjs +0 -788
- package/scripts/compact-prior-context-flatten-test.mjs +0 -252
- package/scripts/compact-recall-digest-test.mjs +0 -57
- package/scripts/compact-smoke.mjs +0 -1017
- package/scripts/compact-trigger-migration-smoke.mjs +0 -261
- package/scripts/compacted-placeholder-scrub-test.mjs +0 -63
- package/scripts/context-mcp-metering-test.mjs +0 -1406
- package/scripts/debounced-skills-async-save-test.mjs +0 -57
- package/scripts/deferred-tool-loading-test.mjs +0 -250
- package/scripts/desktop-session-bridge-test.mjs +0 -1013
- package/scripts/dispatch-persist-recovery-test.mjs +0 -141
- package/scripts/embedding-worker-exit-test.mjs +0 -76
- package/scripts/execution-completion-dedup-test.mjs +0 -205
- package/scripts/execution-pending-resume-kick-test.mjs +0 -151
- package/scripts/execution-resume-esc-integration-test.mjs +0 -176
- package/scripts/explore-bench-tmp.mjs +0 -36
- package/scripts/explore-bench.mjs +0 -248
- package/scripts/explore-prompt-policy-test.mjs +0 -256
- package/scripts/explore-timeout-cancel-test.mjs +0 -345
- package/scripts/find-fuzzy-hidden-test.mjs +0 -267
- package/scripts/forwarder-rebind-tail-test.mjs +0 -67
- package/scripts/freevar-smoke.mjs +0 -98
- package/scripts/gemini-provider-test.mjs +0 -1448
- package/scripts/generate-oc-icons.mjs +0 -11
- package/scripts/grok-oauth-refresh-race-test.mjs +0 -273
- package/scripts/headless-pristine-execution-test.mjs +0 -614
- package/scripts/hook-bus-test.mjs +0 -552
- package/scripts/ingest-pure-conversation-smoke.mjs +0 -175
- package/scripts/internal-comms-bench-test.mjs +0 -226
- package/scripts/internal-comms-bench.mjs +0 -853
- package/scripts/internal-comms-smoke.mjs +0 -277
- package/scripts/internal-tools-normalization-test.mjs +0 -62
- package/scripts/interrupted-turn-history-test.mjs +0 -399
- package/scripts/lead-workflow-smoke.mjs +0 -592
- package/scripts/lifecycle-api-test.mjs +0 -137
- package/scripts/live-share-test.mjs +0 -148
- package/scripts/live-worker-smoke.mjs +0 -333
- package/scripts/log-writer-guard-smoke.mjs +0 -131
- package/scripts/maintenance-default-routes-test.mjs +0 -164
- package/scripts/max-output-recovery-persist-test.mjs +0 -81
- package/scripts/max-output-recovery-test.mjs +0 -368
- package/scripts/mcp-client-normalization-test.mjs +0 -45
- package/scripts/mcp-grace-deferred-test.mjs +0 -225
- package/scripts/memory-core-input-test.mjs +0 -167
- package/scripts/memory-cycle-routing-test.mjs +0 -111
- package/scripts/memory-meta-concurrency-test.mjs +0 -20
- package/scripts/memory-pg-recovery-test.mjs +0 -59
- package/scripts/memory-rule-contract-test.mjs +0 -93
- package/scripts/model-list-sanitize-test.mjs +0 -37
- package/scripts/mouse-tracking-restore-smoke.mjs +0 -107
- package/scripts/native-edit-wire-test.mjs +0 -152
- package/scripts/notify-completion-mirror-test.mjs +0 -73
- package/scripts/openai-oauth-refresh-race-test.mjs +0 -120
- package/scripts/openai-oauth-ws-1006-retry-test.mjs +0 -954
- package/scripts/openai-ws-early-settle-test.mjs +0 -216
- package/scripts/output-style-bench.mjs +0 -292
- package/scripts/output-style-smoke.mjs +0 -145
- package/scripts/parent-abort-link-test.mjs +0 -90
- package/scripts/patch-binary-cache-test.mjs +0 -181
- package/scripts/path-suffix-test.mjs +0 -57
- package/scripts/pending-completion-drop-test.mjs +0 -239
- package/scripts/pending-messages-lock-nonblocking-test.mjs +0 -62
- package/scripts/pretool-ask-runtime-test.mjs +0 -54
- package/scripts/process-lifecycle-test.mjs +0 -461
- package/scripts/prompt-immediate-render-test.mjs +0 -89
- package/scripts/prompt-input-parity-test.mjs +0 -145
- package/scripts/provider-admission-scheduler-test.mjs +0 -681
- package/scripts/provider-contract-test.mjs +0 -632
- package/scripts/provider-stream-stall-test.mjs +0 -276
- package/scripts/provider-toolcall-test.mjs +0 -3063
- package/scripts/reactive-compact-persist-smoke.mjs +0 -203
- package/scripts/recall-bench.mjs +0 -359
- package/scripts/resource-admission-test.mjs +0 -792
- package/scripts/result-classification-test.mjs +0 -75
- package/scripts/rg-runner-test.mjs +0 -240
- package/scripts/routing-corpus-test.mjs +0 -349
- package/scripts/sanitize-tool-pairs-test.mjs +0 -260
- package/scripts/session-bench-cache-break-test.mjs +0 -102
- package/scripts/session-bench.mjs +0 -1708
- package/scripts/session-context-bench.mjs +0 -344
- package/scripts/session-heartbeat-lifecycle-test.mjs +0 -68
- package/scripts/session-ingest-compaction-smoke.mjs +0 -279
- package/scripts/session-ingest-smoke.mjs +0 -177
- package/scripts/session-orphan-sweep-test.mjs +0 -109
- package/scripts/set-effort-config-test.mjs +0 -41
- package/scripts/shell-failure-diagnostics-test.mjs +0 -280
- package/scripts/shell-hardening-test.mjs +0 -209
- package/scripts/shell-jobs-windows-hide-test.mjs +0 -164
- package/scripts/ship-mode-test.mjs +0 -98
- package/scripts/smoke-loop-failure-summary-test.mjs +0 -38
- package/scripts/smoke-loop-failure-summary.mjs +0 -16
- package/scripts/smoke-loop-report.mjs +0 -221
- package/scripts/smoke-loop.mjs +0 -202
- package/scripts/smoke-runtime-negative.ps1 +0 -106
- package/scripts/statusline-quota-hysteresis-test.mjs +0 -62
- package/scripts/steering-drain-buckets-test.mjs +0 -467
- package/scripts/steering-fold-provenance-test.mjs +0 -71
- package/scripts/streaming-tail-window-test.mjs +0 -175
- package/scripts/submit-commandbusy-race-test.mjs +0 -257
- package/scripts/task-bench.mjs +0 -205
- package/scripts/terminal-bench-isolation-guards-test.mjs +0 -102
- package/scripts/termio-input-smoke.mjs +0 -199
- package/scripts/tool-result-hook-test.mjs +0 -48
- package/scripts/tool-smoke.mjs +0 -2453
- package/scripts/tool-tui-presentation-test.mjs +0 -286
- package/scripts/toolcall-args-test.mjs +0 -239
- package/scripts/tui-ambiguous-width-test.mjs +0 -113
- package/scripts/tui-background-failure-smoke.mjs +0 -73
- package/scripts/tui-perf-run.ps1 +0 -26
- package/scripts/tui-render-smoke.mjs +0 -90
- package/scripts/tui-runtime-load-bench-entry.jsx +0 -608
- package/scripts/tui-runtime-load-bench.mjs +0 -50
- package/scripts/tui-store-frame-batch-test.mjs +0 -99
- package/scripts/tui-transcript-jitter-harness-entry.jsx +0 -383
- package/scripts/tui-transcript-perf-test.mjs +0 -934
- package/scripts/usage-metrics-epoch-smoke.mjs +0 -114
- package/scripts/verify-release-assets-test.mjs +0 -281
- package/scripts/web-fetch-routing-test.mjs +0 -158
- package/scripts/webhook-smoke.mjs +0 -201
- package/scripts/windows-hide-spawn-options-test.mjs +0 -19
- package/scripts/worker-notify-rejection-test.mjs +0 -51
- package/scripts/write-backpressure-test.mjs +0 -147
|
@@ -18,6 +18,7 @@ import {
|
|
|
18
18
|
getBackgroundTask,
|
|
19
19
|
} from '../../../../shared/background-tasks.mjs';
|
|
20
20
|
import { startChildGuardian } from '../../../../shared/child-guardian.mjs';
|
|
21
|
+
import { _startBackgroundShellJobImpl } from './shell-job-spawn.mjs';
|
|
21
22
|
import { detachedSpawnOpts } from '../../../../shared/spawn-flags.mjs';
|
|
22
23
|
import {
|
|
23
24
|
getShellJobsDir,
|
|
@@ -133,7 +134,7 @@ export function killShellJob(jobId) {
|
|
|
133
134
|
return { ...attachJobInsights(detail), killed: true };
|
|
134
135
|
}
|
|
135
136
|
|
|
136
|
-
function refreshShellJob(jobId) {
|
|
137
|
+
export function refreshShellJob(jobId) {
|
|
137
138
|
const detail = readShellJobDetail(jobId);
|
|
138
139
|
if (!detail) return null;
|
|
139
140
|
if (detail.status !== 'running') return detail;
|
|
@@ -239,7 +240,7 @@ const SHELL_JOB_WATCH_GRACE_MS = 5000;
|
|
|
239
240
|
// and fire immediately. The resolved timeout is already clamped at parse time
|
|
240
241
|
// (bash-tool.mjs TIMER_MAX_MS), but sites that add grace to it can still exceed
|
|
241
242
|
// the ceiling, so clamp the summed delay here too.
|
|
242
|
-
const TIMER_MAX_MS = 2_147_483_647;
|
|
243
|
+
export const TIMER_MAX_MS = 2_147_483_647;
|
|
243
244
|
// Registry of armed background-job watchers keyed by jobId. task wait
|
|
244
245
|
// and `kill` actions already hold the completed outcome, so they cancel the
|
|
245
246
|
// armed watcher here to prevent a double-notify when its next poll fires.
|
|
@@ -292,7 +293,7 @@ export function attachShellJobResourceLease(jobId, lease, { allowUnpersisted = f
|
|
|
292
293
|
shellJobResourceLeases.set(jobId, lease);
|
|
293
294
|
return true;
|
|
294
295
|
}
|
|
295
|
-
function releaseShellJobOwnershipWhenQuiescent(jobId, pid, {
|
|
296
|
+
export function releaseShellJobOwnershipWhenQuiescent(jobId, pid, {
|
|
296
297
|
onConfirmed = null,
|
|
297
298
|
allowLateLease = false,
|
|
298
299
|
deferUntilRootExit = false,
|
|
@@ -324,7 +325,7 @@ function releaseShellJobOwnershipWhenQuiescent(jobId, pid, {
|
|
|
324
325
|
}, { waitForRootExit: deferUntilRootExit });
|
|
325
326
|
return true;
|
|
326
327
|
}
|
|
327
|
-
function trackChildUntilConfirmedExit(child, jobId, onConfirmed = null) {
|
|
328
|
+
export function trackChildUntilConfirmedExit(child, jobId, onConfirmed = null) {
|
|
328
329
|
const pid = Number(child?.pid);
|
|
329
330
|
if (!Number.isFinite(pid) || pid <= 0) return false;
|
|
330
331
|
_installShellJobsExitHook();
|
|
@@ -752,448 +753,3 @@ function _armAdoptedJobCapPoll(jobId) {
|
|
|
752
753
|
if (typeof tick.unref === 'function') tick.unref();
|
|
753
754
|
return tick;
|
|
754
755
|
}
|
|
755
|
-
|
|
756
|
-
async function _startBackgroundShellJobImpl({
|
|
757
|
-
command, timeoutMs, workDir, mergeStderr, spawnEnv, shell, shellArg, shellArgs,
|
|
758
|
-
shellType, clientHostPid, spawnFn = spawn, writeDetailFn = writeShellJobDetail,
|
|
759
|
-
rollbackTimeoutMs = 5000,
|
|
760
|
-
}) {
|
|
761
|
-
// Route ANY PowerShell shell to the PS wrapper, regardless of platform.
|
|
762
|
-
// Gating on win32 sent shell:'powershell' on macOS/Linux down the POSIX
|
|
763
|
-
// path below, which spawns `pwsh <wrapper>.sh` — pwsh then tries to run a
|
|
764
|
-
// bash script. isPowerShellShell already matches pwsh/powershell by stem.
|
|
765
|
-
if (isPowerShellShell(shell, shellType)) {
|
|
766
|
-
return startBackgroundPowerShellJob({
|
|
767
|
-
command, timeoutMs, workDir, mergeStderr, spawnEnv, shell, clientHostPid,
|
|
768
|
-
spawnFn, writeDetailFn,
|
|
769
|
-
});
|
|
770
|
-
}
|
|
771
|
-
|
|
772
|
-
// POSIX-shell wrapper path. On Windows this runs for shell:'bash' (Git
|
|
773
|
-
// Bash): the previous hard rejection here was a policy choice, NOT a real
|
|
774
|
-
// invariant — every piece of this path's plumbing is shell-neutral.
|
|
775
|
-
// The wrapper uses `command -v timeout`, `if ... fi`, single-quote escape
|
|
776
|
-
// and POSIX exit-code propagation, all of which Git Bash executes; output
|
|
777
|
-
// and exit-code plumbing flows through the staged script's own
|
|
778
|
-
// `exec > … 2> …` redirect plus `printf … > exitPath` / `touch donePath`
|
|
779
|
-
// (filesystem ops, not shell features); and kill goes through
|
|
780
|
-
// killProcessTree(), which on win32 uses `taskkill /pid /t /f` regardless
|
|
781
|
-
// of which shell spawned the tree. So Git Bash background tasks cancel,
|
|
782
|
-
// capture output, and report exit codes correctly.
|
|
783
|
-
const jobId = `job_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
|
|
784
|
-
const stdoutPath = shellJobStdoutPath(jobId);
|
|
785
|
-
const stderrPath = shellJobStderrPath(jobId);
|
|
786
|
-
const exitPath = shellJobExitPath(jobId);
|
|
787
|
-
const donePath = shellJobDonePath(jobId);
|
|
788
|
-
// timeoutMs <= 0 means unlimited (async omitted default): no kernel
|
|
789
|
-
// `timeout` wrapper, no enforced marker — exec the inner shell directly.
|
|
790
|
-
const enforceTimeout = Number(timeoutMs) > 0;
|
|
791
|
-
const timeoutSeconds = enforceTimeout ? Math.max(1, Math.ceil(timeoutMs / 1000)) : 0;
|
|
792
|
-
// P2 fix: wrap with POSIX `timeout` so the kernel terminates the process
|
|
793
|
-
// at deadline even if the JS-side timer is interrupted. --preserve-status
|
|
794
|
-
// keeps the user command's exit code on success; on timeout the wrapper
|
|
795
|
-
// exits 124.
|
|
796
|
-
// `timeout` ships with GNU coreutils on Linux. On macOS, Homebrew
|
|
797
|
-
// coreutils installs it as `gtimeout` (the un-prefixed name is NOT created
|
|
798
|
-
// by default), so the wrapper picks `timeout` if present else `gtimeout`.
|
|
799
|
-
// When neither exists it falls through to the inner command (the parent
|
|
800
|
-
// setTimeout still calls refreshShellJob to clean up).
|
|
801
|
-
const userCmdQuoted = shellQuoteSingle(command);
|
|
802
|
-
// P2 fix: invoke the resolved shell (not bash -c) so zsh / dash /
|
|
803
|
-
// alternate shells run snapshot-aware commands correctly. Drop
|
|
804
|
-
// --preserve-status so timeout returns 124 unambiguously, making
|
|
805
|
-
// it trivial to distinguish a timeout (124) from a user-side
|
|
806
|
-
// SIGTERM exit (143).
|
|
807
|
-
const innerShellQ = shellQuoteSingle(shell);
|
|
808
|
-
const innerArgs = Array.isArray(shellArgs) && shellArgs.length > 0 ? shellArgs : [shellArg];
|
|
809
|
-
const innerArgsQ = innerArgs.filter((arg) => arg != null && String(arg).length > 0).map(shellQuoteSingle).join(' ');
|
|
810
|
-
// Runtime enforcement proof: the wrapper touches <jobId>.enforced right
|
|
811
|
-
// before exec'ing `timeout`, so the marker exists iff the timeout branch
|
|
812
|
-
// actually ran under the wrapper's own env/cwd. A spawn-time probe can't
|
|
813
|
-
// guarantee that (env is scrubbed, cwd differs) — see shellJobEnforcedPath.
|
|
814
|
-
const enforcedPath = shellJobEnforcedPath(jobId);
|
|
815
|
-
// Lifecycle ordering invariant: write the exit-code file FIRST and
|
|
816
|
-
// `touch donePath` strictly AFTER. refreshShellJob() gates completion
|
|
817
|
-
// on donePath existence and only then trusts the exit file — without
|
|
818
|
-
// this strict ordering, readFileSync on a partially-flushed exit file
|
|
819
|
-
// returned '' -> parseInt NaN -> exitCode null -> spurious 'failed'
|
|
820
|
-
// status for processes that actually exited 0. `rm -- "$0"` removes
|
|
821
|
-
// the staged wrapper .cmd.sh after donePath is published so a host
|
|
822
|
-
// crash before this point still leaves the file for the sweep to GC.
|
|
823
|
-
const _innerRun = `${innerShellQ} ${innerArgsQ} ${userCmdQuoted}`;
|
|
824
|
-
const _execPart = enforceTimeout
|
|
825
|
-
? `if command -v timeout >/dev/null 2>&1; then _to=timeout; elif command -v gtimeout >/dev/null 2>&1; then _to=gtimeout; else _to=; fi; if [ -n "$_to" ]; then touch ${shellQuoteSingle(enforcedPath)}; "$_to" ${timeoutSeconds} ${_innerRun}; else ${_innerRun}; fi`
|
|
826
|
-
: _innerRun;
|
|
827
|
-
const wrapped = `{ ${_execPart}; rc=$?; printf '%s' "$rc" > ${shellQuoteSingle(exitPath)}; touch ${shellQuoteSingle(donePath)}; rm -- "$0" 2>/dev/null; exit $rc; }`;
|
|
828
|
-
// Stage the wrapped command to a .sh and let the script open its own
|
|
829
|
-
// output files via `exec > … 2> …`. The parent does NOT pass file
|
|
830
|
-
// descriptors via stdio inheritance (`stdio: 'ignore'` for all three).
|
|
831
|
-
//
|
|
832
|
-
// Let the shell own redirects via `exec > ... 2> ...` inside the staged
|
|
833
|
-
// script. The child remains referenced by this CLI process; detached here
|
|
834
|
-
// is only used on POSIX to create a process group for tree-kill.
|
|
835
|
-
const outRedirect = mergeStderr
|
|
836
|
-
? `> ${shellQuoteSingle(stdoutPath)} 2>&1`
|
|
837
|
-
: `> ${shellQuoteSingle(stdoutPath)} 2> ${shellQuoteSingle(stderrPath)}`;
|
|
838
|
-
const scriptBody = `#!/usr/bin/env bash\nexec ${outRedirect}\n${wrapped}\n`;
|
|
839
|
-
const wrappedTempPath = `${exitPath}.cmd.sh`;
|
|
840
|
-
try {
|
|
841
|
-
writeFileSync(wrappedTempPath, scriptBody);
|
|
842
|
-
} catch (e) {
|
|
843
|
-
return { jobId, kind: 'bash', status: 'failed', error: `failed to stage shell background task: ${e?.message || e}` };
|
|
844
|
-
}
|
|
845
|
-
// R11: scrub loader/execution vars even though bash-tool.mjs already
|
|
846
|
-
// scrubs upstream — defense-in-depth at the spawn site catches future
|
|
847
|
-
// callers that build their own spawnEnv.
|
|
848
|
-
let child;
|
|
849
|
-
try {
|
|
850
|
-
child = spawnFn(shell, [wrappedTempPath], {
|
|
851
|
-
cwd: workDir,
|
|
852
|
-
env: scrubLoaderVars(scrubProviderSecrets({ ...spawnEnv })),
|
|
853
|
-
stdio: 'ignore',
|
|
854
|
-
...detachedSpawnOpts,
|
|
855
|
-
});
|
|
856
|
-
await awaitSpawnReady(child, 'POSIX background task');
|
|
857
|
-
} catch (e) {
|
|
858
|
-
try { unlinkSync(wrappedTempPath); } catch {}
|
|
859
|
-
return { jobId, kind: 'bash', status: 'failed', error: `failed to spawn shell background task: ${e?.message || e}` };
|
|
860
|
-
}
|
|
861
|
-
const detail = {
|
|
862
|
-
jobId,
|
|
863
|
-
kind: 'bash',
|
|
864
|
-
status: 'running',
|
|
865
|
-
command,
|
|
866
|
-
cwd: workDir,
|
|
867
|
-
pid: child.pid,
|
|
868
|
-
mergeStderr,
|
|
869
|
-
timeoutMs,
|
|
870
|
-
timeoutSeconds,
|
|
871
|
-
stdoutPath,
|
|
872
|
-
stderrPath: mergeStderr ? stdoutPath : stderrPath,
|
|
873
|
-
exitPath,
|
|
874
|
-
donePath,
|
|
875
|
-
// Per-terminal session stamp (see resolveJobOwnerHostPid).
|
|
876
|
-
ownerHostPid: resolveJobOwnerHostPid(clientHostPid),
|
|
877
|
-
startedAt: new Date().toISOString(),
|
|
878
|
-
};
|
|
879
|
-
let terminal = false;
|
|
880
|
-
let runtimeOwned = false;
|
|
881
|
-
const onRuntimeError = (error) => {
|
|
882
|
-
if (terminal) return;
|
|
883
|
-
terminal = true;
|
|
884
|
-
detail.status = 'failed';
|
|
885
|
-
detail.terminationPending = true;
|
|
886
|
-
detail.error = `shell background task process error: ${error?.message || error}`;
|
|
887
|
-
detail.finishedAt = new Date().toISOString();
|
|
888
|
-
try { writeDetailFn(detail); } catch {}
|
|
889
|
-
if (runtimeOwned) void (async () => {
|
|
890
|
-
const rollback = await rollbackSpawnedChild(child, { timeoutMs: rollbackTimeoutMs });
|
|
891
|
-
if (rollback.confirmed) {
|
|
892
|
-
releaseShellJobOwnershipWhenQuiescent(jobId, child.pid, {
|
|
893
|
-
onConfirmed: () => {
|
|
894
|
-
detail.terminationPending = false;
|
|
895
|
-
try { writeDetailFn(detail); } catch {}
|
|
896
|
-
},
|
|
897
|
-
});
|
|
898
|
-
} else {
|
|
899
|
-
detail.error += `; termination unconfirmed after ${rollbackTimeoutMs}ms (process remains tracked)`;
|
|
900
|
-
try { writeDetailFn(detail); } catch {}
|
|
901
|
-
trackChildUntilConfirmedExit(child, jobId, () => {
|
|
902
|
-
detail.terminationPending = false;
|
|
903
|
-
try { writeDetailFn(detail); } catch {}
|
|
904
|
-
});
|
|
905
|
-
}
|
|
906
|
-
})();
|
|
907
|
-
};
|
|
908
|
-
adoptSpawnErrorHandler(child, onRuntimeError);
|
|
909
|
-
if (terminal) {
|
|
910
|
-
const rollback = await rollbackSpawnedChild(child, { timeoutMs: rollbackTimeoutMs });
|
|
911
|
-
detail.terminationPending = !rollback.confirmed;
|
|
912
|
-
try { writeDetailFn(detail); } catch {}
|
|
913
|
-
try { unlinkSync(wrappedTempPath); } catch {}
|
|
914
|
-
return rollback.confirmed
|
|
915
|
-
? detail
|
|
916
|
-
: {
|
|
917
|
-
...detail,
|
|
918
|
-
rollbackPending: trackChildUntilConfirmedExit(child, jobId, () => {
|
|
919
|
-
detail.terminationPending = false;
|
|
920
|
-
try { writeDetailFn(detail); } catch {}
|
|
921
|
-
}),
|
|
922
|
-
error: `${detail.error}; termination unconfirmed after ${rollbackTimeoutMs}ms (process remains tracked)`,
|
|
923
|
-
};
|
|
924
|
-
}
|
|
925
|
-
try {
|
|
926
|
-
writeDetailFn(detail);
|
|
927
|
-
} catch (e) {
|
|
928
|
-
terminal = true;
|
|
929
|
-
const rollback = await rollbackSpawnedChild(child, { timeoutMs: rollbackTimeoutMs });
|
|
930
|
-
child.removeListener('error', onRuntimeError);
|
|
931
|
-
discardSpawnErrorGuard(child);
|
|
932
|
-
try { unlinkSync(wrappedTempPath); } catch {}
|
|
933
|
-
const failure = { jobId, kind: 'bash', status: 'failed', error: `failed to persist shell background task: ${e?.message || e}` };
|
|
934
|
-
if (!rollback.confirmed) {
|
|
935
|
-
failure.rollbackPending = trackChildUntilConfirmedExit(child, jobId);
|
|
936
|
-
failure.error += `; termination unconfirmed after ${rollbackTimeoutMs}ms (process remains tracked)`;
|
|
937
|
-
}
|
|
938
|
-
return failure;
|
|
939
|
-
}
|
|
940
|
-
startChildGuardian({
|
|
941
|
-
childPid: child.pid,
|
|
942
|
-
childGroupPid: child.pid,
|
|
943
|
-
label: 'shell-job',
|
|
944
|
-
});
|
|
945
|
-
_installShellJobsExitHook();
|
|
946
|
-
_registerLiveJobPid(child.pid, jobId);
|
|
947
|
-
runtimeOwned = true;
|
|
948
|
-
releaseShellJobOwnershipWhenQuiescent(jobId, child.pid, {
|
|
949
|
-
allowLateLease: true,
|
|
950
|
-
deferUntilRootExit: true,
|
|
951
|
-
});
|
|
952
|
-
// Deadline cleanup poke only when a timeout is enforced; an unlimited
|
|
953
|
-
// (timeoutMs<=0) job has no deadline — completion is observed via the
|
|
954
|
-
// fs.watch/poll watcher and refreshShellJob on task queries.
|
|
955
|
-
if (enforceTimeout) {
|
|
956
|
-
const timer = setTimeout(() => { refreshShellJob(jobId); }, Math.min(TIMER_MAX_MS, timeoutMs + 25));
|
|
957
|
-
if (typeof timer.unref === 'function') timer.unref();
|
|
958
|
-
}
|
|
959
|
-
return detail;
|
|
960
|
-
}
|
|
961
|
-
|
|
962
|
-
async function startBackgroundPowerShellJob({
|
|
963
|
-
command, timeoutMs, workDir, mergeStderr, spawnEnv, shell, clientHostPid,
|
|
964
|
-
spawnFn = spawn, writeDetailFn = writeShellJobDetail, rollbackTimeoutMs = 5000,
|
|
965
|
-
}) {
|
|
966
|
-
const jobId = `job_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
|
|
967
|
-
const stdoutPath = shellJobStdoutPath(jobId);
|
|
968
|
-
const rawStderrPath = shellJobStderrPath(jobId);
|
|
969
|
-
const exitPath = shellJobExitPath(jobId);
|
|
970
|
-
const donePath = shellJobDonePath(jobId);
|
|
971
|
-
const wrappedTempPath = `${exitPath}.cmd.ps1`;
|
|
972
|
-
// Stage the USER command as its own .ps1 and run it via `-File` instead of
|
|
973
|
-
// `-EncodedCommand <base64>`: the base64 token landed on the grandchild's
|
|
974
|
-
// visible command line, which is a prime Defender obfuscation signature
|
|
975
|
-
// (same family as the PowhidSubExec false positive). A file path on the
|
|
976
|
-
// command line carries no such signature, and the payload stays opaque to
|
|
977
|
-
// outer-shell quoting exactly as base64 did. UTF-8 BOM so Windows
|
|
978
|
-
// PowerShell 5.1 doesn't misread non-ASCII as ANSI.
|
|
979
|
-
// Trailer mirrors -Command/-EncodedCommand exit semantics (-File alone
|
|
980
|
-
// does NOT propagate the last native command's exit code).
|
|
981
|
-
const innerTempPath = `${exitPath}.user.ps1`;
|
|
982
|
-
const innerScript = `\ufeff${command}\nif ($null -ne $LASTEXITCODE) { exit $LASTEXITCODE }\n`;
|
|
983
|
-
const mergeLiteral = mergeStderr ? '$true' : '$false';
|
|
984
|
-
const wrapper = [
|
|
985
|
-
"$ErrorActionPreference = 'Continue'",
|
|
986
|
-
'[Console]::OutputEncoding=[System.Text.Encoding]::UTF8',
|
|
987
|
-
'$OutputEncoding=[System.Text.Encoding]::UTF8',
|
|
988
|
-
'$exe = (Get-Process -Id $PID).Path',
|
|
989
|
-
`$innerPath = ${psSingleQuote(innerTempPath)}`,
|
|
990
|
-
`$stdoutPath = ${psSingleQuote(stdoutPath)}`,
|
|
991
|
-
`$stderrPath = ${psSingleQuote(rawStderrPath)}`,
|
|
992
|
-
`$exitPath = ${psSingleQuote(exitPath)}`,
|
|
993
|
-
`$donePath = ${psSingleQuote(donePath)}`,
|
|
994
|
-
`$mergeStderr = ${mergeLiteral}`,
|
|
995
|
-
// 0 (or negative) = unlimited: the wrapper's `if ($timeoutMs -gt 0 ...`
|
|
996
|
-
// guard falls through to a plain WaitForExit() with no deadline. Do NOT
|
|
997
|
-
// floor to 1 — that would enforce a 1ms timeout on unlimited jobs.
|
|
998
|
-
`$timeoutMs = ${Math.max(0, Math.floor(timeoutMs || 0))}`,
|
|
999
|
-
'$code = 1',
|
|
1000
|
-
'try {',
|
|
1001
|
-
// -ExecutionPolicy Bypass: unlike -EncodedCommand, -File is subject to
|
|
1002
|
-
// the execution policy on Windows PowerShell; pwsh on macOS/Linux
|
|
1003
|
-
// accepts the parameter as a no-op, so it is safe unconditionally.
|
|
1004
|
-
" $argList = @('-NoLogo', '-NoProfile', '-NonInteractive', '-ExecutionPolicy', 'Bypass', '-File', $innerPath)",
|
|
1005
|
-
// Do not set WindowStyle: Hidden creates a separate transient conhost.
|
|
1006
|
-
// On Windows, explicitly reuse the wrapper's CREATE_NO_WINDOW console.
|
|
1007
|
-
// NoNewWindow is unavailable on non-Windows pwsh, so only add it for
|
|
1008
|
-
// Windows (where $IsWindows is absent/null in Windows PowerShell 5.1).
|
|
1009
|
-
' $spArgs = @{ FilePath = $exe; ArgumentList = $argList; RedirectStandardOutput = $stdoutPath; RedirectStandardError = $stderrPath; PassThru = $true }',
|
|
1010
|
-
' if ($IsWindows -or $null -eq $IsWindows) { $spArgs[\'NoNewWindow\'] = $true }',
|
|
1011
|
-
' $p = Start-Process @spArgs',
|
|
1012
|
-
' if ($timeoutMs -gt 0 -and -not $p.WaitForExit($timeoutMs)) {',
|
|
1013
|
-
// Kill the whole process TREE, not just the direct child: Start-Process
|
|
1014
|
-
// launches an intermediate pwsh that spawns the user command, so
|
|
1015
|
-
// Stop-Process on $p.Id alone orphans the grandchildren. On Windows use
|
|
1016
|
-
// `taskkill /T /F` (tree-terminate); on macOS/Linux pwsh, fall back to
|
|
1017
|
-
// Stop-Process (no taskkill there).
|
|
1018
|
-
' try {',
|
|
1019
|
-
' if ($IsWindows -or $null -eq $IsWindows) {',
|
|
1020
|
-
' & taskkill.exe /pid $p.Id /t /f 2>$null | Out-Null',
|
|
1021
|
-
' } else {',
|
|
1022
|
-
' try { Stop-Process -Id $p.Id -Force -ErrorAction SilentlyContinue } catch {}',
|
|
1023
|
-
' }',
|
|
1024
|
-
' } catch {}',
|
|
1025
|
-
' $code = 124',
|
|
1026
|
-
' } else {',
|
|
1027
|
-
' try { $p.WaitForExit() } catch {}',
|
|
1028
|
-
' $code = if ($null -ne $p.ExitCode) { [int]$p.ExitCode } else { 0 }',
|
|
1029
|
-
' }',
|
|
1030
|
-
'} catch {',
|
|
1031
|
-
' try { Add-Content -LiteralPath $stderrPath -Value ($_ | Out-String) -Encoding utf8 } catch {}',
|
|
1032
|
-
' $code = 1',
|
|
1033
|
-
'}',
|
|
1034
|
-
'if ($mergeStderr) {',
|
|
1035
|
-
' try {',
|
|
1036
|
-
' if (Test-Path -LiteralPath $stderrPath) {',
|
|
1037
|
-
' $err = Get-Content -LiteralPath $stderrPath -Raw -ErrorAction SilentlyContinue',
|
|
1038
|
-
' if ($err) { Add-Content -LiteralPath $stdoutPath -Value $err -Encoding utf8 }',
|
|
1039
|
-
' Remove-Item -LiteralPath $stderrPath -Force -ErrorAction SilentlyContinue',
|
|
1040
|
-
' }',
|
|
1041
|
-
' } catch {}',
|
|
1042
|
-
'}',
|
|
1043
|
-
'try { Set-Content -LiteralPath $exitPath -Value ([string]$code) -NoNewline -Encoding ascii } catch {}',
|
|
1044
|
-
'try { Set-Content -LiteralPath $donePath -Value "" -NoNewline -Encoding ascii } catch {}',
|
|
1045
|
-
'try { Remove-Item -LiteralPath $innerPath -Force -ErrorAction SilentlyContinue } catch {}',
|
|
1046
|
-
'try { Remove-Item -LiteralPath $PSCommandPath -Force -ErrorAction SilentlyContinue } catch {}',
|
|
1047
|
-
'exit $code',
|
|
1048
|
-
'',
|
|
1049
|
-
].join('\n');
|
|
1050
|
-
try {
|
|
1051
|
-
writeFileSync(wrappedTempPath, wrapper, 'utf-8');
|
|
1052
|
-
writeFileSync(innerTempPath, innerScript, 'utf-8');
|
|
1053
|
-
} catch (e) {
|
|
1054
|
-
return { jobId, kind: 'bash', status: 'failed', error: `failed to stage PowerShell background task: ${e?.message || e}` };
|
|
1055
|
-
}
|
|
1056
|
-
|
|
1057
|
-
const shellStem = basename(String(shell || '')).toLowerCase().replace(/\.exe$/, '');
|
|
1058
|
-
// No `-WindowStyle Hidden` CLI switch: windowsHide:true on the spawn below
|
|
1059
|
-
// already gives CREATE_NO_WINDOW, and the visible command-line token trips
|
|
1060
|
-
// Defender's hidden-PowerShell dropper signature (PowhidSubExec). The
|
|
1061
|
-
// in-wrapper Start-Process uses Windows-only NoNewWindow to reuse this
|
|
1062
|
-
// hidden console rather than creating a transient conhost window.
|
|
1063
|
-
// `-ExecutionPolicy` only applies to Windows PowerShell; build per-platform.
|
|
1064
|
-
const isWin = process.platform === 'win32';
|
|
1065
|
-
const wrapperArgs = ['-NoLogo', '-NoProfile', '-NonInteractive'];
|
|
1066
|
-
if (isWin && shellStem === 'powershell') wrapperArgs.push('-ExecutionPolicy', 'Bypass');
|
|
1067
|
-
wrapperArgs.push('-File', wrappedTempPath);
|
|
1068
|
-
// Spawn the staged wrapper directly. detached MUST be false on Windows:
|
|
1069
|
-
// a native pwsh launched with detached:true + stdio:'ignore' exits
|
|
1070
|
-
// immediately without running -File (verified — the detached child dies
|
|
1071
|
-
// even while the parent stays alive; the non-detached child runs to
|
|
1072
|
-
// completion). windowsHide:true gives CREATE_NO_WINDOW so no console
|
|
1073
|
-
// window flashes on screen. The wrapper owns its own stdout/stderr file
|
|
1074
|
-
// redirect (exec-equivalent Set-Content paths above), so stdio:'ignore'
|
|
1075
|
-
// drops no output. The child remains referenced so it is owned by the CLI
|
|
1076
|
-
// process; runtime.close()/exit hooks reap it instead of daemonizing it.
|
|
1077
|
-
let child;
|
|
1078
|
-
try {
|
|
1079
|
-
child = spawnFn(shell, wrapperArgs, {
|
|
1080
|
-
cwd: workDir,
|
|
1081
|
-
env: scrubLoaderVars(scrubProviderSecrets({ ...spawnEnv })),
|
|
1082
|
-
detached: false,
|
|
1083
|
-
stdio: 'ignore',
|
|
1084
|
-
windowsHide: true,
|
|
1085
|
-
});
|
|
1086
|
-
await awaitSpawnReady(child, 'PowerShell background task');
|
|
1087
|
-
} catch (e) {
|
|
1088
|
-
try { unlinkSync(wrappedTempPath); } catch {}
|
|
1089
|
-
try { unlinkSync(innerTempPath); } catch {}
|
|
1090
|
-
return { jobId, kind: 'bash', status: 'failed', error: `failed to spawn PowerShell background task: ${e?.message || e}` };
|
|
1091
|
-
}
|
|
1092
|
-
const childPid = child.pid;
|
|
1093
|
-
if (!Number.isFinite(childPid) || childPid <= 0) {
|
|
1094
|
-
return { jobId, kind: 'bash', status: 'failed', error: 'PowerShell background task spawn returned no pid' };
|
|
1095
|
-
}
|
|
1096
|
-
const detail = {
|
|
1097
|
-
jobId,
|
|
1098
|
-
kind: 'bash',
|
|
1099
|
-
shellType: 'powershell',
|
|
1100
|
-
status: 'running',
|
|
1101
|
-
command,
|
|
1102
|
-
cwd: workDir,
|
|
1103
|
-
pid: childPid,
|
|
1104
|
-
mergeStderr,
|
|
1105
|
-
timeoutMs,
|
|
1106
|
-
timeoutSeconds: Number(timeoutMs) > 0 ? Math.max(1, Math.ceil(timeoutMs / 1000)) : 0,
|
|
1107
|
-
stdoutPath,
|
|
1108
|
-
stderrPath: mergeStderr ? stdoutPath : rawStderrPath,
|
|
1109
|
-
exitPath,
|
|
1110
|
-
donePath,
|
|
1111
|
-
// The PS wrapper enforces the deadline (WaitForExit($timeoutMs) →
|
|
1112
|
-
// Stop-Process → 124) only when timeoutMs>0; an unlimited job waits
|
|
1113
|
-
// with no deadline, so don't falsely claim enforcement.
|
|
1114
|
-
timeoutEnforced: Number(timeoutMs) > 0,
|
|
1115
|
-
// Per-terminal session stamp (see resolveJobOwnerHostPid).
|
|
1116
|
-
ownerHostPid: resolveJobOwnerHostPid(clientHostPid),
|
|
1117
|
-
startedAt: new Date().toISOString(),
|
|
1118
|
-
};
|
|
1119
|
-
let terminal = false;
|
|
1120
|
-
let runtimeOwned = false;
|
|
1121
|
-
const onRuntimeError = (error) => {
|
|
1122
|
-
if (terminal) return;
|
|
1123
|
-
terminal = true;
|
|
1124
|
-
detail.status = 'failed';
|
|
1125
|
-
detail.terminationPending = true;
|
|
1126
|
-
detail.error = `PowerShell background task process error: ${error?.message || error}`;
|
|
1127
|
-
detail.finishedAt = new Date().toISOString();
|
|
1128
|
-
try { writeDetailFn(detail); } catch {}
|
|
1129
|
-
if (runtimeOwned) void (async () => {
|
|
1130
|
-
const rollback = await rollbackSpawnedChild(child, { timeoutMs: rollbackTimeoutMs });
|
|
1131
|
-
if (rollback.confirmed) {
|
|
1132
|
-
releaseShellJobOwnershipWhenQuiescent(jobId, childPid, {
|
|
1133
|
-
onConfirmed: () => {
|
|
1134
|
-
detail.terminationPending = false;
|
|
1135
|
-
try { writeDetailFn(detail); } catch {}
|
|
1136
|
-
},
|
|
1137
|
-
});
|
|
1138
|
-
} else {
|
|
1139
|
-
detail.error += `; termination unconfirmed after ${rollbackTimeoutMs}ms (process remains tracked)`;
|
|
1140
|
-
try { writeDetailFn(detail); } catch {}
|
|
1141
|
-
trackChildUntilConfirmedExit(child, jobId, () => {
|
|
1142
|
-
detail.terminationPending = false;
|
|
1143
|
-
try { writeDetailFn(detail); } catch {}
|
|
1144
|
-
});
|
|
1145
|
-
}
|
|
1146
|
-
})();
|
|
1147
|
-
};
|
|
1148
|
-
adoptSpawnErrorHandler(child, onRuntimeError);
|
|
1149
|
-
if (terminal) {
|
|
1150
|
-
const rollback = await rollbackSpawnedChild(child, { timeoutMs: rollbackTimeoutMs });
|
|
1151
|
-
detail.terminationPending = !rollback.confirmed;
|
|
1152
|
-
try { writeDetailFn(detail); } catch {}
|
|
1153
|
-
try { unlinkSync(wrappedTempPath); } catch {}
|
|
1154
|
-
try { unlinkSync(innerTempPath); } catch {}
|
|
1155
|
-
return rollback.confirmed
|
|
1156
|
-
? detail
|
|
1157
|
-
: {
|
|
1158
|
-
...detail,
|
|
1159
|
-
rollbackPending: trackChildUntilConfirmedExit(child, jobId, () => {
|
|
1160
|
-
detail.terminationPending = false;
|
|
1161
|
-
try { writeDetailFn(detail); } catch {}
|
|
1162
|
-
}),
|
|
1163
|
-
error: `${detail.error}; termination unconfirmed after ${rollbackTimeoutMs}ms (process remains tracked)`,
|
|
1164
|
-
};
|
|
1165
|
-
}
|
|
1166
|
-
try {
|
|
1167
|
-
writeDetailFn(detail);
|
|
1168
|
-
} catch (e) {
|
|
1169
|
-
terminal = true;
|
|
1170
|
-
const rollback = await rollbackSpawnedChild(child, { timeoutMs: rollbackTimeoutMs });
|
|
1171
|
-
child.removeListener('error', onRuntimeError);
|
|
1172
|
-
discardSpawnErrorGuard(child);
|
|
1173
|
-
try { unlinkSync(wrappedTempPath); } catch {}
|
|
1174
|
-
try { unlinkSync(innerTempPath); } catch {}
|
|
1175
|
-
const failure = { jobId, kind: 'bash', status: 'failed', error: `failed to persist PowerShell background task: ${e?.message || e}` };
|
|
1176
|
-
if (!rollback.confirmed) {
|
|
1177
|
-
failure.rollbackPending = trackChildUntilConfirmedExit(child, jobId);
|
|
1178
|
-
failure.error += `; termination unconfirmed after ${rollbackTimeoutMs}ms (process remains tracked)`;
|
|
1179
|
-
}
|
|
1180
|
-
return failure;
|
|
1181
|
-
}
|
|
1182
|
-
startChildGuardian({
|
|
1183
|
-
childPid,
|
|
1184
|
-
childGroupPid: childPid,
|
|
1185
|
-
label: 'shell-job-powershell',
|
|
1186
|
-
});
|
|
1187
|
-
_installShellJobsExitHook();
|
|
1188
|
-
_registerLiveJobPid(childPid, jobId);
|
|
1189
|
-
runtimeOwned = true;
|
|
1190
|
-
releaseShellJobOwnershipWhenQuiescent(jobId, childPid, {
|
|
1191
|
-
allowLateLease: true,
|
|
1192
|
-
deferUntilRootExit: true,
|
|
1193
|
-
});
|
|
1194
|
-
if (Number(timeoutMs) > 0) {
|
|
1195
|
-
const timer = setTimeout(() => { refreshShellJob(jobId); }, Math.min(TIMER_MAX_MS, timeoutMs + 25));
|
|
1196
|
-
if (typeof timer.unref === 'function') timer.unref();
|
|
1197
|
-
}
|
|
1198
|
-
return detail;
|
|
1199
|
-
}
|
|
@@ -112,7 +112,7 @@ export function snapshotCoversFullFile(snapshot) {
|
|
|
112
112
|
}
|
|
113
113
|
|
|
114
114
|
/** Logical line count captured at read-time (wc-l compatible). Undefined if absent. */
|
|
115
|
-
|
|
115
|
+
function snapshotFileLineCount(snapshot) {
|
|
116
116
|
const n = snapshot?.fileLineCount;
|
|
117
117
|
return Number.isFinite(n) && n >= 0 ? Math.trunc(n) : undefined;
|
|
118
118
|
}
|