mixdog 0.9.63 → 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
|
@@ -1,792 +0,0 @@
|
|
|
1
|
-
import assert from 'node:assert/strict';
|
|
2
|
-
import test from 'node:test';
|
|
3
|
-
import { EventEmitter } from 'node:events';
|
|
4
|
-
import {
|
|
5
|
-
RESOURCE_ADMISSION_DEFAULTS,
|
|
6
|
-
ResourceAdmissionController,
|
|
7
|
-
} from '../src/runtime/shared/resource-admission.mjs';
|
|
8
|
-
import { execShellCommand } from '../src/runtime/agent/orchestrator/tools/shell-command.mjs';
|
|
9
|
-
import { executeBashTool } from '../src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs';
|
|
10
|
-
import {
|
|
11
|
-
attachShellJobResourceLease,
|
|
12
|
-
killShellJob,
|
|
13
|
-
startBackgroundShellJob,
|
|
14
|
-
waitForShellJob,
|
|
15
|
-
} from '../src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs';
|
|
16
|
-
import { trackProcessTreeQuiescence } from '../src/runtime/agent/orchestrator/tools/builtin/shell-job-process.mjs';
|
|
17
|
-
import { makeAgentDispatch } from '../src/runtime/agent/orchestrator/agent-runtime/agent-dispatch.mjs';
|
|
18
|
-
|
|
19
|
-
const healthy = () => ({
|
|
20
|
-
rssBytes: 100 * 1024 * 1024,
|
|
21
|
-
freeMemoryBytes: 8 * 1024 * 1024 * 1024,
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
function fakeChild({ pid = 424242, event = 'spawn', killMode = 'exit' } = {}) {
|
|
25
|
-
const child = new EventEmitter();
|
|
26
|
-
child.pid = pid;
|
|
27
|
-
child.exitCode = null;
|
|
28
|
-
child.signalCode = null;
|
|
29
|
-
child.killCalls = 0;
|
|
30
|
-
child.kill = () => {
|
|
31
|
-
child.killCalls += 1;
|
|
32
|
-
if (killMode === 'error-exit') {
|
|
33
|
-
queueMicrotask(() => child.emit('error', new Error('kill-time child error')));
|
|
34
|
-
}
|
|
35
|
-
if (killMode === 'none') return;
|
|
36
|
-
queueMicrotask(() => {
|
|
37
|
-
child.signalCode = 'SIGKILL';
|
|
38
|
-
child.emit('exit', null, 'SIGKILL');
|
|
39
|
-
child.emit('close', null, 'SIGKILL');
|
|
40
|
-
});
|
|
41
|
-
};
|
|
42
|
-
queueMicrotask(() => {
|
|
43
|
-
if (event === 'error') child.emit('error', Object.assign(new Error('async spawn denied'), { code: 'EACCES' }));
|
|
44
|
-
else child.emit('spawn');
|
|
45
|
-
});
|
|
46
|
-
return child;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
50
|
-
async function waitUntil(predicate, timeoutMs = 5000) {
|
|
51
|
-
const deadline = Date.now() + timeoutMs;
|
|
52
|
-
while (!predicate()) {
|
|
53
|
-
if (Date.now() >= deadline) assert.fail(`condition not met within ${timeoutMs}ms`);
|
|
54
|
-
await delay(25);
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
test('safe defaults bound agents, shells, combined work, queue, and memory', () => {
|
|
59
|
-
assert.deepEqual(RESOURCE_ADMISSION_DEFAULTS, {
|
|
60
|
-
maxAgents: 4,
|
|
61
|
-
maxShells: 4,
|
|
62
|
-
maxHighLoad: 6,
|
|
63
|
-
maxQueue: 32,
|
|
64
|
-
minFreeMemoryMb: 1024,
|
|
65
|
-
maxRssMb: 3072,
|
|
66
|
-
});
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
test('combined and per-kind limits queue without revoking in-flight work', async () => {
|
|
70
|
-
let now = 10;
|
|
71
|
-
const admission = new ResourceAdmissionController({
|
|
72
|
-
limits: { maxAgents: 2, maxShells: 2, maxHighLoad: 2, maxQueue: 3 },
|
|
73
|
-
metrics: healthy,
|
|
74
|
-
now: () => now,
|
|
75
|
-
});
|
|
76
|
-
const agent = await admission.acquire('agent');
|
|
77
|
-
const shell = await admission.acquire('shell');
|
|
78
|
-
let thirdStarted = false;
|
|
79
|
-
const third = admission.acquire('agent').then((lease) => {
|
|
80
|
-
thirdStarted = true;
|
|
81
|
-
return lease;
|
|
82
|
-
});
|
|
83
|
-
await new Promise((resolve) => setImmediate(resolve));
|
|
84
|
-
assert.equal(thirdStarted, false);
|
|
85
|
-
assert.deepEqual(admission.snapshot().active, { agent: 1, shell: 1 });
|
|
86
|
-
now = 25;
|
|
87
|
-
shell.release();
|
|
88
|
-
const queuedLease = await third;
|
|
89
|
-
assert.equal(queuedLease.queuedMs, 15);
|
|
90
|
-
assert.deepEqual(admission.snapshot().active, { agent: 2, shell: 0 });
|
|
91
|
-
agent.release();
|
|
92
|
-
queuedLease.release();
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
test('saturated parent agents yield their permits while awaited nested work progresses', async () => {
|
|
96
|
-
const admission = new ResourceAdmissionController({
|
|
97
|
-
limits: { maxAgents: 2, maxShells: 2, maxHighLoad: 2, maxQueue: 4 },
|
|
98
|
-
metrics: healthy,
|
|
99
|
-
});
|
|
100
|
-
const parents = await Promise.all([
|
|
101
|
-
admission.acquire('agent'),
|
|
102
|
-
admission.acquire('agent'),
|
|
103
|
-
]);
|
|
104
|
-
const gates = [Promise.withResolvers(), Promise.withResolvers()];
|
|
105
|
-
let nestedStarted = 0;
|
|
106
|
-
const nested = parents.map((parent, index) => admission.runWithLease(parent, async () => {
|
|
107
|
-
const child = await admission.acquire('agent');
|
|
108
|
-
nestedStarted += 1;
|
|
109
|
-
await gates[index].promise;
|
|
110
|
-
await child.release();
|
|
111
|
-
}));
|
|
112
|
-
await new Promise((resolve) => setImmediate(resolve));
|
|
113
|
-
assert.equal(nestedStarted, 2);
|
|
114
|
-
assert.deepEqual(admission.snapshot().active, { agent: 2, shell: 0 });
|
|
115
|
-
gates.forEach((gate) => gate.resolve());
|
|
116
|
-
await Promise.all(nested);
|
|
117
|
-
assert.deepEqual(admission.snapshot().active, { agent: 2, shell: 0 });
|
|
118
|
-
parents.forEach((lease) => lease.release());
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
test('yielded parent waits for bounded re-admission after a sibling steals its slot', async () => {
|
|
122
|
-
const admission = new ResourceAdmissionController({
|
|
123
|
-
limits: { maxAgents: 2, maxHighLoad: 2, maxQueue: 4 },
|
|
124
|
-
metrics: healthy,
|
|
125
|
-
});
|
|
126
|
-
const parent = await admission.acquire('agent');
|
|
127
|
-
const blocker = await admission.acquire('agent');
|
|
128
|
-
let parentContinued = false;
|
|
129
|
-
const childReady = Promise.withResolvers();
|
|
130
|
-
const releaseChild = Promise.withResolvers();
|
|
131
|
-
const dependency = admission.runWithLease(parent, async () => {
|
|
132
|
-
const child = await admission.acquire('agent');
|
|
133
|
-
childReady.resolve();
|
|
134
|
-
await releaseChild.promise;
|
|
135
|
-
await child.release();
|
|
136
|
-
}).then(() => { parentContinued = true; });
|
|
137
|
-
await childReady.promise;
|
|
138
|
-
const sibling = admission.acquire('agent');
|
|
139
|
-
releaseChild.resolve();
|
|
140
|
-
const siblingLease = await sibling;
|
|
141
|
-
await new Promise((resolve) => setImmediate(resolve));
|
|
142
|
-
assert.equal(parentContinued, false);
|
|
143
|
-
await siblingLease.release();
|
|
144
|
-
await dependency;
|
|
145
|
-
assert.equal(parentContinued, true);
|
|
146
|
-
assert.ok(admission.snapshot().active.agent <= 2);
|
|
147
|
-
await blocker.release();
|
|
148
|
-
await parent.release();
|
|
149
|
-
});
|
|
150
|
-
|
|
151
|
-
test('cancellation rejects a queued parent restoration without over-counting', async () => {
|
|
152
|
-
const admission = new ResourceAdmissionController({
|
|
153
|
-
limits: { maxAgents: 2, maxHighLoad: 2, maxQueue: 4 },
|
|
154
|
-
metrics: healthy,
|
|
155
|
-
});
|
|
156
|
-
const controller = new AbortController();
|
|
157
|
-
const parent = await admission.acquire('agent', { signal: controller.signal });
|
|
158
|
-
const blocker = await admission.acquire('agent');
|
|
159
|
-
const childReady = Promise.withResolvers();
|
|
160
|
-
const releaseChild = Promise.withResolvers();
|
|
161
|
-
const outcome = admission.runWithLease(parent, async () => {
|
|
162
|
-
const child = await admission.acquire('agent');
|
|
163
|
-
childReady.resolve();
|
|
164
|
-
await releaseChild.promise;
|
|
165
|
-
await child.release();
|
|
166
|
-
});
|
|
167
|
-
await childReady.promise;
|
|
168
|
-
const sibling = admission.acquire('agent');
|
|
169
|
-
releaseChild.resolve();
|
|
170
|
-
const siblingLease = await sibling;
|
|
171
|
-
controller.abort(new Error('parent restoration cancelled'));
|
|
172
|
-
await assert.rejects(outcome, /parent restoration cancelled/);
|
|
173
|
-
await siblingLease.release();
|
|
174
|
-
await blocker.release();
|
|
175
|
-
await parent.release();
|
|
176
|
-
assert.ok(admission.snapshot().active.agent <= 2);
|
|
177
|
-
});
|
|
178
|
-
|
|
179
|
-
test('detached nested lifetime keeps parent admitted or refuses without capacity', async () => {
|
|
180
|
-
const admission = new ResourceAdmissionController({
|
|
181
|
-
limits: { maxAgents: 2, maxShells: 1, maxHighLoad: 2 },
|
|
182
|
-
metrics: healthy,
|
|
183
|
-
});
|
|
184
|
-
const parent = await admission.acquire('agent');
|
|
185
|
-
await admission.runWithLease(parent, async () => {
|
|
186
|
-
const detached = await admission.acquire('shell', { dependency: 'detached' });
|
|
187
|
-
assert.deepEqual(admission.snapshot().active, { agent: 1, shell: 1 });
|
|
188
|
-
await detached.release();
|
|
189
|
-
});
|
|
190
|
-
const blocker = await admission.acquire('agent');
|
|
191
|
-
await admission.runWithLease(parent, async () => {
|
|
192
|
-
await assert.rejects(
|
|
193
|
-
admission.acquire('shell', { dependency: 'detached' }),
|
|
194
|
-
(error) => error?.code === 'ERESOURCEDEPENDENCY',
|
|
195
|
-
);
|
|
196
|
-
assert.equal(parent.counted, true);
|
|
197
|
-
});
|
|
198
|
-
await blocker.release();
|
|
199
|
-
await parent.release();
|
|
200
|
-
});
|
|
201
|
-
|
|
202
|
-
test('memory pressure rejects only new and queued work with actionable errors', async () => {
|
|
203
|
-
let sample = healthy();
|
|
204
|
-
const admission = new ResourceAdmissionController({
|
|
205
|
-
limits: { maxAgents: 1, maxHighLoad: 1, maxQueue: 2, minFreeMemoryMb: 1024, maxRssMb: 512 },
|
|
206
|
-
metrics: () => sample,
|
|
207
|
-
});
|
|
208
|
-
const running = await admission.acquire('agent');
|
|
209
|
-
const queued = admission.acquire('agent');
|
|
210
|
-
sample = { rssBytes: 600 * 1024 * 1024, freeMemoryBytes: 8 * 1024 * 1024 * 1024 };
|
|
211
|
-
running.release();
|
|
212
|
-
await assert.rejects(queued, (error) => (
|
|
213
|
-
error?.code === 'ERESOURCEPRESSURE' && /RSS 600 MB reached 512 MB limit/.test(error.message)
|
|
214
|
-
));
|
|
215
|
-
assert.deepEqual(admission.snapshot().active, { agent: 0, shell: 0 });
|
|
216
|
-
await assert.rejects(admission.acquire('shell'), /resource pressure: Mixdog RSS/);
|
|
217
|
-
});
|
|
218
|
-
|
|
219
|
-
test('queued cancellation removes work without affecting the running lease', async () => {
|
|
220
|
-
const admission = new ResourceAdmissionController({
|
|
221
|
-
limits: { maxAgents: 1, maxHighLoad: 1, maxQueue: 2 },
|
|
222
|
-
metrics: healthy,
|
|
223
|
-
});
|
|
224
|
-
const running = await admission.acquire('agent');
|
|
225
|
-
const controller = new AbortController();
|
|
226
|
-
const queued = admission.acquire('agent', { signal: controller.signal });
|
|
227
|
-
controller.abort(new Error('caller cancelled'));
|
|
228
|
-
await assert.rejects(queued, /caller cancelled/);
|
|
229
|
-
assert.equal(admission.snapshot().active.agent, 1);
|
|
230
|
-
assert.equal(admission.snapshot().queued, 0);
|
|
231
|
-
running.release();
|
|
232
|
-
});
|
|
233
|
-
|
|
234
|
-
test('shell refusal is a normal tool result and starts no child process', async () => {
|
|
235
|
-
let acquireCalls = 0;
|
|
236
|
-
const refusal = Object.assign(new Error('resource pressure: test limit reached'), {
|
|
237
|
-
code: 'ERESOURCEPRESSURE',
|
|
238
|
-
});
|
|
239
|
-
const result = await execShellCommand({
|
|
240
|
-
shell: process.platform === 'win32' ? (process.env.ComSpec || 'cmd.exe') : '/bin/sh',
|
|
241
|
-
shellArg: process.platform === 'win32' ? '/c' : '-c',
|
|
242
|
-
command: 'echo must-not-run',
|
|
243
|
-
env: process.env,
|
|
244
|
-
cwd: process.cwd(),
|
|
245
|
-
timeoutMs: 1000,
|
|
246
|
-
admission: {
|
|
247
|
-
acquire() {
|
|
248
|
-
acquireCalls += 1;
|
|
249
|
-
return Promise.reject(refusal);
|
|
250
|
-
},
|
|
251
|
-
},
|
|
252
|
-
});
|
|
253
|
-
assert.equal(acquireCalls, 1);
|
|
254
|
-
assert.equal(result.failureReason, 'resource pressure');
|
|
255
|
-
assert.equal(result.exitCode, 1);
|
|
256
|
-
assert.match(result.stderr, /resource pressure: test limit reached/);
|
|
257
|
-
});
|
|
258
|
-
|
|
259
|
-
test('real shell lifetime holds and releases one lease', async () => {
|
|
260
|
-
const admission = new ResourceAdmissionController({
|
|
261
|
-
limits: { maxShells: 1, maxHighLoad: 1 },
|
|
262
|
-
metrics: healthy,
|
|
263
|
-
});
|
|
264
|
-
const isWindows = process.platform === 'win32';
|
|
265
|
-
const result = await execShellCommand({
|
|
266
|
-
shell: isWindows ? (process.env.ComSpec || 'cmd.exe') : '/bin/sh',
|
|
267
|
-
shellArg: isWindows ? '/c' : '-c',
|
|
268
|
-
command: 'echo admitted',
|
|
269
|
-
env: process.env,
|
|
270
|
-
cwd: process.cwd(),
|
|
271
|
-
timeoutMs: 5000,
|
|
272
|
-
admission,
|
|
273
|
-
});
|
|
274
|
-
assert.equal(result.exitCode, 0);
|
|
275
|
-
assert.deepEqual(admission.snapshot().active, { agent: 0, shell: 0 });
|
|
276
|
-
});
|
|
277
|
-
|
|
278
|
-
test('process-tree quiescence tracker retains ownership and confirms exactly once', async () => {
|
|
279
|
-
let alive = true;
|
|
280
|
-
let confirmed = 0;
|
|
281
|
-
const tracker = trackProcessTreeQuiescence(123, () => { confirmed += 1; }, {
|
|
282
|
-
pollMs: 5,
|
|
283
|
-
probe: () => alive,
|
|
284
|
-
});
|
|
285
|
-
assert.equal(tracker.pending, true);
|
|
286
|
-
await delay(15);
|
|
287
|
-
assert.equal(confirmed, 0);
|
|
288
|
-
alive = false;
|
|
289
|
-
await waitUntil(() => confirmed === 1);
|
|
290
|
-
await delay(15);
|
|
291
|
-
assert.equal(confirmed, 1);
|
|
292
|
-
assert.equal(tracker.pending, false);
|
|
293
|
-
});
|
|
294
|
-
|
|
295
|
-
test('Windows tree tracking seeds ownership before an absent root snapshot', async () => {
|
|
296
|
-
let rows = [
|
|
297
|
-
{ pid: 124, parentPid: 123, identity: 'child-a' },
|
|
298
|
-
{ pid: 125, parentPid: 124, identity: 'grandchild-a' },
|
|
299
|
-
];
|
|
300
|
-
let confirmed = 0;
|
|
301
|
-
const tracker = trackProcessTreeQuiescence(123, () => { confirmed += 1; }, {
|
|
302
|
-
platform: 'win32',
|
|
303
|
-
pollMs: 5,
|
|
304
|
-
waitForRootExit: true,
|
|
305
|
-
windowsSnapshot: () => rows,
|
|
306
|
-
});
|
|
307
|
-
tracker.rootExited();
|
|
308
|
-
await delay(20);
|
|
309
|
-
assert.equal(confirmed, 0);
|
|
310
|
-
rows = [];
|
|
311
|
-
await waitUntil(() => confirmed === 1);
|
|
312
|
-
await delay(15);
|
|
313
|
-
assert.equal(confirmed, 1);
|
|
314
|
-
});
|
|
315
|
-
|
|
316
|
-
test('delayed asynchronous Windows snapshots keep timers moving and coalesce trackers onto one CIM request', async () => {
|
|
317
|
-
let cimRequests = 0;
|
|
318
|
-
let timerProgressed = false;
|
|
319
|
-
const spawnFn = (_file, args) => {
|
|
320
|
-
cimRequests += 1;
|
|
321
|
-
assert.match(args.at(-1), /Get-CimInstance Win32_Process/);
|
|
322
|
-
const child = new EventEmitter();
|
|
323
|
-
child.stdout = new EventEmitter();
|
|
324
|
-
child.stdout.setEncoding = () => {};
|
|
325
|
-
child.kill = () => {};
|
|
326
|
-
setTimeout(() => {
|
|
327
|
-
child.stdout.emit('data', '123\t1\troot-a\n');
|
|
328
|
-
child.emit('close', 0);
|
|
329
|
-
}, 40);
|
|
330
|
-
return child;
|
|
331
|
-
};
|
|
332
|
-
const first = trackProcessTreeQuiescence(123, () => assert.fail('root is present'), {
|
|
333
|
-
platform: 'win32',
|
|
334
|
-
pollMs: 5,
|
|
335
|
-
windowsSnapshotSpawn: spawnFn,
|
|
336
|
-
waitForRootExit: true,
|
|
337
|
-
});
|
|
338
|
-
const second = trackProcessTreeQuiescence(123, () => assert.fail('root is present'), {
|
|
339
|
-
platform: 'win32',
|
|
340
|
-
pollMs: 5,
|
|
341
|
-
windowsSnapshotSpawn: spawnFn,
|
|
342
|
-
waitForRootExit: true,
|
|
343
|
-
});
|
|
344
|
-
first.rootExited();
|
|
345
|
-
second.rootExited();
|
|
346
|
-
setTimeout(() => { timerProgressed = true; }, 5);
|
|
347
|
-
await delay(20);
|
|
348
|
-
assert.equal(timerProgressed, true);
|
|
349
|
-
assert.equal(cimRequests, 1);
|
|
350
|
-
await delay(40);
|
|
351
|
-
assert.equal(cimRequests, 1);
|
|
352
|
-
assert.equal(first.cancel(), true);
|
|
353
|
-
assert.equal(second.cancel(), true);
|
|
354
|
-
});
|
|
355
|
-
|
|
356
|
-
test('quiescence cleanup rejection is consumed after one callback', async () => {
|
|
357
|
-
let callbacks = 0;
|
|
358
|
-
let unhandled = 0;
|
|
359
|
-
const onUnhandled = () => { unhandled += 1; };
|
|
360
|
-
process.on('unhandledRejection', onUnhandled);
|
|
361
|
-
try {
|
|
362
|
-
trackProcessTreeQuiescence(123, async () => {
|
|
363
|
-
callbacks += 1;
|
|
364
|
-
throw new Error('fixture cleanup rejection');
|
|
365
|
-
}, { probe: () => false });
|
|
366
|
-
await delay(20);
|
|
367
|
-
assert.equal(callbacks, 1);
|
|
368
|
-
assert.equal(unhandled, 0);
|
|
369
|
-
} finally {
|
|
370
|
-
process.removeListener('unhandledRejection', onUnhandled);
|
|
371
|
-
}
|
|
372
|
-
});
|
|
373
|
-
|
|
374
|
-
test('rejected foreground lease cleanup still returns one shell result', async () => {
|
|
375
|
-
let releases = 0;
|
|
376
|
-
const isWindows = process.platform === 'win32';
|
|
377
|
-
const result = await execShellCommand({
|
|
378
|
-
shell: isWindows ? (process.env.ComSpec || 'cmd.exe') : '/bin/sh',
|
|
379
|
-
shellArg: isWindows ? '/c' : '-c',
|
|
380
|
-
command: 'echo cleanup-result',
|
|
381
|
-
env: process.env,
|
|
382
|
-
cwd: process.cwd(),
|
|
383
|
-
timeoutMs: 5000,
|
|
384
|
-
admission: {
|
|
385
|
-
async acquire() {
|
|
386
|
-
return {
|
|
387
|
-
release() {
|
|
388
|
-
releases += 1;
|
|
389
|
-
return Promise.reject(new Error('fixture release rejection'));
|
|
390
|
-
},
|
|
391
|
-
};
|
|
392
|
-
},
|
|
393
|
-
},
|
|
394
|
-
});
|
|
395
|
-
assert.equal(result.exitCode, 0);
|
|
396
|
-
assert.equal(releases, 1);
|
|
397
|
-
});
|
|
398
|
-
|
|
399
|
-
test('spawn failure contains rejected lease cleanup and returns one result', async () => {
|
|
400
|
-
let releases = 0;
|
|
401
|
-
const result = await execShellCommand({
|
|
402
|
-
shell: `mixdog-missing-shell-${process.pid}`,
|
|
403
|
-
shellArg: '-c',
|
|
404
|
-
command: 'echo unreachable',
|
|
405
|
-
env: process.env,
|
|
406
|
-
cwd: process.cwd(),
|
|
407
|
-
timeoutMs: 1000,
|
|
408
|
-
admission: {
|
|
409
|
-
async acquire() {
|
|
410
|
-
return {
|
|
411
|
-
release() {
|
|
412
|
-
releases += 1;
|
|
413
|
-
return Promise.reject(new Error('spawn cleanup rejected'));
|
|
414
|
-
},
|
|
415
|
-
};
|
|
416
|
-
},
|
|
417
|
-
},
|
|
418
|
-
});
|
|
419
|
-
assert.equal(result.exitCode, 1);
|
|
420
|
-
assert.equal(result.failureReason, 'spawn failed');
|
|
421
|
-
assert.match(result.stderr, /resource cleanup failed: spawn cleanup rejected/);
|
|
422
|
-
assert.equal(releases, 1);
|
|
423
|
-
});
|
|
424
|
-
|
|
425
|
-
for (const trigger of ['threshold', 'timeout']) {
|
|
426
|
-
test(`background ${trigger} promotion contains detach rejection and returns once`, async () => {
|
|
427
|
-
let releases = 0;
|
|
428
|
-
let detachCalls = 0;
|
|
429
|
-
const isWindows = process.platform === 'win32';
|
|
430
|
-
const result = await execShellCommand({
|
|
431
|
-
shell: isWindows ? (process.env.ComSpec || 'cmd.exe') : '/bin/sh',
|
|
432
|
-
shellArg: isWindows ? '/c' : '-c',
|
|
433
|
-
command: isWindows ? 'ping 127.0.0.1 -n 4 > nul' : 'sleep 2',
|
|
434
|
-
env: process.env,
|
|
435
|
-
cwd: process.cwd(),
|
|
436
|
-
timeoutMs: trigger === 'timeout' ? 20 : 5000,
|
|
437
|
-
autoBackgroundMs: trigger === 'threshold' ? 20 : undefined,
|
|
438
|
-
backgroundOnTimeout: trigger === 'timeout',
|
|
439
|
-
admission: {
|
|
440
|
-
async acquire() {
|
|
441
|
-
return {
|
|
442
|
-
release() { releases += 1; },
|
|
443
|
-
detachDependency() {
|
|
444
|
-
detachCalls += 1;
|
|
445
|
-
return Promise.reject(new Error('detach restoration rejected'));
|
|
446
|
-
},
|
|
447
|
-
};
|
|
448
|
-
},
|
|
449
|
-
},
|
|
450
|
-
});
|
|
451
|
-
assert.equal(result.backgrounded, false);
|
|
452
|
-
assert.equal(result.failureReason, 'resource cleanup failed');
|
|
453
|
-
assert.match(result.stderr, /detach restoration rejected/);
|
|
454
|
-
assert.equal(detachCalls, 1);
|
|
455
|
-
await waitUntil(() => releases === 1);
|
|
456
|
-
});
|
|
457
|
-
}
|
|
458
|
-
|
|
459
|
-
test('abort during adoption detaches once, resolves once, and releases once', async () => {
|
|
460
|
-
const controller = new AbortController();
|
|
461
|
-
const signal = controller.signal;
|
|
462
|
-
let abortedReads = 0;
|
|
463
|
-
Object.defineProperty(signal, 'aborted', {
|
|
464
|
-
configurable: true,
|
|
465
|
-
get() {
|
|
466
|
-
abortedReads += 1;
|
|
467
|
-
// Read #1: the bounded admission-wait wrapper (_acquireShellLeaseBounded).
|
|
468
|
-
// Read #2: the pre-spawn abort check. Read #3: the _autoBackground
|
|
469
|
-
// adoption re-check — the abort must land exactly there.
|
|
470
|
-
if (abortedReads === 3) controller.abort(new Error('cancelled during adoption'));
|
|
471
|
-
return abortedReads >= 3;
|
|
472
|
-
},
|
|
473
|
-
});
|
|
474
|
-
let detachCalls = 0;
|
|
475
|
-
let releases = 0;
|
|
476
|
-
let results = 0;
|
|
477
|
-
const isWindows = process.platform === 'win32';
|
|
478
|
-
const pending = execShellCommand({
|
|
479
|
-
shell: isWindows ? (process.env.ComSpec || 'cmd.exe') : '/bin/sh',
|
|
480
|
-
shellArg: isWindows ? '/c' : '-c',
|
|
481
|
-
command: isWindows ? 'ping 127.0.0.1 -n 4 > nul' : 'sleep 2',
|
|
482
|
-
env: process.env,
|
|
483
|
-
cwd: process.cwd(),
|
|
484
|
-
timeoutMs: 5000,
|
|
485
|
-
autoBackgroundMs: 20,
|
|
486
|
-
abortSignal: signal,
|
|
487
|
-
admission: {
|
|
488
|
-
async acquire() {
|
|
489
|
-
return {
|
|
490
|
-
detachDependency() { detachCalls += 1; },
|
|
491
|
-
release() { releases += 1; },
|
|
492
|
-
};
|
|
493
|
-
},
|
|
494
|
-
},
|
|
495
|
-
}).then((result) => {
|
|
496
|
-
results += 1;
|
|
497
|
-
return result;
|
|
498
|
-
});
|
|
499
|
-
const result = await pending;
|
|
500
|
-
assert.equal(result.backgrounded, false);
|
|
501
|
-
assert.equal(result.killed, true);
|
|
502
|
-
assert.equal(result.killCause, 'cancellation');
|
|
503
|
-
assert.equal(results, 1);
|
|
504
|
-
assert.equal(detachCalls, 1);
|
|
505
|
-
await waitUntil(() => releases === 1);
|
|
506
|
-
await delay(30);
|
|
507
|
-
assert.equal(results, 1);
|
|
508
|
-
assert.equal(detachCalls, 1);
|
|
509
|
-
assert.equal(releases, 1);
|
|
510
|
-
});
|
|
511
|
-
|
|
512
|
-
test('foreground shell lease follows a surviving POSIX process group', {
|
|
513
|
-
skip: process.platform === 'win32' ? 'Node cannot prove Windows descendant quiescence after root exit' : false,
|
|
514
|
-
}, async () => {
|
|
515
|
-
const admission = new ResourceAdmissionController({
|
|
516
|
-
limits: { maxShells: 1, maxHighLoad: 1 },
|
|
517
|
-
metrics: healthy,
|
|
518
|
-
});
|
|
519
|
-
const result = await execShellCommand({
|
|
520
|
-
shell: '/bin/sh',
|
|
521
|
-
shellArg: '-c',
|
|
522
|
-
command: 'sleep 1 &',
|
|
523
|
-
env: process.env,
|
|
524
|
-
cwd: process.cwd(),
|
|
525
|
-
timeoutMs: 5000,
|
|
526
|
-
admission,
|
|
527
|
-
});
|
|
528
|
-
assert.equal(result.exitCode, 0);
|
|
529
|
-
assert.equal(admission.snapshot().active.shell, 1);
|
|
530
|
-
await waitUntil(() => admission.snapshot().active.shell === 0, 3000);
|
|
531
|
-
});
|
|
532
|
-
|
|
533
|
-
test('background completion marker does not release before POSIX group quiescence', {
|
|
534
|
-
skip: process.platform === 'win32' ? 'Node cannot prove Windows descendant quiescence after root exit' : false,
|
|
535
|
-
}, async () => {
|
|
536
|
-
const job = await startBackgroundShellJob({
|
|
537
|
-
command: 'sleep 1 &',
|
|
538
|
-
timeoutMs: 5000,
|
|
539
|
-
workDir: process.cwd(),
|
|
540
|
-
mergeStderr: false,
|
|
541
|
-
spawnEnv: process.env,
|
|
542
|
-
shell: '/bin/sh',
|
|
543
|
-
shellArg: '-c',
|
|
544
|
-
shellType: 'posix',
|
|
545
|
-
});
|
|
546
|
-
let releases = 0;
|
|
547
|
-
assert.equal(attachShellJobResourceLease(job.jobId, { release() { releases += 1; } }), true);
|
|
548
|
-
const completed = await waitForShellJob(job.jobId, { timeoutMs: 2000, pollMs: 20 });
|
|
549
|
-
assert.equal(completed.status, 'completed');
|
|
550
|
-
assert.equal(releases, 0);
|
|
551
|
-
await waitUntil(() => releases === 1, 3000);
|
|
552
|
-
await delay(50);
|
|
553
|
-
assert.equal(releases, 1);
|
|
554
|
-
});
|
|
555
|
-
|
|
556
|
-
test('promotion detaches caller abort listener and holds lease until child close', async () => {
|
|
557
|
-
const admission = new ResourceAdmissionController({
|
|
558
|
-
limits: { maxShells: 1, maxHighLoad: 1 },
|
|
559
|
-
metrics: healthy,
|
|
560
|
-
});
|
|
561
|
-
const controller = new AbortController();
|
|
562
|
-
let listeners = 0;
|
|
563
|
-
const signal = controller.signal;
|
|
564
|
-
const add = signal.addEventListener.bind(signal);
|
|
565
|
-
const remove = signal.removeEventListener.bind(signal);
|
|
566
|
-
signal.addEventListener = (...args) => { listeners += 1; return add(...args); };
|
|
567
|
-
signal.removeEventListener = (...args) => { listeners -= 1; return remove(...args); };
|
|
568
|
-
const isWindows = process.platform === 'win32';
|
|
569
|
-
const result = await execShellCommand({
|
|
570
|
-
shell: isWindows ? (process.env.ComSpec || 'cmd.exe') : '/bin/sh',
|
|
571
|
-
shellArg: isWindows ? '/c' : '-c',
|
|
572
|
-
command: isWindows ? 'ping 127.0.0.1 -n 4 > nul' : 'sleep 2',
|
|
573
|
-
env: process.env,
|
|
574
|
-
cwd: process.cwd(),
|
|
575
|
-
timeoutMs: 5000,
|
|
576
|
-
autoBackgroundMs: 25,
|
|
577
|
-
abortSignal: signal,
|
|
578
|
-
admission,
|
|
579
|
-
});
|
|
580
|
-
assert.equal(result.backgrounded, true);
|
|
581
|
-
assert.equal(listeners, 0);
|
|
582
|
-
assert.equal(admission.snapshot().active.shell, 1);
|
|
583
|
-
killShellJob(result.jobId);
|
|
584
|
-
await waitUntil(() => admission.snapshot().active.shell === 0, 3000);
|
|
585
|
-
});
|
|
586
|
-
|
|
587
|
-
test('explicit async shell is admitted for its background lifetime', async () => {
|
|
588
|
-
const admission = new ResourceAdmissionController({
|
|
589
|
-
limits: { maxShells: 1, maxHighLoad: 1 },
|
|
590
|
-
metrics: healthy,
|
|
591
|
-
});
|
|
592
|
-
const isWindows = process.platform === 'win32';
|
|
593
|
-
const output = await executeBashTool({
|
|
594
|
-
command: isWindows ? 'ping 127.0.0.1 -n 4 > nul' : 'sleep 2',
|
|
595
|
-
mode: 'async',
|
|
596
|
-
shell: isWindows ? 'powershell' : 'bash',
|
|
597
|
-
}, process.cwd(), { resourceAdmission: admission });
|
|
598
|
-
const jobId = /task_id:\s*(\S+)/i.exec(String(output))?.[1];
|
|
599
|
-
assert.ok(jobId, String(output));
|
|
600
|
-
assert.equal(admission.snapshot().active.shell, 1);
|
|
601
|
-
killShellJob(jobId);
|
|
602
|
-
await waitUntil(() => admission.snapshot().active.shell === 0);
|
|
603
|
-
});
|
|
604
|
-
|
|
605
|
-
for (const shellType of ['posix', 'powershell']) {
|
|
606
|
-
test(`${shellType} background spawn handles asynchronous child errors`, async () => {
|
|
607
|
-
const child = fakeChild({ event: 'error' });
|
|
608
|
-
const result = await startBackgroundShellJob({
|
|
609
|
-
command: 'echo unreachable',
|
|
610
|
-
timeoutMs: 1000,
|
|
611
|
-
workDir: process.cwd(),
|
|
612
|
-
mergeStderr: false,
|
|
613
|
-
spawnEnv: process.env,
|
|
614
|
-
shell: shellType === 'powershell' ? 'pwsh' : '/bin/sh',
|
|
615
|
-
shellArg: '-c',
|
|
616
|
-
shellType,
|
|
617
|
-
spawnFn: () => child,
|
|
618
|
-
});
|
|
619
|
-
assert.equal(result.status, 'failed');
|
|
620
|
-
assert.match(result.error, /async spawn denied/);
|
|
621
|
-
assert.equal(child.listenerCount('error'), 0);
|
|
622
|
-
});
|
|
623
|
-
}
|
|
624
|
-
|
|
625
|
-
for (const shellType of ['posix', 'powershell']) {
|
|
626
|
-
test(`${shellType} post-readiness process error retains admission until confirmed exit`, async () => {
|
|
627
|
-
const child = fakeChild({ killMode: 'none' });
|
|
628
|
-
const result = await startBackgroundShellJob({
|
|
629
|
-
command: 'echo runtime-error',
|
|
630
|
-
timeoutMs: 1000,
|
|
631
|
-
workDir: process.cwd(),
|
|
632
|
-
mergeStderr: false,
|
|
633
|
-
spawnEnv: process.env,
|
|
634
|
-
shell: shellType === 'powershell' ? 'pwsh' : '/bin/sh',
|
|
635
|
-
shellArg: '-c',
|
|
636
|
-
shellType,
|
|
637
|
-
spawnFn: () => child,
|
|
638
|
-
rollbackTimeoutMs: 100,
|
|
639
|
-
});
|
|
640
|
-
let releases = 0;
|
|
641
|
-
assert.equal(attachShellJobResourceLease(result.jobId, { release() { releases += 1; } }), true);
|
|
642
|
-
child.emit('error', new Error('runtime pipe failure'));
|
|
643
|
-
await new Promise((resolve) => setImmediate(resolve));
|
|
644
|
-
assert.equal(releases, 0);
|
|
645
|
-
child.signalCode = 'SIGKILL';
|
|
646
|
-
child.emit('exit', null, 'SIGKILL');
|
|
647
|
-
child.emit('close', null, 'SIGKILL');
|
|
648
|
-
await waitUntil(() => releases === 1);
|
|
649
|
-
});
|
|
650
|
-
}
|
|
651
|
-
|
|
652
|
-
for (const shellType of ['posix', 'powershell']) {
|
|
653
|
-
test(`${shellType} persistence failure kills and awaits the spawned child`, async () => {
|
|
654
|
-
const child = fakeChild();
|
|
655
|
-
const result = await startBackgroundShellJob({
|
|
656
|
-
command: 'echo rollback',
|
|
657
|
-
timeoutMs: 1000,
|
|
658
|
-
workDir: process.cwd(),
|
|
659
|
-
mergeStderr: false,
|
|
660
|
-
spawnEnv: process.env,
|
|
661
|
-
shell: shellType === 'powershell' ? 'pwsh' : '/bin/sh',
|
|
662
|
-
shellArg: '-c',
|
|
663
|
-
shellType,
|
|
664
|
-
spawnFn: () => child,
|
|
665
|
-
writeDetailFn: () => { throw new Error('detail disk full'); },
|
|
666
|
-
});
|
|
667
|
-
assert.equal(result.status, 'failed');
|
|
668
|
-
assert.match(result.error, /failed to persist/);
|
|
669
|
-
assert.ok(child.killCalls >= 1);
|
|
670
|
-
assert.ok(child.signalCode || child.exitCode != null);
|
|
671
|
-
});
|
|
672
|
-
}
|
|
673
|
-
|
|
674
|
-
test('rollback consumes kill-time child errors', async () => {
|
|
675
|
-
const child = fakeChild({ killMode: 'error-exit' });
|
|
676
|
-
const result = await startBackgroundShellJob({
|
|
677
|
-
command: 'echo rollback-error',
|
|
678
|
-
timeoutMs: 1000,
|
|
679
|
-
workDir: process.cwd(),
|
|
680
|
-
mergeStderr: false,
|
|
681
|
-
spawnEnv: process.env,
|
|
682
|
-
shell: '/bin/sh',
|
|
683
|
-
shellArg: '-c',
|
|
684
|
-
shellType: 'posix',
|
|
685
|
-
spawnFn: () => child,
|
|
686
|
-
writeDetailFn: () => { throw new Error('persist failed'); },
|
|
687
|
-
});
|
|
688
|
-
assert.match(result.error, /failed to persist/);
|
|
689
|
-
assert.equal(result.rollbackPending, undefined);
|
|
690
|
-
});
|
|
691
|
-
|
|
692
|
-
test('rollback timeout returns a tracked diagnosable failure within its bound', async () => {
|
|
693
|
-
const child = fakeChild({ killMode: 'none' });
|
|
694
|
-
const started = Date.now();
|
|
695
|
-
const result = await startBackgroundShellJob({
|
|
696
|
-
command: 'echo rollback-timeout',
|
|
697
|
-
timeoutMs: 1000,
|
|
698
|
-
workDir: process.cwd(),
|
|
699
|
-
mergeStderr: false,
|
|
700
|
-
spawnEnv: process.env,
|
|
701
|
-
shell: '/bin/sh',
|
|
702
|
-
shellArg: '-c',
|
|
703
|
-
shellType: 'posix',
|
|
704
|
-
spawnFn: () => child,
|
|
705
|
-
writeDetailFn: () => { throw new Error('persist failed'); },
|
|
706
|
-
rollbackTimeoutMs: 20,
|
|
707
|
-
});
|
|
708
|
-
assert.equal(result.rollbackPending, true);
|
|
709
|
-
assert.match(result.error, /termination unconfirmed after 20ms .*remains tracked/);
|
|
710
|
-
assert.ok(Date.now() - started < 1000);
|
|
711
|
-
let releases = 0;
|
|
712
|
-
assert.equal(attachShellJobResourceLease(
|
|
713
|
-
result.jobId,
|
|
714
|
-
{ release() { releases += 1; } },
|
|
715
|
-
{ allowUnpersisted: true },
|
|
716
|
-
), true);
|
|
717
|
-
child.signalCode = 'SIGKILL';
|
|
718
|
-
child.emit('exit', null, 'SIGKILL');
|
|
719
|
-
child.emit('close', null, 'SIGKILL');
|
|
720
|
-
await waitUntil(() => releases === 1);
|
|
721
|
-
});
|
|
722
|
-
|
|
723
|
-
test('explicit async cancellation after admission prevents spawn and releases once', async () => {
|
|
724
|
-
const controller = new AbortController();
|
|
725
|
-
let releases = 0;
|
|
726
|
-
let spawns = 0;
|
|
727
|
-
const output = await executeBashTool({
|
|
728
|
-
command: 'echo must-not-spawn',
|
|
729
|
-
mode: 'async',
|
|
730
|
-
shell: process.platform === 'win32' ? 'powershell' : 'bash',
|
|
731
|
-
}, process.cwd(), {
|
|
732
|
-
abortSignal: controller.signal,
|
|
733
|
-
resourceAdmission: {
|
|
734
|
-
async acquire() {
|
|
735
|
-
controller.abort(new Error('cancelled after admission'));
|
|
736
|
-
return { release() { releases += 1; } };
|
|
737
|
-
},
|
|
738
|
-
},
|
|
739
|
-
shellJobRuntime: {
|
|
740
|
-
spawnFn() {
|
|
741
|
-
spawns += 1;
|
|
742
|
-
return fakeChild();
|
|
743
|
-
},
|
|
744
|
-
},
|
|
745
|
-
});
|
|
746
|
-
assert.match(String(output), /cancelled after admission/);
|
|
747
|
-
assert.equal(spawns, 0);
|
|
748
|
-
assert.equal(releases, 1);
|
|
749
|
-
});
|
|
750
|
-
|
|
751
|
-
test('persistent shell holds one lease across commands and releases on close', {
|
|
752
|
-
skip: process.platform === 'win32' ? 'persistent shells are disabled on Windows' : false,
|
|
753
|
-
}, async () => {
|
|
754
|
-
const admission = new ResourceAdmissionController({
|
|
755
|
-
limits: { maxShells: 1, maxHighLoad: 1 },
|
|
756
|
-
metrics: healthy,
|
|
757
|
-
});
|
|
758
|
-
const sessionId = `resource-test-${process.pid}`;
|
|
759
|
-
const first = await executeBashTool({
|
|
760
|
-
command: 'printf persistent',
|
|
761
|
-
persistent: true,
|
|
762
|
-
session_id: sessionId,
|
|
763
|
-
create: true,
|
|
764
|
-
}, process.cwd(), { resourceAdmission: admission });
|
|
765
|
-
assert.match(String(first), /persistent/);
|
|
766
|
-
assert.equal(admission.snapshot().active.shell, 1);
|
|
767
|
-
await executeBashTool({ command: '', session_id: sessionId, close: true }, process.cwd(), {
|
|
768
|
-
resourceAdmission: admission,
|
|
769
|
-
});
|
|
770
|
-
assert.equal(admission.snapshot().active.shell, 0);
|
|
771
|
-
});
|
|
772
|
-
|
|
773
|
-
test('hidden agent admission combines factory and call cancellation sources', async () => {
|
|
774
|
-
const admission = new ResourceAdmissionController({
|
|
775
|
-
limits: { maxAgents: 1, maxHighLoad: 1 },
|
|
776
|
-
metrics: healthy,
|
|
777
|
-
});
|
|
778
|
-
const occupied = await admission.acquire('agent');
|
|
779
|
-
const factory = new AbortController();
|
|
780
|
-
const call = new AbortController();
|
|
781
|
-
const dispatch = makeAgentDispatch({
|
|
782
|
-
agent: 'explorer',
|
|
783
|
-
config: {},
|
|
784
|
-
parentSignal: factory.signal,
|
|
785
|
-
resourceAdmission: admission,
|
|
786
|
-
});
|
|
787
|
-
const pending = dispatch({ prompt: 'locate', parentSignal: call.signal });
|
|
788
|
-
factory.abort(new Error('factory parent cancelled'));
|
|
789
|
-
await assert.rejects(pending, /factory parent cancelled/);
|
|
790
|
-
assert.equal(admission.snapshot().queued, 0);
|
|
791
|
-
occupied.release();
|
|
792
|
-
});
|