mixdog 1.2.5 → 1.3.0
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/LICENSES/simple-icons-CC0-1.0.txt +30 -0
- package/NOTICE.md +19 -0
- package/package.json +4 -1
- package/scripts/check-model-recommendations.mjs +89 -0
- package/scripts/gate-local.mjs +7 -0
- package/scripts/lib/gate-scheduler.mjs +2 -2
- package/scripts/lib/isolated-test-env.mjs +16 -0
- package/scripts/lib/run-node-tests.mjs +5 -14
- package/scripts/provider-pingpong-live.mjs +9 -9
- package/src/agents/advisor/AGENT.md +4 -4
- package/src/agents/advisor/agent.json +1 -1
- package/src/agents/reviewer/AGENT.md +5 -4
- package/src/agents/reviewer/agent.json +1 -1
- package/src/defaults/shipped-history.json +17 -0
- package/src/defaults/skills/browser-use/SKILL.md +7 -1
- package/src/defaults/skills/computer-use/SKILL.md +3 -1
- package/src/defaults/skills/memory-management/SKILL.md +4 -1
- package/src/defaults/skills/setup/references/actions.md +11 -4
- package/src/defaults/skills/setup-guide/SKILL.md +175 -0
- package/src/defaults/skills/setup-guide/model-recommendations.json +333 -0
- package/src/defaults/skills/visualize/SKILL.md +3 -2
- package/src/lib/rules-builder.cjs +56 -7
- package/src/output-styles/common.md +22 -6
- package/src/output-styles/detailed.md +7 -5
- package/src/output-styles/minimal.md +7 -3
- package/src/output-styles/simple.md +2 -3
- package/src/rules/agent/AGENT.md +7 -1
- package/src/rules/agent-mode/AGENT_MODE.md +24 -0
- package/src/rules/agent-mode/MIXY.md +21 -0
- package/src/rules/agent-mode/ROUTINE.md +6 -0
- package/src/rules/lead/LEAD.md +9 -4
- package/src/rules/shared/10-tool-workflow.md +2 -1
- package/src/rules/shared/60-verification.md +4 -5
- package/src/rules/shared/70-delivery.md +3 -0
- package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +2 -1
- package/src/runtime/agent/orchestrator/agent-runtime/room-supervisor-completion.mjs +86 -0
- package/src/runtime/agent/orchestrator/config.mjs +6 -0
- package/src/runtime/agent/orchestrator/context/deferred-tools.mjs +77 -14
- package/src/runtime/agent/orchestrator/context/role-instructions.mjs +182 -13
- package/src/runtime/agent/orchestrator/context/skill-catalog.mjs +5 -3
- package/src/runtime/agent/orchestrator/internal-agents.mjs +10 -0
- package/src/runtime/agent/orchestrator/mcp/agent-connectors.mjs +215 -0
- package/src/runtime/agent/orchestrator/mcp/child-tree.mjs +58 -64
- package/src/runtime/agent/orchestrator/mcp/client-transport.mjs +10 -1
- package/src/runtime/agent/orchestrator/mcp/client.mjs +122 -15
- package/src/runtime/agent/orchestrator/mcp/oauth-account.mjs +97 -0
- package/src/runtime/agent/orchestrator/mcp/oauth-callback.mjs +2 -3
- package/src/runtime/agent/orchestrator/mcp/oauth-client.mjs +49 -0
- package/src/runtime/agent/orchestrator/mcp/oauth-credentials.mjs +59 -9
- package/src/runtime/agent/orchestrator/mcp/oauth-login.mjs +32 -7
- package/src/runtime/agent/orchestrator/mcp/oauth-provider.mjs +45 -6
- package/src/runtime/agent/orchestrator/mcp/oauth.mjs +195 -6
- package/src/runtime/agent/orchestrator/mcp/server-connectors.mjs +17 -0
- package/src/runtime/agent/orchestrator/providers/account-pool.mjs +9 -2
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-client-version.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-credentials.mjs +41 -19
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +3 -2
- package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-events/content-block-start.mjs +4 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-events.mjs +2 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn.mjs +3 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +2 -1
- package/src/runtime/agent/orchestrator/providers/anthropic.mjs +3 -1
- package/src/runtime/agent/orchestrator/providers/antigravity-oauth-login.mjs +11 -5
- package/src/runtime/agent/orchestrator/providers/antigravity-oauth-tokens.mjs +4 -0
- package/src/runtime/agent/orchestrator/providers/catalog-deadline.mjs +163 -0
- package/src/runtime/agent/orchestrator/providers/catalog-failures.mjs +34 -0
- package/src/runtime/agent/orchestrator/providers/cursor-client-version.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/grok-oauth-login.mjs +38 -14
- package/src/runtime/agent/orchestrator/providers/lib/oauth-page.mjs +100 -26
- package/src/runtime/agent/orchestrator/providers/lib/send-callbacks.mjs +24 -0
- package/src/runtime/agent/orchestrator/providers/oauth-credential-probes.mjs +13 -0
- package/src/runtime/agent/orchestrator/providers/oauth-usage.mjs +2 -1
- package/src/runtime/agent/orchestrator/providers/openai-compat-chat-send.mjs +1 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/chunk-events.mjs +4 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/stream-state.mjs +3 -1
- package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream.mjs +2 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-models.mjs +49 -4
- package/src/runtime/agent/orchestrator/providers/openai-compat-presets.mjs +0 -7
- package/src/runtime/agent/orchestrator/providers/openai-compat-responses-events.mjs +7 -1
- package/src/runtime/agent/orchestrator/providers/openai-compat-responses.mjs +1 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-stream.mjs +2 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-xai-send.mjs +1 -0
- package/src/runtime/agent/orchestrator/providers/openai-error-contract.mjs +17 -12
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-events.mjs +5 -1
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state.mjs +11 -2
- package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +1 -0
- package/src/runtime/agent/orchestrator/providers/openai-oauth-login.mjs +45 -16
- package/src/runtime/agent/orchestrator/providers/openai-oauth-tokens.mjs +10 -0
- package/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs +1 -0
- package/src/runtime/agent/orchestrator/providers/openai-ws-connect.mjs +196 -0
- package/src/runtime/agent/orchestrator/providers/openai-ws-pool.mjs +74 -9
- package/src/runtime/agent/orchestrator/providers/openai-ws-stream/event-switch.mjs +14 -1
- package/src/runtime/agent/orchestrator/providers/openai-ws-stream.mjs +2 -0
- package/src/runtime/agent/orchestrator/providers/opencode-go-oauth-login.mjs +194 -0
- package/src/runtime/agent/orchestrator/providers/opencode-go-oauth-tokens.mjs +353 -0
- package/src/runtime/agent/orchestrator/providers/opencode-go-oauth.mjs +293 -0
- package/src/runtime/agent/orchestrator/providers/opencode-go-usage.mjs +33 -100
- package/src/runtime/agent/orchestrator/providers/opencode-go.mjs +23 -14
- package/src/runtime/agent/orchestrator/providers/registry.mjs +41 -27
- package/src/runtime/agent/orchestrator/runtime-core/ask-user-tool-defs.mjs +98 -4
- package/src/runtime/agent/orchestrator/runtime-core/context-status-shape.mjs +4 -0
- package/src/runtime/agent/orchestrator/runtime-core/extension-scopes.mjs +6 -4
- package/src/runtime/agent/orchestrator/runtime-core/orchestration.mjs +15 -8
- package/src/runtime/agent/orchestrator/runtime-core/plugin-mcp.mjs +1 -0
- package/src/runtime/agent/orchestrator/runtime-core/tool-bridge.mjs +12 -6
- package/src/runtime/agent/orchestrator/runtime-core/tool-catalog.mjs +32 -5
- package/src/runtime/agent/orchestrator/runtime-core/tool-defs.mjs +10 -4
- package/src/runtime/agent/orchestrator/runtime-core/workflow/agents.mjs +4 -1
- package/src/runtime/agent/orchestrator/runtime-core/workflow/context.mjs +50 -20
- package/src/runtime/agent/orchestrator/runtime-core/workflow-ids.mjs +2 -0
- package/src/runtime/agent/orchestrator/session/agent-home.d.mts +4 -0
- package/src/runtime/agent/orchestrator/session/agent-home.mjs +33 -0
- package/src/runtime/agent/orchestrator/session/agent-mode.mjs +62 -0
- package/src/runtime/agent/orchestrator/session/agent-pool-progress.mjs +15 -3
- package/src/runtime/agent/orchestrator/session/agent-room-supervisor.d.mts +31 -0
- package/src/runtime/agent/orchestrator/session/agent-room-supervisor.mjs +241 -0
- package/src/runtime/agent/orchestrator/session/agent-room.d.mts +162 -0
- package/src/runtime/agent/orchestrator/session/agent-room.mjs +641 -0
- package/src/runtime/agent/orchestrator/session/catalog-db/schema.mjs +7 -1
- package/src/runtime/agent/orchestrator/session/compact/execution-tail.mjs +74 -13
- package/src/runtime/agent/orchestrator/session/compact/forked-summary.mjs +102 -0
- package/src/runtime/agent/orchestrator/session/compact/runner.mjs +8 -2
- package/src/runtime/agent/orchestrator/session/compact/summary.mjs +16 -0
- package/src/runtime/agent/orchestrator/session/compact/user-message-kind.mjs +2 -0
- package/src/runtime/agent/orchestrator/session/compaction-archive-capture.mjs +6 -2
- package/src/runtime/agent/orchestrator/session/human-gate/gate.mjs +165 -0
- package/src/runtime/agent/orchestrator/session/human-gate/held-message.mjs +95 -0
- package/src/runtime/agent/orchestrator/session/human-gate/outbound-policy.mjs +221 -0
- package/src/runtime/agent/orchestrator/session/human-gate/requests.mjs +195 -0
- package/src/runtime/agent/orchestrator/session/human-gate/store.mjs +167 -0
- package/src/runtime/agent/orchestrator/session/lifecycle-scan.mjs +9 -0
- package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +3 -1
- package/src/runtime/agent/orchestrator/session/loop/fresh-context.mjs +39 -2
- package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +10 -0
- package/src/runtime/agent/orchestrator/session/loop/send-phase.mjs +16 -1
- package/src/runtime/agent/orchestrator/session/loop/tool-exec/before-hook.mjs +82 -33
- package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +11 -4
- package/src/runtime/agent/orchestrator/session/loop/tool-exposure-migration.mjs +5 -4
- package/src/runtime/agent/orchestrator/session/loop/tool-helpers.mjs +13 -3
- package/src/runtime/agent/orchestrator/session/loop/usage.mjs +7 -0
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-execute.mjs +1 -0
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-loop-options.mjs +6 -1
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-prepare.mjs +6 -0
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-settle.mjs +2 -0
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-steering-drain.mjs +13 -5
- package/src/runtime/agent/orchestrator/session/manager/pending-lifecycle-epoch.mjs +0 -8
- package/src/runtime/agent/orchestrator/session/manager/pending-message-entry.mjs +10 -7
- package/src/runtime/agent/orchestrator/session/manager/pending-messages.mjs +62 -22
- package/src/runtime/agent/orchestrator/session/manager/prompt-utils.mjs +44 -16
- package/src/runtime/agent/orchestrator/session/manager/rules-cache.mjs +22 -5
- package/src/runtime/agent/orchestrator/session/manager/runtime-liveness.mjs +23 -1
- package/src/runtime/agent/orchestrator/session/manager/session-close.mjs +81 -34
- package/src/runtime/agent/orchestrator/session/manager/session-crud.mjs +39 -4
- package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +27 -5
- package/src/runtime/agent/orchestrator/session/manager/session-prompt-composition.mjs +55 -8
- package/src/runtime/agent/orchestrator/session/manager/session-record.mjs +16 -0
- package/src/runtime/agent/orchestrator/session/manager/tool-resolution.mjs +3 -2
- package/src/runtime/agent/orchestrator/session/manager/usage-metrics.mjs +6 -0
- package/src/runtime/agent/orchestrator/session/manager/workflow-switch-notice.mjs +58 -0
- package/src/runtime/agent/orchestrator/session/manager.mjs +2 -2
- package/src/runtime/agent/orchestrator/session/mixy-name.mjs +24 -0
- package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +32 -5
- package/src/runtime/agent/orchestrator/session/request-fork.mjs +47 -0
- package/src/runtime/agent/orchestrator/session/routine-result.d.mts +70 -0
- package/src/runtime/agent/orchestrator/session/routine-result.mjs +277 -0
- package/src/runtime/agent/orchestrator/session/side-question-store.mjs +16 -1
- package/src/runtime/agent/orchestrator/session/side-question.mjs +5 -43
- package/src/runtime/agent/orchestrator/session/store/fs-probe.mjs +10 -0
- package/src/runtime/agent/orchestrator/session/store/lifecycle-barriers.mjs +73 -6
- package/src/runtime/agent/orchestrator/session/store/load-session.mjs +40 -0
- package/src/runtime/agent/orchestrator/session/store/save-fault.mjs +15 -0
- package/src/runtime/agent/orchestrator/session/store/session-paths.mjs +12 -0
- package/src/runtime/agent/orchestrator/session/store/sweep/sweep-row.mjs +8 -0
- package/src/runtime/agent/orchestrator/session/store-agent-worker-header.mjs +36 -0
- package/src/runtime/agent/orchestrator/session/store-agent-worker-rows.mjs +258 -43
- package/src/runtime/agent/orchestrator/session/store-record-strict-scan.mjs +325 -0
- package/src/runtime/agent/orchestrator/session/store-summary-catalog.mjs +84 -21
- package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +44 -12
- package/src/runtime/agent/orchestrator/session/store-summary-last-message.mjs +46 -0
- package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +46 -30
- package/src/runtime/agent/orchestrator/session/store-summary-visibility.mjs +0 -4
- package/src/runtime/agent/orchestrator/session/store-transcript-cache.mjs +144 -45
- package/src/runtime/agent/orchestrator/session/store-transcript-projection.mjs +32 -13
- package/src/runtime/agent/orchestrator/session/store-transcript-worker.mjs +364 -38
- package/src/runtime/agent/orchestrator/session/store.mjs +9 -2
- package/src/runtime/agent/orchestrator/session/task-wait-control.mjs +43 -12
- package/src/runtime/agent/orchestrator/session/tool-result-offload.mjs +23 -6
- package/src/runtime/agent/orchestrator/session/transcript-restore/restore.mjs +44 -1
- package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +12 -6
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/backgrounded-result.mjs +13 -7
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/bashism-rewrite.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/benign-exit.mjs +7 -1
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/completed-result.mjs +40 -7
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/deadline-plan.mjs +37 -34
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/inline-hoist.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/spawn-env.mjs +0 -4
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +6 -0
- package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +29 -27
- package/src/runtime/agent/orchestrator/tools/builtin/external-tool-adapters.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/builtin/find-files-tool.mjs +28 -11
- package/src/runtime/agent/orchestrator/tools/builtin/git-command-policy.mjs +12 -3
- package/src/runtime/agent/orchestrator/tools/builtin/git-command-tool.mjs +203 -101
- package/src/runtime/agent/orchestrator/tools/builtin/git-repo-rw-lock.mjs +90 -41
- package/src/runtime/agent/orchestrator/tools/builtin/lib/list-helpers.mjs +17 -1
- package/src/runtime/agent/orchestrator/tools/builtin/lib/shell-delivery.mjs +199 -0
- package/src/runtime/agent/orchestrator/tools/builtin/lib/shell-job-records.mjs +12 -0
- package/src/runtime/agent/orchestrator/tools/builtin/lib/shell-task-output.mjs +165 -39
- package/src/runtime/agent/orchestrator/tools/builtin/list-tool.mjs +25 -13
- package/src/runtime/agent/orchestrator/tools/builtin/native-search-client.mjs +113 -5
- package/src/runtime/agent/orchestrator/tools/builtin/read-batch.mjs +7 -6
- package/src/runtime/agent/orchestrator/tools/builtin/read-formatting.mjs +19 -4
- package/src/runtime/agent/orchestrator/tools/builtin/read-line-collector.mjs +5 -3
- package/src/runtime/agent/orchestrator/tools/builtin/read-single-render.mjs +25 -7
- package/src/runtime/agent/orchestrator/tools/builtin/read-special-files.mjs +2 -1
- package/src/runtime/agent/orchestrator/tools/builtin/read-tool/batch-render.mjs +3 -1
- package/src/runtime/agent/orchestrator/tools/builtin/search-glob-tool.mjs +12 -5
- package/src/runtime/agent/orchestrator/tools/builtin/search-grep-tool.mjs +61 -22
- package/src/runtime/agent/orchestrator/tools/builtin/search-path-diagnostics.mjs +32 -8
- package/src/runtime/agent/orchestrator/tools/builtin/shell-analysis.mjs +193 -19
- package/src/runtime/agent/orchestrator/tools/builtin/shell-direct-exe.mjs +57 -7
- package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +123 -32
- package/src/runtime/agent/orchestrator/tools/builtin/shell-lossless-compact.mjs +26 -1
- package/src/runtime/agent/orchestrator/tools/builtin/shell-output.mjs +13 -28
- package/src/runtime/agent/orchestrator/tools/builtin/task-tool.mjs +90 -11
- package/src/runtime/agent/orchestrator/tools/builtin/wait-budgets.mjs +23 -0
- package/src/runtime/agent/orchestrator/tools/builtin.mjs +54 -10
- package/src/runtime/agent/orchestrator/tools/code-graph/build.mjs +22 -28
- package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.mjs +11 -7
- package/src/runtime/agent/orchestrator/tools/destructive-warning.mjs +126 -9
- package/src/runtime/agent/orchestrator/tools/graph-manifest.json +13 -13
- package/src/runtime/agent/orchestrator/tools/lib/owned-shell-trees.mjs +15 -6
- package/src/runtime/agent/orchestrator/tools/lib/shell-descendants.mjs +64 -2
- package/src/runtime/agent/orchestrator/tools/lib/shell-run-background/promote-job.mjs +10 -2
- package/src/runtime/agent/orchestrator/tools/lib/shell-run-background/promotion-results.mjs +13 -3
- package/src/runtime/agent/orchestrator/tools/lib/shell-run-settle.mjs +8 -0
- package/src/runtime/agent/orchestrator/tools/lib/shell-run-state.mjs +6 -1
- package/src/runtime/agent/orchestrator/tools/patch/v4a-convert.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/shell-command.mjs +104 -55
- package/src/runtime/agent/orchestrator/tools/shell-policy.mjs +7 -2
- package/src/runtime/agent/orchestrator/tools/shell-powershell.mjs +39 -23
- package/src/runtime/agent/orchestrator/tools/shell-snapshot.mjs +6 -2
- package/src/runtime/agent/orchestrator/tools/spawn-manifest.json +13 -13
- package/src/runtime/attachments/office-extract.mjs +11 -9
- package/src/runtime/browser-bridge/client.mjs +24 -0
- package/src/runtime/channels/lib/scheduler.mjs +40 -12
- package/src/runtime/channels/lib/schedules-db.mjs +71 -12
- package/src/runtime/channels/lib/webhook/session-dispatch.mjs +36 -16
- package/src/runtime/channels/lib/webhook.mjs +0 -1
- package/src/runtime/channels/lib/webhooks-db.mjs +100 -10
- package/src/runtime/channels/lib/worker-main.mjs +17 -20
- package/src/runtime/computer-bridge/client.mjs +28 -1
- package/src/runtime/connectors/gmail-app-password.mjs +690 -0
- package/src/runtime/connectors/google-mcp.mjs +446 -0
- package/src/runtime/memory/lib/core-memory-access.mjs +69 -0
- package/src/runtime/memory/lib/core-memory-file.mjs +16 -8
- package/src/runtime/memory/lib/core-memory-index.mjs +15 -9
- package/src/runtime/memory/lib/core-memory-management.mjs +10 -4
- package/src/runtime/memory/lib/core-memory-store.mjs +53 -30
- package/src/runtime/memory/lib/core-memory-uniqueness.mjs +10 -6
- package/src/runtime/memory/lib/embedding-tokenizer.mjs +32 -18
- package/src/runtime/memory/lib/http-router/session-routes.mjs +3 -2
- package/src/runtime/memory/lib/memory-action-handlers/core-actions.mjs +44 -14
- package/src/runtime/memory/lib/memory-schema/core-and-meta.mjs +11 -0
- package/src/runtime/memory/lib/memory.mjs +7 -1
- package/src/runtime/memory/lib/query-core-recall.mjs +9 -1
- package/src/runtime/memory/lib/query-hybrid-search.mjs +2 -0
- package/src/runtime/memory/lib/runtime-fetcher.mjs +1 -1
- package/src/runtime/memory/lib/tool-call-handler.mjs +9 -5
- package/src/runtime/memory/tool-defs.mjs +5 -0
- package/src/runtime/office/authoring/pptx-script-normalize.mjs +11 -8
- package/src/runtime/office/core/tabular.mjs +1 -1
- package/src/runtime/office/design/library/design-template-inspect.mjs +7 -4
- package/src/runtime/office/portable/docx-formatting.mjs +1 -1
- package/src/runtime/office/portable/docx-runs.mjs +1 -1
- package/src/runtime/office/portable/docx-tracked-edits.mjs +2 -2
- package/src/runtime/office/portable/portable-docx-edits.mjs +11 -10
- package/src/runtime/office/portable/portable-docx-operations.mjs +21 -17
- package/src/runtime/office/portable/portable-docx-parts.mjs +15 -9
- package/src/runtime/office/portable/portable-docx-styles.mjs +4 -1
- package/src/runtime/office/portable/portable-docx-xml.mjs +9 -9
- package/src/runtime/office/portable/portable-opc.mjs +27 -13
- package/src/runtime/office/portable/portable-pivot.mjs +3 -3
- package/src/runtime/office/portable/portable-pptx-chart.mjs +3 -3
- package/src/runtime/office/portable/portable-pptx-charts.mjs +24 -18
- package/src/runtime/office/portable/portable-pptx-core.mjs +5 -5
- package/src/runtime/office/portable/portable-pptx-deck.mjs +8 -4
- package/src/runtime/office/portable/portable-pptx-package.mjs +7 -4
- package/src/runtime/office/portable/portable-pptx-shapes.mjs +18 -9
- package/src/runtime/office/portable/portable-sheet-parts.mjs +9 -3
- package/src/runtime/office/portable/portable-sheet-xml.mjs +5 -5
- package/src/runtime/office/portable/portable-xlsx-charts.mjs +4 -1
- package/src/runtime/office/portable/portable-xlsx-drawings.mjs +10 -6
- package/src/runtime/office/portable/portable-xlsx-operations.mjs +8 -5
- package/src/runtime/office/portable/portable-xlsx-pivot-ops.mjs +1 -1
- package/src/runtime/office/portable/portable-xlsx-reference-shift.mjs +11 -7
- package/src/runtime/office/portable/portable-xlsx-sheet-edits.mjs +6 -3
- package/src/runtime/office/portable/portable-xml.mjs +1 -1
- package/src/runtime/shared/agent-avatar-sprites.mjs +445 -0
- package/src/runtime/shared/agent-avatar.d.mts +64 -0
- package/src/runtime/shared/agent-avatar.mjs +320 -0
- package/src/runtime/shared/agent-route-config.d.mts +3 -0
- package/src/runtime/shared/agent-route-config.mjs +16 -0
- package/src/runtime/shared/atomic-file.mjs +36 -14
- package/src/runtime/shared/background-tasks.mjs +60 -20
- package/src/runtime/shared/child-spawn-gate.mjs +26 -16
- package/src/runtime/shared/config.mjs +8 -11
- package/src/runtime/shared/connector-accounts.mjs +15 -0
- package/src/runtime/shared/connector-catalog.d.mts +52 -0
- package/src/runtime/shared/connector-catalog.mjs +230 -0
- package/src/runtime/shared/connector-triggers.d.mts +3 -0
- package/src/runtime/shared/connector-triggers.mjs +43 -0
- package/src/runtime/shared/file-lock.mjs +7 -1
- package/src/runtime/shared/file-permissions-worker.mjs +38 -0
- package/src/runtime/shared/file-permissions.d.mts +19 -4
- package/src/runtime/shared/file-permissions.mjs +310 -4
- package/src/runtime/shared/git-runner-worker.mjs +6 -78
- package/src/runtime/shared/google-oauth-client.mjs +24 -0
- package/src/runtime/shared/held-request-host.d.mts +12 -0
- package/src/runtime/shared/held-request-host.mjs +71 -0
- package/src/runtime/shared/llm/http-agent.mjs +7 -3
- package/src/runtime/shared/llm/provider-catalog-registry.mjs +10 -0
- package/src/runtime/shared/llm/usage-accounting.mjs +2 -0
- package/src/runtime/shared/llm/usage-ledger-import.mjs +32 -3
- package/src/runtime/shared/llm/usage-ledger-rollup.mjs +12 -5
- package/src/runtime/shared/llm/usage-ledger-worker.mjs +1 -1
- package/src/runtime/shared/llm/usage-ledger.mjs +20 -5
- package/src/runtime/shared/memory-snapshot.mjs +5 -1
- package/src/runtime/shared/notify-trace.mjs +8 -1
- package/src/runtime/shared/plain-preview.d.mts +1 -0
- package/src/runtime/shared/plain-preview.mjs +28 -0
- package/src/runtime/shared/pristine-execution-contract.json +5 -1
- package/src/runtime/shared/process-runner-worker.mjs +7 -0
- package/src/runtime/shared/process-runner.mjs +38 -0
- package/src/runtime/shared/provider-accounts.mjs +2 -1
- package/src/runtime/shared/provider-api-key.mjs +0 -1
- package/src/runtime/shared/read-row-numbers.mjs +1 -1
- package/src/runtime/shared/resource-admission.mjs +60 -18
- package/src/runtime/shared/routine-limits.d.mts +11 -0
- package/src/runtime/shared/routine-limits.mjs +21 -0
- package/src/runtime/shared/routine-trigger.d.mts +10 -0
- package/src/runtime/shared/routine-trigger.mjs +44 -0
- package/src/runtime/shared/run-child.mjs +84 -0
- package/src/runtime/shared/schedule-model-ref.mjs +9 -10
- package/src/runtime/shared/session-activity-signal.mjs +23 -0
- package/src/runtime/shared/shell-output-summary.mjs +23 -0
- package/src/runtime/shared/singleton-owner.mjs +126 -42
- package/src/runtime/shared/slow-span.mjs +4 -12
- package/src/runtime/shared/statusline/statusline-segments.mjs +13 -1
- package/src/runtime/shared/tool-output-limit.mjs +4 -2
- package/src/runtime/shared/tool-output-marker.mjs +58 -0
- package/src/runtime/shared/transcript-metadata.mjs +10 -0
- package/src/runtime/shared/turn-changes.mjs +0 -8
- package/src/runtime/shared/usage-readout-totals.mjs +56 -0
- package/src/runtime/shared/user-data-guard.mjs +131 -34
- package/src/runtime/shared/worktrees/activity-gate.mjs +33 -8
- package/src/runtime/shared/worktrees/git-head.mjs +20 -2
- package/src/runtime/shared/worktrees/lock.mjs +3 -2
- package/src/runtime/terminal-bridge/tool-defs.mjs +1 -1
- package/src/runtime/tidy/runners/biome.mjs +3 -1
- package/src/runtime/tidy/shadow.mjs +2 -1
- package/src/runtime/web-search/index.mjs +5 -7
- package/src/runtime/web-search/lib/formatter.mjs +42 -6
- package/src/runtime/web-search/tool-defs.mjs +6 -1
- package/src/session-runtime/agent-home-delivery.mjs +459 -0
- package/src/session-runtime/agent-profile-changes.mjs +21 -0
- package/src/session-runtime/agent-room-delivery.mjs +92 -0
- package/src/session-runtime/agent-task-sessions.mjs +200 -0
- package/src/session-runtime/boot/apis.mjs +50 -18
- package/src/session-runtime/boot/facade.mjs +5 -8
- package/src/session-runtime/boot/infrastructure.mjs +28 -17
- package/src/session-runtime/boot/tools.mjs +3 -0
- package/src/session-runtime/channel-config-api.mjs +53 -5
- package/src/session-runtime/cwd-plugins/core-memory-context.mjs +36 -20
- package/src/session-runtime/cwd-plugins/plugin-status.mjs +6 -1
- package/src/session-runtime/held-requests.mjs +228 -0
- package/src/session-runtime/internal-tool-executor/ask-user-tool.mjs +121 -0
- package/src/session-runtime/internal-tool-executor/feature-tools.mjs +5 -1
- package/src/session-runtime/internal-tool-executor/knowledge-tools.mjs +31 -2
- package/src/session-runtime/internal-tool-executor/workspace-tools.mjs +31 -2
- package/src/session-runtime/internal-tool-executor.mjs +2 -0
- package/src/session-runtime/lifecycle/session-catalog.mjs +9 -0
- package/src/session-runtime/lifecycle/session-switch/context-switch.mjs +4 -3
- package/src/session-runtime/lifecycle/session-switch/resume.mjs +11 -4
- package/src/session-runtime/lifecycle/session-switch/session-delete.mjs +3 -3
- package/src/session-runtime/lifecycle/shared.mjs +25 -2
- package/src/session-runtime/lifecycle/teardown/session-close.mjs +2 -0
- package/src/session-runtime/lifecycle/teardown.mjs +5 -2
- package/src/session-runtime/lifecycle-api.mjs +3 -1
- package/src/session-runtime/mcp-glue/server-catalog.mjs +8 -0
- package/src/session-runtime/mcp-glue/server-input.mjs +3 -0
- package/src/session-runtime/mcp-oauth-login.mjs +112 -15
- package/src/session-runtime/model-route/set-route.mjs +14 -3
- package/src/session-runtime/owned-automation-run.mjs +229 -0
- package/src/session-runtime/provider-auth/login.mjs +10 -6
- package/src/session-runtime/provider-auth/setup.mjs +19 -2
- package/src/session-runtime/provider-catalog-cache.mjs +101 -25
- package/src/session-runtime/provider-models/catalog-load.mjs +14 -3
- package/src/session-runtime/provider-models/row-hydration.mjs +4 -0
- package/src/session-runtime/provider-models.mjs +76 -10
- package/src/session-runtime/provider-readiness.mjs +4 -0
- package/src/session-runtime/resource-api.mjs +5 -0
- package/src/session-runtime/resource-mcp-api.mjs +107 -13
- package/src/session-runtime/resource-skills-api.mjs +12 -0
- package/src/session-runtime/room-member-gate.mjs +48 -0
- package/src/session-runtime/routine-home-turn.mjs +156 -0
- package/src/session-runtime/routine-owner.mjs +48 -0
- package/src/session-runtime/schedule-session-run.mjs +30 -64
- package/src/session-runtime/services/agent-tool/close-flow.mjs +2 -1
- package/src/session-runtime/services/agent-tool/helpers.mjs +10 -0
- package/src/session-runtime/services/agent-tool/job-views/agent-list.mjs +10 -2
- package/src/session-runtime/services/agent-tool/job-views/session-progress.mjs +13 -4
- package/src/session-runtime/services/agent-tool/notify.mjs +15 -4
- package/src/session-runtime/services/agent-tool/restart-notify.mjs +134 -0
- package/src/session-runtime/services/agent-tool/send-flow.mjs +24 -1
- package/src/session-runtime/services/agent-tool/spawn-flow/deferred-spawn.mjs +2 -1
- package/src/session-runtime/services/agent-tool/spawn-flow/job-start.mjs +4 -3
- package/src/session-runtime/services/agent-tool/spawn-flow/spawn-plan.mjs +19 -4
- package/src/session-runtime/services/agent-tool/spawn-flow/spawn-prep.mjs +2 -0
- package/src/session-runtime/services/agent-tool/spawn-flow/spawn-run.mjs +5 -1
- package/src/session-runtime/services/agent-tool/tag-registry/tag-lookup.mjs +8 -3
- package/src/session-runtime/services/agent-tool/tag-registry.mjs +3 -0
- package/src/session-runtime/services/agent-tool/worker-index/row-shape.mjs +4 -0
- package/src/session-runtime/services/agent-tool/worker-index.mjs +44 -10
- package/src/session-runtime/services/agent-tool.mjs +20 -0
- package/src/session-runtime/services/channel-admin.mjs +93 -24
- package/src/session-runtime/services/memory-access.mjs +37 -0
- package/src/session-runtime/services/provider-admin.mjs +166 -59
- package/src/session-runtime/services/schedule-access.mjs +91 -0
- package/src/session-runtime/services/usage-dashboard-model.mjs +2 -2
- package/src/session-runtime/services/usage-dashboard.mjs +27 -69
- package/src/session-runtime/session-create/session-options.mjs +34 -7
- package/src/session-runtime/session-hooks.mjs +16 -8
- package/src/session-runtime/setup-tool/executor.mjs +68 -6
- package/src/session-runtime/setup-tool/extended-actions.mjs +72 -14
- package/src/session-runtime/setup-tool/settings-contract.mjs +8 -6
- package/src/session-runtime/setup-tool/tool-defs.mjs +8 -0
- package/src/session-runtime/skills-api.mjs +16 -1
- package/src/session-runtime/tool-policy-refresh.mjs +238 -29
- package/src/session-runtime/turn/session-ops.mjs +10 -3
- package/src/session-runtime/turn/turn-run/ask-callbacks.mjs +11 -2
- package/src/session-runtime/turn/turn-run/turn-prepare.mjs +3 -0
- package/src/session-runtime/usage-stats-api.mjs +6 -1
- package/src/session-runtime/webhook-session-run.mjs +49 -67
- package/src/session-runtime/workflow-agents-api/agent-connectors.mjs +239 -0
- package/src/session-runtime/workflow-agents-api/agent-editor.mjs +556 -22
- package/src/session-runtime/workflow-agents-api/workflow-packs.mjs +11 -1
- package/src/session-runtime/workflow-agents-api.mjs +4 -1
- package/src/standalone/channel-transport/http-routes.mjs +10 -9
- package/src/standalone/daemon-agent-control.mjs +1 -8
- package/src/standalone/daemon-desktop-runtime.mjs +20 -7
- package/src/standalone/daemon-owner.mjs +117 -25
- package/src/standalone/daemon-session-control.mjs +11 -2
- package/src/standalone/daemon.mjs +61 -17
- package/src/standalone/session-call-cache.mjs +6 -0
- package/src/standalone/session-protocol.mjs +28 -1
- package/src/standalone/session-service/retention/busy.mjs +7 -2
- package/src/standalone/session-service/session-calls/session-turns.mjs +9 -0
- package/src/standalone/session-service.mjs +19 -0
- package/src/standalone/session-transport/routes.mjs +7 -6
- package/src/tui/app/model-options.mjs +39 -2
- package/src/tui/app/model-picker.mjs +16 -2
- package/src/tui/app/provider-setup-picker.mjs +1 -2
- package/src/tui/app/slash-commands.mjs +20 -0
- package/src/tui/dist/index.mjs +487 -128
- package/src/tui/session/context-state.mjs +6 -1
- package/src/tui/session/notice-surface.mjs +35 -4
- package/src/tui/session/notice-text.mjs +0 -1
- package/src/tui/session/queue-helpers.mjs +2 -1
- package/src/tui/session/session-api/extensions.mjs +4 -0
- package/src/tui/session/session-api/intake/submission.mjs +12 -13
- package/src/tui/session/session-api/integrations.mjs +9 -5
- package/src/tui/session/session-api/lifecycle/session-reset.mjs +13 -3
- package/src/tui/session/session-api/lifecycle.mjs +17 -1
- package/src/tui/session/session-api/routes.mjs +48 -3
- package/src/tui/session/session-flow/drain.mjs +20 -3
- package/src/tui/session/session-flow/queue/entry-shape.mjs +2 -1
- package/src/tui/session/session-flow/queue/take-entries.mjs +3 -2
- package/src/tui/session/session-flow/steering.mjs +104 -24
- package/src/tui/session/session-flow.mjs +2 -0
- package/src/tui/session/tool-approval.mjs +26 -5
- package/src/tui/session/transcript-intake.mjs +6 -2
- package/src/tui/session/transcript-spill.mjs +5 -1
- package/src/tui/session/tui-steering-persist.mjs +65 -42
- package/src/tui/session/turn-live-call.mjs +56 -0
- package/src/tui/session/turn-stream.mjs +33 -0
- package/src/tui/session/turn.mjs +50 -7
- package/src/ui/statusline.mjs +1 -6
- package/src/ui/streaming-markdown-heal.mjs +24 -7
- package/src/vendor/statusline/bin/statusline-route.mjs +0 -1
- package/src/vendor/statusline/src/gateway/route-meta.mjs +0 -1
- package/src/workflows/default/WORKFLOW.md +9 -13
- package/src/session-runtime/automation-agents.mjs +0 -94
- package/src/session-runtime/automation-workflow.mjs +0 -59
|
@@ -9,6 +9,7 @@ import { readFile } from 'node:fs/promises';
|
|
|
9
9
|
import { sameFileVersion } from './file-version.mjs';
|
|
10
10
|
import { canCacheReadFile, READ_PREFIX_HASH_BYTES } from './read-cache-policy.mjs';
|
|
11
11
|
import { decodeUtf16Body, isUtf16Encoding } from './snapshot-helpers.mjs';
|
|
12
|
+
import { capMarker, windowMarker } from './tool-output-limit.mjs';
|
|
12
13
|
|
|
13
14
|
/**
|
|
14
15
|
* Encoding-aware decode (fresh read AND raw-content cache hit both flow
|
|
@@ -51,17 +52,27 @@ function rangeFooter(ctx, render, rendered) {
|
|
|
51
52
|
const emittedStart = offset + 1;
|
|
52
53
|
if (byteCapTruncated) {
|
|
53
54
|
const emittedEnd = offset + renderedLineCount;
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
55
|
+
return {
|
|
56
|
+
footer: windowMarker({
|
|
57
|
+
from: emittedStart,
|
|
58
|
+
to: emittedEnd,
|
|
59
|
+
total: lineCount,
|
|
60
|
+
capBytes: readMaxOutputBytes,
|
|
61
|
+
nextOffset: emittedEnd < lineCount ? emittedEnd + readOffsetBase : null,
|
|
62
|
+
}),
|
|
63
|
+
};
|
|
57
64
|
}
|
|
58
65
|
if (Buffer.byteLength(rendered, 'utf8') > readMaxOutputBytes) return {};
|
|
59
66
|
const emittedEnd = offset + sliced.length;
|
|
60
67
|
// Continuation uses the originating caller's coordinate base. Remaining
|
|
61
68
|
// content is not automatically required evidence; when it is, it fits one
|
|
62
69
|
// wider read, not a walk window by window.
|
|
63
|
-
const
|
|
64
|
-
|
|
70
|
+
const footer = windowMarker({
|
|
71
|
+
from: emittedStart,
|
|
72
|
+
to: emittedEnd,
|
|
73
|
+
total: lineCount,
|
|
74
|
+
nextOffset: emittedEnd < lineCount ? emittedEnd + readOffsetBase : null,
|
|
75
|
+
});
|
|
65
76
|
return { footer: widenNote ? `${footer}\n${widenNote}` : footer };
|
|
66
77
|
}
|
|
67
78
|
|
|
@@ -110,8 +121,15 @@ function renderReadWindow(content, ctx, helpers) {
|
|
|
110
121
|
const slice = byteCapSlice(rendered, readMaxOutputBytes);
|
|
111
122
|
render.renderedLineCount = Math.max(0, slice.split('\n').length - 1);
|
|
112
123
|
render.byteCapTruncated = true;
|
|
113
|
-
const ofFile = st?.size > 0 ?
|
|
114
|
-
|
|
124
|
+
const ofFile = st?.size > 0 ? `${Math.max(1, Math.round(st.size / 1024))} KB file` : '';
|
|
125
|
+
// A windowed read's footer already carries the cap and continuation.
|
|
126
|
+
out = hasRangeArgs
|
|
127
|
+
? slice
|
|
128
|
+
: `${slice}\n\n${capMarker({
|
|
129
|
+
limit: readMaxOutputBytes,
|
|
130
|
+
shown: ofFile ? `${render.renderedLineCount} of ${lineCount} lines shown (${ofFile})` : '',
|
|
131
|
+
nextOffset: offset + render.renderedLineCount + ctx.readOffsetBase,
|
|
132
|
+
})}`;
|
|
115
133
|
} else {
|
|
116
134
|
out = rendered;
|
|
117
135
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { readFile, stat } from 'node:fs/promises';
|
|
2
2
|
import { MAX_PDF_BYTES, MAX_PDF_PAGES, isPdfBuffer } from '../../../../attachments/limits.mjs';
|
|
3
3
|
import { READ_MAX_SIZE_BYTES } from './read-constants.mjs';
|
|
4
|
+
import { capMarker } from './tool-output-limit.mjs';
|
|
4
5
|
import { imageBlocksFromBuffer } from './read-image-resize.mjs';
|
|
5
6
|
import { inspectPdfBuffer } from '../../../../attachments/pdf-extract.mjs';
|
|
6
7
|
|
|
@@ -190,7 +191,7 @@ function capNotebookText(blocks, maxOutputBytes) {
|
|
|
190
191
|
if (truncated) {
|
|
191
192
|
capped.push({
|
|
192
193
|
type: 'text',
|
|
193
|
-
text: `\n\n
|
|
194
|
+
text: `\n\n${capMarker({ limit: maxOutputBytes, shown: 'notebook cells cut', narrow: 'grep the notebook for the cell text you need' })}`,
|
|
194
195
|
});
|
|
195
196
|
}
|
|
196
197
|
return capped;
|
|
@@ -27,7 +27,9 @@ const bodyTextFor = (value) => {
|
|
|
27
27
|
.join('\n');
|
|
28
28
|
};
|
|
29
29
|
|
|
30
|
-
|
|
30
|
+
// The smart-read cap marker (read-formatting.mjs buildSmartReadTruncationMarker):
|
|
31
|
+
// `[output capped at X; lines a-b of N omitted (K KB file); offset:a for more]`.
|
|
32
|
+
const TRUNCATED_MARKER = /\[output capped at [^;\]]+; lines \d+-\d+ of (\d+) omitted \((\d+) KB file\);/;
|
|
31
33
|
// When `read` emitted a smart-cap marker, surface the truncation state in the
|
|
32
34
|
// header so downstream skimming spots it without parsing the body.
|
|
33
35
|
const truncatedSuffix = (textBody) => {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { statSync } from 'node:fs';
|
|
2
|
+
import { capMarker, windowMarker } from './tool-output-limit.mjs';
|
|
2
3
|
import { isAbsolute, resolve } from 'node:path';
|
|
3
4
|
import { expandAbsoluteGlobs } from './lib/absolute-glob-expand.mjs';
|
|
4
5
|
import { buildGlobPatternGroups } from './lib/glob-static-prefix.mjs';
|
|
@@ -407,17 +408,22 @@ async function orderGlobPaths(unique, { nativeMtimeTotal, groupCount, sortMode,
|
|
|
407
408
|
|
|
408
409
|
function globIntegritySuffix({ accumTruncated, rgStdoutTruncated, rgStdoutPartial, rgErrors }) {
|
|
409
410
|
let truncSuffix = '';
|
|
410
|
-
|
|
411
|
-
if (
|
|
411
|
+
const narrow = 'narrow the pattern or path';
|
|
412
|
+
if (accumTruncated) {
|
|
413
|
+
truncSuffix = `\n${capMarker({ limit: `${GLOB_ACCUM_CAP} matches`, shown: 'results incomplete', narrow })}`;
|
|
414
|
+
}
|
|
415
|
+
if (rgStdoutTruncated) {
|
|
416
|
+
truncSuffix += `\n${capMarker({ limit: '20 MB of rg stdout', shown: 'results incomplete', narrow })}`;
|
|
417
|
+
}
|
|
412
418
|
if (rgStdoutPartial) truncSuffix += '\n... [warning] rg exit 2 (partial results); listing may be incomplete';
|
|
413
419
|
const errorSuffix = rgErrors.length > 0 ? `\n... [warning] ${rgErrors.join(' | ')}` : '';
|
|
414
420
|
return errorSuffix + truncSuffix;
|
|
415
421
|
}
|
|
416
422
|
|
|
417
|
-
function globMoreSuffix({ rgWindowIncomplete, remaining, totalBeforeOffset, nextOffset }) {
|
|
418
|
-
if (rgWindowIncomplete) return `\n
|
|
423
|
+
function globMoreSuffix({ rgWindowIncomplete, remaining, totalBeforeOffset, from, nextOffset }) {
|
|
424
|
+
if (rgWindowIncomplete) return `\n${windowMarker({ unit: 'entries', from, to: nextOffset, nextOffset })}`;
|
|
419
425
|
if (remaining > 0) {
|
|
420
|
-
return `\n
|
|
426
|
+
return `\n${windowMarker({ unit: 'entries', from, to: nextOffset, total: totalBeforeOffset, nextOffset })}`;
|
|
421
427
|
}
|
|
422
428
|
return '';
|
|
423
429
|
}
|
|
@@ -489,6 +495,7 @@ async function renderGlobPage(scan, merged, { capped, remaining, totalBeforeOffs
|
|
|
489
495
|
rgWindowIncomplete: merged.rgWindowIncomplete,
|
|
490
496
|
remaining,
|
|
491
497
|
totalBeforeOffset,
|
|
498
|
+
from: offset + 1,
|
|
492
499
|
nextOffset: offset + capped.length,
|
|
493
500
|
});
|
|
494
501
|
if (capped.length > 0) return `${capped.join('\n')}${moreSuffix}${errSuffix}`;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createHash } from 'node:crypto';
|
|
2
|
-
import { isAbsolute } from 'node:path';
|
|
2
|
+
import { isAbsolute, resolve as resolvePath, sep as pathSep } from 'node:path';
|
|
3
3
|
import {
|
|
4
4
|
coerceReadFamilyPathArg,
|
|
5
5
|
GREP_AUTO_CONTEXT_LINES,
|
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
appendNotFoundCwdNote,
|
|
13
13
|
buildNotFoundHintBody,
|
|
14
14
|
finalizeReadFamilyEnoentTail,
|
|
15
|
+
realRootOf,
|
|
15
16
|
} from './search-path-diagnostics.mjs';
|
|
16
17
|
import { buildGrepCacheKey, buildGrepRgArgs } from './search-builders.mjs';
|
|
17
18
|
import { runRg, runRgWindowedLines } from './native-search-runner.mjs';
|
|
@@ -498,10 +499,7 @@ async function runLegacyContextGrep(scope, rgArgs) {
|
|
|
498
499
|
async function runPlainGrep(scope, rgArgs, effectiveHeadLimit) {
|
|
499
500
|
const { options, outputMode, headLimit, offset, cacheKey, grepResolvedPath } = scope;
|
|
500
501
|
const window = await collectGrepWindow(scope, rgArgs, effectiveHeadLimit);
|
|
501
|
-
|
|
502
|
-
if (outputMode === 'files_with_matches' && windowed.length > 1) {
|
|
503
|
-
windowed = await sortNewestFirst(windowed, scope.rgSpawnCwd);
|
|
504
|
-
}
|
|
502
|
+
const { windowed } = window;
|
|
505
503
|
let body = formatGrepOutput({
|
|
506
504
|
windowed,
|
|
507
505
|
totalWindowed: window.totalWindowed,
|
|
@@ -542,7 +540,12 @@ async function runPlainGrep(scope, rgArgs, effectiveHeadLimit) {
|
|
|
542
540
|
// complete.
|
|
543
541
|
async function collectGrepWindow(scope, rgArgs, effectiveHeadLimit) {
|
|
544
542
|
const { outputMode, offset, searchExecOptions } = scope;
|
|
545
|
-
|
|
543
|
+
// The native scan emits content matches in inventory path order (stable, so
|
|
544
|
+
// the streamed content window pages directly). File lists are presented
|
|
545
|
+
// newest-first, so they are pageable only once the WHOLE set is re-ordered;
|
|
546
|
+
// `offset` slices after the sort.
|
|
547
|
+
const fileList = outputMode === 'files_with_matches' || outputMode === 'count';
|
|
548
|
+
if (effectiveHeadLimit !== Infinity && !fileList) {
|
|
546
549
|
const streamed = await runRgWindowedLines(rgArgs, searchExecOptions, {
|
|
547
550
|
offset,
|
|
548
551
|
limit: effectiveHeadLimit,
|
|
@@ -556,7 +559,8 @@ async function collectGrepWindow(scope, rgArgs, effectiveHeadLimit) {
|
|
|
556
559
|
};
|
|
557
560
|
}
|
|
558
561
|
const stdout = await runRg(rgArgs, searchExecOptions);
|
|
559
|
-
|
|
562
|
+
let allLines = String(stdout).split('\n').filter(Boolean);
|
|
563
|
+
if (fileList) allLines = await orderFileListLines(unifyRootSpelling(allLines, scope), outputMode, scope.rgSpawnCwd);
|
|
560
564
|
const windowed = offset > 0 ? allLines.slice(offset) : allLines;
|
|
561
565
|
const boxed = typeof stdout === 'object' ? stdout : {};
|
|
562
566
|
return {
|
|
@@ -568,25 +572,60 @@ async function collectGrepWindow(scope, rgArgs, effectiveHeadLimit) {
|
|
|
568
572
|
};
|
|
569
573
|
}
|
|
570
574
|
|
|
571
|
-
//
|
|
572
|
-
//
|
|
573
|
-
//
|
|
574
|
-
//
|
|
575
|
-
//
|
|
576
|
-
|
|
577
|
-
|
|
575
|
+
// A search root reached through a symlink or junction (macOS /var ->
|
|
576
|
+
// /private/var) can come back from the walker under either spelling: rows that
|
|
577
|
+
// the inventory repaired after a filesystem event carry the realpath while the
|
|
578
|
+
// rest carry the given root. Left alone they sort apart, page twice and print
|
|
579
|
+
// as absolute paths. Rewrite realpath rows to the given spelling, then drop the
|
|
580
|
+
// repeats, so ordering, offset paging and relativization see one spelling.
|
|
581
|
+
function unifyRootSpelling(lines, scope) {
|
|
582
|
+
const given = scope.searchPath;
|
|
583
|
+
const absGiven = resolvePath(scope.rgSpawnCwd, given);
|
|
584
|
+
const real = realRootOf(absGiven);
|
|
585
|
+
if (real === absGiven) return lines;
|
|
586
|
+
const fold = (text) => (process.platform === 'win32' ? text.toLocaleLowerCase() : text);
|
|
587
|
+
const realPrefix = fold(`${real.replace(/\\/g, '/').replace(/\/+$/, '')}/`);
|
|
588
|
+
const sep = given.includes('/') && !given.includes('\\') ? '/' : pathSep;
|
|
589
|
+
const givenPrefix = `${given.replace(/[\\/]+$/, '')}${sep}`;
|
|
590
|
+
const seen = new Set();
|
|
591
|
+
const unified = [];
|
|
592
|
+
for (const line of lines) {
|
|
593
|
+
const fwd = line.replace(/\\/g, '/');
|
|
594
|
+
const row = fold(fwd).startsWith(realPrefix)
|
|
595
|
+
? givenPrefix + fwd.slice(realPrefix.length).replace(/\//g, sep)
|
|
596
|
+
: line;
|
|
597
|
+
if (seen.has(row)) continue;
|
|
598
|
+
seen.add(row);
|
|
599
|
+
unified.push(row);
|
|
600
|
+
}
|
|
601
|
+
return unified;
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
function compareCodeUnits(a, b) {
|
|
605
|
+
if (a === b) return 0;
|
|
606
|
+
return a < b ? -1 : 1;
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
// Total order over the complete file list, applied BEFORE offset/limit slice:
|
|
610
|
+
// files_with_matches is newest-first (mtime desc, path tiebreak) so recently
|
|
611
|
+
// edited files surface first; stat-failed entries follow in path order
|
|
612
|
+
// (statting is bounded: 64 workers, 5s deadline). count lines sort by path.
|
|
613
|
+
async function orderFileListLines(lines, outputMode, rgSpawnCwd) {
|
|
614
|
+
if (lines.length < 2) return lines;
|
|
615
|
+
if (outputMode === 'count') {
|
|
616
|
+
const pathOf = (line) => parseGrepCountLine(line)?.path ?? line;
|
|
617
|
+
return [...lines].sort((a, b) => compareCodeUnits(pathOf(a), pathOf(b)) || compareCodeUnits(a, b));
|
|
618
|
+
}
|
|
619
|
+
const withStat = await statPathsForMtime(lines, rgSpawnCwd, 64, { deadlineMs: 5000 });
|
|
578
620
|
const statted = [];
|
|
579
621
|
const unstatted = [];
|
|
580
|
-
for (let i = 0; i <
|
|
622
|
+
for (let i = 0; i < lines.length; i++) {
|
|
581
623
|
const entry = withStat[i];
|
|
582
|
-
if (entry?.stat) statted.push({ line:
|
|
583
|
-
else unstatted.push(
|
|
624
|
+
if (entry?.stat) statted.push({ line: lines[i], mtimeMs: entry.mtimeMs });
|
|
625
|
+
else unstatted.push(lines[i]);
|
|
584
626
|
}
|
|
585
|
-
statted.sort((a, b) =>
|
|
586
|
-
|
|
587
|
-
if (dm !== 0) return dm;
|
|
588
|
-
return a.line.localeCompare(b.line);
|
|
589
|
-
});
|
|
627
|
+
statted.sort((a, b) => b.mtimeMs - a.mtimeMs || compareCodeUnits(a.line, b.line));
|
|
628
|
+
unstatted.sort(compareCodeUnits);
|
|
590
629
|
return [...statted.map((e) => e.line), ...unstatted];
|
|
591
630
|
}
|
|
592
631
|
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* grep/glob in search-tool.mjs. search-tool.mjs re-exports these for unchanged
|
|
4
4
|
* importers so it can stay a thin executor.
|
|
5
5
|
*/
|
|
6
|
-
import { statSync } from 'node:fs';
|
|
6
|
+
import { realpathSync, statSync } from 'node:fs';
|
|
7
7
|
import { basename, dirname, isAbsolute, join, relative, resolve, sep } from 'node:path';
|
|
8
8
|
import { findDirectoryByBasename, findFileByBasename, listSiblings } from './path-diagnostics.mjs';
|
|
9
9
|
import { normalizeOutputPath, resolveAgainstCwd } from './path-utils.mjs';
|
|
@@ -233,16 +233,40 @@ export async function buildNotFoundHintBody(workDir, missingPath, actionVerb, er
|
|
|
233
233
|
return elsewhere.exhaustive === true ? ENOENT_ABSENT : '';
|
|
234
234
|
}
|
|
235
235
|
|
|
236
|
+
// The realpath of a working directory, memoized: a search root reached through
|
|
237
|
+
// a symlink or junction (macOS /var -> /private/var) is reported by the walker
|
|
238
|
+
// under its realpath, so results must be relativized against both spellings.
|
|
239
|
+
const realRootCache = new Map();
|
|
240
|
+
const REAL_ROOT_CACHE_MAX = 64;
|
|
241
|
+
export function realRootOf(root) {
|
|
242
|
+
if (realRootCache.has(root)) return realRootCache.get(root);
|
|
243
|
+
let real;
|
|
244
|
+
try {
|
|
245
|
+
real = realpathSync.native(root);
|
|
246
|
+
} catch {
|
|
247
|
+
return root;
|
|
248
|
+
}
|
|
249
|
+
if (realRootCache.size >= REAL_ROOT_CACHE_MAX) realRootCache.clear();
|
|
250
|
+
realRootCache.set(root, real);
|
|
251
|
+
return real;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
function relativeUnderRoot(absFwd, root) {
|
|
255
|
+
const rootFwd = root.replace(/\\/g, '/').replace(/\/+$/, '');
|
|
256
|
+
const haystack = process.platform === 'win32' ? absFwd.toLocaleLowerCase() : absFwd;
|
|
257
|
+
const needle = process.platform === 'win32' ? rootFwd.toLocaleLowerCase() : rootFwd;
|
|
258
|
+
if (haystack.startsWith(`${needle}/`) || haystack === needle) return absFwd.slice(rootFwd.length + 1) || '.';
|
|
259
|
+
return null;
|
|
260
|
+
}
|
|
261
|
+
|
|
236
262
|
export function relativePathPrefix(pathPrefix, workDir) {
|
|
237
263
|
if (!workDir) return pathPrefix;
|
|
238
|
-
const cwdFwd = workDir.replace(/\\/g, '/').replace(/\/+$/, '');
|
|
239
264
|
const absFwd = String(pathPrefix || '').replace(/\\/g, '/');
|
|
240
|
-
const
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
return pathPrefix;
|
|
265
|
+
const given = relativeUnderRoot(absFwd, workDir);
|
|
266
|
+
if (given !== null) return given;
|
|
267
|
+
const real = realRootOf(workDir);
|
|
268
|
+
if (real === workDir) return pathPrefix;
|
|
269
|
+
return relativeUnderRoot(absFwd, real) ?? pathPrefix;
|
|
246
270
|
}
|
|
247
271
|
|
|
248
272
|
export function relativeSearchResultPath(path, workDir) {
|
|
@@ -4,7 +4,9 @@ import { isAbsolute, join, resolve } from 'node:path';
|
|
|
4
4
|
import { tmpdir } from 'node:os';
|
|
5
5
|
import { randomUUID } from 'node:crypto';
|
|
6
6
|
import { isBuiltin } from 'node:module';
|
|
7
|
+
import { Script } from 'node:vm';
|
|
7
8
|
import { cwdRelativePath, normalizeInputPath, normalizeOutputPath, resolveAgainstCwd } from './path-utils.mjs';
|
|
9
|
+
import { codeMask } from './bash-tool/bashism-rewrite.mjs';
|
|
8
10
|
|
|
9
11
|
// Hard-block patterns live exclusively in ../shell-policy.mjs (BLOCKED_PATTERNS /
|
|
10
12
|
// isBlockedCommand). This lighter detector only classifies cache invalidation
|
|
@@ -35,6 +37,14 @@ const SHELL_GLOBAL_MUTATORS = new Set([
|
|
|
35
37
|
'dd',
|
|
36
38
|
]);
|
|
37
39
|
|
|
40
|
+
// Whether a backslash escapes the character after it: anything outside quotes,
|
|
41
|
+
// only `"` or `\` inside double quotes, never inside single quotes (`'C:\'` is
|
|
42
|
+
// a closed string, not an escaped quote).
|
|
43
|
+
function startsEscape(quote, next) {
|
|
44
|
+
if (quote === "'") return false;
|
|
45
|
+
return quote === '"' ? next === '"' || next === '\\' : true;
|
|
46
|
+
}
|
|
47
|
+
|
|
38
48
|
export function shellSplitSegments(command) {
|
|
39
49
|
const parts = [];
|
|
40
50
|
let current = '';
|
|
@@ -47,7 +57,7 @@ export function shellSplitSegments(command) {
|
|
|
47
57
|
escaped = false;
|
|
48
58
|
continue;
|
|
49
59
|
}
|
|
50
|
-
if (ch === '\\') {
|
|
60
|
+
if (ch === '\\' && startsEscape(quote, command[i + 1])) {
|
|
51
61
|
current += ch;
|
|
52
62
|
escaped = true;
|
|
53
63
|
continue;
|
|
@@ -83,20 +93,24 @@ export function shellTokenize(segment) {
|
|
|
83
93
|
const tokens = [];
|
|
84
94
|
let current = '';
|
|
85
95
|
let quote = null;
|
|
86
|
-
let escaped = false;
|
|
87
96
|
const push = () => {
|
|
88
97
|
if (current !== '') tokens.push(current);
|
|
89
98
|
current = '';
|
|
90
99
|
};
|
|
91
100
|
for (let i = 0; i < segment.length; i++) {
|
|
92
101
|
const ch = segment[i];
|
|
93
|
-
if (
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
102
|
+
if (ch === '\\' && quote !== "'") {
|
|
103
|
+
// A backslash escapes only what a shell would need it for. Before an
|
|
104
|
+
// ordinary character it is a path separator (`C:\Users\x`, `\\server\s`),
|
|
105
|
+
// which dropping would turn into a different, nonexistent path.
|
|
106
|
+
const next = segment[i + 1];
|
|
107
|
+
const escapes = quote === '"' ? next === '"' : next !== undefined && /[\s"'$`;&|<>()*?]/.test(next);
|
|
108
|
+
if (!escapes) {
|
|
109
|
+
current += ch;
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
current += next;
|
|
113
|
+
i++;
|
|
100
114
|
continue;
|
|
101
115
|
}
|
|
102
116
|
if (quote) {
|
|
@@ -228,7 +242,7 @@ export function shellSplitPipelineSegments(segment) {
|
|
|
228
242
|
escaped = false;
|
|
229
243
|
continue;
|
|
230
244
|
}
|
|
231
|
-
if (ch === '\\') {
|
|
245
|
+
if (ch === '\\' && startsEscape(quote, segment[i + 1])) {
|
|
232
246
|
current += ch;
|
|
233
247
|
escaped = true;
|
|
234
248
|
continue;
|
|
@@ -518,6 +532,17 @@ const MSYS_ABS_PATH_RE = /(^|[\s"'=,(])\/([A-Za-z])\/([^\s"'`|&;<>()]*)/g;
|
|
|
518
532
|
// the drive-rewrite and bash-ism regexes against the mask so quoted literals /
|
|
519
533
|
// regex patterns are never treated as real path tokens or connectors.
|
|
520
534
|
function maskQuotedRegions(command) {
|
|
535
|
+
// The full PowerShell tokeniser first: it knows here-strings, comments and
|
|
536
|
+
// doubled quotes, which a bare quote-pair scan reads wrongly (an apostrophe
|
|
537
|
+
// inside `@'…'@` or a `# don't` comment flips every region after it). It
|
|
538
|
+
// declines on text it cannot tokenise with confidence; the pair scan below
|
|
539
|
+
// then keeps the previous behaviour.
|
|
540
|
+
const codeOnly = codeMask(command);
|
|
541
|
+
if (codeOnly) {
|
|
542
|
+
let masked = '';
|
|
543
|
+
for (let i = 0; i < command.length; i++) masked += codeOnly[i] ? command[i] : '\0';
|
|
544
|
+
return masked;
|
|
545
|
+
}
|
|
521
546
|
const FILL = '\0';
|
|
522
547
|
let out = '';
|
|
523
548
|
let quote = null;
|
|
@@ -655,6 +680,12 @@ export function hasPowerShellOnlySyntax(command) {
|
|
|
655
680
|
const INLINE_SCRIPT_RE =
|
|
656
681
|
/\b(node(?:\.exe)?|python3?|py)((?:\s+--?[\w-]+(?:=[^\s"']+)?)*)\s+(-e|--eval|-c)\s+"((?:[^"\\]|\\.)*)"/;
|
|
657
682
|
const INLINE_UNSAFE_BODY = /[$`\\]/;
|
|
683
|
+
// What may follow the closing quote for the run to be the same as a file run:
|
|
684
|
+
// end of command or a shell operator/newline. Anything else is either more
|
|
685
|
+
// argument text (`""` is an escaped quote in PowerShell, and adjacent quoted
|
|
686
|
+
// strings concatenate in bash, so the body did not end at that quote) or script
|
|
687
|
+
// arguments, which `-e` and a file run number differently in process.argv.
|
|
688
|
+
const INLINE_TRAILING_OK = /^[ \t]*(?:$|[\r\n|;&)<>]|\d+>)/;
|
|
658
689
|
const INLINE_FILE_RELATIVE =
|
|
659
690
|
/(?:require|import)\s*\(\s*['"]\.{1,2}\/|from\s+['"]\.{1,2}\/|import\.meta|__dirname|__filename|__file__|require\.main|module\.(?:filename|parent)|sys\.argv\s*\[\s*0\s*\]|sys\.path\s*\[\s*0\s*\]|argv\s*\[\s*1\s*\]/;
|
|
660
691
|
const WINDOWS_INLINE_COMMAND_FILE_THRESHOLD = 24_000;
|
|
@@ -695,21 +726,143 @@ function inlineScriptFileShape(exe, rawFlags) {
|
|
|
695
726
|
return { extension, keptFlags: flags.replace(/\s+--input-type=\w+/g, '') };
|
|
696
727
|
}
|
|
697
728
|
|
|
698
|
-
|
|
729
|
+
// `python -c` puts the working directory first on sys.path; a script file puts
|
|
730
|
+
// its own (temp) directory there. Only a body whose every import is a standard
|
|
731
|
+
// library module resolves identically from both, so any other import — a
|
|
732
|
+
// project-local module, a relative import, a dynamic import — keeps `-c`.
|
|
733
|
+
const PYTHON_STDLIB = new Set(
|
|
734
|
+
`__future__ abc argparse array ast asyncio atexit base64 bdb binascii bisect builtins bz2 cProfile calendar cmath cmd code codecs codeop collections colorsys compileall concurrent configparser contextlib contextvars copy copyreg csv ctypes curses dataclasses datetime dbm decimal difflib dis doctest email encodings ensurepip enum errno faulthandler fcntl filecmp fileinput fnmatch fractions ftplib functools gc genericpath getopt getpass gettext glob graphlib grp gzip hashlib heapq hmac html http imaplib inspect io ipaddress itertools json keyword linecache locale logging lzma mailbox marshal math mimetypes mmap modulefinder msvcrt multiprocessing netrc nt ntpath numbers opcode operator optparse os pathlib pdb pickle pickletools pkgutil platform plistlib poplib posix posixpath pprint profile pstats pty pwd py_compile pyclbr pydoc pyexpat queue quopri random re readline reprlib resource rlcompleter runpy sched secrets select selectors shelve shlex shutil signal site smtplib socket socketserver sqlite3 ssl stat statistics string stringprep struct subprocess symtable sys sysconfig syslog tabnanny tarfile tempfile termios textwrap threading time timeit tkinter token tokenize tomllib trace traceback tracemalloc tty turtle types typing unicodedata unittest urllib uuid venv warnings wave weakref webbrowser winreg winsound wsgiref xml xmlrpc zipapp zipfile zipimport zlib zoneinfo`.split(
|
|
735
|
+
' '
|
|
736
|
+
)
|
|
737
|
+
);
|
|
738
|
+
const PYTHON_DYNAMIC_IMPORT = /\b(?:__import__|importlib|exec|eval|compile|runpy)\b/;
|
|
739
|
+
function pythonImportsOutsideStdlib(body) {
|
|
740
|
+
if (PYTHON_DYNAMIC_IMPORT.test(body)) return true;
|
|
741
|
+
for (const [, module] of body.matchAll(/(?:^|[;:\n])[ \t]*from[ \t]+([.\w]+)[ \t]+import\b/g)) {
|
|
742
|
+
if (module.startsWith('.') || !PYTHON_STDLIB.has(module.split('.')[0])) return true;
|
|
743
|
+
}
|
|
744
|
+
for (const [, list] of body.matchAll(/(?:^|[;:\n])[ \t]*import[ \t]+([^;\n]+)/g)) {
|
|
745
|
+
for (const part of list.split(',')) {
|
|
746
|
+
const name = part
|
|
747
|
+
.trim()
|
|
748
|
+
.split(/\s+as\s+/)[0]
|
|
749
|
+
.trim()
|
|
750
|
+
.split('.')[0];
|
|
751
|
+
if (name && !PYTHON_STDLIB.has(name)) return true;
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
return false;
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
// `node -e` accepts ESM syntax and top-level `await` (Node's syntax detection);
|
|
758
|
+
// the `.cjs` file a hoist writes does not, so a body using either stays inline.
|
|
759
|
+
// An `await` is top-level exactly when the body no longer compiles as CommonJS.
|
|
760
|
+
const NODE_ESM_SYNTAX = /(?:^|[;{}\n])[ \t]*(?:import[ \t]*[\w$*{'"]|export[ \t])/;
|
|
761
|
+
function nodeBodyNeedsModuleSyntax(body) {
|
|
762
|
+
if (NODE_ESM_SYNTAX.test(body)) return true;
|
|
763
|
+
if (!/\bawait\b/.test(body)) return false;
|
|
764
|
+
try {
|
|
765
|
+
new Script(`(function (exports, require, module, __filename, __dirname) {${body}\n})`);
|
|
766
|
+
return false;
|
|
767
|
+
} catch {
|
|
768
|
+
return true;
|
|
769
|
+
}
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
// PowerShell ends a double-quoted string at any typographic double quote and a
|
|
773
|
+
// single-quoted one at any typographic single quote.
|
|
774
|
+
const POWERSHELL_SMART_DOUBLE = /[\u201C\u201D\u201E\u201F]/;
|
|
775
|
+
const POWERSHELL_SMART_SINGLE = /[\u2018\u2019\u201A\u201B]/;
|
|
776
|
+
|
|
777
|
+
function defaultInlineShellType() {
|
|
778
|
+
return process.platform === 'win32' ? 'powershell' : 'posix';
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
// True when `index` is in live shell code of a POSIX shell: not inside a
|
|
782
|
+
// quote, `$'…'`, comment, or a heredoc body (any heredoc earlier in the text
|
|
783
|
+
// declines, since its body cannot be bounded here).
|
|
784
|
+
function posixLiveAt(text, index) {
|
|
785
|
+
let i = 0;
|
|
786
|
+
const skipTo = (end) => {
|
|
787
|
+
if (end < 0 || end >= index) return false;
|
|
788
|
+
i = end + 1;
|
|
789
|
+
return true;
|
|
790
|
+
};
|
|
791
|
+
// The quote that closes a region whose backslashes escape (`"…"`, `$'…'`).
|
|
792
|
+
const closer = (from, quote) => {
|
|
793
|
+
let j = from;
|
|
794
|
+
while (j < text.length && text[j] !== quote) j += text[j] === '\\' ? 2 : 1;
|
|
795
|
+
return j < text.length ? j : -1;
|
|
796
|
+
};
|
|
797
|
+
while (i < index) {
|
|
798
|
+
const ch = text[i];
|
|
799
|
+
if (ch === '\\') i += 2;
|
|
800
|
+
else if (ch === "'") {
|
|
801
|
+
if (!skipTo(text.indexOf("'", i + 1))) return false;
|
|
802
|
+
} else if (ch === '$' && text[i + 1] === "'") {
|
|
803
|
+
if (!skipTo(closer(i + 2, "'"))) return false;
|
|
804
|
+
} else if (ch === '"') {
|
|
805
|
+
if (!skipTo(closer(i + 1, '"'))) return false;
|
|
806
|
+
} else if (ch === '#' && (i === 0 || /[\s;&|(]/.test(text[i - 1]))) {
|
|
807
|
+
const nl = text.indexOf('\n', i);
|
|
808
|
+
if (nl < 0 || nl >= index) return false;
|
|
809
|
+
i = nl;
|
|
810
|
+
} else if (ch === '<' && text[i + 1] === '<' && text[i + 2] !== '<') return false;
|
|
811
|
+
else i++;
|
|
812
|
+
}
|
|
813
|
+
return true;
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
// A node/python invocation may be turned into a file run only when the shell
|
|
817
|
+
// itself runs it: live code (not text inside a string, here-string, comment or
|
|
818
|
+
// heredoc) at command position (not an argument of docker/ssh/wsl/ts-node/…,
|
|
819
|
+
// which would run the file somewhere that cannot see it).
|
|
820
|
+
function isShellInvocationAt(text, index, shellType) {
|
|
821
|
+
if (shellType === 'powershell') {
|
|
822
|
+
if (!codeMask(text)?.[index]) return false;
|
|
823
|
+
} else if (!posixLiveAt(text, index)) {
|
|
824
|
+
return false;
|
|
825
|
+
}
|
|
826
|
+
let start = index;
|
|
827
|
+
while (start > 0 && /[^\s;|&(){}<>'"]/.test(text[start - 1])) start--;
|
|
828
|
+
if (start < index && !/[\\/]$/.test(text.slice(start, index))) return false;
|
|
829
|
+
const before = text.slice(0, start);
|
|
830
|
+
const assignments = /(?:^|[\s;|&(){}])[A-Za-z_]\w*=[^\s;|&(){}]*[ \t]+$/;
|
|
831
|
+
let head = before;
|
|
832
|
+
while (assignments.test(head)) head = head.replace(assignments, (m) => m.match(/^[\s;|&(){}]?/)[0]);
|
|
833
|
+
const trimmed = head.replace(/[ \t]+$/, '');
|
|
834
|
+
if (trimmed === '') return true;
|
|
835
|
+
const last = trimmed.at(-1);
|
|
836
|
+
if (!';|&({\n'.includes(last)) return false;
|
|
837
|
+
// A line continuation joins the next line onto this command's arguments.
|
|
838
|
+
return !(last === '\n' && /[\\`]\r?\n$/.test(trimmed));
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
export function planInlineScriptHoist(command, { shellType = defaultInlineShellType() } = {}) {
|
|
699
842
|
const text = String(command || '');
|
|
700
843
|
if (!text.trim()) return null;
|
|
701
844
|
const match = text.match(INLINE_SCRIPT_RE);
|
|
702
845
|
if (!match) return null;
|
|
703
846
|
const [whole, exe, rawFlags, , body] = match;
|
|
704
847
|
if (INLINE_UNSAFE_BODY.test(body)) return null;
|
|
848
|
+
if (!INLINE_TRAILING_OK.test(text.slice(match.index + whole.length))) return null;
|
|
705
849
|
if (INLINE_FILE_RELATIVE.test(body)) return null;
|
|
706
|
-
if (
|
|
850
|
+
if (shellType === 'powershell' && POWERSHELL_SMART_DOUBLE.test(body)) return null;
|
|
851
|
+
const isNode = /^node/i.test(exe);
|
|
852
|
+
if (isNode && importsPackage(body)) return null;
|
|
853
|
+
if (isNode && !/--input-type=module/.test(rawFlags) && nodeBodyNeedsModuleSyntax(body)) return null;
|
|
854
|
+
if (!isNode && pythonImportsOutsideStdlib(body)) return null;
|
|
855
|
+
if (!isShellInvocationAt(text, match.index, shellType)) return null;
|
|
707
856
|
const { extension, keptFlags } = inlineScriptFileShape(exe, rawFlags);
|
|
708
857
|
return {
|
|
709
858
|
exe,
|
|
710
859
|
extension,
|
|
711
860
|
body,
|
|
712
|
-
|
|
861
|
+
// Spliced at the match offset, not by searching for its text: an earlier
|
|
862
|
+
// look-alike (`xnode -e "…"`) must not be the one rewritten. Plain
|
|
863
|
+
// concatenation also keeps a path holding `$&` / `$1` from being expanded.
|
|
864
|
+
replace: (filePath) =>
|
|
865
|
+
`${text.slice(0, match.index)}${exe}${keptFlags} "${filePath}"${text.slice(match.index + whole.length)}`,
|
|
713
866
|
};
|
|
714
867
|
}
|
|
715
868
|
|
|
@@ -727,11 +880,26 @@ export function planLongInlineScriptFileTransport(
|
|
|
727
880
|
const match = text.match(LONG_INLINE_SCRIPT_RE);
|
|
728
881
|
if (!match) return null;
|
|
729
882
|
const [whole, exe, rawFlags, , doubleBody, singleBody] = match;
|
|
883
|
+
const powershell = String(shellType || '').toLowerCase() === 'powershell';
|
|
884
|
+
// The same refusals as the short hoist: text after the closing quote means
|
|
885
|
+
// the body did not end there (or the script has arguments), and the match
|
|
886
|
+
// must be a command the shell itself runs.
|
|
887
|
+
if (!INLINE_TRAILING_OK.test(text.slice(match.index + whole.length))) return null;
|
|
888
|
+
if (!isShellInvocationAt(text, match.index, powershell ? 'powershell' : 'posix')) return null;
|
|
730
889
|
const { extension, keptFlags } = inlineScriptFileShape(exe, rawFlags);
|
|
731
890
|
let body = doubleBody;
|
|
732
891
|
if (body === undefined) {
|
|
733
892
|
body = String(singleBody || '');
|
|
734
|
-
|
|
893
|
+
if (powershell && POWERSHELL_SMART_SINGLE.test(body)) return null;
|
|
894
|
+
body = powershell ? body.replace(/''/g, "'") : body.replace(/''/g, '');
|
|
895
|
+
} else if (
|
|
896
|
+
// Inside double quotes the shell rewrites `$…`, backtick and backslash
|
|
897
|
+
// escapes (and PowerShell ends the string at a typographic quote) before
|
|
898
|
+
// the child sees them, so the verbatim body would be a different script.
|
|
899
|
+
/[$`]|\\"/.test(body) ||
|
|
900
|
+
(powershell ? POWERSHELL_SMART_DOUBLE.test(body) : body.includes('\\\\'))
|
|
901
|
+
) {
|
|
902
|
+
return null;
|
|
735
903
|
}
|
|
736
904
|
return {
|
|
737
905
|
command: text,
|
|
@@ -739,7 +907,7 @@ export function planLongInlineScriptFileTransport(
|
|
|
739
907
|
body,
|
|
740
908
|
replace(filePath) {
|
|
741
909
|
const normalized = String(filePath || '').replace(/\\/g, '/');
|
|
742
|
-
return text.
|
|
910
|
+
return `${text.slice(0, match.index)}${exe}${keptFlags} "${normalized}"${text.slice(match.index + whole.length)}`;
|
|
743
911
|
},
|
|
744
912
|
};
|
|
745
913
|
}
|
|
@@ -769,7 +937,11 @@ export function planLongShellScriptFileTransport(
|
|
|
769
937
|
const escaped = String(filePath || '')
|
|
770
938
|
.replace(/\\/g, '/')
|
|
771
939
|
.replace(/'/g, "''");
|
|
772
|
-
|
|
940
|
+
// Evaluated in the host's own scope, not called as a script: `& file.ps1`
|
|
941
|
+
// turns `exit N` into exit code 1 and a failing last command into 0,
|
|
942
|
+
// where the command itself would have ended with N and 1. The trailing
|
|
943
|
+
// statement reads the last command's `$?` the way -Command does.
|
|
944
|
+
return `Invoke-Expression ([System.IO.File]::ReadAllText('${escaped}', [System.Text.Encoding]::UTF8) + "\`n" + '$global:__mixdogLastOk = $?'); if ($global:__mixdogLastOk -eq $false) { exit 1 }`;
|
|
773
945
|
},
|
|
774
946
|
};
|
|
775
947
|
}
|
|
@@ -937,9 +1109,8 @@ export async function analyzeShellCommandEffects(command, cwd) {
|
|
|
937
1109
|
const sub = String(tokens[1] || '').toLowerCase();
|
|
938
1110
|
if (['status', 'diff', 'show', 'log', 'rev-parse', 'branch', 'remote', 'ls-files'].includes(sub)) continue;
|
|
939
1111
|
}
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
}
|
|
1112
|
+
// A python run can write anywhere (alone it already classifies as global
|
|
1113
|
+
// below the read-only check), so it never narrows a chain's effects.
|
|
943
1114
|
global = true;
|
|
944
1115
|
continue;
|
|
945
1116
|
}
|
|
@@ -1054,6 +1225,9 @@ export function buildPowerShellFilterTeePlan(command) {
|
|
|
1054
1225
|
const trimmed = cmd.trimEnd();
|
|
1055
1226
|
if (!trimmed.endsWith(last)) return null;
|
|
1056
1227
|
if (!_groupingBalanced(last) || !_groupingBalanced(trimmed.slice(0, trimmed.length - last.length))) return null;
|
|
1228
|
+
// Behind `&&` / `||` the rewrite's leading assignment is not a pipeline (a
|
|
1229
|
+
// parse error), and it would run the producer even when `||` skips it.
|
|
1230
|
+
if (/(?:&&|\|\|)\s*$/.test(trimmed.slice(0, trimmed.length - last.length))) return null;
|
|
1057
1231
|
const stages = shellSplitPipelineSegments(last);
|
|
1058
1232
|
if (stages.length < 2) return null;
|
|
1059
1233
|
const tokenized = [];
|