@gajae-code/coding-agent 0.17.2 → 0.17.5
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 +267 -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 +1049 -3
- package/dist/types/config/file-lock.d.ts +13 -3
- package/dist/types/config/model-profile-activation.d.ts +45 -1
- package/dist/types/config/model-registry.d.ts +14 -3
- package/dist/types/config/model-resolver.d.ts +1 -1
- 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/exec/bash-executor.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/gjc-plugins/runtime-adapters.d.ts +11 -2
- 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/gjc-runtime/state-writer.d.ts +14 -1
- package/dist/types/harness-control-plane/control-endpoint.d.ts +4 -1
- package/dist/types/harness-control-plane/finalize.d.ts +14 -3
- 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 +23 -1
- 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/loader.d.ts +2 -0
- package/dist/types/runtime-mcp/manager.d.ts +69 -6
- 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 +17 -4
- package/dist/types/sdk/broker/ensure.d.ts +3 -0
- package/dist/types/sdk/broker/identity.d.ts +5 -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/managed-task-dag.d.ts +1335 -0
- package/dist/types/sdk/broker/managed-task-verification.d.ts +37 -0
- 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 +12 -0
- package/dist/types/sdk/broker/startup-failure.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 +7 -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 +85 -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/session.d.ts +3 -3
- 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 +175 -23
- 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/task/types.d.ts +2 -0
- package/dist/types/tools/bash-interactive.d.ts +1 -0
- package/dist/types/tools/bash.d.ts +37 -11
- package/dist/types/tools/fetch.d.ts +10 -1
- package/dist/types/tools/job.d.ts +1 -1
- package/dist/types/tools/steer-fold.d.ts +31 -0
- package/dist/types/tools/subagent.d.ts +8 -1
- 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 +541 -0
- package/src/cli/session-picker.ts +2 -1
- package/src/cli/setup-cli.ts +10 -1
- package/src/cli/skills-cli.ts +46 -2
- package/src/cli/stats-cli.ts +11 -1
- 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 +543 -3
- package/src/config/file-lock.ts +206 -65
- package/src/config/model-discovery-manager.ts +4 -4
- package/src/config/model-profile-activation.ts +417 -66
- package/src/config/model-registry.ts +254 -36
- package/src/config/model-resolver.ts +6 -1
- 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 +179 -9
- package/src/coordinator-mcp/server.ts +521 -87
- 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/exec/bash-executor.ts +3 -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/gjc-plugins/runtime-adapters.ts +46 -8
- 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/state-writer.ts +317 -6
- package/src/gjc-runtime/ultragoal-succession.ts +31 -3
- package/src/harness-control-plane/control-endpoint.ts +24 -6
- package/src/harness-control-plane/finalize.ts +312 -25
- package/src/harness-control-plane/owner.ts +323 -89
- package/src/internal-urls/docs-index.generated.ts +23 -20
- 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 +192 -74
- package/src/modes/acp/acp-agent.ts +973 -56
- package/src/modes/acp/startup-provenance.ts +59 -0
- package/src/modes/components/custom-provider-wizard.ts +308 -7
- package/src/modes/components/gjc-bundle-settings.ts +21 -1
- package/src/modes/components/oauth-selector.ts +9 -0
- package/src/modes/components/session-selector.ts +47 -4
- package/src/modes/components/tool-status-header.ts +72 -6
- 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/runtime-mcp-command-controller.ts +1 -1
- 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/loader.ts +6 -3
- package/src/runtime-mcp/manager.ts +845 -109
- 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 +1431 -198
- package/src/sdk/broker/ensure.ts +21 -3
- package/src/sdk/broker/identity.ts +22 -1
- package/src/sdk/broker/lifecycle-ledger.ts +95 -31
- package/src/sdk/broker/lifecycle.ts +690 -141
- package/src/sdk/broker/managed-task-dag.ts +1927 -0
- package/src/sdk/broker/managed-task-verification.ts +642 -0
- package/src/sdk/broker/runtime.ts +1 -1
- package/src/sdk/broker/session-index.ts +97 -4
- package/src/sdk/broker/spawn-authority.ts +175 -14
- package/src/sdk/broker/startup-failure.ts +80 -1
- package/src/sdk/broker/stderr-drainer.ts +59 -0
- package/src/sdk/broker/transport.ts +1 -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 +707 -81
- package/src/sdk/bus/telegram-daemon-cli.ts +31 -2
- package/src/sdk/bus/telegram-daemon-contract.ts +7 -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 +705 -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 +184 -2
- package/src/sdk/host/session-runtime.ts +322 -101
- 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/lifecycle/service.ts +2 -0
- package/src/sdk/prompt-deadline-flush.ts +356 -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 +763 -116
- 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 +2472 -407
- 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 +68 -18
- package/src/session/internal/managed-session-storage.ts +334 -12
- 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/setup/provider-presets.json +2 -1
- package/src/slash-commands/builtin-registry.ts +259 -15
- package/src/task/executor.ts +46 -17
- package/src/task/index.ts +14 -2
- package/src/task/types.ts +2 -0
- package/src/tools/bash-interactive.ts +2 -0
- package/src/tools/bash.ts +118 -78
- package/src/tools/fetch.ts +140 -25
- 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/subagent-render.ts +24 -1
- package/src/tools/subagent.ts +27 -1
- 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 +136 -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/fetch.ts
CHANGED
|
@@ -11,15 +11,20 @@ import { type Theme, theme } from "../modes/theme/theme";
|
|
|
11
11
|
import type { ToolSession } from "../sdk";
|
|
12
12
|
import type { AgentStorage } from "../session/agent-storage";
|
|
13
13
|
import { DEFAULT_MAX_BYTES, type TruncationDirection, truncateContent } from "../session/streaming-output";
|
|
14
|
-
import { renderStatusLine } from "../tui";
|
|
15
14
|
import { CachedOutputBlock } from "../tui/output-block";
|
|
15
|
+
import { renderStatusLine } from "../tui/status-line";
|
|
16
16
|
import { formatDimensionNote, resizeImage } from "../utils/image-resize";
|
|
17
17
|
import { parseHtmlLazy } from "../utils/linkedom";
|
|
18
18
|
import { INSANE_NOTES } from "../web/insane/bridge";
|
|
19
19
|
import { validatePublicHttpUrl } from "../web/insane/url-guard";
|
|
20
|
-
import {
|
|
21
|
-
|
|
22
|
-
|
|
20
|
+
import {
|
|
21
|
+
finalizeOutput,
|
|
22
|
+
loadPage,
|
|
23
|
+
looksLikeHtml,
|
|
24
|
+
MAX_OUTPUT_CHARS,
|
|
25
|
+
type RenderResult,
|
|
26
|
+
type SpecialHandler,
|
|
27
|
+
} from "../web/scrapers/types";
|
|
23
28
|
import { convertWithMarkit, fetchBinary } from "../web/scrapers/utils";
|
|
24
29
|
import { applyListLimit } from "./list-limit";
|
|
25
30
|
import { formatStyledArtifactReference, type OutputMeta } from "./output-meta";
|
|
@@ -30,6 +35,12 @@ import { clampTimeout } from "./tool-timeouts";
|
|
|
30
35
|
|
|
31
36
|
type NativeHtmlBindings = { htmlToMarkdown: typeof htmlToMarkdownFn };
|
|
32
37
|
let nativeHtmlBindings: NativeHtmlBindings | undefined;
|
|
38
|
+
let specialHandlersCache: SpecialHandler[] | undefined;
|
|
39
|
+
|
|
40
|
+
function getSpecialHandlers(): SpecialHandler[] {
|
|
41
|
+
specialHandlersCache ??= (require("../web/scrapers") as { specialHandlers: SpecialHandler[] }).specialHandlers;
|
|
42
|
+
return specialHandlersCache;
|
|
43
|
+
}
|
|
33
44
|
|
|
34
45
|
/**
|
|
35
46
|
* Lazy native access for HTML conversion. The module is cached, never the
|
|
@@ -49,6 +60,7 @@ const FETCH_DEFAULT_MAX_LINES = 300;
|
|
|
49
60
|
// Convertible document types handled by markit.
|
|
50
61
|
const CONVERTIBLE_MIMES = new Set([
|
|
51
62
|
"application/pdf",
|
|
63
|
+
"application/x-pdf",
|
|
52
64
|
"application/msword",
|
|
53
65
|
"application/vnd.ms-powerpoint",
|
|
54
66
|
"application/vnd.ms-excel",
|
|
@@ -228,17 +240,52 @@ function normalizeMime(contentType: string): string {
|
|
|
228
240
|
return contentType.split(";")[0].trim().toLowerCase();
|
|
229
241
|
}
|
|
230
242
|
|
|
243
|
+
// Bound both parsing work and decoded filename size to a single modest header.
|
|
244
|
+
function getDispositionFilename(header: string): string | undefined {
|
|
245
|
+
if (header.length > 8192 || /[\x00-\x1f\x7f]/.test(header.replaceAll("\t", ""))) return undefined;
|
|
246
|
+
const separator = header.indexOf(";");
|
|
247
|
+
if (separator < 0) return undefined;
|
|
248
|
+
const parameter = /;[ \t]*([!#$%&'*+.^_`|~\w-]+)[ \t]*=[ \t]*(?:"((?:[^"\\]|\\.)*)"|([!#$%&'*+.^_`|~\w-]+))[ \t]*/y;
|
|
249
|
+
let offset = separator;
|
|
250
|
+
let plain: string | undefined;
|
|
251
|
+
let extended: string | undefined;
|
|
252
|
+
const seen = new Set<string>();
|
|
253
|
+
while (offset < header.length) {
|
|
254
|
+
parameter.lastIndex = offset;
|
|
255
|
+
const match = parameter.exec(header);
|
|
256
|
+
if (!match) return undefined;
|
|
257
|
+
offset = parameter.lastIndex;
|
|
258
|
+
const name = match[1].toLowerCase();
|
|
259
|
+
if (name !== "filename" && name !== "filename*") continue;
|
|
260
|
+
// Duplicate filename parameters are ambiguous, not an ordering override.
|
|
261
|
+
if (seen.has(name)) return undefined;
|
|
262
|
+
seen.add(name);
|
|
263
|
+
const value = match[2] === undefined ? match[3] : match[2].replace(/\\(.)/g, "$1");
|
|
264
|
+
if (name === "filename") {
|
|
265
|
+
if (value) plain = value;
|
|
266
|
+
continue;
|
|
267
|
+
}
|
|
268
|
+
// RFC 8187 ext-value: only UTF-8 is supported; quoted ext-values are invalid.
|
|
269
|
+
const encoded =
|
|
270
|
+
match[2] === undefined ? /^UTF-8'[A-Za-z0-9-]*'((?:[!#$&+.^_`|~\w-]|%[\da-fA-F]{2})*)$/i.exec(value) : null;
|
|
271
|
+
if (!encoded) continue;
|
|
272
|
+
try {
|
|
273
|
+
const decoded = decodeURIComponent(encoded[1]);
|
|
274
|
+
if (decoded && !/[\x00-\x1f\x7f]/.test(decoded)) extended = decoded;
|
|
275
|
+
} catch {
|
|
276
|
+
// Invalid UTF-8 or percent encoding must not escape classification.
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
return extended ?? plain;
|
|
280
|
+
}
|
|
281
|
+
|
|
231
282
|
/**
|
|
232
283
|
* Get extension from URL or Content-Disposition
|
|
233
284
|
*/
|
|
234
285
|
function getExtensionHint(url: string, contentDisposition?: string): string {
|
|
235
|
-
// Try Content-Disposition filename first
|
|
236
286
|
if (contentDisposition) {
|
|
237
|
-
const
|
|
238
|
-
if (
|
|
239
|
-
const ext = path.extname(match[1]).toLowerCase();
|
|
240
|
-
if (ext) return ext;
|
|
241
|
-
}
|
|
287
|
+
const filename = getDispositionFilename(contentDisposition);
|
|
288
|
+
if (filename !== undefined) return path.extname(filename).toLowerCase();
|
|
242
289
|
}
|
|
243
290
|
|
|
244
291
|
// Fall back to URL path
|
|
@@ -251,21 +298,25 @@ function getExtensionHint(url: string, contentDisposition?: string): string {
|
|
|
251
298
|
return "";
|
|
252
299
|
}
|
|
253
300
|
|
|
301
|
+
function isGenericMimeType(mime: string): boolean {
|
|
302
|
+
return (
|
|
303
|
+
mime.length === 0 || mime === "application/octet-stream" || mime === "binary/octet-stream" || mime === "unknown"
|
|
304
|
+
);
|
|
305
|
+
}
|
|
306
|
+
|
|
254
307
|
/**
|
|
255
308
|
* Check if content type is convertible via markit.
|
|
256
309
|
*/
|
|
257
310
|
function isConvertible(mime: string, extensionHint: string): boolean {
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
311
|
+
return (
|
|
312
|
+
CONVERTIBLE_MIMES.has(mime) ||
|
|
313
|
+
(CONVERTIBLE_EXTENSIONS.has(extensionHint) && (extensionHint !== ".pdf" || isGenericMimeType(mime)))
|
|
314
|
+
);
|
|
262
315
|
}
|
|
263
316
|
|
|
264
317
|
function resolveImageMimeType(mime: string, extensionHint: string): string | null {
|
|
265
318
|
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;
|
|
319
|
+
if (!isGenericMimeType(mime)) return null;
|
|
269
320
|
return IMAGE_MIME_BY_EXTENSION.get(extensionHint) ?? null;
|
|
270
321
|
}
|
|
271
322
|
|
|
@@ -619,7 +670,7 @@ async function handleSpecialUrls(
|
|
|
619
670
|
signal: AbortSignal | undefined,
|
|
620
671
|
storage: AgentStorage | null,
|
|
621
672
|
): Promise<FetchRenderResult | null> {
|
|
622
|
-
for (const handler of
|
|
673
|
+
for (const handler of getSpecialHandlers()) {
|
|
623
674
|
if (signal?.aborted) {
|
|
624
675
|
throw new ToolAbortError();
|
|
625
676
|
}
|
|
@@ -629,6 +680,11 @@ async function handleSpecialUrls(
|
|
|
629
680
|
return null;
|
|
630
681
|
}
|
|
631
682
|
|
|
683
|
+
/** Test-only seam for verifying lazy special-handler loading and dispatch. */
|
|
684
|
+
export const fetchSpecialHandlerTestHooks = {
|
|
685
|
+
dispatch: handleSpecialUrls,
|
|
686
|
+
};
|
|
687
|
+
|
|
632
688
|
// =============================================================================
|
|
633
689
|
// Main Render Function
|
|
634
690
|
// =============================================================================
|
|
@@ -741,15 +797,60 @@ async function renderUrl(
|
|
|
741
797
|
const { finalUrl, content: rawContent } = response;
|
|
742
798
|
const mime = normalizeMime(response.contentType);
|
|
743
799
|
const extHint = getExtensionHint(finalUrl);
|
|
800
|
+
let isPdf =
|
|
801
|
+
mime === "application/pdf" || mime === "application/x-pdf" || (extHint === ".pdf" && isGenericMimeType(mime));
|
|
802
|
+
|
|
803
|
+
// Raw PDF inspection is explicit; never substitute PDF bytes for failed text extraction.
|
|
804
|
+
if (raw && isPdf) {
|
|
805
|
+
const output = finalizeOutput(rawContent);
|
|
806
|
+
return {
|
|
807
|
+
url,
|
|
808
|
+
finalUrl,
|
|
809
|
+
contentType: mime,
|
|
810
|
+
method: "raw",
|
|
811
|
+
content: output.content,
|
|
812
|
+
fetchedAt,
|
|
813
|
+
truncated: output.truncated,
|
|
814
|
+
notes,
|
|
815
|
+
};
|
|
816
|
+
}
|
|
744
817
|
|
|
745
|
-
|
|
818
|
+
// Classify generic downloads from the bounded binary response before dispatch.
|
|
819
|
+
// Both image rendering and document conversion reuse these bytes.
|
|
820
|
+
const dispositionBinary =
|
|
821
|
+
!raw && isGenericMimeType(mime)
|
|
822
|
+
? response.buffer
|
|
823
|
+
? { ok: true as const, buffer: response.buffer, contentDisposition: response.contentDisposition }
|
|
824
|
+
: await fetchBinary(finalUrl, timeout, signal)
|
|
825
|
+
: undefined;
|
|
826
|
+
if (dispositionBinary && !dispositionBinary.ok) {
|
|
827
|
+
notes.push(dispositionBinary.error ? `Binary fetch failed: ${dispositionBinary.error}` : "Binary fetch failed");
|
|
828
|
+
return {
|
|
829
|
+
url,
|
|
830
|
+
finalUrl,
|
|
831
|
+
contentType: mime,
|
|
832
|
+
method: "failed",
|
|
833
|
+
content: "",
|
|
834
|
+
fetchedAt,
|
|
835
|
+
truncated: false,
|
|
836
|
+
notes,
|
|
837
|
+
};
|
|
838
|
+
}
|
|
839
|
+
const effectiveExt = dispositionBinary?.ok
|
|
840
|
+
? getExtensionHint(finalUrl, dispositionBinary.contentDisposition)
|
|
841
|
+
: extHint;
|
|
842
|
+
isPdf =
|
|
843
|
+
mime === "application/pdf" ||
|
|
844
|
+
mime === "application/x-pdf" ||
|
|
845
|
+
(effectiveExt === ".pdf" && isGenericMimeType(mime));
|
|
846
|
+
const imageMimeType = isPdf ? undefined : resolveImageMimeType(mime, effectiveExt);
|
|
746
847
|
let skipConvertibleBinaryRetry = false;
|
|
747
848
|
if (imageMimeType) {
|
|
748
849
|
if (!isInlineImageMimeTypeSupported(imageMimeType)) {
|
|
749
850
|
notes.push(
|
|
750
851
|
`Image MIME type ${imageMimeType} is unsupported for inline model serialization; returning text metadata only`,
|
|
751
852
|
);
|
|
752
|
-
const shouldTryConvertibleFallback = isConvertible(mime,
|
|
853
|
+
const shouldTryConvertibleFallback = isConvertible(mime, effectiveExt);
|
|
753
854
|
if (shouldTryConvertibleFallback) {
|
|
754
855
|
notes.push("Attempting binary conversion fallback for unsupported image MIME type");
|
|
755
856
|
} else {
|
|
@@ -757,7 +858,7 @@ async function renderUrl(
|
|
|
757
858
|
}
|
|
758
859
|
skipConvertibleBinaryRetry = !shouldTryConvertibleFallback;
|
|
759
860
|
} else {
|
|
760
|
-
const binary = await fetchBinary(finalUrl, timeout, signal);
|
|
861
|
+
const binary = dispositionBinary ?? (await fetchBinary(finalUrl, timeout, signal));
|
|
761
862
|
if (binary.ok) {
|
|
762
863
|
notes.push("Fetched image binary");
|
|
763
864
|
const conversionExtension = getExtensionHint(finalUrl, binary.contentDisposition) || extHint;
|
|
@@ -868,10 +969,10 @@ async function renderUrl(
|
|
|
868
969
|
}
|
|
869
970
|
|
|
870
971
|
// Step 3: Handle convertible binary files (PDF, DOCX, etc.)
|
|
871
|
-
if (!skipConvertibleBinaryRetry && isConvertible(mime,
|
|
872
|
-
const binary = await fetchBinary(finalUrl, timeout, signal);
|
|
972
|
+
if (!skipConvertibleBinaryRetry && (isPdf || isConvertible(mime, effectiveExt))) {
|
|
973
|
+
const binary = dispositionBinary ?? (await fetchBinary(finalUrl, timeout, signal));
|
|
873
974
|
if (binary.ok) {
|
|
874
|
-
const ext = getExtensionHint(finalUrl, binary.contentDisposition) || extHint;
|
|
975
|
+
const ext = isPdf ? ".pdf" : getExtensionHint(finalUrl, binary.contentDisposition) || extHint;
|
|
875
976
|
const converted = await convertWithMarkit(binary.buffer, ext, timeout, signal);
|
|
876
977
|
if (converted.ok) {
|
|
877
978
|
// Any non-empty markit conversion is preferable to a raw-bytes
|
|
@@ -902,6 +1003,18 @@ async function renderUrl(
|
|
|
902
1003
|
} else {
|
|
903
1004
|
notes.push("Binary fetch failed");
|
|
904
1005
|
}
|
|
1006
|
+
if (isPdf) {
|
|
1007
|
+
return {
|
|
1008
|
+
url,
|
|
1009
|
+
finalUrl,
|
|
1010
|
+
contentType: mime,
|
|
1011
|
+
method: "failed",
|
|
1012
|
+
content: "",
|
|
1013
|
+
fetchedAt,
|
|
1014
|
+
truncated: false,
|
|
1015
|
+
notes,
|
|
1016
|
+
};
|
|
1017
|
+
}
|
|
905
1018
|
}
|
|
906
1019
|
|
|
907
1020
|
// Step 4: Handle non-HTML text content
|
|
@@ -1066,7 +1179,7 @@ async function renderUrl(
|
|
|
1066
1179
|
if (binary.ok) {
|
|
1067
1180
|
const ext = getExtensionHint(docUrl, binary.contentDisposition);
|
|
1068
1181
|
const converted = await convertWithMarkit(binary.buffer, ext, timeout, signal);
|
|
1069
|
-
if (converted.ok && converted.content.trim().length > htmlResult.content.length) {
|
|
1182
|
+
if (converted.ok && converted.content.trim().length > (ext === ".pdf" ? 0 : htmlResult.content.length)) {
|
|
1070
1183
|
notes.push(`Extracted and converted document: ${docUrl}`);
|
|
1071
1184
|
const output = finalizeOutput(converted.content);
|
|
1072
1185
|
return {
|
|
@@ -1082,6 +1195,8 @@ async function renderUrl(
|
|
|
1082
1195
|
}
|
|
1083
1196
|
if (!converted.ok && converted.error) {
|
|
1084
1197
|
notes.push(`markit conversion failed: ${converted.error}`);
|
|
1198
|
+
} else if (converted.ok && !converted.content.trim()) {
|
|
1199
|
+
notes.push("markit conversion produced no usable output");
|
|
1085
1200
|
}
|
|
1086
1201
|
} else if (binary.error) {
|
|
1087
1202
|
notes.push(`Binary fetch failed: ${binary.error}`);
|
package/src/tools/job.ts
CHANGED
|
@@ -7,6 +7,7 @@ import { type AsyncJob, AsyncJobManager, type FoldReason, isBackgroundJobSupport
|
|
|
7
7
|
import type { RenderResultOptions } from "../extensibility/custom-tools/types";
|
|
8
8
|
import type { Theme } from "../modes/theme/theme";
|
|
9
9
|
import jobDescription from "../prompts/tools/job.md" with { type: "text" };
|
|
10
|
+
import type { FoldAdapter } from "../session/fold-coordinator";
|
|
10
11
|
import { lookupOwnedRegistration, unregisterOwnedRegistration } from "../session/terminal-abort";
|
|
11
12
|
import { Ellipsis, Hasher, type RenderCache, renderStatusLine, renderTreeList, truncateToWidth } from "../tui";
|
|
12
13
|
import type { ToolSession } from "./index";
|
|
@@ -21,6 +22,7 @@ import {
|
|
|
21
22
|
type ToolUIColor,
|
|
22
23
|
type ToolUIStatus,
|
|
23
24
|
} from "./render-utils";
|
|
25
|
+
import { foldAwaitReasonLine, watchSteerForFold } from "./steer-fold";
|
|
24
26
|
import { ToolError } from "./tool-errors";
|
|
25
27
|
|
|
26
28
|
const jobSchema = z.object({
|
|
@@ -47,7 +49,9 @@ function parseWaitDurationMs(value: string | undefined): number {
|
|
|
47
49
|
interface JobSnapshot {
|
|
48
50
|
id: string;
|
|
49
51
|
type: "bash" | "task";
|
|
50
|
-
|
|
52
|
+
// Mirrors the manager's job statuses, including "paused": a folded subagent
|
|
53
|
+
// stays listed and resumable, so its snapshot is paused rather than terminal.
|
|
54
|
+
status: "running" | "paused" | "completed" | "failed" | "cancelled";
|
|
51
55
|
label: string;
|
|
52
56
|
durationMs: number;
|
|
53
57
|
/** Present when the job was folded out of a foreground wait. */
|
|
@@ -56,6 +60,11 @@ interface JobSnapshot {
|
|
|
56
60
|
errorText?: string;
|
|
57
61
|
}
|
|
58
62
|
|
|
63
|
+
/** Terminal statuses are the ones a consumer may treat as finished work. */
|
|
64
|
+
function isTerminalJobStatus(status: JobSnapshot["status"]): boolean {
|
|
65
|
+
return status === "completed" || status === "failed" || status === "cancelled";
|
|
66
|
+
}
|
|
67
|
+
|
|
59
68
|
type CancelStatus = "cancelled" | "not_found" | "already_completed" | "already_cancelled";
|
|
60
69
|
|
|
61
70
|
interface CancelOutcome {
|
|
@@ -234,6 +243,73 @@ export class JobTool implements AgentTool<typeof jobSchema, JobToolDetails> {
|
|
|
234
243
|
const waitMs = parseWaitDurationMs(this.session.settings.get("async.pollWaitDuration"));
|
|
235
244
|
const { promise: timeoutPromise, resolve: timeoutResolve } = Promise.withResolvers<void>();
|
|
236
245
|
const timeoutHandle = setTimeout(() => timeoutResolve(), waitMs);
|
|
246
|
+
// Settled the moment ANY watched wait is released by a fold, so the await
|
|
247
|
+
// never outlives its own fold. `job-await` adapters are never implicit
|
|
248
|
+
// chord / SDK-control targets (the jobs are already in the background);
|
|
249
|
+
// only the steer watcher below folds them, explicitly. `task`/`subagent`
|
|
250
|
+
// waits are a non-goal, so task-backed jobs are watched but never folded.
|
|
251
|
+
const foldedAwait = Promise.withResolvers<void>();
|
|
252
|
+
const foldedJobs = new Map<string, FoldReason>();
|
|
253
|
+
const foldTargets: Array<{ adapter: FoldAdapter; release: (reason: FoldReason) => boolean }> = [];
|
|
254
|
+
const unregisterFoldAdapters: Array<() => void> = [];
|
|
255
|
+
const startedAt = Date.now();
|
|
256
|
+
for (const job of runningJobs) {
|
|
257
|
+
if (job.type === "task" || job.metadata?.subagent !== undefined) continue;
|
|
258
|
+
const generation = job.generation;
|
|
259
|
+
let detached = false;
|
|
260
|
+
const release = (reason: FoldReason): boolean => {
|
|
261
|
+
if (detached) return false;
|
|
262
|
+
detached = true;
|
|
263
|
+
manager.markBackgrounded(job.id, generation, reason);
|
|
264
|
+
foldedJobs.set(job.id, reason);
|
|
265
|
+
foldedAwait.resolve();
|
|
266
|
+
return true;
|
|
267
|
+
};
|
|
268
|
+
const adapter: FoldAdapter = {
|
|
269
|
+
kind: "job-await",
|
|
270
|
+
jobId: job.id,
|
|
271
|
+
jobGeneration: generation,
|
|
272
|
+
label: job.label,
|
|
273
|
+
cwdSensitive: false,
|
|
274
|
+
signal,
|
|
275
|
+
originatingTurn: false,
|
|
276
|
+
outputRef: {
|
|
277
|
+
jobId: job.id,
|
|
278
|
+
generation,
|
|
279
|
+
instruction: `Use the job tool's tail operation for ${job.id} to read this job's output.`,
|
|
280
|
+
},
|
|
281
|
+
getJob: () => {
|
|
282
|
+
const current = manager.getJob(job.id);
|
|
283
|
+
return current?.generation === generation ? current : undefined;
|
|
284
|
+
},
|
|
285
|
+
detachObserver: receipt => (release(receipt.reason) ? "resolved" : "already-settled"),
|
|
286
|
+
resolveForegroundObserver: () => (detached ? "already-settled" : "resolved"),
|
|
287
|
+
};
|
|
288
|
+
foldTargets.push({ adapter, release });
|
|
289
|
+
unregisterFoldAdapters.push(this.session.registerForegroundFoldParticipant?.(adapter) ?? (() => {}));
|
|
290
|
+
}
|
|
291
|
+
// Fold every foldable wait on one steer. A job that an earlier fold already
|
|
292
|
+
// moved still owns its receipt slot until it completes, so the coordinator
|
|
293
|
+
// refuses a second fold for it; that receipt already guarantees the wake,
|
|
294
|
+
// so this await is simply released for it. Every other job folds through
|
|
295
|
+
// the coordinator, whose `detachObserver` call releases the await with the
|
|
296
|
+
// recorded reason.
|
|
297
|
+
const stopSteerWatch =
|
|
298
|
+
foldTargets.length > 0
|
|
299
|
+
? watchSteerForFold(this.session, startedAt, async fold => {
|
|
300
|
+
await Promise.all(
|
|
301
|
+
foldTargets.map(async ({ adapter, release }) => {
|
|
302
|
+
const current = adapter.getJob();
|
|
303
|
+
if (current?.status !== "running") return;
|
|
304
|
+
if (current.metadata?.foldReason !== undefined) {
|
|
305
|
+
release("steer");
|
|
306
|
+
return;
|
|
307
|
+
}
|
|
308
|
+
await fold("steer", adapter);
|
|
309
|
+
}),
|
|
310
|
+
);
|
|
311
|
+
})
|
|
312
|
+
: () => {};
|
|
237
313
|
racePromises.push(timeoutPromise);
|
|
238
314
|
|
|
239
315
|
const watchedJobIds = runningJobs.map(job => job.id);
|
|
@@ -277,25 +353,35 @@ export class JobTool implements AgentTool<typeof jobSchema, JobToolDetails> {
|
|
|
277
353
|
signal.addEventListener("abort", onAbort, { once: true });
|
|
278
354
|
racePromises.push(abortPromise);
|
|
279
355
|
try {
|
|
280
|
-
await Promise.race(racePromises);
|
|
356
|
+
await Promise.race([...racePromises, foldedAwait.promise]);
|
|
281
357
|
} finally {
|
|
282
358
|
signal.removeEventListener("abort", onAbort);
|
|
283
359
|
}
|
|
284
360
|
} else {
|
|
285
|
-
await Promise.race(racePromises);
|
|
361
|
+
await Promise.race([...racePromises, foldedAwait.promise]);
|
|
286
362
|
}
|
|
287
363
|
} finally {
|
|
364
|
+
stopSteerWatch();
|
|
365
|
+
for (const unregister of unregisterFoldAdapters) unregister();
|
|
288
366
|
manager.unwatchJobs(watchedJobIds);
|
|
289
367
|
clearTimeout(timeoutHandle);
|
|
290
368
|
if (progressTimer) clearInterval(progressTimer);
|
|
291
369
|
}
|
|
292
370
|
|
|
293
|
-
|
|
371
|
+
const result = this.#buildResult(
|
|
294
372
|
manager,
|
|
295
373
|
allTrackedJobs,
|
|
296
374
|
cancelOutcomes,
|
|
297
375
|
this.#readOutputTails(manager, params.tail, ownerFilter),
|
|
298
376
|
);
|
|
377
|
+
if (foldedJobs.size > 0) {
|
|
378
|
+
const existingText = result.content.find(block => block.type === "text")?.text ?? "";
|
|
379
|
+
return {
|
|
380
|
+
...result,
|
|
381
|
+
content: [{ type: "text", text: `${existingText}\n\n${foldAwaitReasonLine(foldedJobs)}`.trim() }],
|
|
382
|
+
};
|
|
383
|
+
}
|
|
384
|
+
return result;
|
|
299
385
|
}
|
|
300
386
|
|
|
301
387
|
/**
|
|
@@ -396,9 +482,7 @@ export class JobTool implements AgentTool<typeof jobSchema, JobToolDetails> {
|
|
|
396
482
|
// — otherwise a scope:"owned" abort could find an empty causal set and
|
|
397
483
|
// report stopped_owned while the job remains visibly paused (review
|
|
398
484
|
// thread P2).
|
|
399
|
-
const terminalJobs = jobResults.filter(
|
|
400
|
-
j => j.status === "completed" || j.status === "failed" || j.status === "cancelled",
|
|
401
|
-
);
|
|
485
|
+
const terminalJobs = jobResults.filter(j => isTerminalJobStatus(j.status));
|
|
402
486
|
manager.acknowledgeDeliveries(terminalJobs.map(j => j.id));
|
|
403
487
|
// A terminal job whose deliveries were just acknowledged is
|
|
404
488
|
// synchronously consumed: its owned registration is settled and must
|
|
@@ -417,8 +501,11 @@ export class JobTool implements AgentTool<typeof jobSchema, JobToolDetails> {
|
|
|
417
501
|
if (registration) unregisterOwnedRegistration(registration);
|
|
418
502
|
}
|
|
419
503
|
|
|
420
|
-
const
|
|
421
|
-
|
|
504
|
+
const runningJobs = jobResults.filter(j => j.status === "running");
|
|
505
|
+
// Paused work (a folded or queued-resume subagent) and any status this build
|
|
506
|
+
// does not know are non-terminal: reporting them under ## Completed would
|
|
507
|
+
// tell consumers that resumable work finished.
|
|
508
|
+
const waitingJobs = jobResults.filter(j => !isTerminalJobStatus(j.status) && j.status !== "running");
|
|
422
509
|
|
|
423
510
|
const lines: string[] = [];
|
|
424
511
|
|
|
@@ -428,9 +515,9 @@ export class JobTool implements AgentTool<typeof jobSchema, JobToolDetails> {
|
|
|
428
515
|
lines.push("");
|
|
429
516
|
}
|
|
430
517
|
|
|
431
|
-
if (
|
|
432
|
-
lines.push(`## Completed (${
|
|
433
|
-
for (const j of
|
|
518
|
+
if (terminalJobs.length > 0) {
|
|
519
|
+
lines.push(`## Completed (${terminalJobs.length})\n`);
|
|
520
|
+
for (const j of terminalJobs) {
|
|
434
521
|
lines.push(`### ${j.id} [${j.type}] — ${j.status}`);
|
|
435
522
|
lines.push(`Label: ${j.label}`);
|
|
436
523
|
if (j.resultText) {
|
|
@@ -443,13 +530,20 @@ export class JobTool implements AgentTool<typeof jobSchema, JobToolDetails> {
|
|
|
443
530
|
}
|
|
444
531
|
}
|
|
445
532
|
|
|
446
|
-
if (
|
|
447
|
-
lines.push(`## Still Running (${
|
|
448
|
-
for (const j of
|
|
533
|
+
if (runningJobs.length > 0) {
|
|
534
|
+
lines.push(`## Still Running (${runningJobs.length})\n`);
|
|
535
|
+
for (const j of runningJobs) {
|
|
449
536
|
lines.push(`- \`${j.id}\` [${j.type}] — ${j.label}`);
|
|
450
537
|
}
|
|
451
538
|
}
|
|
452
539
|
|
|
540
|
+
if (waitingJobs.length > 0) {
|
|
541
|
+
lines.push(`## Waiting (${waitingJobs.length})\n`);
|
|
542
|
+
for (const j of waitingJobs) {
|
|
543
|
+
lines.push(`- \`${j.id}\` [${j.type}] — ${j.label} (${j.status})`);
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
|
|
453
547
|
if (outputTails.length > 0) {
|
|
454
548
|
lines.push("", `## Retained Output (${outputTails.length})\n`);
|
|
455
549
|
for (const tail of outputTails) {
|
|
@@ -498,6 +592,12 @@ function statusToIcon(status: JobSnapshot["status"]): ToolUIStatus {
|
|
|
498
592
|
return "aborted";
|
|
499
593
|
case "running":
|
|
500
594
|
return "running";
|
|
595
|
+
case "paused":
|
|
596
|
+
return "pending";
|
|
597
|
+
// Snapshot details are read back from persisted sessions, so a status this
|
|
598
|
+
// build does not know must still render instead of dropping the component.
|
|
599
|
+
default:
|
|
600
|
+
return "pending";
|
|
501
601
|
}
|
|
502
602
|
}
|
|
503
603
|
|
|
@@ -511,6 +611,12 @@ function statusToColor(status: JobSnapshot["status"]): ToolUIColor {
|
|
|
511
611
|
return "warning";
|
|
512
612
|
case "running":
|
|
513
613
|
return "accent";
|
|
614
|
+
case "paused":
|
|
615
|
+
return "muted";
|
|
616
|
+
// Never fall through to undefined: theme.fg() throws on an undefined color
|
|
617
|
+
// and would take the whole job list render down with it.
|
|
618
|
+
default:
|
|
619
|
+
return "muted";
|
|
514
620
|
}
|
|
515
621
|
}
|
|
516
622
|
|
|
@@ -554,19 +660,36 @@ export const jobToolRenderer = {
|
|
|
554
660
|
return new Text([header, formatEmptyMessage(fallback, uiTheme)].join("\n"), 0, 0);
|
|
555
661
|
}
|
|
556
662
|
|
|
557
|
-
const counts
|
|
558
|
-
|
|
663
|
+
const counts: Record<JobSnapshot["status"], number> = {
|
|
664
|
+
running: 0,
|
|
665
|
+
paused: 0,
|
|
666
|
+
completed: 0,
|
|
667
|
+
failed: 0,
|
|
668
|
+
cancelled: 0,
|
|
669
|
+
};
|
|
670
|
+
// Snapshots are read back from persisted sessions, so a status this build
|
|
671
|
+
// does not know must stay out of the typed counts instead of becoming NaN.
|
|
672
|
+
let unknownStatusCount = 0;
|
|
673
|
+
for (const job of jobs) {
|
|
674
|
+
if (job.status in counts) counts[job.status] += 1;
|
|
675
|
+
else unknownStatusCount += 1;
|
|
676
|
+
}
|
|
559
677
|
|
|
560
678
|
const meta: string[] = [];
|
|
561
679
|
if (counts.completed > 0) meta.push(uiTheme.fg("success", `${counts.completed} done`));
|
|
562
680
|
if (counts.failed > 0) meta.push(uiTheme.fg("error", `${counts.failed} failed`));
|
|
563
681
|
if (counts.cancelled > 0) meta.push(uiTheme.fg("warning", `${counts.cancelled} cancelled`));
|
|
682
|
+
if (counts.paused > 0) meta.push(uiTheme.fg("muted", `${counts.paused} paused`));
|
|
564
683
|
if (counts.running > 0) meta.push(uiTheme.fg("accent", `${counts.running} running`));
|
|
684
|
+
if (unknownStatusCount > 0) meta.push(uiTheme.fg("muted", `${unknownStatusCount} unknown`));
|
|
565
685
|
|
|
566
|
-
|
|
686
|
+
// Paused and unknown-status rows are unsettled work: reporting success and a
|
|
687
|
+
// "settled" description for a resumable snapshot would contradict the rows.
|
|
688
|
+
const pendingCount = counts.running + counts.paused + unknownStatusCount;
|
|
689
|
+
const headerIcon: ToolUIStatus = counts.failed > 0 ? "warning" : pendingCount > 0 ? "info" : "success";
|
|
567
690
|
const description =
|
|
568
|
-
|
|
569
|
-
? `waiting on ${
|
|
691
|
+
pendingCount > 0
|
|
692
|
+
? `waiting on ${pendingCount} of ${jobs.length}`
|
|
570
693
|
: `${jobs.length} ${jobs.length === 1 ? "job" : "jobs"} settled`;
|
|
571
694
|
|
|
572
695
|
const header = renderStatusLine(
|
|
@@ -580,15 +703,18 @@ export const jobToolRenderer = {
|
|
|
580
703
|
uiTheme,
|
|
581
704
|
);
|
|
582
705
|
|
|
583
|
-
// Sort: running first (so user sees what's still pending), then
|
|
706
|
+
// Sort: running first (so user sees what's still pending), then paused, then
|
|
707
|
+
// failed, then completed/cancelled.
|
|
584
708
|
const statusOrder: Record<JobSnapshot["status"], number> = {
|
|
585
709
|
running: 0,
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
710
|
+
paused: 1,
|
|
711
|
+
failed: 2,
|
|
712
|
+
cancelled: 3,
|
|
713
|
+
completed: 4,
|
|
589
714
|
};
|
|
715
|
+
const statusRank = (status: JobSnapshot["status"]): number => statusOrder[status] ?? Number.MAX_SAFE_INTEGER;
|
|
590
716
|
const sortedJobs = [...jobs].sort((a, b) => {
|
|
591
|
-
const diff =
|
|
717
|
+
const diff = statusRank(a.status) - statusRank(b.status);
|
|
592
718
|
if (diff !== 0) return diff;
|
|
593
719
|
return b.durationMs - a.durationMs;
|
|
594
720
|
});
|
package/src/tools/output-meta.ts
CHANGED
|
@@ -26,7 +26,7 @@ import {
|
|
|
26
26
|
} from "../session/streaming-output";
|
|
27
27
|
|
|
28
28
|
import { formatBytes, wrapBrackets } from "./render-utils";
|
|
29
|
-
import { renderError } from "./tool-errors";
|
|
29
|
+
import { renderError, ToolError } from "./tool-errors";
|
|
30
30
|
|
|
31
31
|
/**
|
|
32
32
|
* Truncation metadata for the output notice.
|
|
@@ -1130,7 +1130,20 @@ async function wrappedExecute(
|
|
|
1130
1130
|
}
|
|
1131
1131
|
return result;
|
|
1132
1132
|
} catch (e) {
|
|
1133
|
-
//
|
|
1133
|
+
// Preserve the original Error class and stack so designed tool outcomes can
|
|
1134
|
+
// be classified by the agent telemetry path and genuine failures retain the
|
|
1135
|
+
// frame where they actually occurred. Non-Error throws still need a stable
|
|
1136
|
+
// Error envelope for the agent loop.
|
|
1137
|
+
if (e instanceof ToolError) {
|
|
1138
|
+
const rendered = renderError(e);
|
|
1139
|
+
if (rendered !== e.message) {
|
|
1140
|
+
const renderedError = new ToolError(rendered, e.context);
|
|
1141
|
+
if (e.stack) renderedError.stack = e.stack;
|
|
1142
|
+
throw renderedError;
|
|
1143
|
+
}
|
|
1144
|
+
throw e;
|
|
1145
|
+
}
|
|
1146
|
+
if (e instanceof Error) throw e;
|
|
1134
1147
|
throw new Error(renderError(e));
|
|
1135
1148
|
}
|
|
1136
1149
|
}
|
package/src/tools/path-utils.ts
CHANGED
|
@@ -48,6 +48,18 @@ function rawSkillPrefixForName(authority: string, skillName: string): string | u
|
|
|
48
48
|
return undefined;
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
+
function splitSkillPathSel(rawPath: string, authorityEnd: number): { path: string; sel?: string } {
|
|
52
|
+
const suffix = rawPath.slice(authorityEnd);
|
|
53
|
+
const suffixQueryOrFragment = suffix.search(/[?#]/);
|
|
54
|
+
const pathnameEnd = suffixQueryOrFragment === -1 ? rawPath.length : authorityEnd + suffixQueryOrFragment;
|
|
55
|
+
const pathnameTarget = splitPathAndSel(rawPath.slice(authorityEnd, pathnameEnd));
|
|
56
|
+
if (pathnameTarget.sel === undefined) return { path: rawPath };
|
|
57
|
+
return {
|
|
58
|
+
path: `${rawPath.slice(0, authorityEnd)}${pathnameTarget.path}${rawPath.slice(pathnameEnd)}`,
|
|
59
|
+
sel: pathnameTarget.sel,
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
|
|
51
63
|
function tryMacOSScreenshotPath(filePath: string): string {
|
|
52
64
|
// macOS writes a narrow no-break space before AM/PM, but the model normalizes it
|
|
53
65
|
// to a plain space. The original name is not always `…PM.png`: attachment paths
|
|
@@ -177,6 +189,9 @@ export function splitInternalUrlSel(
|
|
|
177
189
|
const schemeMatch = rawPath.match(INTERNAL_URL_SCHEME_RE);
|
|
178
190
|
if (!schemeMatch) return { path: rawPath };
|
|
179
191
|
const scheme = schemeMatch[1].toLowerCase();
|
|
192
|
+
// `embedded://gjc/<path>` identifies a bundled file, so the selector always
|
|
193
|
+
// trails the path rather than the identifier-shaped authority.
|
|
194
|
+
if (scheme === "embedded") return splitPathAndSel(rawPath);
|
|
180
195
|
if (!INTERNAL_SCHEMES_WITH_SELECTORS[scheme]) return { path: rawPath };
|
|
181
196
|
|
|
182
197
|
const schemeEnd = schemeMatch[0].length;
|
|
@@ -185,7 +200,14 @@ export function splitInternalUrlSel(
|
|
|
185
200
|
const authority = rawPath.slice(schemeEnd, authorityEnd);
|
|
186
201
|
const authoritySuffix = rawPath.slice(authorityEnd);
|
|
187
202
|
const firstColon = authority.indexOf(":");
|
|
188
|
-
if (firstColon === -1)
|
|
203
|
+
if (firstColon === -1) {
|
|
204
|
+
if (scheme === "skill") {
|
|
205
|
+
// Skill relative paths carry selectors after the authority (for example,
|
|
206
|
+
// `skill://foo/docs/reference.md:10-20`).
|
|
207
|
+
return splitSkillPathSel(rawPath, authorityEnd);
|
|
208
|
+
}
|
|
209
|
+
return { path: rawPath };
|
|
210
|
+
}
|
|
189
211
|
if (firstColon === 0) return { path: rawPath };
|
|
190
212
|
|
|
191
213
|
if (scheme === "skill") {
|
|
@@ -197,7 +219,9 @@ export function splitInternalUrlSel(
|
|
|
197
219
|
// Let the resolver report malformed URL encoding when no selector boundary
|
|
198
220
|
// can be established from the raw authority.
|
|
199
221
|
}
|
|
200
|
-
if (decodedAuthority !== undefined && activeSkillNames.includes(decodedAuthority))
|
|
222
|
+
if (decodedAuthority !== undefined && activeSkillNames.includes(decodedAuthority)) {
|
|
223
|
+
return splitSkillPathSel(rawPath, authorityEnd);
|
|
224
|
+
}
|
|
201
225
|
const rawSkillPrefix = activeSkillNames
|
|
202
226
|
.map(name => ({ name, prefix: rawSkillPrefixForName(authority, name) }))
|
|
203
227
|
.filter(item => item.prefix !== undefined)
|
|
@@ -217,7 +241,7 @@ export function splitInternalUrlSel(
|
|
|
217
241
|
return { path: `${rawPath.slice(0, schemeEnd + firstColon)}${authoritySuffix}`, sel: firstTail };
|
|
218
242
|
}
|
|
219
243
|
const selectorColon = authority.indexOf(":", firstColon + 1);
|
|
220
|
-
if (selectorColon === -1) return
|
|
244
|
+
if (selectorColon === -1) return splitSkillPathSel(rawPath, authorityEnd);
|
|
221
245
|
return {
|
|
222
246
|
path: `${rawPath.slice(0, schemeEnd + selectorColon)}${authoritySuffix}`,
|
|
223
247
|
sel: authority.slice(selectorColon + 1),
|