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
|
@@ -0,0 +1,505 @@
|
|
|
1
|
+
// Shell exec output plumbing: inline/disk caps, ANSI strip, tree-kill, bounded capture, results.
|
|
2
|
+
// Extracted from shell-command.mjs.
|
|
3
|
+
'use strict';
|
|
4
|
+
// Async one-shot shell runner.
|
|
5
|
+
//
|
|
6
|
+
// Replaces the legacy spawnSync path in builtin.mjs shell execution. The
|
|
7
|
+
// improvements over spawnSync are:
|
|
8
|
+
// - tree-kill on timeout / abort (Windows taskkill /T /F, POSIX process
|
|
9
|
+
// group SIGTERM->SIGKILL escalation) so forked children come down with
|
|
10
|
+
// the parent shell instead of being orphaned holding pipes.
|
|
11
|
+
// - automatic spill to $PLUGIN_DATA/shell-output/<taskId>.* once the
|
|
12
|
+
// in-memory buffers exceed SHELL_OUTPUT_INLINE_CAP*4 bytes. The caller
|
|
13
|
+
// receives an outputFilePath marker the model can FileRead later
|
|
14
|
+
// instead of losing the tail past the inline cap.
|
|
15
|
+
// - external AbortSignal hookup so a session-scoped abort (ESC, new
|
|
16
|
+
// prompt) cancels in-flight bash work without orphaning the child.
|
|
17
|
+
//
|
|
18
|
+
// Persistent shells in bash-session.mjs keep their separate stdin-marker
|
|
19
|
+
// protocol — that runner is stateful and uses a different model entirely.
|
|
20
|
+
|
|
21
|
+
import { spawn } from 'node:child_process';
|
|
22
|
+
import {
|
|
23
|
+
mkdirSync,
|
|
24
|
+
openSync,
|
|
25
|
+
closeSync,
|
|
26
|
+
readFileSync,
|
|
27
|
+
readSync,
|
|
28
|
+
writeSync,
|
|
29
|
+
fsyncSync,
|
|
30
|
+
unlinkSync,
|
|
31
|
+
writeFileSync,
|
|
32
|
+
statSync,
|
|
33
|
+
} from 'node:fs';
|
|
34
|
+
import { join } from 'node:path';
|
|
35
|
+
import { randomUUID } from 'node:crypto';
|
|
36
|
+
import * as nodeUtil from 'node:util';
|
|
37
|
+
import { getPluginData } from '../config.mjs';
|
|
38
|
+
import { startChildGuardian } from '../../../shared/child-guardian.mjs';
|
|
39
|
+
import { resourceAdmission } from '../../../shared/resource-admission.mjs';
|
|
40
|
+
// Runtime-only import (used inside execShellCommand's auto-background
|
|
41
|
+
// transition). shell-jobs.mjs imports stripAnsi from this module, so this is
|
|
42
|
+
// a static cycle — safe because neither binding is touched at module-eval
|
|
43
|
+
// time, only when the respective functions actually run.
|
|
44
|
+
import { adoptForegroundShellJob, killShellJob } from './builtin/shell-jobs.mjs';
|
|
45
|
+
import { trackProcessTreeQuiescence } from './builtin/shell-job-process.mjs';
|
|
46
|
+
import {
|
|
47
|
+
_maybeEncodePowerShellCommand,
|
|
48
|
+
extractPowerShellCommandInner,
|
|
49
|
+
} from './shell-powershell.mjs';
|
|
50
|
+
import { spawnShellWithRetry as _spawnShellWithRetry } from './lib/shell-spawn-retry.mjs';
|
|
51
|
+
|
|
52
|
+
export {
|
|
53
|
+
_maybeEncodePowerShellCommand,
|
|
54
|
+
extractPowerShellCommandInner,
|
|
55
|
+
} from './shell-powershell.mjs';
|
|
56
|
+
|
|
57
|
+
// Inline cap. Output above this size is spilled to disk and the caller
|
|
58
|
+
// renders a path marker instead of pasting the tail. Matches the
|
|
59
|
+
// SHELL_OUTPUT_MAX_CHARS used by the smart-truncate renderer in
|
|
60
|
+
// builtin.mjs so spilled output and inline output share the same boundary.
|
|
61
|
+
|
|
62
|
+
export const SHELL_OUTPUT_INLINE_CAP = 30_000;
|
|
63
|
+
|
|
64
|
+
// Hard ceiling on disk-backed output. Past this the SIZE_WATCHDOG (G2)
|
|
65
|
+
// SIGKILLs the child to avoid filling the filesystem. 100 MB is generous
|
|
66
|
+
// for any legitimate command output and tight enough to catch a runaway
|
|
67
|
+
// loop within ~seconds on a typical SSD.
|
|
68
|
+
export const SHELL_OUTPUT_DISK_CAP = 100 * 1024 * 1024;
|
|
69
|
+
|
|
70
|
+
// Background-task disk watchdog cadence. The size guard polls the spilled
|
|
71
|
+
// stdout/stderr files every interval and SIGKILLs the child once the
|
|
72
|
+
// combined size exceeds SHELL_OUTPUT_DISK_CAP — short enough that a runaway loop is caught within a
|
|
73
|
+
// few seconds, long enough that the stat overhead is negligible.
|
|
74
|
+
export const SIZE_WATCHDOG_INTERVAL_MS = 1_000;
|
|
75
|
+
|
|
76
|
+
// fsync throttle for spilled output reads. getStdout/getStderr call
|
|
77
|
+
// fsyncSync before reading to ensure the caller sees the latest bytes.
|
|
78
|
+
// On Windows every fsyncSync is a noticeable I/O syscall, so throttle
|
|
79
|
+
// consecutive calls to at most one per this interval. Tune via env
|
|
80
|
+
// MIXDOG_SHELL_FSYNC_THROTTLE_MS (default 1000 ms, positive integer).
|
|
81
|
+
export function positiveIntEnv(name, fallback) {
|
|
82
|
+
const value = Number(process.env[name]);
|
|
83
|
+
return Number.isFinite(value) && value > 0 ? Math.floor(value) : fallback;
|
|
84
|
+
}
|
|
85
|
+
const MIXDOG_SHELL_FSYNC_THROTTLE_MS = positiveIntEnv('MIXDOG_SHELL_FSYNC_THROTTLE_MS', 1000);
|
|
86
|
+
|
|
87
|
+
// ANSI / VT control sequence stripper. Falls back to a regex sweep when
|
|
88
|
+
// node:util's stripVTControlCharacters isn't available (older Node).
|
|
89
|
+
export const _ANSI_REGEX =
|
|
90
|
+
/(?:\[[0-?]*[ -/]*[@-~]|\][\s\S]*?(?:|\\|))/g;
|
|
91
|
+
export const _stripAnsiImpl =
|
|
92
|
+
typeof nodeUtil.stripVTControlCharacters === 'function'
|
|
93
|
+
? (s) => nodeUtil.stripVTControlCharacters(s)
|
|
94
|
+
: (s) => String(s).replace(_ANSI_REGEX, () => '');
|
|
95
|
+
|
|
96
|
+
export function stripAnsi(s) {
|
|
97
|
+
if (typeof s !== 'string' || s.length === 0) return s;
|
|
98
|
+
return _stripAnsiImpl(s);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Tree-kill helper. spawn alone only signals the direct child, so a
|
|
102
|
+
// `sleep 1000 &` or a forked node server inside the shell stays alive
|
|
103
|
+
// holding the pipes open. POSIX path signals the process group (we spawn
|
|
104
|
+
// with detached:true to give the child its own pgid). Windows uses
|
|
105
|
+
// taskkill /T /F to walk the tree. Safe to call repeatedly; all errors
|
|
106
|
+
// swallowed.
|
|
107
|
+
export function treeKill(child) {
|
|
108
|
+
if (!child) return;
|
|
109
|
+
// Track close/exit via the standard child fields (set by Node when
|
|
110
|
+
// the corresponding events fire) instead of `child.killed`, which is
|
|
111
|
+
// true the moment any signal is delivered — even before the child has
|
|
112
|
+
// actually terminated. Using exitCode/signalCode means the SIGKILL
|
|
113
|
+
// escalation only suppresses itself when the process is genuinely
|
|
114
|
+
// gone.
|
|
115
|
+
if (child.exitCode != null || child.signalCode != null) return;
|
|
116
|
+
const pid = child.pid;
|
|
117
|
+
if (!pid) return;
|
|
118
|
+
try {
|
|
119
|
+
if (process.platform === 'win32') {
|
|
120
|
+
spawn('taskkill', ['/pid', String(pid), '/t', '/f'], {
|
|
121
|
+
windowsHide: true,
|
|
122
|
+
stdio: 'ignore',
|
|
123
|
+
});
|
|
124
|
+
} else {
|
|
125
|
+
try {
|
|
126
|
+
process.kill(-pid, 'SIGTERM');
|
|
127
|
+
} catch {
|
|
128
|
+
try {
|
|
129
|
+
child.kill('SIGTERM');
|
|
130
|
+
} catch {}
|
|
131
|
+
}
|
|
132
|
+
// Escalate to SIGKILL after 3s so a child that ignores SIGTERM
|
|
133
|
+
// still comes down. Windows taskkill /F is already forceful so
|
|
134
|
+
// skip the escalation timer there.
|
|
135
|
+
const esc = setTimeout(() => {
|
|
136
|
+
if (child.exitCode != null || child.signalCode != null) return;
|
|
137
|
+
try {
|
|
138
|
+
process.kill(-pid, 'SIGKILL');
|
|
139
|
+
} catch {
|
|
140
|
+
try {
|
|
141
|
+
child.kill('SIGKILL');
|
|
142
|
+
} catch {}
|
|
143
|
+
}
|
|
144
|
+
}, 3000);
|
|
145
|
+
if (esc.unref) esc.unref();
|
|
146
|
+
}
|
|
147
|
+
} catch {
|
|
148
|
+
/* swallow */
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Head+tail read helper: avoid pulling a large spill back into memory, but
|
|
153
|
+
// preserve BOTH the start of the output (where build / compiler / test errors
|
|
154
|
+
// are usually printed first) and the most recent output. Past INLINE_CAP*4
|
|
155
|
+
// bytes we return the first half-budget and the trailing half-budget with an
|
|
156
|
+
// elision marker between; below that the full body is returned as-is. A tail-
|
|
157
|
+
// only slice silently dropped early diagnostics. UTF-8 sequences are at most
|
|
158
|
+
// 4 B, so a small padding window lets us cut/advance on codepoint boundaries
|
|
159
|
+
// instead of emitting a U+FFFD glyph at the seam.
|
|
160
|
+
function _readHeadTail(filePath, fileSize) {
|
|
161
|
+
if (fileSize <= SHELL_OUTPUT_INLINE_CAP * 4) {
|
|
162
|
+
return readFileSync(filePath, 'utf-8');
|
|
163
|
+
}
|
|
164
|
+
const padding = 4;
|
|
165
|
+
const headBudget = Math.floor(SHELL_OUTPUT_INLINE_CAP / 2);
|
|
166
|
+
const tailBudget = SHELL_OUTPUT_INLINE_CAP - headBudget;
|
|
167
|
+
const fd = openSync(filePath, 'r');
|
|
168
|
+
try {
|
|
169
|
+
// Head: first headBudget bytes, dropping a split trailing codepoint.
|
|
170
|
+
const headBuf = Buffer.allocUnsafe(headBudget + padding);
|
|
171
|
+
const hn = readSync(fd, headBuf, 0, headBudget + padding, 0);
|
|
172
|
+
let hEnd = Math.min(headBudget, hn);
|
|
173
|
+
while (hEnd > 0 && hEnd < hn && (headBuf[hEnd] & 0xC0) === 0x80) hEnd--;
|
|
174
|
+
const head = headBuf.slice(0, hEnd).toString('utf-8');
|
|
175
|
+
// Tail: last tailBudget bytes, advancing past a leading split codepoint.
|
|
176
|
+
const tailReadSize = tailBudget + padding;
|
|
177
|
+
const tailStart = Math.max(hEnd, fileSize - tailReadSize);
|
|
178
|
+
const tailBuf = Buffer.allocUnsafe(tailReadSize);
|
|
179
|
+
const tn = readSync(fd, tailBuf, 0, tailReadSize, tailStart);
|
|
180
|
+
let tOff = 0;
|
|
181
|
+
if (tailStart > 0) {
|
|
182
|
+
while (tOff < tn && tOff < padding && (tailBuf[tOff] & 0xC0) === 0x80) tOff++;
|
|
183
|
+
}
|
|
184
|
+
const tail = tailBuf.slice(tOff, tn).toString('utf-8');
|
|
185
|
+
const elided = Math.max(0, (tailStart + tOff) - hEnd);
|
|
186
|
+
return `${head}\n... [${elided} bytes elided of ${fileSize} total — head+tail shown; full output spilled to disk] ...\n${tail}`;
|
|
187
|
+
} finally {
|
|
188
|
+
try { closeSync(fd); } catch {}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// Owns the captured stdout/stderr buffers for a single command run. Starts
|
|
193
|
+
// fully in memory; once the combined byte total exceeds the spill threshold
|
|
194
|
+
// (SHELL_OUTPUT_INLINE_CAP*4), opens append-only files in
|
|
195
|
+
// $PLUGIN_DATA/shell-output/ and from then on writes go straight to disk.
|
|
196
|
+
// On settle, the caller (execShellCommand) decides whether to keep the
|
|
197
|
+
// spilled files based on the final size.
|
|
198
|
+
export class TaskOutput {
|
|
199
|
+
constructor(taskId) {
|
|
200
|
+
this.taskId = taskId;
|
|
201
|
+
this.stdoutBuf = '';
|
|
202
|
+
this.stderrBuf = '';
|
|
203
|
+
this._inlineBytes = 0;
|
|
204
|
+
this.stdoutFd = null;
|
|
205
|
+
this.stderrFd = null;
|
|
206
|
+
this.stdoutPath = null;
|
|
207
|
+
this.stderrPath = null;
|
|
208
|
+
this.spilled = false;
|
|
209
|
+
this.stdoutFileSize = 0;
|
|
210
|
+
this.stderrFileSize = 0;
|
|
211
|
+
this.writeError = null;
|
|
212
|
+
// CC parity: direct-capture mode hands the spill files to the child as
|
|
213
|
+
// stdio fds (no JS pipes). Sizes are then tracked via stat, not writes.
|
|
214
|
+
this.direct = false;
|
|
215
|
+
// fsync throttle: task wait/read + tail-read polling can call getStdout/
|
|
216
|
+
// getStderr many times per second. Every call used to fsyncSync(fd),
|
|
217
|
+
// a noticeable I/O tax on Windows. Skip if a recent fsync landed
|
|
218
|
+
// within MIXDOG_SHELL_FSYNC_THROTTLE_MS — the next read still picks up
|
|
219
|
+
// writes via the kernel's normal write-back. Final settle (closeFds)
|
|
220
|
+
// flushes via close anyway.
|
|
221
|
+
this._lastStdoutFsyncMs = 0;
|
|
222
|
+
this._lastStderrFsyncMs = 0;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
_ensureFileBacking() {
|
|
226
|
+
if (this.spilled) return;
|
|
227
|
+
const dir = join(getPluginData(), 'shell-output');
|
|
228
|
+
try {
|
|
229
|
+
mkdirSync(dir, { recursive: true });
|
|
230
|
+
} catch {}
|
|
231
|
+
this.stdoutPath = join(dir, `${this.taskId}.stdout`);
|
|
232
|
+
this.stderrPath = join(dir, `${this.taskId}.stderr`);
|
|
233
|
+
// openSync failure (EMFILE, EACCES, ENOSPC, ENOTDIR after a race) used
|
|
234
|
+
// to throw straight up into the stream `data` handler, which left the
|
|
235
|
+
// child running with no further writes captured. Catch + record so the
|
|
236
|
+
// run settles cleanly under inline-only mode; the partial buffer in
|
|
237
|
+
// stdoutBuf/stderrBuf survives.
|
|
238
|
+
try {
|
|
239
|
+
this.stdoutFd = openSync(this.stdoutPath, 'a');
|
|
240
|
+
this.stderrFd = openSync(this.stderrPath, 'a');
|
|
241
|
+
} catch (err) {
|
|
242
|
+
this._recordWriteError('spill-open', err);
|
|
243
|
+
if (this.stdoutFd != null) {
|
|
244
|
+
try { closeSync(this.stdoutFd); } catch {}
|
|
245
|
+
this.stdoutFd = null;
|
|
246
|
+
}
|
|
247
|
+
this.stderrFd = null;
|
|
248
|
+
this.stdoutPath = null;
|
|
249
|
+
this.stderrPath = null;
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
if (this.stdoutBuf) {
|
|
253
|
+
try {
|
|
254
|
+
writeSync(this.stdoutFd, this.stdoutBuf);
|
|
255
|
+
this.stdoutFileSize += Buffer.byteLength(this.stdoutBuf, 'utf-8');
|
|
256
|
+
} catch (err) {
|
|
257
|
+
this._recordWriteError('stdout-spill-flush', err);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
if (this.stderrBuf) {
|
|
261
|
+
try {
|
|
262
|
+
writeSync(this.stderrFd, this.stderrBuf);
|
|
263
|
+
this.stderrFileSize += Buffer.byteLength(this.stderrBuf, 'utf-8');
|
|
264
|
+
} catch (err) {
|
|
265
|
+
this._recordWriteError('stderr-spill-flush', err);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
this.spilled = true;
|
|
269
|
+
// The flushed bytes now live in the spill files and getStdout/getStderr
|
|
270
|
+
// read from disk once spilled — drop the inline copies.
|
|
271
|
+
this.stdoutBuf = '';
|
|
272
|
+
this.stderrBuf = '';
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
_maybeSpill() {
|
|
276
|
+
if (this.spilled) return;
|
|
277
|
+
// Threshold is in BYTES — string .length counts UTF-16 units, which
|
|
278
|
+
// understates CJK output by up to 3x against the byte-sized cap.
|
|
279
|
+
if (this._inlineBytes > SHELL_OUTPUT_INLINE_CAP * 4) {
|
|
280
|
+
this._ensureFileBacking();
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// Force the in-memory buffers onto disk-backed files regardless of the
|
|
285
|
+
// SHELL_OUTPUT_INLINE_CAP*4 threshold. Used by the auto-background
|
|
286
|
+
// transition: once a foreground command is adopted into a tracked job,
|
|
287
|
+
// every subsequent stdout/stderr chunk must land in the spill files so
|
|
288
|
+
// task wait/read can read it (the caller has already settled and will no
|
|
289
|
+
// longer drain the in-memory buffers). No-op once already spilled.
|
|
290
|
+
forceSpill() {
|
|
291
|
+
if (this.spilled) return;
|
|
292
|
+
this._ensureFileBacking();
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// CC parity (ShellCommand file mode): open the spill files BEFORE spawn and
|
|
296
|
+
// hand their fds to the child as stdio[1]/stdio[2]. The child (and any
|
|
297
|
+
// grandchildren) write straight to disk with no parent-side pipes, so a
|
|
298
|
+
// surviving grandchild can never wedge the caller by holding a pipe handle.
|
|
299
|
+
// Returns null when file backing could not be opened — caller falls back to
|
|
300
|
+
// pipe capture.
|
|
301
|
+
openDirectCapture() {
|
|
302
|
+
this._ensureFileBacking();
|
|
303
|
+
if (!this.spilled || this.stdoutFd == null || this.stderrFd == null) return null;
|
|
304
|
+
this.direct = true;
|
|
305
|
+
return { stdoutFd: this.stdoutFd, stderrFd: this.stderrFd };
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
// Direct mode has no JS write path, so byte counters must come from the
|
|
309
|
+
// filesystem (CC "poll the file tail" analogue).
|
|
310
|
+
_refreshDirectSizes() {
|
|
311
|
+
if (!this.direct) return;
|
|
312
|
+
try { this.stdoutFileSize = statSync(this.stdoutPath).size; } catch {}
|
|
313
|
+
try { this.stderrFileSize = statSync(this.stderrPath).size; } catch {}
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
_recordWriteError(stage, err) {
|
|
317
|
+
if (this.writeError) return;
|
|
318
|
+
const msg = (err && err.message) ? err.message : String(err);
|
|
319
|
+
this.writeError = `[output-capture-error: ${stage}] ${msg}`;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
writeStdout(s) {
|
|
323
|
+
if (!s) return;
|
|
324
|
+
if (this.spilled) {
|
|
325
|
+
try {
|
|
326
|
+
writeSync(this.stdoutFd, s);
|
|
327
|
+
this.stdoutFileSize += Buffer.byteLength(s, 'utf-8');
|
|
328
|
+
} catch (err) {
|
|
329
|
+
this._recordWriteError('stdout-write', err);
|
|
330
|
+
}
|
|
331
|
+
return;
|
|
332
|
+
}
|
|
333
|
+
this.stdoutBuf += s;
|
|
334
|
+
this._inlineBytes += Buffer.byteLength(s, 'utf-8');
|
|
335
|
+
this._maybeSpill();
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
writeStderr(s) {
|
|
339
|
+
if (!s) return;
|
|
340
|
+
if (this.spilled) {
|
|
341
|
+
try {
|
|
342
|
+
writeSync(this.stderrFd, s);
|
|
343
|
+
this.stderrFileSize += Buffer.byteLength(s, 'utf-8');
|
|
344
|
+
} catch (err) {
|
|
345
|
+
this._recordWriteError('stderr-write', err);
|
|
346
|
+
}
|
|
347
|
+
return;
|
|
348
|
+
}
|
|
349
|
+
this.stderrBuf += s;
|
|
350
|
+
this._inlineBytes += Buffer.byteLength(s, 'utf-8');
|
|
351
|
+
this._maybeSpill();
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
totalDiskBytes() {
|
|
355
|
+
this._refreshDirectSizes();
|
|
356
|
+
return this.stdoutFileSize + this.stderrFileSize;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
// Cheap synchronous tail for live-progress consumers (running tool cards).
|
|
360
|
+
// Never fsyncs and never throws: inline mode slices the in-memory buffers;
|
|
361
|
+
// spilled mode reads the file tails directly (kernel write-back is fresh
|
|
362
|
+
// enough for display). Best-effort — any FS error yields ''.
|
|
363
|
+
getLiveTail(maxChars = 4000) {
|
|
364
|
+
try {
|
|
365
|
+
const merge = (out, err) => {
|
|
366
|
+
const merged = err ? `${out}${out && err ? '\n' : ''}${err}` : out;
|
|
367
|
+
return merged.length > maxChars ? merged.slice(-maxChars) : merged;
|
|
368
|
+
};
|
|
369
|
+
if (!this.spilled) return merge(this.stdoutBuf, this.stderrBuf);
|
|
370
|
+
this._refreshDirectSizes();
|
|
371
|
+
const readTail = (path, size) => {
|
|
372
|
+
if (!path || !(size > 0)) return '';
|
|
373
|
+
const bytes = Math.min(size, maxChars * 3);
|
|
374
|
+
const fd = openSync(path, 'r');
|
|
375
|
+
try {
|
|
376
|
+
const buffer = Buffer.alloc(bytes);
|
|
377
|
+
const read = readSync(fd, buffer, 0, bytes, Math.max(0, size - bytes));
|
|
378
|
+
return buffer.toString('utf-8', 0, read);
|
|
379
|
+
} finally {
|
|
380
|
+
try { closeSync(fd); } catch {}
|
|
381
|
+
}
|
|
382
|
+
};
|
|
383
|
+
return merge(
|
|
384
|
+
readTail(this.stdoutPath, this.stdoutFileSize),
|
|
385
|
+
readTail(this.stderrPath, this.stderrFileSize),
|
|
386
|
+
);
|
|
387
|
+
} catch {
|
|
388
|
+
return '';
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
async getStdout() {
|
|
393
|
+
if (this.spilled) {
|
|
394
|
+
this._refreshDirectSizes();
|
|
395
|
+
const now = Date.now();
|
|
396
|
+
if (!this.direct && now - this._lastStdoutFsyncMs >= MIXDOG_SHELL_FSYNC_THROTTLE_MS) {
|
|
397
|
+
try { fsyncSync(this.stdoutFd); } catch {}
|
|
398
|
+
this._lastStdoutFsyncMs = now;
|
|
399
|
+
}
|
|
400
|
+
try {
|
|
401
|
+
return _readHeadTail(this.stdoutPath, this.stdoutFileSize);
|
|
402
|
+
} catch (err) {
|
|
403
|
+
throw new Error(`[shell-command] spilled stdout read failed (${this.stdoutPath}): ${err.message}`);
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
return this.stdoutBuf;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
async getStderr() {
|
|
410
|
+
if (this.spilled) {
|
|
411
|
+
this._refreshDirectSizes();
|
|
412
|
+
const now = Date.now();
|
|
413
|
+
if (!this.direct && now - this._lastStderrFsyncMs >= MIXDOG_SHELL_FSYNC_THROTTLE_MS) {
|
|
414
|
+
try { fsyncSync(this.stderrFd); } catch {}
|
|
415
|
+
this._lastStderrFsyncMs = now;
|
|
416
|
+
}
|
|
417
|
+
try {
|
|
418
|
+
return _readHeadTail(this.stderrPath, this.stderrFileSize);
|
|
419
|
+
} catch {
|
|
420
|
+
return '';
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
return this.stderrBuf;
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
closeFds() {
|
|
427
|
+
if (this.stdoutFd != null) {
|
|
428
|
+
try {
|
|
429
|
+
closeSync(this.stdoutFd);
|
|
430
|
+
} catch {}
|
|
431
|
+
this.stdoutFd = null;
|
|
432
|
+
}
|
|
433
|
+
if (this.stderrFd != null) {
|
|
434
|
+
try {
|
|
435
|
+
closeSync(this.stderrFd);
|
|
436
|
+
} catch {}
|
|
437
|
+
this.stderrFd = null;
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
// Drop the spilled files when the inline body already covers the full
|
|
442
|
+
// output. Called when total spilled bytes <= SHELL_OUTPUT_INLINE_CAP, so
|
|
443
|
+
// outputFilePath would only point at a duplicate of what the caller is
|
|
444
|
+
// already pasting into the result.
|
|
445
|
+
deleteFiles() {
|
|
446
|
+
this.closeFds();
|
|
447
|
+
if (this.stdoutPath) {
|
|
448
|
+
try {
|
|
449
|
+
unlinkSync(this.stdoutPath);
|
|
450
|
+
} catch {}
|
|
451
|
+
this.stdoutPath = null;
|
|
452
|
+
}
|
|
453
|
+
if (this.stderrPath) {
|
|
454
|
+
try {
|
|
455
|
+
unlinkSync(this.stderrPath);
|
|
456
|
+
} catch {}
|
|
457
|
+
this.stderrPath = null;
|
|
458
|
+
}
|
|
459
|
+
this.spilled = false;
|
|
460
|
+
this.direct = false;
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
|
|
465
|
+
// Result envelope. Status markers ([exit code: N], [signal: SIGTERM]) are
|
|
466
|
+
// the caller's responsibility — shell execution owns that
|
|
467
|
+
// rendering convention.
|
|
468
|
+
export class ExecResult {
|
|
469
|
+
constructor(opts) {
|
|
470
|
+
this.stdout = opts.stdout;
|
|
471
|
+
this.stderr = opts.stderr;
|
|
472
|
+
this.exitCode = opts.exitCode;
|
|
473
|
+
this.signal = opts.signal || null;
|
|
474
|
+
this.timedOut = opts.timedOut === true;
|
|
475
|
+
this.killed = opts.killed === true;
|
|
476
|
+
// Why mixdog initiated a tree kill. Keep this separate from `signal`,
|
|
477
|
+
// which is the process-reported termination signal (SIGTERM/SIGKILL).
|
|
478
|
+
this.killCause = opts.killCause || null;
|
|
479
|
+
this.stdoutPath = opts.stdoutPath || null;
|
|
480
|
+
this.stdoutFileSize = opts.stdoutFileSize || 0;
|
|
481
|
+
this.stderrPath = opts.stderrPath || null;
|
|
482
|
+
this.stderrFileSize = opts.stderrFileSize || 0;
|
|
483
|
+
this.taskId = opts.taskId;
|
|
484
|
+
this.partialOutput = opts.partialOutput === true;
|
|
485
|
+
this.outputCaptureError = opts.outputCaptureError || null;
|
|
486
|
+
// Distinguish a shell tool/control-plane failure (spawn/preflight/capture)
|
|
487
|
+
// from a command process failure (non-zero exit/signal/timeout). The
|
|
488
|
+
// renderer in builtin/bash-tool.mjs turns this into a model-visible marker.
|
|
489
|
+
this.failurePhase = opts.failurePhase || null;
|
|
490
|
+
this.failureReason = opts.failureReason || null;
|
|
491
|
+
// Auto-background transition (CC startBackgrounding analogue). When a
|
|
492
|
+
// foreground command outlives autoBackgroundMs the call settles with
|
|
493
|
+
// backgrounded:true + the jobId for manual task control. The
|
|
494
|
+
// child stays owned by the CLI process; stdout/stderr keep flowing to
|
|
495
|
+
// the spill files now adopted by the shell-jobs registry.
|
|
496
|
+
this.backgrounded = opts.backgrounded === true;
|
|
497
|
+
this.jobId = opts.jobId || null;
|
|
498
|
+
this.backgroundMessage = opts.backgroundMessage || null;
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
// One-shot async shell runner. abortSignal optional (session-scoped abort
|
|
503
|
+
// from getAbortSignalForSession in builtin.mjs). Timeout implemented via
|
|
504
|
+
// treeKill so forked grandchildren also come down. Output streams capture
|
|
505
|
+
// to TaskOutput which transparently spills to disk past the inline cap.
|
|
@@ -39,7 +39,7 @@ function _firstCommandName(command) {
|
|
|
39
39
|
return null;
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
-
|
|
42
|
+
function classifyExecPolicy(command) {
|
|
43
43
|
const text = String(command || '');
|
|
44
44
|
if (!text.trim()) return { decision: 'allow', reason: '' };
|
|
45
45
|
if (isBlockedCommand(text)) {
|
|
@@ -65,7 +65,7 @@ export function classifyExecPolicy(command) {
|
|
|
65
65
|
return { decision: 'allow', reason: '' };
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
-
|
|
68
|
+
function mergeExecPolicyDecisions(a, b) {
|
|
69
69
|
const left = a && a.decision ? a : { decision: 'allow', reason: '' };
|
|
70
70
|
const right = b && b.decision ? b : { decision: 'allow', reason: '' };
|
|
71
71
|
if (_POLICY_RANK[right.decision] > _POLICY_RANK[left.decision]) return right;
|
|
@@ -39,7 +39,7 @@ const _failedShells = new Set();
|
|
|
39
39
|
const _activeSnapshots = new Set();
|
|
40
40
|
let _cleanupRegistered = false;
|
|
41
41
|
|
|
42
|
-
|
|
42
|
+
function drainShellSnapshots() {
|
|
43
43
|
for (const p of _activeSnapshots) {
|
|
44
44
|
try { unlinkSync(p); } catch {}
|
|
45
45
|
}
|
|
@@ -5,16 +5,31 @@ import { recordFetchedMessages } from "./status-snapshot.mjs";
|
|
|
5
5
|
// reloads keep the original file-level reference semantics.
|
|
6
6
|
function createBackendDispatch({ getConfig, getBackend, scheduler }) {
|
|
7
7
|
async function dispatchReply(args) {
|
|
8
|
+
const config = getConfig();
|
|
9
|
+
const channelId = String(args?.chat_id || args?.channel_id || args?.channel || config.channelId || '').trim();
|
|
10
|
+
const message = args?.message ?? args?.text;
|
|
11
|
+
if (!channelId) throw new Error('reply requires a configured channel id');
|
|
12
|
+
if (typeof message !== 'string' || !message.trim()) throw new Error('reply requires message text');
|
|
13
|
+
let files = args?.files ?? [];
|
|
14
|
+
if (typeof files === 'string') {
|
|
15
|
+
try {
|
|
16
|
+
const parsed = JSON.parse(files);
|
|
17
|
+
files = Array.isArray(parsed) ? parsed : [files];
|
|
18
|
+
} catch {
|
|
19
|
+
files = [files];
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
if (!Array.isArray(files)) files = [files];
|
|
8
23
|
const sendOpts = {
|
|
9
|
-
replyTo: args
|
|
10
|
-
files
|
|
11
|
-
embeds: args
|
|
12
|
-
components: args
|
|
24
|
+
replyTo: args?.reply_to ?? args?.replyTo,
|
|
25
|
+
files,
|
|
26
|
+
embeds: args?.embeds ?? [],
|
|
27
|
+
components: args?.components ?? []
|
|
13
28
|
};
|
|
14
29
|
let ids;
|
|
15
30
|
// Pre-send activity bump keeps idle gating consistent during the await.
|
|
16
31
|
scheduler.noteActivity();
|
|
17
|
-
const sendResult = await getBackend().sendMessage(
|
|
32
|
+
const sendResult = await getBackend().sendMessage(channelId, message, sendOpts);
|
|
18
33
|
scheduler.noteActivity();
|
|
19
34
|
ids = sendResult.sentIds;
|
|
20
35
|
const text = ids.length === 1 ? `sent (id: ${ids[0]})` : `sent ${ids.length} parts (ids: ${ids.join(", ")})`;
|
|
@@ -64,7 +64,7 @@ function isCronExpression(time) {
|
|
|
64
64
|
}
|
|
65
65
|
/** Validate a cron expression and throw a descriptive error if invalid.
|
|
66
66
|
* Used by schedule_control / schedules POST before accepting input. */
|
|
67
|
-
|
|
67
|
+
function validateCronExpression(time) {
|
|
68
68
|
if (typeof time !== "string" || !time) throw new Error(`invalid cron expression: ${JSON.stringify(time)}`);
|
|
69
69
|
if (!cron) throw new Error(`cron expression "${time}" rejected: node-cron is not available (install node-cron to use cron expressions)`);
|
|
70
70
|
const tokens = time.trim().split(/\s+/);
|
|
@@ -581,7 +581,7 @@ export async function ensureWhisperRuntime(dataDir, onProgress = null) {
|
|
|
581
581
|
// Read-only resolver: returns the cached binary path when the managed runtime
|
|
582
582
|
// is fully installed, null otherwise. Used by the transcribe hot path and the
|
|
583
583
|
// /cli-check endpoint to test installation state without triggering a fetch.
|
|
584
|
-
|
|
584
|
+
function resolveManagedWhisperCmd(dataDir) {
|
|
585
585
|
const activeFile = join(dataDir, 'voice-runtime', 'active-version')
|
|
586
586
|
if (!existsSync(activeFile)) return null
|
|
587
587
|
const activeName = readFileSync(activeFile, 'utf8').trim()
|
|
@@ -618,13 +618,13 @@ export function resolveManagedWhisperCmd(dataDir) {
|
|
|
618
618
|
// Read-only resolver matching the managed model layout. The bundled manifest
|
|
619
619
|
// path is read synchronously because this runs on the per-message hot path
|
|
620
620
|
// and an async fetch would add latency to every voice transcribe.
|
|
621
|
-
|
|
621
|
+
function resolveManagedWhisperModel(dataDir) {
|
|
622
622
|
if (!existsSync(BUNDLED_MANIFEST_PATH)) return null
|
|
623
623
|
const manifest = JSON.parse(readFileSync(BUNDLED_MANIFEST_PATH, 'utf8'))
|
|
624
624
|
return resolveManagedWhisperModelForId(manifest, dataDir, 'standard')
|
|
625
625
|
}
|
|
626
626
|
|
|
627
|
-
|
|
627
|
+
function resolveManagedWhisperModelById(dataDir, modelId = 'standard') {
|
|
628
628
|
if (!existsSync(BUNDLED_MANIFEST_PATH)) return null
|
|
629
629
|
const manifest = JSON.parse(readFileSync(BUNDLED_MANIFEST_PATH, 'utf8'))
|
|
630
630
|
return resolveManagedWhisperModelForId(manifest, dataDir, modelId)
|
|
@@ -732,7 +732,7 @@ export async function ensureFfmpegRuntime(dataDir, onProgress = null) {
|
|
|
732
732
|
})
|
|
733
733
|
}
|
|
734
734
|
|
|
735
|
-
|
|
735
|
+
function resolveManagedFfmpegPath(dataDir) {
|
|
736
736
|
const activeFile = join(dataDir, 'ffmpeg-runtime', 'active-version')
|
|
737
737
|
if (!existsSync(activeFile)) return null
|
|
738
738
|
const activeName = readFileSync(activeFile, 'utf8').trim()
|
|
@@ -7,8 +7,7 @@ export const TOOL_DEFS = [
|
|
|
7
7
|
inputSchema: {
|
|
8
8
|
type: "object",
|
|
9
9
|
properties: {
|
|
10
|
-
|
|
11
|
-
text: { type: "string", description: "Message text." },
|
|
10
|
+
message: { type: "string", description: "Message text for the configured channel." },
|
|
12
11
|
reply_to: { type: "string", description: "Reply message id." },
|
|
13
12
|
files: {
|
|
14
13
|
type: "array",
|
|
@@ -26,7 +25,7 @@ export const TOOL_DEFS = [
|
|
|
26
25
|
description: "Discord components."
|
|
27
26
|
}
|
|
28
27
|
},
|
|
29
|
-
required: ["
|
|
28
|
+
required: ["message"]
|
|
30
29
|
}
|
|
31
30
|
},
|
|
32
31
|
{
|
|
@@ -1,9 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
|
-
|
|
3
|
-
function __mixdogMemoryLog(...args) {
|
|
4
|
-
if (process.env.MIXDOG_QUIET_MEMORY_LOG) return true;
|
|
5
|
-
return __mixdogMemoryStderrWrite(...args);
|
|
6
|
-
}
|
|
2
|
+
import { __mixdogMemoryLog } from './lib/memory-log.mjs';
|
|
7
3
|
|
|
8
4
|
process.removeAllListeners('warning')
|
|
9
5
|
process.on('warning', () => {})
|