@gajae-code/coding-agent 0.17.2 → 0.17.4
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/CHANGELOG.md +194 -0
- package/dist/types/cli/daemon-cli.d.ts +3 -1
- package/dist/types/cli/github-release.d.ts +17 -0
- package/dist/types/cli/public-command-entry.d.ts +56 -0
- package/dist/types/cli/public-command-errors.d.ts +126 -0
- package/dist/types/cli/public-command-evidence.d.ts +99 -0
- package/dist/types/cli/public-command-help.d.ts +65 -0
- package/dist/types/cli/public-command-registry.d.ts +69 -0
- package/dist/types/cli/session-picker.d.ts +1 -1
- package/dist/types/cli/setup-cli.d.ts +1 -0
- package/dist/types/cli/skills-cli.d.ts +5 -0
- package/dist/types/cli.d.ts +3 -0
- package/dist/types/commands/daemon.d.ts +4 -50
- package/dist/types/commands/sdk.d.ts +20 -37
- package/dist/types/commands/setup.d.ts +3 -0
- package/dist/types/commands/skills.d.ts +9 -0
- package/dist/types/config/autorouting-tier-map.d.ts +18 -0
- package/dist/types/config/file-lock.d.ts +9 -1
- package/dist/types/config/model-profile-activation.d.ts +45 -1
- package/dist/types/config/model-registry.d.ts +11 -2
- package/dist/types/config/settings-schema.d.ts +11 -0
- package/dist/types/coordinator-mcp/question-state.d.ts +6 -1
- package/dist/types/daemon/operator-contract.d.ts +5 -1
- package/dist/types/eval/js/worker-protocol.d.ts +2 -0
- package/dist/types/extensibility/extensions/types.d.ts +12 -0
- package/dist/types/extensibility/gjc-plugins/registry.d.ts +4 -0
- package/dist/types/extensibility/runtime-skill-discovery.d.ts +18 -0
- package/dist/types/extensibility/shared-events.d.ts +3 -1
- package/dist/types/gjc-runtime/session-state-lock.d.ts +61 -0
- package/dist/types/internal-urls/embedded-protocol.d.ts +9 -0
- package/dist/types/internal-urls/index.d.ts +1 -0
- package/dist/types/internal-urls/parse.d.ts +9 -1
- package/dist/types/main.d.ts +22 -0
- package/dist/types/modes/acp/acp-agent.d.ts +55 -1
- package/dist/types/modes/acp/startup-provenance.d.ts +16 -0
- package/dist/types/modes/components/custom-provider-wizard.d.ts +19 -1
- package/dist/types/modes/components/session-selector.d.ts +5 -2
- package/dist/types/modes/components/user-message-selector.d.ts +5 -2
- package/dist/types/modes/controllers/command-controller.d.ts +1 -1
- package/dist/types/modes/controllers/selector-controller.d.ts +1 -1
- package/dist/types/modes/interactive-mode.d.ts +1 -1
- package/dist/types/modes/shared/agent-wire/workflow-gate-broker.d.ts +11 -1
- package/dist/types/modes/shared/agent-wire/workflow-gate-types.d.ts +14 -1
- package/dist/types/modes/types.d.ts +2 -2
- package/dist/types/runtime/process-lifecycle.d.ts +2 -0
- package/dist/types/runtime-mcp/manager.d.ts +56 -0
- package/dist/types/runtime-mcp/pool.d.ts +17 -0
- package/dist/types/runtime-mcp/redaction.d.ts +20 -0
- package/dist/types/runtime-mcp/startup-policy.d.ts +6 -0
- package/dist/types/runtime-mcp/tool-bridge.d.ts +9 -0
- package/dist/types/runtime-mcp/transports/http.d.ts +11 -0
- package/dist/types/sdk/broker/broker.d.ts +13 -2
- package/dist/types/sdk/broker/identity.d.ts +3 -1
- package/dist/types/sdk/broker/lifecycle-ledger.d.ts +23 -3
- package/dist/types/sdk/broker/lifecycle.d.ts +15 -8
- package/dist/types/sdk/broker/runtime.d.ts +1 -1
- package/dist/types/sdk/broker/session-index.d.ts +22 -3
- package/dist/types/sdk/broker/spawn-authority.d.ts +4 -0
- package/dist/types/sdk/broker/stderr-drainer.d.ts +13 -0
- package/dist/types/sdk/bus/index.d.ts +19 -0
- package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +3 -1
- package/dist/types/sdk/bus/telegram-daemon.d.ts +5 -0
- package/dist/types/sdk/cli/master-cli.d.ts +6 -10
- package/dist/types/sdk/cli/rows.d.ts +19 -1
- package/dist/types/sdk/cli/session-cli.d.ts +22 -5
- package/dist/types/sdk/client/client.d.ts +2 -0
- package/dist/types/sdk/guides/cli.d.ts +3 -4
- package/dist/types/sdk/guides/verify.d.ts +2 -0
- package/dist/types/sdk/host/host.d.ts +9 -0
- package/dist/types/sdk/host/session-runtime.d.ts +31 -0
- package/dist/types/sdk/host/types.d.ts +10 -0
- package/dist/types/sdk/index.d.ts +1 -0
- package/dist/types/sdk/prompt-deadline-flush.d.ts +83 -0
- package/dist/types/sdk/prompt-deadline-manager.d.ts +37 -0
- package/dist/types/sdk/prompt-tool-boundary.d.ts +73 -0
- package/dist/types/sdk/router/session-router.d.ts +2 -0
- package/dist/types/sdk/startup-capability.d.ts +2 -0
- package/dist/types/sdk/transport/relay.d.ts +1 -0
- package/dist/types/sdk/transport/serve-cli.d.ts +48 -4
- package/dist/types/sdk/transport/stdio.d.ts +5 -1
- package/dist/types/session/agent-session.d.ts +168 -21
- package/dist/types/session/fallback-chain-controller.d.ts +6 -6
- package/dist/types/session/fold-coordinator.d.ts +17 -3
- package/dist/types/session/internal/managed-session-storage.d.ts +7 -1
- package/dist/types/session/session-manager.d.ts +64 -1
- package/dist/types/session/session-work-lease.d.ts +14 -0
- package/dist/types/setup/model-onboarding-guidance.d.ts +1 -1
- package/dist/types/setup/provider-onboarding.d.ts +81 -1
- package/dist/types/tools/bash.d.ts +37 -11
- package/dist/types/tools/job.d.ts +1 -1
- package/dist/types/tools/steer-fold.d.ts +31 -0
- package/dist/types/utils/git.d.ts +28 -2
- package/dist/types/utils/mupdf-embedded.d.ts +2 -0
- package/dist/types/utils/mupdf.d.ts +4 -0
- package/dist/types/utils/session-title-generation.d.ts +6 -0
- package/dist/types/utils/title-generator.d.ts +11 -2
- package/dist/types/web/scrapers/types.d.ts +3 -0
- package/package.json +18 -10
- package/scripts/build-binary.ts +9 -3
- package/scripts/compile-args.ts +0 -9
- package/scripts/embed-mupdf.ts +52 -0
- package/scripts/generate-sdk-operation-inventory.ts +31 -1
- package/scripts/vendor-markit.ts +196 -0
- package/src/async/job-manager.ts +22 -1
- package/src/cli/daemon-cli.ts +53 -18
- package/src/cli/github-release.ts +87 -5
- package/src/cli/mcp-cli.ts +38 -4
- package/src/cli/public-command-entry.ts +450 -0
- package/src/cli/public-command-errors.ts +581 -0
- package/src/cli/public-command-evidence.ts +717 -0
- package/src/cli/public-command-help.ts +360 -0
- package/src/cli/public-command-registry.ts +535 -0
- package/src/cli/session-picker.ts +2 -1
- package/src/cli/setup-cli.ts +9 -1
- package/src/cli/skills-cli.ts +46 -2
- package/src/cli.ts +61 -1
- package/src/commands/daemon.ts +65 -69
- package/src/commands/sdk.ts +175 -117
- package/src/commands/setup.ts +2 -0
- package/src/commands/skills.ts +11 -0
- package/src/config/autorouting-tier-map.ts +14 -0
- package/src/config/file-lock.ts +147 -51
- package/src/config/model-profile-activation.ts +334 -43
- package/src/config/model-registry.ts +183 -25
- package/src/config/settings-schema.ts +26 -2
- package/src/coordinator-mcp/policy.ts +10 -5
- package/src/coordinator-mcp/question-gate-codec.ts +17 -0
- package/src/coordinator-mcp/question-state.ts +175 -8
- package/src/coordinator-mcp/server.ts +507 -81
- package/src/coordinator-mcp/session-reaper.ts +6 -4
- package/src/crash/record-loader.ts +6 -2
- package/src/daemon/operator-contract.ts +26 -12
- package/src/defaults/gjc/skills/ultragoal/SKILL.md +1 -1
- package/src/eval/js/context-manager.ts +8 -8
- package/src/eval/js/worker-core.ts +9 -4
- package/src/eval/js/worker-protocol.ts +2 -0
- package/src/extensibility/extensions/runner.ts +52 -4
- package/src/extensibility/extensions/types.ts +10 -0
- package/src/extensibility/gjc-plugins/registry.ts +196 -37
- package/src/extensibility/runtime-skill-discovery.ts +61 -3
- package/src/extensibility/shared-events.ts +3 -1
- package/src/gjc-runtime/session-state-lock.ts +165 -24
- package/src/gjc-runtime/ultragoal-succession.ts +31 -3
- package/src/internal-urls/docs-index.generated.ts +20 -19
- package/src/internal-urls/embedded-protocol.ts +83 -0
- package/src/internal-urls/index.ts +1 -0
- package/src/internal-urls/parse.ts +21 -1
- package/src/internal-urls/router.ts +4 -2
- package/src/main.ts +123 -55
- package/src/modes/acp/acp-agent.ts +408 -35
- package/src/modes/acp/startup-provenance.ts +59 -0
- package/src/modes/components/custom-provider-wizard.ts +308 -7
- package/src/modes/components/oauth-selector.ts +9 -0
- package/src/modes/components/session-selector.ts +47 -4
- package/src/modes/components/user-message-selector.ts +57 -15
- package/src/modes/controllers/command-controller.ts +58 -32
- package/src/modes/controllers/extension-ui-controller.ts +33 -7
- package/src/modes/controllers/input-controller.ts +4 -0
- package/src/modes/controllers/selector-controller.ts +161 -15
- package/src/modes/interactive-mode.ts +21 -7
- package/src/modes/runtime-init.ts +1 -0
- package/src/modes/shared/agent-wire/workflow-gate-broker.ts +124 -7
- package/src/modes/shared/agent-wire/workflow-gate-types.ts +16 -2
- package/src/modes/types.ts +2 -2
- package/src/prompts/agent-fragments/browser-aside.md +1 -0
- package/src/prompts/system/title-system.md +1 -1
- package/src/prompts/tools/bash.md +1 -1
- package/src/prompts/tools/job.md +1 -1
- package/src/prompts/tools/read.md +2 -0
- package/src/runtime/network-prewarm-service.test.ts +36 -1
- package/src/runtime/network-prewarm-service.ts +28 -3
- package/src/runtime/process-lifecycle.ts +22 -11
- package/src/runtime-mcp/config.ts +16 -1
- package/src/runtime-mcp/manager.ts +689 -55
- package/src/runtime-mcp/pool.test.ts +31 -0
- package/src/runtime-mcp/pool.ts +304 -28
- package/src/runtime-mcp/redaction.ts +42 -0
- package/src/runtime-mcp/startup-policy.ts +7 -0
- package/src/runtime-mcp/tool-bridge.ts +71 -0
- package/src/runtime-mcp/transports/http.ts +108 -25
- package/src/sdk/acp/adapter.ts +27 -8
- package/src/sdk/broker/broker.ts +378 -52
- package/src/sdk/broker/identity.ts +9 -1
- package/src/sdk/broker/lifecycle-ledger.ts +95 -31
- package/src/sdk/broker/lifecycle.ts +690 -141
- package/src/sdk/broker/runtime.ts +1 -1
- package/src/sdk/broker/session-index.ts +97 -4
- package/src/sdk/broker/spawn-authority.ts +35 -7
- package/src/sdk/broker/stderr-drainer.ts +59 -0
- package/src/sdk/bus/chat-daemon-control.ts +2 -2
- package/src/sdk/bus/chat-daemon-runtime.ts +1 -0
- package/src/sdk/bus/index.ts +690 -80
- package/src/sdk/bus/telegram-daemon-cli.ts +31 -2
- package/src/sdk/bus/telegram-daemon-contract.ts +3 -1
- package/src/sdk/bus/telegram-daemon.ts +10 -3
- package/src/sdk/cli/master-cli.ts +47 -36
- package/src/sdk/cli/rows.ts +30 -7
- package/src/sdk/cli/session-cli.ts +687 -184
- package/src/sdk/client/client.ts +18 -0
- package/src/sdk/guides/catalog.ts +2 -2
- package/src/sdk/guides/cli.ts +20 -62
- package/src/sdk/guides/verify.ts +14 -0
- package/src/sdk/host/host.ts +49 -5
- package/src/sdk/host/session-runtime.test.ts +138 -2
- package/src/sdk/host/session-runtime.ts +311 -97
- package/src/sdk/host/types.ts +12 -1
- package/src/sdk/host/websocket-transport.lifecycle.test.ts +84 -0
- package/src/sdk/host/websocket-transport.ts +40 -6
- package/src/sdk/index.ts +11 -0
- package/src/sdk/prompt-deadline-flush.ts +334 -0
- package/src/sdk/prompt-deadline-manager.ts +133 -1
- package/src/sdk/prompt-tool-boundary.ts +121 -0
- package/src/sdk/protocol/operation-inventory.generated.json +106 -0
- package/src/sdk/router/session-router.ts +9 -1
- package/src/sdk/session.ts +182 -7
- package/src/sdk/startup-capability.ts +1 -1
- package/src/sdk/transport/relay.ts +71 -6
- package/src/sdk/transport/serve-cli.ts +397 -42
- package/src/sdk/transport/socket.ts +12 -11
- package/src/sdk/transport/stdio.ts +7 -3
- package/src/session/agent-session.ts +2437 -400
- package/src/session/fallback-chain-controller.ts +6 -7
- package/src/session/fold-coordinator.ts +24 -5
- package/src/session/internal/managed-session-scope.ts +58 -18
- package/src/session/internal/managed-session-storage.ts +304 -11
- package/src/session/session-manager.ts +382 -44
- package/src/session/session-work-lease.ts +34 -0
- package/src/setup/model-onboarding-guidance.ts +1 -1
- package/src/setup/provider-onboarding.ts +397 -24
- package/src/slash-commands/builtin-registry.ts +258 -14
- package/src/task/index.ts +14 -2
- package/src/tools/bash.ts +98 -78
- package/src/tools/fetch.ts +119 -20
- package/src/tools/job.ts +151 -25
- package/src/tools/output-meta.ts +15 -2
- package/src/tools/path-utils.ts +27 -3
- package/src/tools/read.ts +3 -3
- package/src/tools/steer-fold.ts +107 -0
- package/src/tools/tool-catalog.generated.ts +29 -2
- package/src/tools/tool-errors.ts +4 -0
- package/src/utils/git.ts +53 -3
- package/src/utils/markit.ts +55 -41
- package/src/utils/mupdf-assets.d.ts +4 -0
- package/src/utils/mupdf-embedded.ts +4 -0
- package/src/utils/mupdf.ts +129 -0
- package/src/utils/session-title-generation.ts +29 -0
- package/src/utils/title-generator.ts +46 -2
- package/src/web/scrapers/types.ts +24 -3
- package/vendor/markit-ai/LICENSE +21 -0
- package/vendor/markit-ai/MANIFEST.json +412 -0
- package/vendor/markit-ai/dist/commands/config.d.ts +4 -0
- package/vendor/markit-ai/dist/commands/config.js +139 -0
- package/vendor/markit-ai/dist/commands/convert.d.ts +6 -0
- package/vendor/markit-ai/dist/commands/convert.js +113 -0
- package/vendor/markit-ai/dist/commands/formats.d.ts +2 -0
- package/vendor/markit-ai/dist/commands/formats.js +92 -0
- package/vendor/markit-ai/dist/commands/init.d.ts +2 -0
- package/vendor/markit-ai/dist/commands/init.js +29 -0
- package/vendor/markit-ai/dist/commands/onboard.d.ts +2 -0
- package/vendor/markit-ai/dist/commands/onboard.js +61 -0
- package/vendor/markit-ai/dist/commands/plugin.d.ts +4 -0
- package/vendor/markit-ai/dist/commands/plugin.js +58 -0
- package/vendor/markit-ai/dist/config.d.ts +26 -0
- package/vendor/markit-ai/dist/config.js +42 -0
- package/vendor/markit-ai/dist/converters/audio.d.ts +7 -0
- package/vendor/markit-ai/dist/converters/audio.js +104 -0
- package/vendor/markit-ai/dist/converters/csv.d.ts +7 -0
- package/vendor/markit-ai/dist/converters/csv.js +83 -0
- package/vendor/markit-ai/dist/converters/docx.d.ts +6 -0
- package/vendor/markit-ai/dist/converters/docx.js +57 -0
- package/vendor/markit-ai/dist/converters/epub.d.ts +8 -0
- package/vendor/markit-ai/dist/converters/epub.js +128 -0
- package/vendor/markit-ai/dist/converters/github.d.ts +18 -0
- package/vendor/markit-ai/dist/converters/github.js +148 -0
- package/vendor/markit-ai/dist/converters/html.d.ts +6 -0
- package/vendor/markit-ai/dist/converters/html.js +30 -0
- package/vendor/markit-ai/dist/converters/image.d.ts +6 -0
- package/vendor/markit-ai/dist/converters/image.js +124 -0
- package/vendor/markit-ai/dist/converters/ipynb.d.ts +6 -0
- package/vendor/markit-ai/dist/converters/ipynb.js +75 -0
- package/vendor/markit-ai/dist/converters/iwork.d.ts +20 -0
- package/vendor/markit-ai/dist/converters/iwork.js +391 -0
- package/vendor/markit-ai/dist/converters/json.d.ts +6 -0
- package/vendor/markit-ai/dist/converters/json.js +21 -0
- package/vendor/markit-ai/dist/converters/pdf/columns.d.ts +35 -0
- package/vendor/markit-ai/dist/converters/pdf/columns.js +93 -0
- package/vendor/markit-ai/dist/converters/pdf/extract.d.ts +19 -0
- package/vendor/markit-ai/dist/converters/pdf/extract.js +514 -0
- package/vendor/markit-ai/dist/converters/pdf/grid.d.ts +25 -0
- package/vendor/markit-ai/dist/converters/pdf/grid.js +795 -0
- package/vendor/markit-ai/dist/converters/pdf/headers.d.ts +24 -0
- package/vendor/markit-ai/dist/converters/pdf/headers.js +108 -0
- package/vendor/markit-ai/dist/converters/pdf/index.d.ts +19 -0
- package/vendor/markit-ai/dist/converters/pdf/index.js +136 -0
- package/vendor/markit-ai/dist/converters/pdf/mupdf-loader.js +9 -0
- package/vendor/markit-ai/dist/converters/pdf/render.d.ts +24 -0
- package/vendor/markit-ai/dist/converters/pdf/render.js +513 -0
- package/vendor/markit-ai/dist/converters/pdf/types.d.ts +75 -0
- package/vendor/markit-ai/dist/converters/pdf/types.js +1 -0
- package/vendor/markit-ai/dist/converters/plain-text.d.ts +6 -0
- package/vendor/markit-ai/dist/converters/plain-text.js +87 -0
- package/vendor/markit-ai/dist/converters/pptx.d.ts +8 -0
- package/vendor/markit-ai/dist/converters/pptx.js +289 -0
- package/vendor/markit-ai/dist/converters/rss.d.ts +11 -0
- package/vendor/markit-ai/dist/converters/rss.js +138 -0
- package/vendor/markit-ai/dist/converters/wikipedia.d.ts +6 -0
- package/vendor/markit-ai/dist/converters/wikipedia.js +33 -0
- package/vendor/markit-ai/dist/converters/xlsx.d.ts +8 -0
- package/vendor/markit-ai/dist/converters/xlsx.js +147 -0
- package/vendor/markit-ai/dist/converters/xml.d.ts +6 -0
- package/vendor/markit-ai/dist/converters/xml.js +18 -0
- package/vendor/markit-ai/dist/converters/yaml.d.ts +6 -0
- package/vendor/markit-ai/dist/converters/yaml.js +17 -0
- package/vendor/markit-ai/dist/converters/zip.d.ts +8 -0
- package/vendor/markit-ai/dist/converters/zip.js +57 -0
- package/vendor/markit-ai/dist/index.d.ts +30 -0
- package/vendor/markit-ai/dist/index.js +26 -0
- package/vendor/markit-ai/dist/main.d.ts +2 -0
- package/vendor/markit-ai/dist/main.js +191 -0
- package/vendor/markit-ai/dist/markit.d.ts +45 -0
- package/vendor/markit-ai/dist/markit.js +267 -0
- package/vendor/markit-ai/dist/plugins/api.d.ts +7 -0
- package/vendor/markit-ai/dist/plugins/api.js +42 -0
- package/vendor/markit-ai/dist/plugins/index.d.ts +4 -0
- package/vendor/markit-ai/dist/plugins/index.js +3 -0
- package/vendor/markit-ai/dist/plugins/installer.d.ts +25 -0
- package/vendor/markit-ai/dist/plugins/installer.js +176 -0
- package/vendor/markit-ai/dist/plugins/loader.d.ts +6 -0
- package/vendor/markit-ai/dist/plugins/loader.js +61 -0
- package/vendor/markit-ai/dist/plugins/types.d.ts +25 -0
- package/vendor/markit-ai/dist/plugins/types.js +1 -0
- package/vendor/markit-ai/dist/providers/anthropic.d.ts +2 -0
- package/vendor/markit-ai/dist/providers/anthropic.js +47 -0
- package/vendor/markit-ai/dist/providers/index.d.ts +21 -0
- package/vendor/markit-ai/dist/providers/index.js +56 -0
- package/vendor/markit-ai/dist/providers/openai.d.ts +2 -0
- package/vendor/markit-ai/dist/providers/openai.js +76 -0
- package/vendor/markit-ai/dist/providers/types.d.ts +26 -0
- package/vendor/markit-ai/dist/providers/types.js +1 -0
- package/vendor/markit-ai/dist/types.d.ts +36 -0
- package/vendor/markit-ai/dist/types.js +1 -0
- package/vendor/markit-ai/dist/utils/exit-codes.d.ts +4 -0
- package/vendor/markit-ai/dist/utils/exit-codes.js +4 -0
- package/vendor/markit-ai/dist/utils/output.d.ts +22 -0
- package/vendor/markit-ai/dist/utils/output.js +31 -0
- package/vendor/markit-ai/dist/utils/turndown.d.ts +8 -0
- package/vendor/markit-ai/dist/utils/turndown.js +70 -0
- package/vendor/markit-ai/package.json +74 -0
- package/vendor/markit-ai.patch +49 -0
package/src/tools/bash.ts
CHANGED
|
@@ -66,10 +66,13 @@ import {
|
|
|
66
66
|
} from "./output-meta";
|
|
67
67
|
import { resolveToCwd } from "./path-utils";
|
|
68
68
|
import { formatToolWorkingDirectory, replaceTabs } from "./render-utils";
|
|
69
|
+
import { steerFoldReasonLine, watchSteerForFold } from "./steer-fold";
|
|
69
70
|
import { checkTmuxSelfInjection } from "./tmux-self-injection-guard";
|
|
70
71
|
import { ToolAbortError, ToolError } from "./tool-errors";
|
|
71
72
|
import { toolResult } from "./tool-result";
|
|
72
73
|
|
|
74
|
+
export { STEER_FOLD_GRACE_MS, steerFoldReasonLine } from "./steer-fold";
|
|
75
|
+
|
|
73
76
|
function sliceTextAfterUtf8ByteOffset(text: string, offsetBytes: number): string {
|
|
74
77
|
if (offsetBytes <= 0) return text;
|
|
75
78
|
let consumed = 0;
|
|
@@ -86,19 +89,6 @@ function sliceTextAfterUtf8ByteOffset(text: string, offsetBytes: number): string
|
|
|
86
89
|
import { clampTimeout, TOOL_TIMEOUTS } from "./tool-timeouts";
|
|
87
90
|
|
|
88
91
|
export const BASH_DEFAULT_PREVIEW_LINES = 10;
|
|
89
|
-
/**
|
|
90
|
-
* A user steer folds a running foreground bash call only after it has been
|
|
91
|
-
* running at least this long. Shorter commands finish normally and the steer is
|
|
92
|
-
* consumed at the ordinary tool boundary, so a quick `git status` never turns
|
|
93
|
-
* into a background job plus a wake-up turn.
|
|
94
|
-
*/
|
|
95
|
-
export const STEER_FOLD_GRACE_MS = 2_000;
|
|
96
|
-
|
|
97
|
-
/** Model-facing line appended to a steer-folded background-start result. */
|
|
98
|
-
export function steerFoldReasonLine(jobId: string): string {
|
|
99
|
-
return `Folded into background job ${jobId} because a user steer arrived; the command keeps running with its original timeout and its result will wake a later turn.`;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
92
|
const BASH_ERROR_MAX_BYTES = 4096;
|
|
103
93
|
const ARTIFACT_SAVE_DIAGNOSTIC_MAX_BYTES = 256;
|
|
104
94
|
const BASH_ENV_NAME_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
@@ -481,12 +471,50 @@ export async function saveBashOriginalArtifactForTests(
|
|
|
481
471
|
return result.status === "saved" ? result.artifactId : undefined;
|
|
482
472
|
}
|
|
483
473
|
|
|
474
|
+
/**
|
|
475
|
+
* Model-declared activity metadata for the browser-backend routing contract
|
|
476
|
+
* (`browser.backend: aside`). When that contract routes a browser task through
|
|
477
|
+
* Bash, it requires the call to declare the activity structurally so consumers
|
|
478
|
+
* can project it without parsing the command text. The declaration is
|
|
479
|
+
* model-declared and schema-validated only: nothing here verifies that the
|
|
480
|
+
* command actually invokes Aside, and the executor never inspects `command`.
|
|
481
|
+
*/
|
|
482
|
+
export type BashActivityProvider = "aside";
|
|
483
|
+
export type BashActivityMode = "repl" | "exec";
|
|
484
|
+
export interface BashActivityDeclaration {
|
|
485
|
+
kind: "browser";
|
|
486
|
+
provider: BashActivityProvider;
|
|
487
|
+
mode: BashActivityMode;
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
const bashActivityDeclarationSchema = z
|
|
491
|
+
.object({
|
|
492
|
+
kind: z.literal("browser"),
|
|
493
|
+
provider: z.literal("aside"),
|
|
494
|
+
mode: z.enum(["repl", "exec"]),
|
|
495
|
+
})
|
|
496
|
+
.strict();
|
|
497
|
+
|
|
498
|
+
/**
|
|
499
|
+
* Optional activity declaration. Anything that is not the exact supported
|
|
500
|
+
* shape is dropped rather than failing the call: the declaration is routing
|
|
501
|
+
* metadata, and a malformed value must never make an otherwise valid command
|
|
502
|
+
* unusable. Values are never coerced — `mode: "EXEC"` is dropped, not guessed.
|
|
503
|
+
*/
|
|
504
|
+
const optionalBashActivity = z
|
|
505
|
+
.preprocess(
|
|
506
|
+
value => (bashActivityDeclarationSchema.safeParse(value).success ? value : undefined),
|
|
507
|
+
bashActivityDeclarationSchema.optional(),
|
|
508
|
+
)
|
|
509
|
+
.describe("declare browser activity for the aside browser backend: repl or exec");
|
|
510
|
+
|
|
484
511
|
const bashSchemaBase = z.object({
|
|
485
512
|
command: z.string().describe("command to execute"),
|
|
486
513
|
env: z.record(z.string().regex(BASH_ENV_NAME_PATTERN), z.string()).optional().describe("extra env vars"),
|
|
487
514
|
timeout: z.number().default(300).describe("timeout in seconds, NOT milliseconds (30 = 30s)").optional(),
|
|
488
515
|
cwd: z.string().describe("working directory").optional(),
|
|
489
516
|
pty: z.boolean().describe("run in pty mode").optional(),
|
|
517
|
+
activity: optionalBashActivity,
|
|
490
518
|
});
|
|
491
519
|
|
|
492
520
|
const bashSchemaWithAsync = bashSchemaBase.extend({
|
|
@@ -503,6 +531,8 @@ export interface BashToolInput {
|
|
|
503
531
|
|
|
504
532
|
async?: boolean;
|
|
505
533
|
pty?: boolean;
|
|
534
|
+
/** Accepted activity declaration, mirrored into {@link BashToolDetails.activity}. */
|
|
535
|
+
activity?: BashActivityDeclaration;
|
|
506
536
|
}
|
|
507
537
|
|
|
508
538
|
export interface BashToolDetails {
|
|
@@ -512,6 +542,8 @@ export interface BashToolDetails {
|
|
|
512
542
|
terminalId?: string;
|
|
513
543
|
/** Why the foreground wait was folded, when this result is a background-start after a fold. */
|
|
514
544
|
foldReason?: FoldReason;
|
|
545
|
+
/** The accepted model-declared activity declaration, when the call carried one. */
|
|
546
|
+
activity?: BashActivityDeclaration;
|
|
515
547
|
async?: {
|
|
516
548
|
state: "running" | "completed" | "failed";
|
|
517
549
|
jobId: string;
|
|
@@ -924,6 +956,7 @@ export class BashTool implements AgentTool<BashToolSchema, BashToolDetails> {
|
|
|
924
956
|
notices?: readonly string[];
|
|
925
957
|
terminalId?: string;
|
|
926
958
|
artifactReferenceInBody?: boolean;
|
|
959
|
+
activity?: BashActivityDeclaration;
|
|
927
960
|
} = {},
|
|
928
961
|
): AgentToolResult<BashToolDetails> {
|
|
929
962
|
const shortArtifactInBody = !result.truncated && result.artifactId !== undefined;
|
|
@@ -947,6 +980,9 @@ export class BashTool implements AgentTool<BashToolSchema, BashToolDetails> {
|
|
|
947
980
|
if (options.terminalId !== undefined) {
|
|
948
981
|
details.terminalId = options.terminalId;
|
|
949
982
|
}
|
|
983
|
+
if (options.activity !== undefined) {
|
|
984
|
+
details.activity = options.activity;
|
|
985
|
+
}
|
|
950
986
|
const resultBuilder = toolResult(details)
|
|
951
987
|
.text(outputText)
|
|
952
988
|
.truncationFromSummary(result, {
|
|
@@ -967,6 +1003,7 @@ export class BashTool implements AgentTool<BashToolSchema, BashToolDetails> {
|
|
|
967
1003
|
notices?: readonly string[];
|
|
968
1004
|
terminalId?: string;
|
|
969
1005
|
foldReason?: FoldReason;
|
|
1006
|
+
activity?: BashActivityDeclaration;
|
|
970
1007
|
} = {},
|
|
971
1008
|
): AgentToolResult<BashToolDetails> {
|
|
972
1009
|
const details: BashToolDetails = {
|
|
@@ -980,6 +1017,7 @@ export class BashTool implements AgentTool<BashToolSchema, BashToolDetails> {
|
|
|
980
1017
|
details.requestedTimeoutSeconds = options.requestedTimeoutSec;
|
|
981
1018
|
}
|
|
982
1019
|
if (options.foldReason !== undefined) details.foldReason = options.foldReason;
|
|
1020
|
+
if (options.activity !== undefined) details.activity = options.activity;
|
|
983
1021
|
const lines: string[] = [];
|
|
984
1022
|
const trimmedPreview = previewText.trimEnd();
|
|
985
1023
|
if (trimmedPreview.length > 0) {
|
|
@@ -1019,6 +1057,8 @@ export class BashTool implements AgentTool<BashToolSchema, BashToolDetails> {
|
|
|
1019
1057
|
onCompletion?: () => void;
|
|
1020
1058
|
/** Immutable attempt-scoped tool call id, when executed via a tool call. */
|
|
1021
1059
|
toolCallId?: string;
|
|
1060
|
+
/** Accepted activity declaration, mirrored into job progress and result details. */
|
|
1061
|
+
activity?: BashActivityDeclaration;
|
|
1022
1062
|
}): ManagedBashJobHandle {
|
|
1023
1063
|
const manager = this.#resolveOwnedJobManager();
|
|
1024
1064
|
if (!manager) {
|
|
@@ -1032,12 +1072,13 @@ export class BashTool implements AgentTool<BashToolSchema, BashToolDetails> {
|
|
|
1032
1072
|
: options.command;
|
|
1033
1073
|
let latestText = "";
|
|
1034
1074
|
let backgrounded = options.startBackgrounded;
|
|
1075
|
+
const activityDetails = options.activity === undefined ? {} : { activity: options.activity };
|
|
1035
1076
|
const runningDetails = (jobId: string): Record<string, unknown> | undefined =>
|
|
1036
|
-
backgrounded ? { async: { state: "running", jobId, type: "bash" } } : undefined;
|
|
1077
|
+
backgrounded ? { async: { state: "running", jobId, type: "bash" }, ...activityDetails } : undefined;
|
|
1037
1078
|
const completedDetails = (jobId: string): Record<string, unknown> | undefined =>
|
|
1038
|
-
backgrounded ? { async: { state: "completed", jobId, type: "bash" } } : undefined;
|
|
1079
|
+
backgrounded ? { async: { state: "completed", jobId, type: "bash" }, ...activityDetails } : undefined;
|
|
1039
1080
|
const failedDetails = (jobId: string): Record<string, unknown> | undefined =>
|
|
1040
|
-
backgrounded ? { async: { state: "failed", jobId, type: "bash" } } : undefined;
|
|
1081
|
+
backgrounded ? { async: { state: "failed", jobId, type: "bash" }, ...activityDetails } : undefined;
|
|
1041
1082
|
const completion = Promise.withResolvers<ManagedBashJobCompletion>();
|
|
1042
1083
|
|
|
1043
1084
|
const jobId = manager.register(
|
|
@@ -1088,6 +1129,7 @@ export class BashTool implements AgentTool<BashToolSchema, BashToolDetails> {
|
|
|
1088
1129
|
requestedTimeoutSec: options.requestedTimeoutSec,
|
|
1089
1130
|
notices: options.notices,
|
|
1090
1131
|
artifactReferenceInBody: true,
|
|
1132
|
+
activity: options.activity,
|
|
1091
1133
|
});
|
|
1092
1134
|
const finalText = this.#extractTextResult(finalResult);
|
|
1093
1135
|
latestText = finalText;
|
|
@@ -1110,7 +1152,10 @@ export class BashTool implements AgentTool<BashToolSchema, BashToolDetails> {
|
|
|
1110
1152
|
latestText = text;
|
|
1111
1153
|
await options.onUpdate?.({
|
|
1112
1154
|
content: [{ type: "text", text }],
|
|
1113
|
-
|
|
1155
|
+
// Backgrounded frames carry the job details verbatim; a foreground
|
|
1156
|
+
// frame has no job facts, but the activity declaration is per-call
|
|
1157
|
+
// metadata and stays attached so partial results are self-describing.
|
|
1158
|
+
details: backgrounded ? ((details ?? {}) as BashToolDetails) : (activityDetails as BashToolDetails),
|
|
1114
1159
|
});
|
|
1115
1160
|
},
|
|
1116
1161
|
},
|
|
@@ -1155,62 +1200,6 @@ export class BashTool implements AgentTool<BashToolSchema, BashToolDetails> {
|
|
|
1155
1200
|
return AsyncJobManager.forEndpoint(endpointId) ?? AsyncJobManager.instance();
|
|
1156
1201
|
}
|
|
1157
1202
|
|
|
1158
|
-
/**
|
|
1159
|
-
* Whether a queued user steer may fold the running foreground wait. Mirrors
|
|
1160
|
-
* the loop's steer admission: `busyPromptMode=queue` never admits a steer
|
|
1161
|
-
* into the busy run, so it never folds. `toolInterruptPolicy` is
|
|
1162
|
-
* deliberately NOT a gate: `finish_tools` means "do not kill the batch to
|
|
1163
|
-
* deliver a steer", and a fold kills nothing. It is the one way to deliver
|
|
1164
|
-
* the steer now AND let the command finish, so it applies under both
|
|
1165
|
-
* policies; the policy only decides what the loop does with sibling tools
|
|
1166
|
-
* after the fold returns. The auto-background setting is likewise not
|
|
1167
|
-
* consulted: like the chord, a steer fold is a user action.
|
|
1168
|
-
*
|
|
1169
|
-
* Fails closed: a session that cannot report newly admitted steering or
|
|
1170
|
-
* accept a fold request is not steer-foldable.
|
|
1171
|
-
*/
|
|
1172
|
-
#steerFoldEnabled(): boolean {
|
|
1173
|
-
const { waitForUserSteering, requestForegroundBashBackground } = this.session;
|
|
1174
|
-
if (!waitForUserSteering || !requestForegroundBashBackground) return false;
|
|
1175
|
-
return this.session.settings.get("busyPromptMode") === "steer";
|
|
1176
|
-
}
|
|
1177
|
-
|
|
1178
|
-
/**
|
|
1179
|
-
* Fold `adapter` on the first user steer that ARRIVES after the wait has run
|
|
1180
|
-
* for {@link STEER_FOLD_GRACE_MS} since `startedAt`. A steer already queued
|
|
1181
|
-
* when the wait starts, or one that arrives inside the grace window, never
|
|
1182
|
-
* folds: the command finishes normally and that steer is consumed at the
|
|
1183
|
-
* ordinary tool boundary. The watcher keeps observing so a later qualifying
|
|
1184
|
-
* steer still folds, and re-checks the gate at that moment so a
|
|
1185
|
-
* `busyPromptMode` change during the command is honored.
|
|
1186
|
-
* Returns a stop function; call it when the wait settles. No-op when steer
|
|
1187
|
-
* folding is gated off at start.
|
|
1188
|
-
*/
|
|
1189
|
-
#watchSteerForFold(adapter: FoldAdapter, startedAt: number): () => void {
|
|
1190
|
-
if (!this.#steerFoldEnabled()) return () => {};
|
|
1191
|
-
const waitForSteer = this.session.waitForUserSteering;
|
|
1192
|
-
const requestFold = this.session.requestForegroundBashBackground;
|
|
1193
|
-
if (!waitForSteer || !requestFold) return () => {};
|
|
1194
|
-
const watch = new AbortController();
|
|
1195
|
-
const observe = async (): Promise<void> => {
|
|
1196
|
-
while (!watch.signal.aborted) {
|
|
1197
|
-
await waitForSteer(watch.signal);
|
|
1198
|
-
if (watch.signal.aborted) return;
|
|
1199
|
-
if (Date.now() - startedAt < STEER_FOLD_GRACE_MS) continue;
|
|
1200
|
-
if (!this.#steerFoldEnabled()) continue;
|
|
1201
|
-
await requestFold("steer", adapter);
|
|
1202
|
-
return;
|
|
1203
|
-
}
|
|
1204
|
-
};
|
|
1205
|
-
observe().catch((error: unknown) => {
|
|
1206
|
-
logger.warn("Steer-triggered fold failed", {
|
|
1207
|
-
jobId: adapter.jobId,
|
|
1208
|
-
error: error instanceof Error ? error.message : String(error),
|
|
1209
|
-
});
|
|
1210
|
-
});
|
|
1211
|
-
return () => watch.abort();
|
|
1212
|
-
}
|
|
1213
|
-
|
|
1214
1203
|
/**
|
|
1215
1204
|
* Race a managed job against the auto-background threshold, an explicit fold
|
|
1216
1205
|
* (chord / SDK control / steer), and abort. A steer that arrives once the
|
|
@@ -1269,7 +1258,9 @@ export class BashTool implements AgentTool<BashToolSchema, BashToolDetails> {
|
|
|
1269
1258
|
}
|
|
1270
1259
|
|
|
1271
1260
|
const stopSteerWatch =
|
|
1272
|
-
backgroundRequest && foldAdapter
|
|
1261
|
+
backgroundRequest && foldAdapter
|
|
1262
|
+
? watchSteerForFold(this.session, startedAt, fold => fold("steer", foldAdapter), foldAdapter.jobId)
|
|
1263
|
+
: () => {};
|
|
1273
1264
|
try {
|
|
1274
1265
|
return await Promise.race(waiters);
|
|
1275
1266
|
} finally {
|
|
@@ -1571,9 +1562,10 @@ export class BashTool implements AgentTool<BashToolSchema, BashToolDetails> {
|
|
|
1571
1562
|
notices: readonly string[],
|
|
1572
1563
|
signal: AbortSignal | undefined,
|
|
1573
1564
|
resolvedEnv: Record<string, string>,
|
|
1565
|
+
activity: BashActivityDeclaration | undefined,
|
|
1574
1566
|
): Promise<AgentToolResult<BashToolDetails>> {
|
|
1575
1567
|
const result = await this.#runDirectMasterSpawn(command, commandCwd, timeoutMs, signal, resolvedEnv);
|
|
1576
|
-
return this.#buildCompletedResult(result, timeoutSec, { requestedTimeoutSec, notices });
|
|
1568
|
+
return this.#buildCompletedResult(result, timeoutSec, { requestedTimeoutSec, notices, activity });
|
|
1577
1569
|
}
|
|
1578
1570
|
|
|
1579
1571
|
/**
|
|
@@ -1723,6 +1715,7 @@ export class BashTool implements AgentTool<BashToolSchema, BashToolDetails> {
|
|
|
1723
1715
|
|
|
1724
1716
|
async: asyncRequested = false,
|
|
1725
1717
|
pty = false,
|
|
1718
|
+
activity,
|
|
1726
1719
|
}: BashToolInput,
|
|
1727
1720
|
signal?: AbortSignal,
|
|
1728
1721
|
onUpdate?: AgentToolUpdateCallback<BashToolDetails>,
|
|
@@ -1767,6 +1760,7 @@ export class BashTool implements AgentTool<BashToolSchema, BashToolDetails> {
|
|
|
1767
1760
|
pendingNotices,
|
|
1768
1761
|
signal,
|
|
1769
1762
|
resolvedEnv,
|
|
1763
|
+
activity,
|
|
1770
1764
|
);
|
|
1771
1765
|
}
|
|
1772
1766
|
|
|
@@ -1792,6 +1786,7 @@ export class BashTool implements AgentTool<BashToolSchema, BashToolDetails> {
|
|
|
1792
1786
|
onUpdate,
|
|
1793
1787
|
startBackgrounded: true,
|
|
1794
1788
|
toolCallId,
|
|
1789
|
+
activity,
|
|
1795
1790
|
});
|
|
1796
1791
|
const jobGeneration = asyncManager.getJob(job.jobId)?.generation ?? job.jobId;
|
|
1797
1792
|
job.setBackgrounded(true);
|
|
@@ -1799,6 +1794,7 @@ export class BashTool implements AgentTool<BashToolSchema, BashToolDetails> {
|
|
|
1799
1794
|
return this.#buildBackgroundStartResult(job.jobId, job.label, "", timeoutSec, {
|
|
1800
1795
|
requestedTimeoutSec,
|
|
1801
1796
|
notices: pendingNotices,
|
|
1797
|
+
activity,
|
|
1802
1798
|
});
|
|
1803
1799
|
}
|
|
1804
1800
|
|
|
@@ -1845,6 +1841,7 @@ export class BashTool implements AgentTool<BashToolSchema, BashToolDetails> {
|
|
|
1845
1841
|
managedForegroundSettled = true;
|
|
1846
1842
|
},
|
|
1847
1843
|
toolCallId,
|
|
1844
|
+
activity,
|
|
1848
1845
|
});
|
|
1849
1846
|
const jobGeneration = ownedManager.getJob(job.jobId)?.generation ?? job.jobId;
|
|
1850
1847
|
if (startBackgrounded) {
|
|
@@ -1853,6 +1850,7 @@ export class BashTool implements AgentTool<BashToolSchema, BashToolDetails> {
|
|
|
1853
1850
|
return this.#buildBackgroundStartResult(job.jobId, job.label, "", timeoutSec, {
|
|
1854
1851
|
requestedTimeoutSec,
|
|
1855
1852
|
notices: pendingNotices,
|
|
1853
|
+
activity,
|
|
1856
1854
|
});
|
|
1857
1855
|
}
|
|
1858
1856
|
const backgroundRequest = Promise.withResolvers<FoldReason>();
|
|
@@ -1948,6 +1946,7 @@ export class BashTool implements AgentTool<BashToolSchema, BashToolDetails> {
|
|
|
1948
1946
|
requestedTimeoutSec,
|
|
1949
1947
|
notices: pendingNotices,
|
|
1950
1948
|
foldReason: waitResult.reason,
|
|
1949
|
+
activity,
|
|
1951
1950
|
});
|
|
1952
1951
|
}
|
|
1953
1952
|
|
|
@@ -2246,6 +2245,7 @@ export class BashTool implements AgentTool<BashToolSchema, BashToolDetails> {
|
|
|
2246
2245
|
requestedTimeoutSec,
|
|
2247
2246
|
notices: timeoutNotices,
|
|
2248
2247
|
terminalId: handle.terminalId,
|
|
2248
|
+
activity,
|
|
2249
2249
|
});
|
|
2250
2250
|
}
|
|
2251
2251
|
|
|
@@ -2378,6 +2378,7 @@ export class BashTool implements AgentTool<BashToolSchema, BashToolDetails> {
|
|
|
2378
2378
|
requestedTimeoutSec,
|
|
2379
2379
|
notices: bridgeNotices,
|
|
2380
2380
|
terminalId: handle.terminalId,
|
|
2381
|
+
activity,
|
|
2381
2382
|
});
|
|
2382
2383
|
};
|
|
2383
2384
|
|
|
@@ -2419,7 +2420,12 @@ export class BashTool implements AgentTool<BashToolSchema, BashToolDetails> {
|
|
|
2419
2420
|
bridgeCompletion.resolve({ kind: "completed", result });
|
|
2420
2421
|
await reportProgress(
|
|
2421
2422
|
finalText,
|
|
2422
|
-
bridgeBackgrounded
|
|
2423
|
+
bridgeBackgrounded
|
|
2424
|
+
? {
|
|
2425
|
+
async: { state: "completed", jobId, type: "bash" },
|
|
2426
|
+
...(activity === undefined ? {} : { activity }),
|
|
2427
|
+
}
|
|
2428
|
+
: undefined,
|
|
2423
2429
|
);
|
|
2424
2430
|
return finalText;
|
|
2425
2431
|
} catch (error) {
|
|
@@ -2429,7 +2435,12 @@ export class BashTool implements AgentTool<BashToolSchema, BashToolDetails> {
|
|
|
2429
2435
|
bridgeCompletion.resolve({ kind: "failed", error });
|
|
2430
2436
|
await reportProgress(
|
|
2431
2437
|
message,
|
|
2432
|
-
bridgeBackgrounded
|
|
2438
|
+
bridgeBackgrounded
|
|
2439
|
+
? {
|
|
2440
|
+
async: { state: "failed", jobId, type: "bash" },
|
|
2441
|
+
...(activity === undefined ? {} : { activity }),
|
|
2442
|
+
}
|
|
2443
|
+
: undefined,
|
|
2433
2444
|
);
|
|
2434
2445
|
throw error;
|
|
2435
2446
|
} finally {
|
|
@@ -2556,6 +2567,7 @@ export class BashTool implements AgentTool<BashToolSchema, BashToolDetails> {
|
|
|
2556
2567
|
notices: pendingNotices,
|
|
2557
2568
|
terminalId: handle.terminalId,
|
|
2558
2569
|
foldReason: bridgeWait.reason,
|
|
2570
|
+
activity,
|
|
2559
2571
|
});
|
|
2560
2572
|
}
|
|
2561
2573
|
|
|
@@ -2640,6 +2652,7 @@ export class BashTool implements AgentTool<BashToolSchema, BashToolDetails> {
|
|
|
2640
2652
|
outcome = await controls.terminalCompletion;
|
|
2641
2653
|
const completed = this.#buildCompletedResult(outcome, timeoutSec, {
|
|
2642
2654
|
requestedTimeoutSec,
|
|
2655
|
+
activity,
|
|
2643
2656
|
});
|
|
2644
2657
|
return this.#extractTextResult(completed);
|
|
2645
2658
|
} finally {
|
|
@@ -2707,6 +2720,7 @@ export class BashTool implements AgentTool<BashToolSchema, BashToolDetails> {
|
|
|
2707
2720
|
requestedTimeoutSec,
|
|
2708
2721
|
notices: pendingNotices,
|
|
2709
2722
|
foldReason: receipt.reason,
|
|
2723
|
+
activity,
|
|
2710
2724
|
});
|
|
2711
2725
|
const outcome = controls.detachObserver(
|
|
2712
2726
|
interactiveResultFromText(this.#extractTextResult(started)),
|
|
@@ -2722,7 +2736,12 @@ export class BashTool implements AgentTool<BashToolSchema, BashToolDetails> {
|
|
|
2722
2736
|
resolveForegroundObserver: () => "already-settled",
|
|
2723
2737
|
};
|
|
2724
2738
|
const unregisterPtyFold = this.session.registerForegroundFoldParticipant?.(ptyFoldAdapter);
|
|
2725
|
-
const stopPtySteerWatch =
|
|
2739
|
+
const stopPtySteerWatch = watchSteerForFold(
|
|
2740
|
+
this.session,
|
|
2741
|
+
ptyStartedAt,
|
|
2742
|
+
fold => fold("steer", ptyFoldAdapter),
|
|
2743
|
+
ptyFoldAdapter.jobId,
|
|
2744
|
+
);
|
|
2726
2745
|
ptyFoldUnregister = () => {
|
|
2727
2746
|
stopPtySteerWatch();
|
|
2728
2747
|
unregisterPtyFold?.();
|
|
@@ -2790,6 +2809,7 @@ export class BashTool implements AgentTool<BashToolSchema, BashToolDetails> {
|
|
|
2790
2809
|
return this.#buildCompletedResult(result, timeoutSec, {
|
|
2791
2810
|
requestedTimeoutSec,
|
|
2792
2811
|
notices: pendingNotices,
|
|
2812
|
+
activity,
|
|
2793
2813
|
});
|
|
2794
2814
|
}
|
|
2795
2815
|
}
|
package/src/tools/fetch.ts
CHANGED
|
@@ -49,6 +49,7 @@ const FETCH_DEFAULT_MAX_LINES = 300;
|
|
|
49
49
|
// Convertible document types handled by markit.
|
|
50
50
|
const CONVERTIBLE_MIMES = new Set([
|
|
51
51
|
"application/pdf",
|
|
52
|
+
"application/x-pdf",
|
|
52
53
|
"application/msword",
|
|
53
54
|
"application/vnd.ms-powerpoint",
|
|
54
55
|
"application/vnd.ms-excel",
|
|
@@ -228,17 +229,52 @@ function normalizeMime(contentType: string): string {
|
|
|
228
229
|
return contentType.split(";")[0].trim().toLowerCase();
|
|
229
230
|
}
|
|
230
231
|
|
|
232
|
+
// Bound both parsing work and decoded filename size to a single modest header.
|
|
233
|
+
function getDispositionFilename(header: string): string | undefined {
|
|
234
|
+
if (header.length > 8192 || /[\x00-\x1f\x7f]/.test(header.replaceAll("\t", ""))) return undefined;
|
|
235
|
+
const separator = header.indexOf(";");
|
|
236
|
+
if (separator < 0) return undefined;
|
|
237
|
+
const parameter = /;[ \t]*([!#$%&'*+.^_`|~\w-]+)[ \t]*=[ \t]*(?:"((?:[^"\\]|\\.)*)"|([!#$%&'*+.^_`|~\w-]+))[ \t]*/y;
|
|
238
|
+
let offset = separator;
|
|
239
|
+
let plain: string | undefined;
|
|
240
|
+
let extended: string | undefined;
|
|
241
|
+
const seen = new Set<string>();
|
|
242
|
+
while (offset < header.length) {
|
|
243
|
+
parameter.lastIndex = offset;
|
|
244
|
+
const match = parameter.exec(header);
|
|
245
|
+
if (!match) return undefined;
|
|
246
|
+
offset = parameter.lastIndex;
|
|
247
|
+
const name = match[1].toLowerCase();
|
|
248
|
+
if (name !== "filename" && name !== "filename*") continue;
|
|
249
|
+
// Duplicate filename parameters are ambiguous, not an ordering override.
|
|
250
|
+
if (seen.has(name)) return undefined;
|
|
251
|
+
seen.add(name);
|
|
252
|
+
const value = match[2] === undefined ? match[3] : match[2].replace(/\\(.)/g, "$1");
|
|
253
|
+
if (name === "filename") {
|
|
254
|
+
if (value) plain = value;
|
|
255
|
+
continue;
|
|
256
|
+
}
|
|
257
|
+
// RFC 8187 ext-value: only UTF-8 is supported; quoted ext-values are invalid.
|
|
258
|
+
const encoded =
|
|
259
|
+
match[2] === undefined ? /^UTF-8'[A-Za-z0-9-]*'((?:[!#$&+.^_`|~\w-]|%[\da-fA-F]{2})*)$/i.exec(value) : null;
|
|
260
|
+
if (!encoded) continue;
|
|
261
|
+
try {
|
|
262
|
+
const decoded = decodeURIComponent(encoded[1]);
|
|
263
|
+
if (decoded && !/[\x00-\x1f\x7f]/.test(decoded)) extended = decoded;
|
|
264
|
+
} catch {
|
|
265
|
+
// Invalid UTF-8 or percent encoding must not escape classification.
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
return extended ?? plain;
|
|
269
|
+
}
|
|
270
|
+
|
|
231
271
|
/**
|
|
232
272
|
* Get extension from URL or Content-Disposition
|
|
233
273
|
*/
|
|
234
274
|
function getExtensionHint(url: string, contentDisposition?: string): string {
|
|
235
|
-
// Try Content-Disposition filename first
|
|
236
275
|
if (contentDisposition) {
|
|
237
|
-
const
|
|
238
|
-
if (
|
|
239
|
-
const ext = path.extname(match[1]).toLowerCase();
|
|
240
|
-
if (ext) return ext;
|
|
241
|
-
}
|
|
276
|
+
const filename = getDispositionFilename(contentDisposition);
|
|
277
|
+
if (filename !== undefined) return path.extname(filename).toLowerCase();
|
|
242
278
|
}
|
|
243
279
|
|
|
244
280
|
// Fall back to URL path
|
|
@@ -251,21 +287,25 @@ function getExtensionHint(url: string, contentDisposition?: string): string {
|
|
|
251
287
|
return "";
|
|
252
288
|
}
|
|
253
289
|
|
|
290
|
+
function isGenericMimeType(mime: string): boolean {
|
|
291
|
+
return (
|
|
292
|
+
mime.length === 0 || mime === "application/octet-stream" || mime === "binary/octet-stream" || mime === "unknown"
|
|
293
|
+
);
|
|
294
|
+
}
|
|
295
|
+
|
|
254
296
|
/**
|
|
255
297
|
* Check if content type is convertible via markit.
|
|
256
298
|
*/
|
|
257
299
|
function isConvertible(mime: string, extensionHint: string): boolean {
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
300
|
+
return (
|
|
301
|
+
CONVERTIBLE_MIMES.has(mime) ||
|
|
302
|
+
(CONVERTIBLE_EXTENSIONS.has(extensionHint) && (extensionHint !== ".pdf" || isGenericMimeType(mime)))
|
|
303
|
+
);
|
|
262
304
|
}
|
|
263
305
|
|
|
264
306
|
function resolveImageMimeType(mime: string, extensionHint: string): string | null {
|
|
265
307
|
if (mime.startsWith("image/")) return mime;
|
|
266
|
-
|
|
267
|
-
mime.length === 0 || mime === "application/octet-stream" || mime === "binary/octet-stream" || mime === "unknown";
|
|
268
|
-
if (!shouldUseExtensionHint) return null;
|
|
308
|
+
if (!isGenericMimeType(mime)) return null;
|
|
269
309
|
return IMAGE_MIME_BY_EXTENSION.get(extensionHint) ?? null;
|
|
270
310
|
}
|
|
271
311
|
|
|
@@ -741,15 +781,60 @@ async function renderUrl(
|
|
|
741
781
|
const { finalUrl, content: rawContent } = response;
|
|
742
782
|
const mime = normalizeMime(response.contentType);
|
|
743
783
|
const extHint = getExtensionHint(finalUrl);
|
|
784
|
+
let isPdf =
|
|
785
|
+
mime === "application/pdf" || mime === "application/x-pdf" || (extHint === ".pdf" && isGenericMimeType(mime));
|
|
786
|
+
|
|
787
|
+
// Raw PDF inspection is explicit; never substitute PDF bytes for failed text extraction.
|
|
788
|
+
if (raw && isPdf) {
|
|
789
|
+
const output = finalizeOutput(rawContent);
|
|
790
|
+
return {
|
|
791
|
+
url,
|
|
792
|
+
finalUrl,
|
|
793
|
+
contentType: mime,
|
|
794
|
+
method: "raw",
|
|
795
|
+
content: output.content,
|
|
796
|
+
fetchedAt,
|
|
797
|
+
truncated: output.truncated,
|
|
798
|
+
notes,
|
|
799
|
+
};
|
|
800
|
+
}
|
|
744
801
|
|
|
745
|
-
|
|
802
|
+
// Classify generic downloads from the bounded binary response before dispatch.
|
|
803
|
+
// Both image rendering and document conversion reuse these bytes.
|
|
804
|
+
const dispositionBinary =
|
|
805
|
+
!raw && isGenericMimeType(mime)
|
|
806
|
+
? response.buffer
|
|
807
|
+
? { ok: true as const, buffer: response.buffer, contentDisposition: response.contentDisposition }
|
|
808
|
+
: await fetchBinary(finalUrl, timeout, signal)
|
|
809
|
+
: undefined;
|
|
810
|
+
if (dispositionBinary && !dispositionBinary.ok) {
|
|
811
|
+
notes.push(dispositionBinary.error ? `Binary fetch failed: ${dispositionBinary.error}` : "Binary fetch failed");
|
|
812
|
+
return {
|
|
813
|
+
url,
|
|
814
|
+
finalUrl,
|
|
815
|
+
contentType: mime,
|
|
816
|
+
method: "failed",
|
|
817
|
+
content: "",
|
|
818
|
+
fetchedAt,
|
|
819
|
+
truncated: false,
|
|
820
|
+
notes,
|
|
821
|
+
};
|
|
822
|
+
}
|
|
823
|
+
const effectiveExt = dispositionBinary?.ok
|
|
824
|
+
? getExtensionHint(finalUrl, dispositionBinary.contentDisposition)
|
|
825
|
+
: extHint;
|
|
826
|
+
isPdf =
|
|
827
|
+
mime === "application/pdf" ||
|
|
828
|
+
mime === "application/x-pdf" ||
|
|
829
|
+
(effectiveExt === ".pdf" && isGenericMimeType(mime));
|
|
830
|
+
const imageMimeType = isPdf ? undefined : resolveImageMimeType(mime, effectiveExt);
|
|
746
831
|
let skipConvertibleBinaryRetry = false;
|
|
747
832
|
if (imageMimeType) {
|
|
748
833
|
if (!isInlineImageMimeTypeSupported(imageMimeType)) {
|
|
749
834
|
notes.push(
|
|
750
835
|
`Image MIME type ${imageMimeType} is unsupported for inline model serialization; returning text metadata only`,
|
|
751
836
|
);
|
|
752
|
-
const shouldTryConvertibleFallback = isConvertible(mime,
|
|
837
|
+
const shouldTryConvertibleFallback = isConvertible(mime, effectiveExt);
|
|
753
838
|
if (shouldTryConvertibleFallback) {
|
|
754
839
|
notes.push("Attempting binary conversion fallback for unsupported image MIME type");
|
|
755
840
|
} else {
|
|
@@ -757,7 +842,7 @@ async function renderUrl(
|
|
|
757
842
|
}
|
|
758
843
|
skipConvertibleBinaryRetry = !shouldTryConvertibleFallback;
|
|
759
844
|
} else {
|
|
760
|
-
const binary = await fetchBinary(finalUrl, timeout, signal);
|
|
845
|
+
const binary = dispositionBinary ?? (await fetchBinary(finalUrl, timeout, signal));
|
|
761
846
|
if (binary.ok) {
|
|
762
847
|
notes.push("Fetched image binary");
|
|
763
848
|
const conversionExtension = getExtensionHint(finalUrl, binary.contentDisposition) || extHint;
|
|
@@ -868,10 +953,10 @@ async function renderUrl(
|
|
|
868
953
|
}
|
|
869
954
|
|
|
870
955
|
// Step 3: Handle convertible binary files (PDF, DOCX, etc.)
|
|
871
|
-
if (!skipConvertibleBinaryRetry && isConvertible(mime,
|
|
872
|
-
const binary = await fetchBinary(finalUrl, timeout, signal);
|
|
956
|
+
if (!skipConvertibleBinaryRetry && (isPdf || isConvertible(mime, effectiveExt))) {
|
|
957
|
+
const binary = dispositionBinary ?? (await fetchBinary(finalUrl, timeout, signal));
|
|
873
958
|
if (binary.ok) {
|
|
874
|
-
const ext = getExtensionHint(finalUrl, binary.contentDisposition) || extHint;
|
|
959
|
+
const ext = isPdf ? ".pdf" : getExtensionHint(finalUrl, binary.contentDisposition) || extHint;
|
|
875
960
|
const converted = await convertWithMarkit(binary.buffer, ext, timeout, signal);
|
|
876
961
|
if (converted.ok) {
|
|
877
962
|
// Any non-empty markit conversion is preferable to a raw-bytes
|
|
@@ -902,6 +987,18 @@ async function renderUrl(
|
|
|
902
987
|
} else {
|
|
903
988
|
notes.push("Binary fetch failed");
|
|
904
989
|
}
|
|
990
|
+
if (isPdf) {
|
|
991
|
+
return {
|
|
992
|
+
url,
|
|
993
|
+
finalUrl,
|
|
994
|
+
contentType: mime,
|
|
995
|
+
method: "failed",
|
|
996
|
+
content: "",
|
|
997
|
+
fetchedAt,
|
|
998
|
+
truncated: false,
|
|
999
|
+
notes,
|
|
1000
|
+
};
|
|
1001
|
+
}
|
|
905
1002
|
}
|
|
906
1003
|
|
|
907
1004
|
// Step 4: Handle non-HTML text content
|
|
@@ -1066,7 +1163,7 @@ async function renderUrl(
|
|
|
1066
1163
|
if (binary.ok) {
|
|
1067
1164
|
const ext = getExtensionHint(docUrl, binary.contentDisposition);
|
|
1068
1165
|
const converted = await convertWithMarkit(binary.buffer, ext, timeout, signal);
|
|
1069
|
-
if (converted.ok && converted.content.trim().length > htmlResult.content.length) {
|
|
1166
|
+
if (converted.ok && converted.content.trim().length > (ext === ".pdf" ? 0 : htmlResult.content.length)) {
|
|
1070
1167
|
notes.push(`Extracted and converted document: ${docUrl}`);
|
|
1071
1168
|
const output = finalizeOutput(converted.content);
|
|
1072
1169
|
return {
|
|
@@ -1082,6 +1179,8 @@ async function renderUrl(
|
|
|
1082
1179
|
}
|
|
1083
1180
|
if (!converted.ok && converted.error) {
|
|
1084
1181
|
notes.push(`markit conversion failed: ${converted.error}`);
|
|
1182
|
+
} else if (converted.ok && !converted.content.trim()) {
|
|
1183
|
+
notes.push("markit conversion produced no usable output");
|
|
1085
1184
|
}
|
|
1086
1185
|
} else if (binary.error) {
|
|
1087
1186
|
notes.push(`Binary fetch failed: ${binary.error}`);
|