mixdog 1.3.4 → 1.4.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/oh-my-pi-MIT.txt +23 -0
- package/NOTICE.md +17 -2
- package/README.md +11 -0
- package/package.json +3 -11
- package/scripts/daemon-stub.mjs +1 -1
- package/scripts/embed-google-oauth-client.mjs +49 -0
- package/scripts/gate-local.mjs +4 -2
- package/scripts/lib/test-spawn-ledger.mjs +46 -1
- package/scripts/native-release-status.mjs +2 -2
- package/scripts/recall-history-cases.json +1 -1
- package/scripts/suite-health.mjs +10 -1
- package/scripts/test.mjs +12 -11
- package/src/agents/reviewer/AGENT.md +7 -11
- package/src/agents/reviewer/agent.json +1 -1
- package/src/defaults/shipped-history.json +25 -1
- package/src/defaults/skills/browser-use/SKILL.md +77 -17
- package/src/defaults/skills/code-tidy/SKILL.md +142 -270
- package/src/defaults/skills/code-tidy/references/agent-cleanup.md +4 -4
- package/src/defaults/skills/code-tidy/references/cleanup-report.md +31 -76
- package/src/defaults/skills/code-tidy/references/dead-code.md +6 -12
- package/src/defaults/skills/computer-use/SKILL.md +33 -9
- package/src/defaults/skills/setup/SKILL.md +29 -2
- package/src/defaults/skills/setup/references/actions.md +117 -28
- package/src/defaults/skills/setup/references/surfaces.md +77 -35
- package/src/defaults/skills/setup-guide/SKILL.md +3 -3
- package/src/defaults/skills/visualize/SKILL.md +2 -1
- package/src/lib/keychain-cjs.cjs +359 -29
- package/src/lib/rules-builder.cjs +10 -8
- package/src/rules/agent/AGENT.md +6 -6
- package/src/rules/agent-mode/AGENT_MODE.md +10 -2
- package/src/rules/agent-mode/MIXY.md +12 -0
- package/src/rules/lead/LEAD.md +18 -20
- package/src/rules/shared/05-tool-calls.md +5 -6
- package/src/rules/shared/10-tool-workflow.md +2 -0
- package/src/rules/shared/60-verification.md +7 -5
- package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +2 -3
- package/src/runtime/agent/orchestrator/agent-runtime/heartbeat-completion.mjs +193 -0
- package/src/runtime/agent/orchestrator/agent-runtime/title-completion.mjs +3 -1
- package/src/runtime/agent/orchestrator/agent-trace-format.mjs +3 -1
- package/src/runtime/agent/orchestrator/config.mjs +34 -0
- package/src/runtime/agent/orchestrator/context/collect.mjs +3 -0
- package/src/runtime/agent/orchestrator/context/deferred-tools.mjs +72 -67
- package/src/runtime/agent/orchestrator/context/skill-catalog.mjs +54 -1
- package/src/runtime/agent/orchestrator/mcp/agent-connectors.mjs +3 -3
- package/src/runtime/agent/orchestrator/mcp/client-transport.mjs +177 -73
- package/src/runtime/agent/orchestrator/mcp/client.mjs +25 -2
- package/src/runtime/agent/orchestrator/mcp/oauth-callback.mjs +31 -2
- package/src/runtime/agent/orchestrator/mcp/oauth-client.mjs +16 -3
- package/src/runtime/agent/orchestrator/mcp/oauth-login.mjs +42 -7
- package/src/runtime/agent/orchestrator/mcp/oauth-provider.mjs +21 -2
- package/src/runtime/agent/orchestrator/mcp/oauth-shared.mjs +23 -0
- package/src/runtime/agent/orchestrator/mcp/oauth.mjs +11 -1
- package/src/runtime/agent/orchestrator/providers/account-pool.mjs +230 -137
- package/src/runtime/agent/orchestrator/providers/admission-scheduler.mjs +64 -7
- package/src/runtime/agent/orchestrator/providers/anthropic-api-request.mjs +18 -5
- package/src/runtime/agent/orchestrator/providers/anthropic-cache-warm.mjs +165 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-client-version.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-credentials.mjs +4 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-request/gzip-post.mjs +14 -14
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +160 -69
- package/src/runtime/agent/orchestrator/providers/anthropic-overload-fallback.mjs +25 -3
- package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +64 -33
- package/src/runtime/agent/orchestrator/providers/anthropic.mjs +135 -87
- package/src/runtime/agent/orchestrator/providers/antigravity-oauth-catalog.mjs +52 -8
- package/src/runtime/agent/orchestrator/providers/antigravity-oauth.mjs +10 -1
- package/src/runtime/agent/orchestrator/providers/antigravity-request.mjs +22 -6
- package/src/runtime/agent/orchestrator/providers/antigravity-response.mjs +15 -4
- package/src/runtime/agent/orchestrator/providers/antigravity-stream.mjs +17 -2
- package/src/runtime/agent/orchestrator/providers/antigravity-transport.mjs +142 -104
- package/src/runtime/agent/orchestrator/providers/cache-warm-admission.mjs +90 -0
- package/src/runtime/agent/orchestrator/providers/codex-client-meta.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/cursor-wire-normalization.mjs +10 -4
- package/src/runtime/agent/orchestrator/providers/cursor-wire-protobuf.mjs +10 -217
- package/src/runtime/agent/orchestrator/providers/cursor-wire-transport.mjs +25 -9
- package/src/runtime/agent/orchestrator/providers/cursor.mjs +22 -6
- package/src/runtime/agent/orchestrator/providers/devin-auth.mjs +409 -0
- package/src/runtime/agent/orchestrator/providers/devin-wire-families.mjs +187 -0
- package/src/runtime/agent/orchestrator/providers/devin-wire-identity.mjs +342 -0
- package/src/runtime/agent/orchestrator/providers/devin-wire-models.mjs +541 -0
- package/src/runtime/agent/orchestrator/providers/devin-wire-protobuf.mjs +305 -0
- package/src/runtime/agent/orchestrator/providers/devin-wire-request.mjs +558 -0
- package/src/runtime/agent/orchestrator/providers/devin-wire-schema.mjs +880 -0
- package/src/runtime/agent/orchestrator/providers/devin-wire-session.mjs +190 -0
- package/src/runtime/agent/orchestrator/providers/devin-wire-tool-args.mjs +351 -0
- package/src/runtime/agent/orchestrator/providers/devin-wire-transport.mjs +560 -0
- package/src/runtime/agent/orchestrator/providers/devin.mjs +491 -0
- package/src/runtime/agent/orchestrator/providers/effort-configuration.mjs +3 -0
- package/src/runtime/agent/orchestrator/providers/gemini-output-limit.mjs +46 -0
- package/src/runtime/agent/orchestrator/providers/gemini-reasoning-relay.mjs +30 -0
- package/src/runtime/agent/orchestrator/providers/gemini-request-body.mjs +7 -11
- package/src/runtime/agent/orchestrator/providers/gemini-retry-policy.mjs +61 -22
- package/src/runtime/agent/orchestrator/providers/gemini-sdk-request.mjs +1 -0
- package/src/runtime/agent/orchestrator/providers/gemini-stream.mjs +71 -4
- package/src/runtime/agent/orchestrator/providers/gemini.mjs +25 -2
- package/src/runtime/agent/orchestrator/providers/grok-client-version.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/lib/anthropic-request-utils.mjs +96 -5
- package/src/runtime/agent/orchestrator/providers/lib/oauth-token-utils.mjs +17 -3
- package/src/runtime/agent/orchestrator/providers/lib/tail-reminder-split.mjs +23 -5
- package/src/runtime/agent/orchestrator/providers/media-normalization.mjs +52 -10
- package/src/runtime/agent/orchestrator/providers/model-proxy-channel.d.mts +8 -1
- package/src/runtime/agent/orchestrator/providers/model-proxy-channel.mjs +1 -0
- package/src/runtime/agent/orchestrator/providers/npm-cli-version.mjs +11 -3
- package/src/runtime/agent/orchestrator/providers/oauth-credential-probes.mjs +19 -1
- package/src/runtime/agent/orchestrator/providers/oauth-usage.mjs +39 -3
- 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-error-contract.mjs +6 -1
- package/src/runtime/agent/orchestrator/providers/openai-oauth-tokens.mjs +10 -9
- package/src/runtime/agent/orchestrator/providers/openai-ws-send/send-context.mjs +22 -3
- package/src/runtime/agent/orchestrator/providers/openai-ws-send-attempts/attempt-context.mjs +43 -5
- package/src/runtime/agent/orchestrator/providers/openai-ws-send-attempts/handshake-failure.mjs +3 -1
- package/src/runtime/agent/orchestrator/providers/openai-ws-send-attempts/stream-failure.mjs +6 -3
- package/src/runtime/agent/orchestrator/providers/opencode-go-gemini.mjs +1 -0
- package/src/runtime/agent/orchestrator/providers/provider-availability.mjs +19 -0
- package/src/runtime/agent/orchestrator/providers/provider-error-kind.mjs +65 -1
- package/src/runtime/agent/orchestrator/providers/quota-reading.mjs +38 -12
- package/src/runtime/agent/orchestrator/providers/registry.mjs +43 -3
- package/src/runtime/agent/orchestrator/providers/retry-executor.mjs +39 -7
- package/src/runtime/agent/orchestrator/providers/statusline-route-meta.mjs +6 -0
- package/src/runtime/agent/orchestrator/providers/wire-protobuf-codec.mjs +368 -0
- package/src/runtime/agent/orchestrator/runtime-core/ask-user-tool-defs.mjs +12 -7
- package/src/runtime/agent/orchestrator/runtime-core/builtin-features.mjs +2 -0
- package/src/runtime/agent/orchestrator/runtime-core/compaction-config-keys.mjs +62 -0
- package/src/runtime/agent/orchestrator/runtime-core/config-helpers.mjs +37 -16
- package/src/runtime/agent/orchestrator/runtime-core/deferred-tool-delta.mjs +100 -23
- package/src/runtime/agent/orchestrator/runtime-core/orchestration.mjs +1 -1
- package/src/runtime/agent/orchestrator/runtime-core/reaction-tool-defs.mjs +133 -0
- package/src/runtime/agent/orchestrator/runtime-core/session-text.mjs +2 -0
- package/src/runtime/agent/orchestrator/runtime-core/tool-bridge.mjs +19 -4
- package/src/runtime/agent/orchestrator/runtime-core/tool-catalog-data.mjs +3 -0
- package/src/runtime/agent/orchestrator/runtime-core/tool-catalog.mjs +125 -30
- package/src/runtime/agent/orchestrator/runtime-core/tool-defs.mjs +23 -5
- package/src/runtime/agent/orchestrator/runtime-core/tool-profile.mjs +35 -10
- package/src/runtime/agent/orchestrator/runtime-core/workflow/agents.mjs +57 -10
- package/src/runtime/agent/orchestrator/runtime-core/workflow/context.mjs +9 -6
- package/src/runtime/agent/orchestrator/runtime-core/workflow-ids.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/agent-loop.mjs +18 -0
- package/src/runtime/agent/orchestrator/session/agent-room.d.mts +0 -3
- package/src/runtime/agent/orchestrator/session/agent-room.mjs +10 -4
- package/src/runtime/agent/orchestrator/session/cache-warm.mjs +792 -0
- package/src/runtime/agent/orchestrator/session/compact/runner.mjs +148 -25
- package/src/runtime/agent/orchestrator/session/compaction-archive-capture.mjs +475 -58
- package/src/runtime/agent/orchestrator/session/compaction-archive.mjs +42 -17
- package/src/runtime/agent/orchestrator/session/eager-dispatch.mjs +8 -0
- package/src/runtime/agent/orchestrator/session/human-gate/gate.mjs +47 -9
- package/src/runtime/agent/orchestrator/session/human-gate/outbound-policy.mjs +6 -2
- package/src/runtime/agent/orchestrator/session/human-gate/requests.mjs +36 -4
- package/src/runtime/agent/orchestrator/session/human-gate/store.mjs +14 -2
- package/src/runtime/agent/orchestrator/session/loop/assistant-commit.mjs +8 -0
- package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +18 -22
- package/src/runtime/agent/orchestrator/session/loop/fresh-context.mjs +32 -3
- package/src/runtime/agent/orchestrator/session/loop/loop-state.mjs +11 -2
- package/src/runtime/agent/orchestrator/session/loop/no-tool-turn/segments.mjs +4 -1
- package/src/runtime/agent/orchestrator/session/loop/no-tool-turn.mjs +51 -3
- package/src/runtime/agent/orchestrator/session/loop/route-fallback.mjs +17 -3
- package/src/runtime/agent/orchestrator/session/loop/send-phase.mjs +10 -2
- package/src/runtime/agent/orchestrator/session/loop/tool-exec/after-hook.mjs +2 -1
- package/src/runtime/agent/orchestrator/session/loop/tool-exec/before-hook.mjs +14 -4
- package/src/runtime/agent/orchestrator/session/loop/tool-exec/dispatch.mjs +16 -2
- package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +16 -1
- package/src/runtime/agent/orchestrator/session/loop/tool-exposure-migration.mjs +10 -0
- package/src/runtime/agent/orchestrator/session/loop/tool-helpers.mjs +3 -10
- package/src/runtime/agent/orchestrator/session/loop/tool-phase.mjs +23 -5
- package/src/runtime/agent/orchestrator/session/loop/turn-limits.mjs +60 -0
- package/src/runtime/agent/orchestrator/session/manager/ask-queue.mjs +12 -2
- package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +19 -1
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-commit.mjs +2 -0
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-execute.mjs +133 -90
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-loop-options.mjs +14 -0
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-settle.mjs +2 -1
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-state.mjs +31 -4
- package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +30 -18
- package/src/runtime/agent/orchestrator/session/manager/owner-route-fallbacks.mjs +23 -13
- package/src/runtime/agent/orchestrator/session/manager/pending-message-entry.mjs +11 -2
- package/src/runtime/agent/orchestrator/session/manager/provider-replay.mjs +13 -0
- package/src/runtime/agent/orchestrator/session/manager/route-fallback-chain.mjs +129 -0
- package/src/runtime/agent/orchestrator/session/manager/runtime-liveness.mjs +20 -2
- package/src/runtime/agent/orchestrator/session/manager/session-close.mjs +2 -0
- package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +8 -0
- package/src/runtime/agent/orchestrator/session/manager/session-record.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/manager/session-tool-surface.mjs +13 -0
- package/src/runtime/agent/orchestrator/session/manager/tool-resolution.mjs +7 -2
- package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint-journal.mjs +102 -15
- package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint.mjs +18 -5
- package/src/runtime/agent/orchestrator/session/message-ids.mjs +5 -3
- package/src/runtime/agent/orchestrator/session/message-reactions.mjs +89 -0
- package/src/runtime/agent/orchestrator/session/message-reactions.test-support.mjs +75 -0
- package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +19 -8
- package/src/runtime/agent/orchestrator/session/prompt-cache-idle-gaps.mjs +113 -0
- package/src/runtime/agent/orchestrator/session/prompt-cache-touch-store.mjs +127 -0
- package/src/runtime/agent/orchestrator/session/routine-result.d.mts +0 -2
- package/src/runtime/agent/orchestrator/session/routine-result.mjs +47 -11
- package/src/runtime/agent/orchestrator/session/send-with-recovery.mjs +20 -2
- package/src/runtime/agent/orchestrator/session/sensitive-projection.mjs +100 -0
- package/src/runtime/agent/orchestrator/session/sensitive-values.mjs +257 -0
- package/src/runtime/agent/orchestrator/session/side-question-store.mjs +20 -7
- package/src/runtime/agent/orchestrator/session/side-question.mjs +18 -0
- package/src/runtime/agent/orchestrator/session/skill-update-notice.mjs +60 -0
- package/src/runtime/agent/orchestrator/session/store/live-state.mjs +6 -3
- package/src/runtime/agent/orchestrator/session/store/save-delta.mjs +9 -5
- package/src/runtime/agent/orchestrator/session/store/serialize.mjs +9 -8
- package/src/runtime/agent/orchestrator/session/store/session-persistence-events.mjs +29 -0
- package/src/runtime/agent/orchestrator/session/store-agent-worker-header-cache.mjs +85 -0
- package/src/runtime/agent/orchestrator/session/store-agent-worker-rows.mjs +133 -18
- package/src/runtime/agent/orchestrator/session/store-summary-catalog.mjs +12 -2
- package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +44 -14
- package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +84 -3
- package/src/runtime/agent/orchestrator/session/stored-attachment-history.mjs +32 -0
- package/src/runtime/agent/orchestrator/session/tail-reminder-rules.mjs +11 -5
- package/src/runtime/agent/orchestrator/session/thinking-binding.mjs +108 -0
- package/src/runtime/agent/orchestrator/session/tool-batch/pre-dispatch.mjs +2 -2
- package/src/runtime/agent/orchestrator/session/tool-batch/record-outcome.mjs +2 -1
- package/src/runtime/agent/orchestrator/session/tool-batch/state.mjs +4 -0
- package/src/runtime/agent/orchestrator/session/tool-batch.mjs +5 -0
- package/src/runtime/agent/orchestrator/session/transcript-restore/restore.mjs +25 -2
- package/src/runtime/agent/orchestrator/session/transcript-restore/tool-result-status.mjs +9 -7
- package/src/runtime/agent/orchestrator/session/transcript-restore/tool-result-text.mjs +2 -2
- package/src/runtime/agent/orchestrator/session/turn-failure.mjs +5 -1
- package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +5 -2
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/backgrounded-result.mjs +12 -1
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/misuse-guard.mjs +240 -0
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/result-format.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/shell-cwd.mjs +38 -0
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +25 -5
- package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +65 -7
- package/src/runtime/agent/orchestrator/tools/builtin/shell-runtime.mjs +7 -1
- package/src/runtime/agent/orchestrator/tools/shell-command.mjs +36 -13
- package/src/runtime/browser-bridge/action-schema.mjs +13 -2
- package/src/runtime/browser-bridge/client.mjs +73 -8
- package/src/runtime/browser-bridge/input-fields.mjs +8 -9
- package/src/runtime/browser-bridge/mail-links.mjs +107 -0
- package/src/runtime/browser-bridge/tool-defs.mjs +1 -1
- package/src/runtime/channels/lib/config.mjs +6 -61
- package/src/runtime/channels/lib/crash-log.mjs +1 -1
- package/src/runtime/channels/lib/event-pipeline.mjs +4 -19
- package/src/runtime/channels/lib/event-queue.mjs +3 -18
- package/src/runtime/channels/lib/owned-runtime/automation-services.mjs +2 -2
- package/src/runtime/channels/lib/owned-runtime/automation-start.mjs +3 -4
- package/src/runtime/channels/lib/owned-runtime/config-reload.mjs +4 -59
- package/src/runtime/channels/lib/owned-runtime/owned-start.mjs +33 -115
- package/src/runtime/channels/lib/owned-runtime/owned-stop.mjs +5 -22
- package/src/runtime/channels/lib/owned-runtime/state.mjs +2 -8
- package/src/runtime/channels/lib/owned-runtime.mjs +6 -23
- package/src/runtime/channels/lib/parent-bridge.mjs +2 -2
- package/src/runtime/channels/lib/runtime-paths.mjs +4 -43
- package/src/runtime/channels/lib/schedule-errors.mjs +51 -0
- package/src/runtime/channels/lib/scheduler.mjs +135 -154
- package/src/runtime/channels/lib/schedules-db.mjs +83 -11
- package/src/runtime/channels/lib/status-snapshot.mjs +1 -1
- package/src/runtime/channels/lib/tool-dispatch.mjs +14 -2
- package/src/runtime/channels/lib/webhooks-db.mjs +5 -9
- package/src/runtime/channels/lib/worker-bootstrap.mjs +0 -1
- package/src/runtime/channels/lib/worker-ipc.mjs +0 -1
- package/src/runtime/channels/lib/worker-main.mjs +19 -34
- package/src/runtime/computer-bridge/action-schema.mjs +42 -1
- package/src/runtime/computer-bridge/actions.mjs +9 -1
- package/src/runtime/computer-bridge/client.mjs +180 -8
- package/src/runtime/computer-bridge/core-actions.mjs +2 -2
- package/src/runtime/computer-bridge/error-recovery.mjs +27 -2
- package/src/runtime/computer-bridge/linux-readiness.mjs +65 -23
- package/src/runtime/computer-bridge/pending-continuation.mjs +1 -1
- package/src/runtime/connectors/connector-accounts-mcp.mjs +230 -0
- package/src/runtime/connectors/gmail-app-password.mjs +172 -39
- package/src/runtime/connectors/google-mcp.mjs +10 -12
- package/src/runtime/memory/lib/pg/adapter.mjs +77 -70
- package/src/runtime/memory/lib/pg/supervisor.mjs +24 -12
- package/src/runtime/memory/lib/runtime-fetcher.mjs +23 -5
- package/src/runtime/office/com/com-adapter.mjs +17 -1
- package/src/runtime/office/com/office-com-host.ps1 +51 -1
- package/src/runtime/office/pdf/cfb-fixture.mjs +328 -0
- package/src/runtime/office/pdf/cfb-protection.mjs +311 -0
- package/src/runtime/office/pdf/document-preview.mjs +326 -8
- package/src/runtime/office/pdf/ooxml-crypto.mjs +469 -0
- package/src/runtime/office/pdf/ooxml-key-worker.mjs +14 -0
- package/src/runtime/office/portable/portable-soffice.mjs +45 -5
- package/src/runtime/session-search/session-search-db.mjs +73 -9
- package/src/runtime/session-search/session-search-ingest.mjs +110 -20
- package/src/runtime/shared/abort-race.mjs +89 -0
- package/src/runtime/shared/agent-avatar.d.mts +0 -1
- package/src/runtime/shared/agent-kind.mjs +4 -3
- package/src/runtime/shared/bot-hops.mjs +62 -2
- package/src/runtime/shared/bounded-download.mjs +8 -3
- package/src/runtime/shared/config.mjs +51 -35
- package/src/runtime/shared/connector-accounts.mjs +20 -10
- package/src/runtime/shared/connector-catalog.d.mts +33 -2
- package/src/runtime/shared/connector-catalog.mjs +166 -8
- package/src/runtime/shared/connector-triggers.mjs +22 -19
- package/src/runtime/shared/defaults-separation.mjs +2 -14
- package/src/runtime/shared/delegated-approval-host.mjs +103 -11
- package/src/runtime/shared/developer-options.mjs +32 -1
- package/src/runtime/shared/err-text.mjs +24 -15
- package/src/runtime/shared/file-permissions.d.mts +0 -8
- package/src/runtime/shared/file-permissions.mjs +0 -11
- package/src/runtime/shared/git-direct.test-support.mjs +19 -0
- package/src/runtime/shared/google-oauth-client.generated.json +4 -0
- package/src/runtime/shared/google-oauth-client.mjs +34 -9
- package/src/runtime/shared/heartbeat-interval.d.mts +4 -0
- package/src/runtime/shared/heartbeat-interval.mjs +19 -0
- package/src/runtime/shared/idle-auto-clear-gate.mjs +104 -0
- package/src/runtime/shared/llm/anthropic-betas.mjs +7 -0
- package/src/runtime/shared/llm/anthropic-thinking-contract.mjs +12 -0
- package/src/runtime/shared/llm/provider-catalog-registry.mjs +1 -0
- package/src/runtime/shared/llm/usage-rollup.mjs +1 -1
- package/src/runtime/shared/llm/well-formed.mjs +66 -0
- package/src/runtime/shared/mixy-presence.mjs +22 -0
- package/src/runtime/shared/orchestration.d.mts +2 -0
- package/src/runtime/shared/pg-schema-db.mjs +27 -16
- package/src/runtime/shared/profile-config.cjs +1 -1
- package/src/runtime/shared/prompt-origin.d.mts +4 -0
- package/src/runtime/shared/prompt-origin.mjs +21 -0
- package/src/runtime/shared/provider-accounts.mjs +231 -59
- package/src/runtime/shared/provider-api-key.mjs +19 -8
- package/src/runtime/shared/provider-display-name.mjs +29 -0
- package/src/runtime/shared/provider-timeout-phase.mjs +2 -0
- package/src/runtime/shared/routine-trigger.d.mts +2 -0
- package/src/runtime/shared/routine-trigger.mjs +52 -0
- package/src/runtime/shared/sensitive-path-rules.d.mts +2 -10
- package/src/runtime/shared/sensitive-path-rules.mjs +19 -41
- package/src/runtime/shared/session-activity-signal.mjs +34 -1
- package/src/runtime/shared/session-sidebar-host.d.mts +1 -1
- package/src/runtime/shared/session-sidebar-host.mjs +7 -3
- package/src/runtime/shared/session-turn-prelude.mjs +18 -0
- package/src/runtime/shared/tool-execution-contract.mjs +2 -0
- package/src/runtime/shared/tool-result-summary.mjs +1 -14
- package/src/runtime/shared/transcript-metadata.mjs +38 -0
- package/src/runtime/shared/user-data-guard.mjs +30 -2
- package/src/runtime/shared/worktrees/activity-gate.mjs +12 -5
- package/src/runtime/shared/worktrees/test-support.mjs +9 -4
- package/src/runtime/terminal-bridge/client.mjs +10 -1
- package/src/runtime/visualize-bridge/client.mjs +27 -8
- package/src/runtime/visualize-bridge/tool-defs.mjs +22 -8
- package/src/runtime/visualize-bridge/tool-meta.d.mts +5 -0
- package/src/runtime/visualize-bridge/tool-meta.mjs +22 -0
- package/src/runtime/web-search/lib/fetch-pipeline.mjs +2 -2
- package/src/session-runtime/agent-home-delivery.mjs +486 -179
- package/src/session-runtime/agent-room-delivery.mjs +28 -1
- package/src/session-runtime/agent-task-sessions.mjs +561 -199
- package/src/session-runtime/boot/apis.mjs +9 -0
- package/src/session-runtime/boot/begin.mjs +3 -2
- package/src/session-runtime/boot/infrastructure.mjs +4 -0
- package/src/session-runtime/boot/providers.mjs +3 -0
- package/src/session-runtime/boot/tools.mjs +8 -1
- package/src/session-runtime/bot-chain.mjs +32 -0
- package/src/session-runtime/bot-status.mjs +7 -4
- package/src/session-runtime/channel-config-api.mjs +63 -17
- package/src/session-runtime/config-lifecycle.mjs +6 -0
- package/src/session-runtime/connector-merge-migration.mjs +305 -0
- package/src/session-runtime/internal-tool-executor/ask-user-tool.mjs +132 -28
- package/src/session-runtime/internal-tool-executor/bridge-tools.mjs +2 -0
- package/src/session-runtime/internal-tool-executor/reaction-tool.mjs +45 -0
- package/src/session-runtime/internal-tool-executor/workspace-tools.mjs +72 -5
- package/src/session-runtime/internal-tool-executor.mjs +4 -0
- package/src/session-runtime/lifecycle/inheritance.mjs +16 -3
- package/src/session-runtime/lifecycle/session-catalog.mjs +3 -1
- package/src/session-runtime/lifecycle/teardown/runtime-stops.mjs +50 -4
- package/src/session-runtime/lifecycle/teardown.mjs +23 -5
- package/src/session-runtime/mcp-glue/server-catalog.mjs +61 -3
- package/src/session-runtime/mcp-oauth-login.mjs +5 -3
- package/src/session-runtime/mixy-heartbeat.mjs +927 -0
- package/src/session-runtime/model-route/route-tuning.mjs +10 -6
- package/src/session-runtime/notification-bus.mjs +31 -117
- package/src/session-runtime/notification-completion-queue.mjs +54 -0
- package/src/session-runtime/notification-listeners.mjs +76 -0
- package/src/session-runtime/owned-automation-run.mjs +35 -13
- package/src/session-runtime/prewarm/channel-start.mjs +3 -7
- package/src/session-runtime/provider-auth/login.mjs +7 -0
- package/src/session-runtime/provider-models/row-hydration.mjs +13 -2
- package/src/session-runtime/resource-mcp-api.mjs +143 -224
- package/src/session-runtime/resource-mcp-connector-accounts.mjs +163 -0
- package/src/session-runtime/resource-mcp-oauth-api.mjs +217 -0
- package/src/session-runtime/resource-mcp-secrets.mjs +82 -0
- package/src/session-runtime/resource-mcp-servers.mjs +78 -0
- package/src/session-runtime/resource-mcp-store.mjs +49 -0
- package/src/session-runtime/routine-home-turn.mjs +33 -6
- package/src/session-runtime/routine-owner.mjs +40 -9
- package/src/session-runtime/schedule-session-run.mjs +7 -3
- package/src/session-runtime/services/agent-tool/close-flow.mjs +3 -0
- package/src/session-runtime/services/agent-tool/spawn-flow/spawn-plan.mjs +8 -12
- package/src/session-runtime/services/agent-tool/spawn-preset.mjs +7 -2
- package/src/session-runtime/services/agent-tool/tag-registry.mjs +1 -0
- package/src/session-runtime/services/agent-tool/worker-index/awaiting-user-sync.mjs +51 -0
- package/src/session-runtime/services/agent-tool/worker-index/disposal.mjs +55 -0
- package/src/session-runtime/services/agent-tool/worker-index/released-store.mjs +123 -0
- package/src/session-runtime/services/agent-tool/worker-index/restart-settlement.mjs +112 -0
- package/src/session-runtime/services/agent-tool/worker-index/row-reads.mjs +36 -0
- package/src/session-runtime/services/agent-tool/worker-index/row-shape.mjs +2 -0
- package/src/session-runtime/services/agent-tool/worker-index/row-store.mjs +17 -4
- package/src/session-runtime/services/agent-tool/worker-index/row-writes.mjs +58 -0
- package/src/session-runtime/services/agent-tool/worker-index.mjs +41 -241
- package/src/session-runtime/services/agent-tool/worker-rows.mjs +3 -0
- package/src/session-runtime/services/channel-admin.mjs +73 -31
- package/src/session-runtime/services/channel-session-router.mjs +4 -2
- package/src/session-runtime/services/channel-worker.mjs +1 -1
- package/src/session-runtime/services/provider-admin.mjs +128 -25
- package/src/session-runtime/services/schedule-access.mjs +14 -0
- package/src/session-runtime/services/seeds.mjs +0 -28
- package/src/session-runtime/services/usage-dashboard-model.mjs +1 -0
- package/src/session-runtime/session-compaction-sync.mjs +27 -0
- package/src/session-runtime/session-create/session-options.mjs +8 -4
- package/src/session-runtime/settings-builtin-tools-api.mjs +11 -0
- package/src/session-runtime/settings-compaction-api.mjs +2 -40
- package/src/session-runtime/settings-system-api.mjs +28 -1
- package/src/session-runtime/setup-tool/agent-settings-actions.mjs +65 -0
- package/src/session-runtime/setup-tool/executor.mjs +187 -29
- package/src/session-runtime/setup-tool/extended-actions.mjs +33 -2
- package/src/session-runtime/setup-tool/session-route-actions.mjs +152 -0
- package/src/session-runtime/setup-tool/settings-contract.mjs +68 -6
- package/src/session-runtime/setup-tool/tool-defs.mjs +92 -6
- package/src/session-runtime/task-session-route.mjs +99 -0
- package/src/session-runtime/tool-policy/agent-profile-fold.mjs +17 -13
- package/src/session-runtime/tool-policy/policy-stage.mjs +21 -4
- package/src/session-runtime/tool-policy/surface-helpers.mjs +2 -3
- package/src/session-runtime/tool-surface.mjs +4 -0
- package/src/session-runtime/turn/turn-run/ask-callbacks.mjs +1 -0
- package/src/session-runtime/workflow-agents-api/agent-editor.mjs +98 -21
- package/src/session-runtime/workflow-agents-api/style-and-mode.mjs +4 -0
- package/src/session-runtime/workflow-agents-api/workflow-packs.mjs +6 -1
- package/src/session-runtime/workspace/transitions.mjs +10 -2
- package/src/standalone/channel-transport/client-registry/client-records.mjs +65 -0
- package/src/standalone/channel-transport/client-registry/client-registration.mjs +118 -0
- package/src/standalone/channel-transport/client-registry.mjs +24 -163
- package/src/standalone/channel-transport.mjs +1 -1
- package/src/standalone/daemon-channels-loader.mjs +10 -8
- package/src/standalone/daemon-session-control/list-page.mjs +72 -0
- package/src/standalone/daemon-session-control/request.mjs +51 -0
- package/src/standalone/daemon-session-control/room-host.mjs +57 -0
- package/src/standalone/daemon-session-control/route-control.mjs +48 -0
- package/src/standalone/daemon-session-control/send-turn.mjs +60 -0
- package/src/standalone/daemon-session-control/worktree-control.mjs +40 -0
- package/src/standalone/daemon-session-control.mjs +161 -157
- package/src/standalone/daemon.mjs +33 -2
- package/src/standalone/session-client.mjs +7 -3
- package/src/standalone/session-protocol.mjs +10 -0
- package/src/standalone/session-service/agent-tree/agent-cancel.mjs +6 -2
- package/src/standalone/session-service/agent-tree/agent-child.mjs +1 -0
- package/src/standalone/session-service/quota-resume.mjs +3 -0
- package/src/standalone/session-service/retention/busy.mjs +65 -1
- package/src/standalone/session-service/session-calls/session-action.mjs +23 -2
- package/src/standalone/session-service/session-calls/session-turns.mjs +6 -0
- package/src/standalone/session-service/session-calls/session-views.mjs +8 -0
- package/src/standalone/session-service.mjs +13 -0
- package/src/standalone/session-tool-output.mjs +21 -10
- package/src/tui/app/model-options.mjs +4 -27
- package/src/tui/dist/index.mjs +81 -65
- package/src/tui/session/agent-job-feed/model-notification.mjs +3 -0
- package/src/tui/session/initial-state.mjs +3 -0
- package/src/tui/session/notice-surface.mjs +23 -1
- package/src/tui/session/queue-helpers.mjs +3 -42
- package/src/tui/session/session-api/integrations.mjs +1 -0
- package/src/tui/session/session-api/lifecycle.mjs +8 -0
- package/src/tui/session/session-api/routes-agent-definitions.mjs +14 -2
- package/src/tui/session/session-api/routes-write.mjs +5 -1
- package/src/tui/session/session-api/routes.mjs +1 -0
- package/src/tui/session/session-api/settings.mjs +30 -2
- package/src/tui/session/session-flow/auto-clear.mjs +4 -33
- package/src/tui/session/session-flow/drain-closed-queue.mjs +38 -0
- package/src/tui/session/session-flow/drain-enqueue.mjs +86 -0
- package/src/tui/session/session-flow/drain-retry.mjs +35 -0
- package/src/tui/session/session-flow/drain.mjs +118 -149
- package/src/tui/session/session-flow/queue/entry-shape.mjs +5 -0
- package/src/tui/session/session-flow/queue/take-entries.mjs +5 -2
- package/src/tui/session/tool-approval-delegation.mjs +64 -0
- package/src/tui/session/tool-approval-request.mjs +39 -0
- package/src/tui/session/tool-approval-settle.mjs +62 -0
- package/src/tui/session/tool-approval.mjs +69 -115
- package/src/tui/session/tui-steering-persist.mjs +8 -1
- package/src/tui/session/turn-stream.mjs +36 -3
- package/src/tui/session/turn.mjs +23 -2
- package/src/vendor/statusline/bin/statusline-route.mjs +1 -219
- package/src/vendor/statusline/src/gateway/route-meta.mjs +35 -13
- package/scripts/code-graph-description-contract.mjs +0 -188
- package/src/runtime/channels/lib/owned-runtime/ownership-refresh.mjs +0 -36
|
@@ -1,8 +1,14 @@
|
|
|
1
1
|
// Tail reminders: short per-model/per-language nudges appended to the very end
|
|
2
2
|
// of an outgoing request (after the last tool result), fresh on every request.
|
|
3
|
-
// They are never stored
|
|
4
|
-
//
|
|
5
|
-
//
|
|
3
|
+
// They are never stored as messages of their own; the Anthropic builders place
|
|
4
|
+
// their last cache breakpoint BEFORE them. The assistant turn a request produces
|
|
5
|
+
// keeps that request's reminder text (`turnReminder`, send-with-recovery.mjs).
|
|
6
|
+
// How each provider places them (providers/lib/tail-reminder-split.mjs):
|
|
7
|
+
// - legacy: the reminder ends this request only and is gone from the next;
|
|
8
|
+
// - turn-scoped (first-party Anthropic on a supported model, opt-in per
|
|
9
|
+
// provider): a clear_at system message that every later request re-sends
|
|
10
|
+
// verbatim before its assistant turn, so the wire is append-only and the
|
|
11
|
+
// model's earlier thinking stays bound to an unchanged prefix.
|
|
6
12
|
//
|
|
7
13
|
// HOW TO ADD A RULE (a new per-model/per-language fix needs nothing else):
|
|
8
14
|
// 1. Append {id, when, text} to TAIL_REMINDER_RULES. `when` holds any of
|
|
@@ -17,7 +23,7 @@ import { AGENT_MODE } from './agent-mode.mjs';
|
|
|
17
23
|
import { _buildLeadLanguage } from './manager/rules-cache.mjs';
|
|
18
24
|
import { TAIL_REMINDER_META } from '../providers/lib/tail-reminder-split.mjs';
|
|
19
25
|
|
|
20
|
-
|
|
26
|
+
const TAIL_REMINDER_TAGS = Object.freeze(['provider', 'model', 'language', 'role']);
|
|
21
27
|
const ROLES = ['lead', 'bot', 'worker'];
|
|
22
28
|
|
|
23
29
|
const PROVIDER_FAMILIES = Object.freeze({
|
|
@@ -84,7 +90,7 @@ export function modelFamilyId(model) {
|
|
|
84
90
|
}
|
|
85
91
|
|
|
86
92
|
/** The session's role: 'worker' (agent-owned), 'bot' (agent mode) or 'lead'. */
|
|
87
|
-
|
|
93
|
+
function tailReminderRole(session) {
|
|
88
94
|
if (session?.owner === 'agent') return 'worker';
|
|
89
95
|
return session?.sessionMode === AGENT_MODE ? 'bot' : 'lead';
|
|
90
96
|
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
// Thinking replay is bound to the request prefix that produced it.
|
|
2
|
+
//
|
|
3
|
+
// An Anthropic thinking block's signature records the conversation prefix it
|
|
4
|
+
// was generated under: the top-level system prompt, the tool set, and every
|
|
5
|
+
// earlier message. A request whose system or tools changed since then carries
|
|
6
|
+
// blocks the API drops before the model sees them (unbilled, so the provider's
|
|
7
|
+
// count no longer includes them) or, where the check is enforced, refuses with
|
|
8
|
+
// a 400. The drop also takes every LATER thinking block, so a transcript that
|
|
9
|
+
// keeps the stale blocks loses the reasoning of every turn that follows them.
|
|
10
|
+
//
|
|
11
|
+
// Where the runtime itself changes that prefix — a heir carrying a conversation
|
|
12
|
+
// into a prompt built for it, a folded or migrated prompt — the stored thinking
|
|
13
|
+
// can never be replayed again. It is stripped once, at that boundary, the
|
|
14
|
+
// documented recovery: each turn keeps its text and tool calls, and the next
|
|
15
|
+
// turns' thinking binds to the new prefix and is replayed normally. Leaving it
|
|
16
|
+
// in place also kept charging every signature to the local context estimate,
|
|
17
|
+
// which then ran far above what the provider actually counted.
|
|
18
|
+
//
|
|
19
|
+
// One round is never stripped: a trailing tool round whose next request still
|
|
20
|
+
// continues it (the latest assistant turn called tools and no final reply
|
|
21
|
+
// followed — an idle session can stop there after a provider error). Its
|
|
22
|
+
// latest assistant turn must go back starting with its thinking, so removing
|
|
23
|
+
// that thinking is a refused request too. When the prefix changed under such a
|
|
24
|
+
// round, the round is ended explicitly instead (`rebindToChangedPrefix`).
|
|
25
|
+
import { contextAckMessage } from './compact/user-message-kind.mjs';
|
|
26
|
+
import { collectAssistantToolCallIds } from './context-tool-pairs.mjs';
|
|
27
|
+
import { assistantMessageWithoutThinkingReplay } from './thinking-replay-recovery.mjs';
|
|
28
|
+
|
|
29
|
+
/** The runtime turn that ends an open tool round: a context ack, so the
|
|
30
|
+
* transcript never shows it and Compact never reads it as a reply. */
|
|
31
|
+
function toolRoundClosure() {
|
|
32
|
+
const ack = contextAckMessage();
|
|
33
|
+
return { ...ack, meta: { ...ack.meta, synthetic: true } };
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const callsTools = (message) =>
|
|
37
|
+
collectAssistantToolCallIds(message).length > 0 ||
|
|
38
|
+
(Array.isArray(message?.providerReplay?.items) &&
|
|
39
|
+
message.providerReplay.items.some((item) => item?.type === 'tool_use'));
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Index where the trailing open tool round starts (the assistant/tool run that
|
|
43
|
+
* ends with the latest assistant turn's results; later user or notification
|
|
44
|
+
* input may follow), or -1 when the latest assistant turn is a final reply.
|
|
45
|
+
* Read from the persisted transcript alone.
|
|
46
|
+
*/
|
|
47
|
+
export function openToolRoundStart(messages) {
|
|
48
|
+
if (!Array.isArray(messages)) return -1;
|
|
49
|
+
const latest = messages.findLastIndex((message) => message?.role === 'assistant');
|
|
50
|
+
if (latest < 0 || !callsTools(messages[latest])) return -1;
|
|
51
|
+
let start = latest;
|
|
52
|
+
while (start > 0 && ['assistant', 'tool'].includes(messages[start - 1]?.role)) start -= 1;
|
|
53
|
+
return start;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** What the next request sends ahead of the conversation: the system blocks and tools. */
|
|
57
|
+
export function requestPrefixIdentity(session) {
|
|
58
|
+
return JSON.stringify([
|
|
59
|
+
(Array.isArray(session?.messages) ? session.messages : [])
|
|
60
|
+
.filter((message) => message?.role === 'system')
|
|
61
|
+
.map((message) => message.content),
|
|
62
|
+
session?.tools,
|
|
63
|
+
]);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* `messages` without Anthropic thinking replay (thinking / redacted_thinking
|
|
68
|
+
* blocks, wherever an assistant turn stores them); text and tool calls stay.
|
|
69
|
+
* The trailing open tool round keeps its thinking: everything stripped is a
|
|
70
|
+
* leading run, which leaves the round's own blocks valid.
|
|
71
|
+
* Replaces changed messages, never mutates; the same array when none changed.
|
|
72
|
+
*/
|
|
73
|
+
export function withoutThinkingReplay(messages) {
|
|
74
|
+
if (!Array.isArray(messages)) return messages;
|
|
75
|
+
const open = openToolRoundStart(messages);
|
|
76
|
+
const end = open < 0 ? messages.length : open;
|
|
77
|
+
let changed = false;
|
|
78
|
+
const out = messages.map((message, index) => {
|
|
79
|
+
const stripped = index < end ? assistantMessageWithoutThinkingReplay(message) : null;
|
|
80
|
+
if (!stripped) return message;
|
|
81
|
+
changed = true;
|
|
82
|
+
return stripped;
|
|
83
|
+
});
|
|
84
|
+
return changed ? out : messages;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* `messages` for a request prefix that changed under them: no thinking replay
|
|
89
|
+
* at all. An open tool round that carries thinking can neither keep it (bound
|
|
90
|
+
* to the old prefix) nor lose it (its continuation must start with it), so it
|
|
91
|
+
* is ended first by a runtime turn right after its last tool result, ahead of
|
|
92
|
+
* any later input (a task notification stays pending after it); the next
|
|
93
|
+
* request then starts a new turn. Never mutates; the same array when nothing
|
|
94
|
+
* changed.
|
|
95
|
+
*/
|
|
96
|
+
export function rebindToChangedPrefix(messages) {
|
|
97
|
+
if (!Array.isArray(messages)) return messages;
|
|
98
|
+
const open = openToolRoundStart(messages);
|
|
99
|
+
if (open < 0) return withoutThinkingReplay(messages);
|
|
100
|
+
let end = messages.findLastIndex((message) => message?.role === 'assistant') + 1;
|
|
101
|
+
while (messages[end]?.role === 'tool') end += 1;
|
|
102
|
+
const roundThinks = messages
|
|
103
|
+
.slice(open, end)
|
|
104
|
+
.some((message) => assistantMessageWithoutThinkingReplay(message) !== null);
|
|
105
|
+
return withoutThinkingReplay(
|
|
106
|
+
roundThinks ? [...messages.slice(0, end), toolRoundClosure(), ...messages.slice(end)] : messages
|
|
107
|
+
);
|
|
108
|
+
}
|
|
@@ -103,8 +103,8 @@ function crossTurnStub(batch, call, ctSig) {
|
|
|
103
103
|
// (which spans ONE assistant turn): when the model re-issues an IDENTICAL
|
|
104
104
|
// normalized call that has already failed repeatFailLimit times in a row
|
|
105
105
|
// across iterations, stop re-executing — the result will not change, and
|
|
106
|
-
// each retry burns a full LLM round-trip
|
|
107
|
-
//
|
|
106
|
+
// each retry burns a full LLM round-trip. The call stays unresolved; the
|
|
107
|
+
// guard only skips re-execution.
|
|
108
108
|
function repeatFailureSkip(batch, call, sigs) {
|
|
109
109
|
const { sessionRef, repeatFailLimit } = batch;
|
|
110
110
|
const rfg = sessionRef?._repeatFailGuard;
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
// consumes.
|
|
5
5
|
import { resolve as resolvePath, isAbsolute } from 'node:path';
|
|
6
6
|
import { normalizeToolEnvelope } from '../tool-envelope.mjs';
|
|
7
|
+
import { effectiveToolCwd } from '../../tools/builtin/bash-tool/shell-cwd.mjs';
|
|
7
8
|
import {
|
|
8
9
|
invalidatePathForSession,
|
|
9
10
|
clearReadDedupSession,
|
|
@@ -85,7 +86,7 @@ async function fireToolFailureHook(batch, call, result) {
|
|
|
85
86
|
await hook({
|
|
86
87
|
name: call.name,
|
|
87
88
|
args: call.arguments,
|
|
88
|
-
cwd: batch.cwd,
|
|
89
|
+
cwd: effectiveToolCwd(call.name, call.arguments, batch.cwd),
|
|
89
90
|
sessionId: batch.sessionId,
|
|
90
91
|
toolCallId: call.id,
|
|
91
92
|
result: typeof result === 'string' ? result : String(result ?? ''),
|
|
@@ -33,6 +33,10 @@ export function createBatchState(ctx) {
|
|
|
33
33
|
pushToolResultMessage: ctx.pushToolResultMessage,
|
|
34
34
|
throwIfAborted: ctx.throwIfAborted,
|
|
35
35
|
repeatFailLimit: ctx.repeatFailLimit,
|
|
36
|
+
// Calls of this batch a capped turn may still run (session/loop/turn-limits.mjs).
|
|
37
|
+
callsAllowed: Number.isFinite(ctx.callsAllowed) ? ctx.callsAllowed : Infinity,
|
|
38
|
+
// After this time (epoch ms) no call of the batch starts.
|
|
39
|
+
wrapUpAt: Number.isFinite(ctx.wrapUpAt) ? ctx.wrapUpAt : Infinity,
|
|
36
40
|
toolBatchId: ctx.toolBatchId,
|
|
37
41
|
executeToolFn: typeof ctx.executeToolFn === 'function' ? ctx.executeToolFn : executeTool,
|
|
38
42
|
dedupStubTotal: ctx.dedupStubTotal,
|
|
@@ -12,6 +12,7 @@ import { executeBatchCall } from './tool-batch/execute-call.mjs';
|
|
|
12
12
|
import { recordCallOutcome } from './tool-batch/record-outcome.mjs';
|
|
13
13
|
import { finalizeBatchResults } from './tool-batch/finalize.mjs';
|
|
14
14
|
import { flushBatch } from './tool-batch/flush.mjs';
|
|
15
|
+
import { turnLimitRefusal } from './loop/turn-limits.mjs';
|
|
15
16
|
|
|
16
17
|
export async function processToolBatch(ctx) {
|
|
17
18
|
const batch = createBatchState(ctx);
|
|
@@ -19,6 +20,10 @@ export async function processToolBatch(ctx) {
|
|
|
19
20
|
for (let callIndex = 0; callIndex < calls.length; callIndex += 1) {
|
|
20
21
|
const call = calls[callIndex];
|
|
21
22
|
if (isBuiltinTool(call.name)) call.name = canonicalizeBuiltinToolName(call.name);
|
|
23
|
+
if (callIndex >= batch.callsAllowed || Date.now() >= batch.wrapUpAt) {
|
|
24
|
+
stageToolResult(batch, turnLimitRefusal(call));
|
|
25
|
+
continue;
|
|
26
|
+
}
|
|
22
27
|
const { skip, ctSig, sigs } = preDispatchSkip(batch, call);
|
|
23
28
|
if (skip !== null) {
|
|
24
29
|
stageToolResult(batch, skip);
|
|
@@ -37,7 +37,11 @@ import {
|
|
|
37
37
|
isTranscriptCancelledStatusText,
|
|
38
38
|
} from '../../../../shared/tool-execution-contract.mjs';
|
|
39
39
|
import { toolResultTerminalStatus } from '../../../../shared/tool-status.mjs';
|
|
40
|
-
import {
|
|
40
|
+
import {
|
|
41
|
+
transcriptAttachmentFields,
|
|
42
|
+
transcriptReactionsField,
|
|
43
|
+
transcriptRouteMetadataFields,
|
|
44
|
+
} from '../../../../shared/transcript-metadata.mjs';
|
|
41
45
|
import { routineTriggerField } from '../../../../shared/routine-trigger.mjs';
|
|
42
46
|
import { isImportedMessage } from '../imported-message.mjs';
|
|
43
47
|
import { isContextAckMessage } from '../compact/user-message-kind.mjs';
|
|
@@ -60,6 +64,20 @@ function restoredFailureFields(value) {
|
|
|
60
64
|
};
|
|
61
65
|
}
|
|
62
66
|
|
|
67
|
+
/** Whether `message` was posted into the thread outside any turn: it says so
|
|
68
|
+
* (`transcript.posted`, stamped by agent-home-delivery.mjs), or it is a row
|
|
69
|
+
* stored before that marker existed. Those carry the id agent-home-delivery
|
|
70
|
+
* mints for every posted row (`messageIdOf`: `routine_<runId>`) and are not
|
|
71
|
+
* prompts: an agent's own message (assistant role) or a result/held-request
|
|
72
|
+
* notice (stored under the user role, but not a prompt). The `routine_` scheme
|
|
73
|
+
* alone is not enough: routine-home-turn.mjs:144 gives the prompt of a routine
|
|
74
|
+
* fire (user role) the same prefix, and that prompt opens a real turn. */
|
|
75
|
+
function isPostedMessage(message, transcript) {
|
|
76
|
+
if (transcript.posted === true) return true;
|
|
77
|
+
if (typeof transcript.messageId !== 'string' || !transcript.messageId.startsWith('routine_')) return false;
|
|
78
|
+
return message?.role !== 'user' || isRoutineResultMessage(message) || isHeldRequestMessage(message);
|
|
79
|
+
}
|
|
80
|
+
|
|
63
81
|
function restoredTranscriptMetadata(message) {
|
|
64
82
|
const value = message?.meta?.transcript;
|
|
65
83
|
if (!value || typeof value !== 'object') return {};
|
|
@@ -83,6 +101,8 @@ function restoredTranscriptMetadata(message) {
|
|
|
83
101
|
// messages back to these rows.
|
|
84
102
|
...(typeof value.messageId === 'string' && value.messageId ? { messageId: value.messageId } : {}),
|
|
85
103
|
...(Number.isFinite(Number(value.at)) ? { at: Number(value.at) } : {}),
|
|
104
|
+
// Delivered into the thread outside any turn (agent-home-delivery.mjs).
|
|
105
|
+
...(isPostedMessage(message, value) ? { posted: true } : {}),
|
|
86
106
|
...transcriptRouteMetadataFields(value),
|
|
87
107
|
...(typeof value.sender === 'string' && value.sender ? { sender: value.sender } : {}),
|
|
88
108
|
...(typeof value.device === 'string' && value.device ? { device: value.device } : {}),
|
|
@@ -91,6 +111,8 @@ function restoredTranscriptMetadata(message) {
|
|
|
91
111
|
// The attachments this prompt carried, by content hash: a restored row
|
|
92
112
|
// opens/downloads them by id from the attachment store.
|
|
93
113
|
...transcriptAttachmentFields(value),
|
|
114
|
+
// The bots' emoji reactions on a user message (message-reactions.mjs).
|
|
115
|
+
...transcriptReactionsField(value),
|
|
94
116
|
// A routine fire this turn delivered: painted as its marker row.
|
|
95
117
|
...routineTriggerField(value),
|
|
96
118
|
// Skills this user turn explicitly loaded (header selection or `$name`).
|
|
@@ -394,7 +416,7 @@ function restoredUserTranscriptItems(message, nextId, userText) {
|
|
|
394
416
|
// A schedule's result in its owner's home thread: a notice row.
|
|
395
417
|
// A held request of an unattended run (human-gate/held-message.mjs) likewise.
|
|
396
418
|
if (isRoutineResultMessage(message) || isHeldRequestMessage(message)) {
|
|
397
|
-
const { messageId, at } = restoredTranscriptMetadata(message);
|
|
419
|
+
const { messageId, at, posted } = restoredTranscriptMetadata(message);
|
|
398
420
|
return [
|
|
399
421
|
{
|
|
400
422
|
kind: 'notice',
|
|
@@ -402,6 +424,7 @@ function restoredUserTranscriptItems(message, nextId, userText) {
|
|
|
402
424
|
...(isHeldRequestMessage(message) ? heldRequestNotice(message) : routineResultNotice(message)),
|
|
403
425
|
...(messageId ? { messageId } : {}),
|
|
404
426
|
...(at ? { at } : {}),
|
|
427
|
+
...(posted ? { posted } : {}),
|
|
405
428
|
},
|
|
406
429
|
];
|
|
407
430
|
}
|
|
@@ -13,6 +13,7 @@ import { formatAggregateDetail, summarizeToolResult, toolLoadingTargets } from '
|
|
|
13
13
|
import { normalizeToolName } from '../../../../shared/tool-primitives.mjs';
|
|
14
14
|
import { gitResultError, gitResultExitCode } from '../../../../shared/tool-card-model/git-result.mjs';
|
|
15
15
|
import { readRowsForDisplay } from '../../../../shared/read-row-numbers.mjs';
|
|
16
|
+
import { visualizeOwnerMetaSessionId } from '../../../../visualize-bridge/tool-meta.mjs';
|
|
16
17
|
|
|
17
18
|
const CANCELLED_RESULT_STATUS_LINE = '[status: cancelled]';
|
|
18
19
|
|
|
@@ -292,15 +293,16 @@ export function assignUiDiffFromMessage(target, source) {
|
|
|
292
293
|
}
|
|
293
294
|
|
|
294
295
|
/** Structured side channel a tool result may carry next to its text (the
|
|
295
|
-
* Computer Use capability refusal): a stable code plus the capability names
|
|
296
|
-
*
|
|
297
|
-
|
|
296
|
+
* Computer Use capability refusal): a stable code plus the capability names,
|
|
297
|
+
* and the owner conversation of a `visualize` call run for another session's
|
|
298
|
+
* work (visualize-bridge/tool-meta.mjs). Anything else is dropped so stored
|
|
299
|
+
* history stays small and predictable. */
|
|
300
|
+
function toolMetaFromMessage(source) {
|
|
298
301
|
const meta = source?.toolMeta ?? source?.content?.toolMeta;
|
|
299
302
|
if (!meta || typeof meta !== 'object' || typeof meta.code !== 'string' || !meta.code) return undefined;
|
|
300
|
-
const missing = Array.isArray(meta.missing)
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
return { code: meta.code, missing };
|
|
303
|
+
const missing = Array.isArray(meta.missing) ? meta.missing.filter((name) => typeof name === 'string' && name) : [];
|
|
304
|
+
const ownerSessionId = visualizeOwnerMetaSessionId(meta.ownerSessionId);
|
|
305
|
+
return { code: meta.code, missing, ...(ownerSessionId ? { ownerSessionId } : {}) };
|
|
304
306
|
}
|
|
305
307
|
|
|
306
308
|
export function toolMetaPatchFromMessage(source) {
|
|
@@ -132,8 +132,8 @@ export function toolGroupedDisplayFallback(resultText, text, rawText) {
|
|
|
132
132
|
return resultText;
|
|
133
133
|
}
|
|
134
134
|
|
|
135
|
-
export function toolErrorDisplay(value, surface = 'tool') {
|
|
136
|
-
const text = presentErrorText(value, { surface });
|
|
135
|
+
export function toolErrorDisplay(value, surface = 'tool', { providerLabel } = {}) {
|
|
136
|
+
const text = presentErrorText(value, { surface, providerLabel });
|
|
137
137
|
if (
|
|
138
138
|
/^(?:Search failed|Fetch failed|No first response|No progress|Cancelled|The .+ went stale|(?:Web search agent|Agent|Tool|Request) (?:stopped|was cancelled))/i.test(
|
|
139
139
|
text
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
// session shows the same failure, reason and quota stop as the live one.
|
|
4
4
|
import { errText } from '../../../shared/err-text.mjs';
|
|
5
5
|
import { safeErrorDetails } from '../../../shared/error-presentation.mjs';
|
|
6
|
+
import { providerDisplayName } from '../../../shared/provider-display-name.mjs';
|
|
6
7
|
import { providerErrorKind } from '../providers/provider-error-kind.mjs';
|
|
7
8
|
import { toolErrorDisplay } from './transcript-restore/tool-result-text.mjs';
|
|
8
9
|
|
|
@@ -39,7 +40,10 @@ export function turnFailureRecord(error, route = {}) {
|
|
|
39
40
|
providerKind,
|
|
40
41
|
errorKind,
|
|
41
42
|
quota,
|
|
42
|
-
|
|
43
|
+
// The provider is named from the turn's own route, never from the error.
|
|
44
|
+
detail: toolErrorDisplay(error, 'turn', {
|
|
45
|
+
providerLabel: route.provider ? providerDisplayName(route.provider) : undefined,
|
|
46
|
+
}).replace(/^Error:\s*/i, ''),
|
|
43
47
|
errorDetails: safeErrorDetails(errText(error)),
|
|
44
48
|
};
|
|
45
49
|
}
|
|
@@ -780,10 +780,13 @@ function guardRead(a) {
|
|
|
780
780
|
}
|
|
781
781
|
|
|
782
782
|
function guardShell(a) {
|
|
783
|
-
const allowed = new Set(['command', 'timeout_ms', 'wait_ms', 'background']);
|
|
783
|
+
const allowed = new Set(['command', 'cwd', 'timeout_ms', 'wait_ms', 'background']);
|
|
784
784
|
const unsupported = Object.keys(a).find((key) => !allowed.has(key));
|
|
785
785
|
if (unsupported) {
|
|
786
|
-
return `Error: shell arg "${unsupported}" is unsupported; use only command, timeout_ms, wait_ms, and background`;
|
|
786
|
+
return `Error: shell arg "${unsupported}" is unsupported; use only command, cwd, timeout_ms, wait_ms, and background`;
|
|
787
|
+
}
|
|
788
|
+
if (hasOwn(a, 'cwd') && (typeof a.cwd !== 'string' || a.cwd.trim().length === 0)) {
|
|
789
|
+
return `Error: shell arg "cwd" must be a non-empty string (got ${describeType(a.cwd)})`;
|
|
787
790
|
}
|
|
788
791
|
if (hasOwn(a, 'background') && typeof a.background !== 'boolean') {
|
|
789
792
|
return `Error: shell arg "background" must be a boolean (got ${describeType(a.background)})`;
|
|
@@ -52,7 +52,17 @@ function registerPromotedTask({ result, command, cwd, options, startedAtMs }) {
|
|
|
52
52
|
// running, now promoted as a tracked shell-job. Surface the task_id + partial
|
|
53
53
|
// output for manual task control instead of keeping the tool call open until
|
|
54
54
|
// the hard timeout.
|
|
55
|
-
export function renderBackgroundedResult({
|
|
55
|
+
export function renderBackgroundedResult({
|
|
56
|
+
result,
|
|
57
|
+
command,
|
|
58
|
+
cwd,
|
|
59
|
+
options,
|
|
60
|
+
startedAtMs,
|
|
61
|
+
teePlan,
|
|
62
|
+
stdout,
|
|
63
|
+
stderr,
|
|
64
|
+
notes = '',
|
|
65
|
+
}) {
|
|
56
66
|
let task = null;
|
|
57
67
|
if (result.jobId) {
|
|
58
68
|
task = registerPromotedTask({ result, command, cwd, options, startedAtMs });
|
|
@@ -91,6 +101,7 @@ export function renderBackgroundedResult({ result, command, cwd, options, starte
|
|
|
91
101
|
)
|
|
92
102
|
: renderBackgroundPartialOutput(stdout, stderr);
|
|
93
103
|
const lines = _backgroundResultLines({
|
|
104
|
+
warning: notes,
|
|
94
105
|
taskBlock,
|
|
95
106
|
message: result.backgroundMessage || DEFAULT_BACKGROUND_MESSAGE,
|
|
96
107
|
partialOutput,
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
// Runtime guards against shell-tool misuse, run in the shell tool's validation
|
|
2
|
+
// step before hooks, approvals and execution (tool-exec.mjs) and again, once,
|
|
3
|
+
// for callers that reach executeBashTool without that step:
|
|
4
|
+
// 1. a leading whole-second sleep of 2 s or more is rejected (a wait belongs
|
|
5
|
+
// to background + task wait);
|
|
6
|
+
// 2. a plain leading `cd <dir>` moves into the `cwd` argument;
|
|
7
|
+
// 3. a shell file read (cat/Get-Content/grep/ls …) gets a pointer to the
|
|
8
|
+
// read / grep / list tools — a note now, a rejection when the mode flips.
|
|
9
|
+
import { shellSplitPipelineSegments, shellSplitSegments, shellTokenize } from '../shell-analysis.mjs';
|
|
10
|
+
import { resolveShellFor } from '../shell-runtime.mjs';
|
|
11
|
+
import { formatShellToolFailure } from './result-format.mjs';
|
|
12
|
+
|
|
13
|
+
/** `warn` runs the read with a note; `reject` returns the block without running. */
|
|
14
|
+
export const SHELL_FILE_READ_MODE = 'warn';
|
|
15
|
+
export const SHELL_FILE_READ_NOTE = 'Note: use the read / grep / list tools for files instead of the shell.';
|
|
16
|
+
const SHELL_FILE_READ_BLOCK =
|
|
17
|
+
'Blocked: use read / grep / list for files instead of the shell (read takes offset/limit for a window of a large file).';
|
|
18
|
+
|
|
19
|
+
const isPlainObject = (value) => value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
20
|
+
|
|
21
|
+
// ── 1. leading sleep ─────────────────────────────────────────────────────────
|
|
22
|
+
|
|
23
|
+
// Sub-2-second pacing stays allowed: only a bare whole-second sleep of 2 or
|
|
24
|
+
// more is refused. Float seconds, `-Milliseconds`, unit suffixes, pipelines,
|
|
25
|
+
// subshells and later statements are not matched.
|
|
26
|
+
const LEADING_SLEEP_MIN_SECONDS = 2;
|
|
27
|
+
// The first statement ends the command or is followed by `;`, `&&` or a newline.
|
|
28
|
+
const STATEMENT_END = String.raw`(?=\s*(?:$|;|&&|\r?\n))`;
|
|
29
|
+
// The argument stays on the command's line: a number on the next line is its own statement.
|
|
30
|
+
// PowerShell takes any prefix of `-Seconds` (`-s`, `-sec`, …) as the seconds parameter.
|
|
31
|
+
const POSIX_SLEEP_RE = new RegExp(String.raw`^\s*sleep[ \t]+(\d+)${STATEMENT_END}`);
|
|
32
|
+
const POWERSHELL_SLEEP_RE = new RegExp(
|
|
33
|
+
String.raw`^\s*(?:start-sleep|sleep)[ \t]+(?:-s(?:e(?:c(?:o(?:n(?:d(?:s)?)?)?)?)?)?[ \t:]+)?(\d+)${STATEMENT_END}`,
|
|
34
|
+
'i'
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
/** Whole seconds of the sleep the command starts with, or null when it does not start with one. */
|
|
38
|
+
function leadingSleepSeconds(command, shellType) {
|
|
39
|
+
const match = (shellType === 'powershell' ? POWERSHELL_SLEEP_RE : POSIX_SLEEP_RE).exec(String(command ?? ''));
|
|
40
|
+
return match ? Number(match[1]) : null;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function leadingSleepFailure(command, shellType) {
|
|
44
|
+
const seconds = leadingSleepSeconds(command, shellType);
|
|
45
|
+
if (seconds === null || seconds < LEADING_SLEEP_MIN_SECONDS) return null;
|
|
46
|
+
return formatShellToolFailure(
|
|
47
|
+
`Blocked: the leading sleep of ${seconds} s was not run. For a long-running command use background:true; its completion arrives automatically. To wait for a specific task when the result is needed now, use task wait — do not poll in a sleep loop. A deliberate delay under ${LEADING_SLEEP_MIN_SECONDS} seconds is allowed.`
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// ── 2. leading cd → cwd ──────────────────────────────────────────────────────
|
|
52
|
+
|
|
53
|
+
// One plain path token: quoted (no variable or escape characters) or unquoted
|
|
54
|
+
// (no shell metacharacters, glob characters included), then `;` or `&&` and a remainder.
|
|
55
|
+
const CD_RE =
|
|
56
|
+
/^\s*(?:cd|set-location)\s+(?:-path\s+)?(?:"([^"$`]+)"|'([^'$`]+)'|([^\s;&|<>"'`$(){}*?[,@%#!]+))\s*(?:;|&&)\s*(\S[\s\S]*)$/i;
|
|
57
|
+
// `Set-Location -Path` expands wildcards even in a quoted path; a POSIX shell
|
|
58
|
+
// keeps a quoted glob literal, so only PowerShell leaves those unrewritten.
|
|
59
|
+
const POWERSHELL_WILDCARD_RE = /[*?[]/;
|
|
60
|
+
const CD_LIKE_RE = /^(?:cd|set-location|pushd|popd)(?:\s|;|&|$)/i;
|
|
61
|
+
|
|
62
|
+
/** `{ dir, rest }` for a command that starts with a plain `cd <dir>` and more, else null. */
|
|
63
|
+
function splitLeadingCd(command, shellType) {
|
|
64
|
+
const match = CD_RE.exec(String(command ?? ''));
|
|
65
|
+
if (!match) return null;
|
|
66
|
+
const dir = match[1] ?? match[2] ?? match[3];
|
|
67
|
+
const rest = match[4];
|
|
68
|
+
if (CD_LIKE_RE.test(rest)) return null;
|
|
69
|
+
if (dir.trim() === '' || dir.startsWith('-')) return null;
|
|
70
|
+
if (shellType === 'powershell' && POWERSHELL_WILDCARD_RE.test(dir)) return null;
|
|
71
|
+
// `~user` is another account's home in a shell, not a folder named so; a
|
|
72
|
+
// quoted `~` is a literal directory name, only an unquoted one is home.
|
|
73
|
+
if (dir.startsWith('~') && (match[3] === undefined || !/^~(?:[\\/]|$)/.test(dir))) return null;
|
|
74
|
+
// A POSIX backslash escapes; only single quotes keep it literal.
|
|
75
|
+
if (shellType !== 'powershell' && match[2] === undefined && dir.includes('\\')) return null;
|
|
76
|
+
return { dir, rest };
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// ── 3. shell file reads ──────────────────────────────────────────────────────
|
|
80
|
+
|
|
81
|
+
const POWERSHELL_READERS = {
|
|
82
|
+
'get-content': 'read',
|
|
83
|
+
gc: 'read',
|
|
84
|
+
cat: 'read',
|
|
85
|
+
type: 'read',
|
|
86
|
+
'select-string': 'grep',
|
|
87
|
+
sls: 'grep',
|
|
88
|
+
'get-childitem': 'list',
|
|
89
|
+
gci: 'list',
|
|
90
|
+
ls: 'list',
|
|
91
|
+
dir: 'list',
|
|
92
|
+
rg: 'grep',
|
|
93
|
+
};
|
|
94
|
+
const POSIX_READERS = {
|
|
95
|
+
cat: 'read',
|
|
96
|
+
head: 'read',
|
|
97
|
+
tail: 'read',
|
|
98
|
+
less: 'read',
|
|
99
|
+
grep: 'grep',
|
|
100
|
+
rg: 'grep',
|
|
101
|
+
ls: 'list',
|
|
102
|
+
find: 'list',
|
|
103
|
+
};
|
|
104
|
+
// Flags whose next token is a path or pattern operand, and flags whose next
|
|
105
|
+
// token is a plain value; both are read off the operand count.
|
|
106
|
+
const OPERAND_FLAGS = new Set(['-path', '-literalpath', '-pattern', '-e', '--regexp', '-f', '--file']);
|
|
107
|
+
const VALUE_FLAGS = new Set(['-encoding', '-filter', '-include', '-exclude', '-g', '--glob', '-t', '--type']);
|
|
108
|
+
// A later pipeline stage that only shapes text; anything else is a program
|
|
109
|
+
// the first stage feeds.
|
|
110
|
+
const TEXT_FILTERS = new Set([
|
|
111
|
+
'select-object',
|
|
112
|
+
'select',
|
|
113
|
+
'select-string',
|
|
114
|
+
'sls',
|
|
115
|
+
'where-object',
|
|
116
|
+
'sort-object',
|
|
117
|
+
'sort',
|
|
118
|
+
'measure-object',
|
|
119
|
+
'format-table',
|
|
120
|
+
'format-list',
|
|
121
|
+
'ft',
|
|
122
|
+
'fl',
|
|
123
|
+
'out-string',
|
|
124
|
+
'out-host',
|
|
125
|
+
'head',
|
|
126
|
+
'tail',
|
|
127
|
+
'grep',
|
|
128
|
+
'egrep',
|
|
129
|
+
'rg',
|
|
130
|
+
'wc',
|
|
131
|
+
'uniq',
|
|
132
|
+
'cut',
|
|
133
|
+
'findstr',
|
|
134
|
+
]);
|
|
135
|
+
const FIND_WRITERS = new Set(['-exec', '-execdir', '-ok', '-okdir', '-delete', '-fprint', '-fprintf', '-fls']);
|
|
136
|
+
const BENIGN_REDIRECT_TARGET = /^(?:&\d|\$null|\/dev\/null|nul)$/i;
|
|
137
|
+
|
|
138
|
+
const commandName = (token) =>
|
|
139
|
+
String(token ?? '')
|
|
140
|
+
.toLowerCase()
|
|
141
|
+
.replace(/^.*[\\/]/, '')
|
|
142
|
+
.replace(/\.exe$/, '');
|
|
143
|
+
|
|
144
|
+
// The path/pattern operands of a stage, or null when it writes a file.
|
|
145
|
+
function stageOperands(args) {
|
|
146
|
+
const operands = [];
|
|
147
|
+
for (let i = 0; i < args.length; i++) {
|
|
148
|
+
const token = args[i];
|
|
149
|
+
if (token === '>' || token === '>>') {
|
|
150
|
+
if (!BENIGN_REDIRECT_TARGET.test(args[i + 1] ?? '')) return null;
|
|
151
|
+
i++;
|
|
152
|
+
continue;
|
|
153
|
+
}
|
|
154
|
+
const flag = token.toLowerCase();
|
|
155
|
+
if (flag.startsWith('-') && flag.length > 1) {
|
|
156
|
+
if (OPERAND_FLAGS.has(flag) && i + 1 < args.length) operands.push(args[++i]);
|
|
157
|
+
else if (VALUE_FLAGS.has(flag)) i++;
|
|
158
|
+
continue;
|
|
159
|
+
}
|
|
160
|
+
// A bare count (`head -n 5`, `tail -n +5`) is a flag value, not an operand.
|
|
161
|
+
if (!/^[+-]?\d+$/.test(token)) operands.push(token);
|
|
162
|
+
}
|
|
163
|
+
return operands;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* `{ tool, command }` when the command's first statement starts with a
|
|
168
|
+
* read-only file command on a path — the tool that does that job — else null.
|
|
169
|
+
* A pipeline whose later stage is a program, a write, and a first stage that
|
|
170
|
+
* reads piped input are not file reads.
|
|
171
|
+
*/
|
|
172
|
+
function detectShellFileRead(command, shellType) {
|
|
173
|
+
const first = shellSplitSegments(String(command ?? ''))[0];
|
|
174
|
+
if (!first) return null;
|
|
175
|
+
const stages = shellSplitPipelineSegments(first);
|
|
176
|
+
const tokens = shellTokenize(stages[0] ?? '');
|
|
177
|
+
if (!tokens || tokens.length === 0) return null;
|
|
178
|
+
const name = commandName(tokens[0]);
|
|
179
|
+
const tool = (shellType === 'powershell' ? POWERSHELL_READERS : POSIX_READERS)[name];
|
|
180
|
+
if (!tool) return null;
|
|
181
|
+
for (const stage of stages.slice(1)) {
|
|
182
|
+
if (!TEXT_FILTERS.has(commandName(stage.trim().split(/\s+/)[0]))) return null;
|
|
183
|
+
}
|
|
184
|
+
const args = tokens.slice(1);
|
|
185
|
+
if (name === 'find') {
|
|
186
|
+
const lowered = args.map((token) => token.toLowerCase());
|
|
187
|
+
const matchesNames = lowered.some((token) => ['-name', '-iname', '-path', '-regex'].includes(token));
|
|
188
|
+
return matchesNames && !lowered.some((token) => FIND_WRITERS.has(token)) ? { tool, command: name } : null;
|
|
189
|
+
}
|
|
190
|
+
const operands = stageOperands(args);
|
|
191
|
+
if (!operands) return null;
|
|
192
|
+
// A search names its pattern and then the path it reads; with one operand it reads piped input.
|
|
193
|
+
const minOperands = tool === 'grep' ? 2 : 1;
|
|
194
|
+
return operands.length >= minOperands ? { tool, command: name } : null;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// ── the guard ────────────────────────────────────────────────────────────────
|
|
198
|
+
|
|
199
|
+
/** The syntax family of the shell a call asks for (`args.shell`, as foreign adapters set it), else the default shell's. */
|
|
200
|
+
export function requestedShellType(args) {
|
|
201
|
+
const requested = typeof args?.shell === 'string' && args.shell ? resolveShellFor(args.shell) : null;
|
|
202
|
+
return (requested ?? resolveShellFor('default'))?.shellType;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
function recordFileRead(telemetry, entry) {
|
|
206
|
+
if (!telemetry || typeof telemetry !== 'object') return;
|
|
207
|
+
if (!Array.isArray(telemetry.shellMisuse)) telemetry.shellMisuse = [];
|
|
208
|
+
telemetry.shellMisuse.push({ kind: 'file-read', ...entry });
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Apply the three guards to a shell call.
|
|
213
|
+
* @returns {{ failure: string } | { args: object, notes: string[] }} the
|
|
214
|
+
* rejection, or the call to run (a leading `cd` moved into `cwd`) with the
|
|
215
|
+
* lines to add to its result.
|
|
216
|
+
*/
|
|
217
|
+
export function guardShellCall(
|
|
218
|
+
args,
|
|
219
|
+
{ shellType, fileReadMode = SHELL_FILE_READ_MODE, agent = null, sessionId = null, telemetry = null } = {}
|
|
220
|
+
) {
|
|
221
|
+
if (!isPlainObject(args) || typeof args.command !== 'string') return { args, notes: [] };
|
|
222
|
+
const notes = [];
|
|
223
|
+
let guarded = args;
|
|
224
|
+
const moved = args.cwd == null ? splitLeadingCd(args.command, shellType) : null;
|
|
225
|
+
if (moved) {
|
|
226
|
+
guarded = { ...args, command: moved.rest, cwd: moved.dir };
|
|
227
|
+
notes.push(`Moved \`cd ${moved.dir}\` into cwd.`);
|
|
228
|
+
}
|
|
229
|
+
if (args.background !== true) {
|
|
230
|
+
const sleepFailure = leadingSleepFailure(guarded.command, shellType);
|
|
231
|
+
if (sleepFailure) return { failure: sleepFailure };
|
|
232
|
+
}
|
|
233
|
+
const read = detectShellFileRead(guarded.command, shellType);
|
|
234
|
+
if (read) {
|
|
235
|
+
recordFileRead(telemetry, { mode: fileReadMode, tool: read.tool, command: read.command, agent, sessionId });
|
|
236
|
+
if (fileReadMode === 'reject') return { failure: formatShellToolFailure(SHELL_FILE_READ_BLOCK) };
|
|
237
|
+
notes.push(SHELL_FILE_READ_NOTE);
|
|
238
|
+
}
|
|
239
|
+
return { args: guarded, notes };
|
|
240
|
+
}
|
|
@@ -118,7 +118,7 @@ const _PS_UNIX_EQUIVALENTS = {
|
|
|
118
118
|
head: 'Select-Object -First N',
|
|
119
119
|
tail: 'Select-Object -Last N',
|
|
120
120
|
wc: 'Measure-Object -Line',
|
|
121
|
-
grep: '
|
|
121
|
+
grep: 'the grep tool',
|
|
122
122
|
sed: 'the edit tool',
|
|
123
123
|
awk: 'ForEach-Object / the edit tool',
|
|
124
124
|
pgrep: 'Get-Process -Name <name>',
|