@gajae-code/coding-agent 0.17.1 → 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 +266 -0
- package/dist/types/async/job-manager.d.ts +7 -6
- 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 +24 -0
- package/dist/types/config/file-lock.d.ts +10 -1
- package/dist/types/config/model-profile-activation.d.ts +63 -4
- package/dist/types/config/model-registry.d.ts +20 -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/coordinator-mcp/server.d.ts +3 -0
- 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 +94 -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/thinking-selector.d.ts +2 -1
- 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/acp/adapter.d.ts +27 -0
- package/dist/types/sdk/acp/final-text.d.ts +8 -0
- package/dist/types/sdk/broker/broker.d.ts +13 -2
- package/dist/types/sdk/broker/ensure.d.ts +46 -1
- 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 +38 -1
- package/dist/types/sdk/prompt-failure.d.ts +30 -1
- package/dist/types/sdk/prompt-status.d.ts +8 -0
- package/dist/types/sdk/prompt-tool-boundary.d.ts +73 -0
- package/dist/types/sdk/router/replay-retention-gap-cache.d.ts +13 -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 +73 -2
- 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/thinking.d.ts +3 -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-wasm-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 +60 -9
- 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 +232 -100
- package/src/commands/setup.ts +2 -0
- package/src/commands/skills.ts +11 -0
- package/src/config/autorouting-tier-map.ts +16 -0
- package/src/config/file-lock.ts +199 -68
- package/src/config/model-profile-activation.ts +406 -53
- package/src/config/model-registry.ts +204 -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 +528 -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/debug/report-bundle.ts +114 -24
- 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/managed-owner-admission.ts +67 -50
- 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 +21 -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 +593 -47
- package/src/modes/acp/acp-event-mapper.ts +9 -0
- package/src/modes/acp/startup-provenance.ts +59 -0
- package/src/modes/components/custom-provider-wizard.ts +308 -7
- package/src/modes/components/model-selector.ts +31 -15
- package/src/modes/components/oauth-selector.ts +9 -0
- package/src/modes/components/session-selector.ts +47 -4
- package/src/modes/components/status-line/segments.ts +6 -1
- package/src/modes/components/thinking-selector.ts +7 -3
- 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 +177 -18
- 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 +90 -9
- package/src/sdk/acp/final-text.ts +19 -2
- package/src/sdk/broker/broker.ts +378 -52
- package/src/sdk/broker/ensure.ts +97 -19
- 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 +721 -84
- 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 +636 -9
- package/src/sdk/host/session-runtime.ts +499 -118
- 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 +136 -4
- package/src/sdk/prompt-failure.ts +43 -1
- package/src/sdk/prompt-status.ts +8 -0
- package/src/sdk/prompt-tool-boundary.ts +121 -0
- package/src/sdk/protocol/operation-inventory.generated.json +106 -0
- package/src/sdk/router/replay-retention-gap-cache.ts +27 -0
- package/src/sdk/router/session-router.ts +31 -4
- package/src/sdk/session.ts +194 -16
- package/src/sdk/startup-capability.ts +1 -1
- package/src/sdk/transport/relay.ts +71 -6
- package/src/sdk/transport/serve-cli.ts +494 -49
- 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/session-import/codex.ts +12 -3
- 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/thinking.ts +21 -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-wasm-embedded.ts +3 -0
- package/src/utils/mupdf-wasm.ts +24 -2
- 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/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),
|
package/src/tools/read.ts
CHANGED
|
@@ -14,7 +14,7 @@ import type { RenderResultOptions } from "../extensibility/custom-tools/types";
|
|
|
14
14
|
import { getActiveSkills } from "../extensibility/skills";
|
|
15
15
|
import { formatHashLine, formatHashLines, formatLineHash, HL_BODY_SEP } from "../hashline/hash";
|
|
16
16
|
import { InternalUrlRouter } from "../internal-urls";
|
|
17
|
-
import { parseInternalUrl } from "../internal-urls/parse";
|
|
17
|
+
import { normalizeInternalUrlInput, parseInternalUrl } from "../internal-urls/parse";
|
|
18
18
|
import type { InternalUrl } from "../internal-urls/types";
|
|
19
19
|
import { getLanguageFromPath, type Theme } from "../modes/theme/theme";
|
|
20
20
|
import readDescription from "../prompts/tools/read.md" with { type: "text" };
|
|
@@ -2745,7 +2745,7 @@ export class ReadTool implements AgentTool<typeof readSchema, ReadToolDetails> {
|
|
|
2745
2745
|
// off the URL and surfaced via parseSel rather than confusing handlers.
|
|
2746
2746
|
const internalRouter = InternalUrlRouter.instance();
|
|
2747
2747
|
if (internalRouter.canHandle(readPath)) {
|
|
2748
|
-
const internalTarget = splitInternalUrlSel(readPath, {
|
|
2748
|
+
const internalTarget = splitInternalUrlSel(normalizeInternalUrlInput(readPath), {
|
|
2749
2749
|
activeSkillNames: getActiveSkills().map(skill => skill.name),
|
|
2750
2750
|
});
|
|
2751
2751
|
const parsed = parseSel(internalTarget.sel);
|
|
@@ -3590,7 +3590,7 @@ export class ReadTool implements AgentTool<typeof readSchema, ReadToolDetails> {
|
|
|
3590
3590
|
sourcePath: resource.sourcePath,
|
|
3591
3591
|
sourceInternal: url,
|
|
3592
3592
|
entityLabel: "resource",
|
|
3593
|
-
ignoreResultLimits: scheme === "skill",
|
|
3593
|
+
ignoreResultLimits: scheme === "skill" || scheme === "embedded",
|
|
3594
3594
|
immutable: resource.immutable,
|
|
3595
3595
|
raw,
|
|
3596
3596
|
truncationDirection: direction,
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { logger } from "@gajae-code/utils";
|
|
2
|
+
import type { FoldReason } from "../async/job-manager";
|
|
3
|
+
import type { ToolSession } from ".";
|
|
4
|
+
|
|
5
|
+
/** A short foreground wait stays in the foreground even when a steer is admitted. */
|
|
6
|
+
export const STEER_FOLD_GRACE_MS = 2_000;
|
|
7
|
+
|
|
8
|
+
/** Model-facing line appended to a steer-folded background-start result. */
|
|
9
|
+
export function steerFoldReasonLine(jobId: string): string {
|
|
10
|
+
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.`;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/** Model-facing cause clause for a fold, keyed by the recorded {@link FoldReason}. */
|
|
14
|
+
export function foldReasonClause(reason: FoldReason): string {
|
|
15
|
+
switch (reason) {
|
|
16
|
+
case "steer":
|
|
17
|
+
return "because a user steer arrived";
|
|
18
|
+
case "chord":
|
|
19
|
+
return "because the user pressed the fold chord";
|
|
20
|
+
case "sdk_control":
|
|
21
|
+
return "because an SDK client requested it";
|
|
22
|
+
case "timer":
|
|
23
|
+
return "because the auto-background threshold elapsed";
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** Model-facing line used when the `job` tool's own wait is folded; states the real trigger. */
|
|
28
|
+
export function foldAwaitReasonLine(folded: ReadonlyMap<string, FoldReason>): string {
|
|
29
|
+
const jobIds = [...folded.keys()];
|
|
30
|
+
const names = jobIds.map(id => `\`${id}\``).join(", ");
|
|
31
|
+
const reasons = [...new Set(folded.values())];
|
|
32
|
+
const single = reasons.length === 1 ? reasons[0] : undefined;
|
|
33
|
+
const cause = single ? foldReasonClause(single) : "because the waits were folded";
|
|
34
|
+
const plural = jobIds.length !== 1;
|
|
35
|
+
return `Folded the job await for ${names} ${cause}; the job${plural ? "s keep" : " keeps"} running with ${plural ? "their original deadlines" : "its original deadline"}, and ${plural ? "their results" : "its result"} will wake a later turn.`;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
type SteerFoldSession = Pick<ToolSession, "settings" | "waitForUserSteering" | "requestForegroundBashBackground">;
|
|
39
|
+
|
|
40
|
+
/** The session hooks a steer fold needs, resolved once so callers never re-read and non-null-assert them. */
|
|
41
|
+
interface SteerFoldHooks {
|
|
42
|
+
waitForSteer: NonNullable<ToolSession["waitForUserSteering"]>;
|
|
43
|
+
requestFold: NonNullable<ToolSession["requestForegroundBashBackground"]>;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Whether a queued user steer may fold the running foreground wait. Mirrors
|
|
48
|
+
* the loop's steer admission: `busyPromptMode=queue` never admits a steer
|
|
49
|
+
* into the busy run, so it never folds. `toolInterruptPolicy` is
|
|
50
|
+
* deliberately NOT a gate: `finish_tools` means "do not kill the batch to
|
|
51
|
+
* deliver a steer", and a fold kills nothing. It is the one way to deliver
|
|
52
|
+
* the steer now AND let the command finish, so it applies under both
|
|
53
|
+
* policies; the policy only decides what the loop does with sibling tools
|
|
54
|
+
* after the fold returns. The auto-background setting is likewise not
|
|
55
|
+
* consulted: like the chord, a steer fold is a user action.
|
|
56
|
+
*
|
|
57
|
+
* Fails closed: a session that cannot report newly admitted steering or
|
|
58
|
+
* accept a fold request is not steer-foldable.
|
|
59
|
+
*/
|
|
60
|
+
function steerFoldHooks(session: SteerFoldSession): SteerFoldHooks | undefined {
|
|
61
|
+
const { waitForUserSteering, requestForegroundBashBackground } = session;
|
|
62
|
+
if (!waitForUserSteering || !requestForegroundBashBackground) return undefined;
|
|
63
|
+
if (session.settings.get("busyPromptMode") !== "steer") return undefined;
|
|
64
|
+
return { waitForSteer: waitForUserSteering, requestFold: requestForegroundBashBackground };
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Fold on the first user steer that ARRIVES after the wait has run for
|
|
69
|
+
* {@link STEER_FOLD_GRACE_MS} since `startedAt`. A steer already queued when
|
|
70
|
+
* the wait starts, or one that arrives inside the grace window, never folds:
|
|
71
|
+
* the wait finishes normally and that steer is consumed at the ordinary tool
|
|
72
|
+
* boundary. The watcher keeps observing so a later qualifying steer still
|
|
73
|
+
* folds, and re-checks the gate at that moment so a `busyPromptMode` change
|
|
74
|
+
* during the wait is honored. `requestFold` receives the session's resolved
|
|
75
|
+
* fold request so the caller targets its own adapter without re-reading the
|
|
76
|
+
* optional session hook.
|
|
77
|
+
* Returns a stop function; call it when the wait settles. No-op when steer
|
|
78
|
+
* folding is gated off at start.
|
|
79
|
+
*/
|
|
80
|
+
export function watchSteerForFold(
|
|
81
|
+
session: SteerFoldSession,
|
|
82
|
+
startedAt: number,
|
|
83
|
+
requestFold: (fold: SteerFoldHooks["requestFold"]) => Promise<unknown>,
|
|
84
|
+
jobId?: string,
|
|
85
|
+
): () => void {
|
|
86
|
+
const hooks = steerFoldHooks(session);
|
|
87
|
+
if (!hooks) return () => {};
|
|
88
|
+
const watch = new AbortController();
|
|
89
|
+
const observe = async (): Promise<void> => {
|
|
90
|
+
while (!watch.signal.aborted) {
|
|
91
|
+
await hooks.waitForSteer(watch.signal);
|
|
92
|
+
if (watch.signal.aborted) return;
|
|
93
|
+
if (Date.now() - startedAt < STEER_FOLD_GRACE_MS) continue;
|
|
94
|
+
const current = steerFoldHooks(session);
|
|
95
|
+
if (!current) continue;
|
|
96
|
+
await requestFold(current.requestFold);
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
observe().catch((error: unknown) => {
|
|
101
|
+
logger.warn("Steer-triggered fold failed", {
|
|
102
|
+
...(jobId ? { jobId } : {}),
|
|
103
|
+
error: error instanceof Error ? error.message : String(error),
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
return () => watch.abort();
|
|
107
|
+
}
|
|
@@ -27,7 +27,7 @@ export const TOOL_CATALOG: Readonly<Record<string, ToolCatalogEntry>> = {
|
|
|
27
27
|
"read": {
|
|
28
28
|
"name": "read",
|
|
29
29
|
"label": "Read",
|
|
30
|
-
"description": "Read files, directories, archives, SQLite databases, images, documents, internal resources, and web URLs through a single `path` string.\n\n<instruction>\n- One tool for filesystem, archives, SQLite, images, documents (PDF/DOCX/PPTX/XLSX/RTF/EPUB/ipynb), internal URIs, and web URLs (reader-mode by default).\n- You SHOULD parallelize independent reads when exploring related files.\n- You SHOULD reach for `read` — not a browser/puppeteer tool — for fetching web content.\n</instruction>\n\n## Parameters\n\n- `path` — required. Local path, internal URI (`agent://`, `artifact://`, `rule://`, `local://`), or URL. Append `:<sel>` for line ranges, raw mode, or special modes (e.g. `src/foo.ts:50-200`, `src/foo.ts:raw`, `db.sqlite:users:42`).\n- `truncation` — optional `head` | `last` | `both`; selects which end of an over-budget result to retain. Configured default: last (factory default: `last`); structural summaries and routes such as URLs, directories and converted documents default to `head`. A line-range selector still bounds the selection — this only picks which end of that selection survives the byte/line cap. SQLite row queries page via their own `limit`/`offset` and ignore it.\n## Selectors\nAppend `:<sel>` to `path`. The bare path falls back to the default mode.\n\n- _(none)_ — parseable code → structural summary (signatures kept, bodies elided); a plain text file → a bounded receipt of about undefined lines or undefined KiB, whichever is smaller; the configured truncation direction is last (factory default: last). Line+hash anchors keep their real file line numbers and a footer names the omitted range. Archive members use the larger 3000-line / 50 KiB budget. Converted documents, notebooks, URLs and directory listings still start from the beginning.\n- `:50` / `:50-` — read from line 50 onward.\n- `:50-200` — lines 50–200 inclusive.\n- `:50+150` — 150 lines starting at line 50.\n- `:20+1` — exactly one line.\n- `:5-16,960-973` — multiple ranges in one call (sorted, overlaps merged).\n- `:raw` — verbatim text; no anchors, no summary, no line prefixes.\n- `:2-4:raw` or `:raw:2-4` — range AND verbatim; the two compose in either order.\n- `:conflicts` — one-line-per-block index of every unresolved git merge conflict.\n\n# Files\n\n- Reading a directory path returns a depth-limited dirent listing.\n- Parseable code without a selector returns a **structural summary**: declarations kept, large bodies collapsed to `..` (merged brace pair) or `…` (standalone). Summarized output ends with a footer of the form:\n\n `[NN lines across MM elided regions; read <path>:raw or a line range like <path>:1-9999 for verbatim content]`\n\n If the elided body is what you actually need, re-issue the **exact selector the footer names**. NEVER guess what's inside `..` / `…` — those markers carry no content.\n- Structural summaries honor explicit `truncation`: `head` retains the beginning, `last` the end, and `both` both ends within the summary byte budget. Complete summary units and their original source anchors stay intact and in source order; budget omissions are marked where they occur. This does not switch to raw source. Summaries that fit are unchanged by direction.\n- Directional windows identify the retained first/last lines and the omitted range; use the `re-read <path>:1-<total>` or `:raw` hint in the footer to recover the full content.\n\n# Documents & Notebooks\n\nExtracts text from PDF, Word, PowerPoint, Excel, RTF, and EPUB. Notebooks (`.ipynb`) are shown as editable `# %% [type] cell:N` text; edits round-trip back to the underlying JSON preserving notebook metadata. Add `:raw` to a notebook to bypass the converter and read the JSON directly.\n\n# Images\n\nReading an image path returns the image itself for visual inspection by a vision-capable model.\n\n# Archives\n\nSupports `.tar`, `.tar.gz`, `.tgz`, `.zip`. Use `archive.ext:path/inside/archive` to read a member, and append a normal selector to the inner path: `archive.zip:dir/file.ts:50-60`.\n\n# SQLite\n\nFor `.sqlite`, `.sqlite3`, `.db`, `.db3`:\n- `file.db` — list tables with row counts\n- `file.db:table` — schema + sample rows\n- `file.db:table:key` — single row by primary key\n- `file.db:table?limit=50&offset=100` — paginated rows\n- `file.db:table?where=status='active'&order=created:desc` — filtered rows\n- `file.db?q=SELECT …` — read-only SELECT query\n\n# URLs\n\n- Default reader-mode: HTML pages, GitHub issues/PRs, Stack Overflow, Wikipedia, Reddit, NPM, arXiv, RSS/Atom, JSON endpoints, PDFs → clean text/markdown.\n- `:raw` returns untouched HTML; line selectors (`:50`, `:50-100`, `:50+150`) paginate the cached fetched output.\n- Bare `host:port` URLs collide with the selector grammar — add a trailing slash before the selector: `https://example.com/:80`.\n\n# Internal URIs\n\n`agent://<id>`, `artifact://<id>`, `rule://<name>`, and `local://<name>.md` resolve transparently and accept the same line selectors as filesystem paths. Use `artifact://<id>` to recover full output that a previous bash/eval/tool result spilled or truncated.\n\n<critical>\n- Always include `path`; never call `read` with `{}`.\n- For line ranges, append the selector to `path`.\n- Re-issue the selector named by a summary footer before relying on elided content.\n</critical>",
|
|
30
|
+
"description": "Read files, directories, archives, SQLite databases, images, documents, internal resources, and web URLs through a single `path` string.\n\n<instruction>\n- One tool for filesystem, archives, SQLite, images, documents (PDF/DOCX/PPTX/XLSX/RTF/EPUB/ipynb), internal URIs, and web URLs (reader-mode by default).\n- You SHOULD parallelize independent reads when exploring related files.\n- You SHOULD reach for `read` — not a browser/puppeteer tool — for fetching web content.\n</instruction>\n\n## Parameters\n\n- `path` — required. Local path, internal URI (`agent://`, `artifact://`, `rule://`, `local://`), or URL. Append `:<sel>` for line ranges, raw mode, or special modes (e.g. `src/foo.ts:50-200`, `src/foo.ts:raw`, `db.sqlite:users:42`).\n- `truncation` — optional `head` | `last` | `both`; selects which end of an over-budget result to retain. Configured default: last (factory default: `last`); structural summaries and routes such as URLs, directories and converted documents default to `head`. A line-range selector still bounds the selection — this only picks which end of that selection survives the byte/line cap. SQLite row queries page via their own `limit`/`offset` and ignore it.\n## Selectors\nAppend `:<sel>` to `path`. The bare path falls back to the default mode.\n\n- _(none)_ — parseable code → structural summary (signatures kept, bodies elided); a plain text file → a bounded receipt of about undefined lines or undefined KiB, whichever is smaller; the configured truncation direction is last (factory default: last). Line+hash anchors keep their real file line numbers and a footer names the omitted range. Archive members use the larger 3000-line / 50 KiB budget. Converted documents, notebooks, URLs and directory listings still start from the beginning.\n- `:50` / `:50-` — read from line 50 onward.\n- `:50-200` — lines 50–200 inclusive.\n- `:50+150` — 150 lines starting at line 50.\n- `:20+1` — exactly one line.\n- `:5-16,960-973` — multiple ranges in one call (sorted, overlaps merged).\n- `:raw` — verbatim text; no anchors, no summary, no line prefixes.\n- `:2-4:raw` or `:raw:2-4` — range AND verbatim; the two compose in either order.\n- `:conflicts` — one-line-per-block index of every unresolved git merge conflict.\n\n# Files\n\n- Reading a directory path returns a depth-limited dirent listing.\n- Parseable code without a selector returns a **structural summary**: declarations kept, large bodies collapsed to `..` (merged brace pair) or `…` (standalone). Summarized output ends with a footer of the form:\n\n `[NN lines across MM elided regions; read <path>:raw or a line range like <path>:1-9999 for verbatim content]`\n\n If the elided body is what you actually need, re-issue the **exact selector the footer names**. NEVER guess what's inside `..` / `…` — those markers carry no content.\n- Structural summaries honor explicit `truncation`: `head` retains the beginning, `last` the end, and `both` both ends within the summary byte budget. Complete summary units and their original source anchors stay intact and in source order; budget omissions are marked where they occur. This does not switch to raw source. Summaries that fit are unchanged by direction.\n- Directional windows identify the retained first/last lines and the omitted range; use the `re-read <path>:1-<total>` or `:raw` hint in the footer to recover the full content.\n\n# Documents & Notebooks\n\nExtracts text from PDF, Word, PowerPoint, Excel, RTF, and EPUB. Notebooks (`.ipynb`) are shown as editable `# %% [type] cell:N` text; edits round-trip back to the underlying JSON preserving notebook metadata. Add `:raw` to a notebook to bypass the converter and read the JSON directly.\n\n# Images\n\nReading an image path returns the image itself for visual inspection by a vision-capable model.\n\n# Archives\n\nSupports `.tar`, `.tar.gz`, `.tgz`, `.zip`. Use `archive.ext:path/inside/archive` to read a member, and append a normal selector to the inner path: `archive.zip:dir/file.ts:50-60`.\n\n# SQLite\n\nFor `.sqlite`, `.sqlite3`, `.db`, `.db3`:\n- `file.db` — list tables with row counts\n- `file.db:table` — schema + sample rows\n- `file.db:table:key` — single row by primary key\n- `file.db:table?limit=50&offset=100` — paginated rows\n- `file.db:table?where=status='active'&order=created:desc` — filtered rows\n- `file.db?q=SELECT …` — read-only SELECT query\n\n# URLs\n\n- Default reader-mode: HTML pages, GitHub issues/PRs, Stack Overflow, Wikipedia, Reddit, NPM, arXiv, RSS/Atom, JSON endpoints, PDFs → clean text/markdown.\n- `:raw` returns untouched HTML; line selectors (`:50`, `:50-100`, `:50+150`) paginate the cached fetched output.\n- Bare `host:port` URLs collide with the selector grammar — add a trailing slash before the selector: `https://example.com/:80`.\n\n# Internal URIs\n\n`agent://<id>`, `artifact://<id>`, `rule://<name>`, and `local://<name>.md` resolve transparently and accept the same line selectors as filesystem paths. Use `artifact://<id>` to recover full output that a previous bash/eval/tool result spilled or truncated.\n\nBundled skills have no filesystem home, so the skill tool and skill discovery report them as `embedded:gjc/skills/<name>/SKILL.md`; read that identifier verbatim. Bundled skill fragments, when surfaced, likewise use `embedded:gjc/skill-fragments/...` identifiers.\n\n<critical>\n- Always include `path`; never call `read` with `{}`.\n- For line ranges, append the selector to `path`.\n- Re-issue the selector named by a summary footer before relying on elided content.\n</critical>",
|
|
31
31
|
"parameters": {
|
|
32
32
|
"type": "object",
|
|
33
33
|
"properties": {
|
|
@@ -89,6 +89,33 @@ export const TOOL_CATALOG: Readonly<Record<string, ToolCatalogEntry>> = {
|
|
|
89
89
|
"pty": {
|
|
90
90
|
"type": "boolean",
|
|
91
91
|
"description": "run in pty mode"
|
|
92
|
+
},
|
|
93
|
+
"activity": {
|
|
94
|
+
"description": "declare browser activity for the aside browser backend: repl or exec",
|
|
95
|
+
"type": "object",
|
|
96
|
+
"properties": {
|
|
97
|
+
"kind": {
|
|
98
|
+
"type": "string",
|
|
99
|
+
"const": "browser"
|
|
100
|
+
},
|
|
101
|
+
"provider": {
|
|
102
|
+
"type": "string",
|
|
103
|
+
"const": "aside"
|
|
104
|
+
},
|
|
105
|
+
"mode": {
|
|
106
|
+
"type": "string",
|
|
107
|
+
"enum": [
|
|
108
|
+
"repl",
|
|
109
|
+
"exec"
|
|
110
|
+
]
|
|
111
|
+
}
|
|
112
|
+
},
|
|
113
|
+
"required": [
|
|
114
|
+
"kind",
|
|
115
|
+
"provider",
|
|
116
|
+
"mode"
|
|
117
|
+
],
|
|
118
|
+
"additionalProperties": false
|
|
92
119
|
}
|
|
93
120
|
},
|
|
94
121
|
"required": [
|
|
@@ -2326,7 +2353,7 @@ export const TOOL_CATALOG: Readonly<Record<string, ToolCatalogEntry>> = {
|
|
|
2326
2353
|
"job": {
|
|
2327
2354
|
"name": "job",
|
|
2328
2355
|
"label": "Job",
|
|
2329
|
-
"description": "Inspects, waits, or cancels async jobs.\n\nBackground job results are delivered automatically when complete. Jobs that back task subagents should be controlled via the `subagent` tool when it is available; use `job` for non-subagent jobs (async bash, monitors) and as a compatibility fallback. Running job output stays quiet by default to avoid flooding the conversation; use `tail` when you explicitly want to show/reopen retained output. Reach for this tool only when you need to inspect or intervene.\n\nIn the interactive TUI, a running foreground command can be folded into a background job by pressing the fold chord twice (default `Alt+Shift+B`, plus `Cmd+B` on macOS; remappable via `app.tool.backgroundFold`), or automatically when the user sends a steer after the command has run for at least 2 seconds (with `busyPromptMode=steer` (`toolInterruptPolicy` is not a gate)). This covers managed non-PTY bash, editor/ACP client-terminal commands, and PTY-mode commands. A folded job keeps its original deadline, is never killed or restarted by folding, and its result wakes a later turn automatically; a folded PTY continues output-only and no longer accepts input. Job listings expose `foldReason` (`steer`, `chord`, `timer`, or `sdk_control`) beside `backgrounded` so you can tell why a job left the foreground. `task`/`subagent` waits are not foldable. Raw shell `Ctrl+Z`/`bg` is not the supported path inside GJC because it bypasses job ownership and output-routing contracts.\n\n# Operations\n\n## `list: true`\nUse to inspect what's running.\n\n## `tail: [id, …]`\nShow the retained output buffer for one or more background jobs without waiting.\n- Use this to reopen/tail a backgrounded long-running bash/tool output after folding it away.\n- Output is bounded by the manager retention window; stale cursors may report that only the retained tail is available.\n- Prefer `tail` over polling when you only need to peek at progress, so the conversation can continue without flooding the TUI.\n\n## `poll: [id, …]`\nBlock until the specified jobs finish or the wait window (~30 s, not configurable) elapses.\n- Use when you are genuinely blocked on a result and have no other work to do.\n- Returns the current snapshot when the timer elapses; running jobs remain running.\n- Completed jobs include their final output in the returned snapshot.\n\n## `cancel: [id, …]`\nStop running jobs.\n- Use when a job is stalled, hung, or no longer needed.\n- Returns immediately after cancelling.",
|
|
2356
|
+
"description": "Inspects, waits, or cancels async jobs.\n\nBackground job results are delivered automatically when complete. Jobs that back task subagents should be controlled via the `subagent` tool when it is available; use `job` for non-subagent jobs (async bash, monitors) and as a compatibility fallback. Running job output stays quiet by default to avoid flooding the conversation; use `tail` when you explicitly want to show/reopen retained output. Reach for this tool only when you need to inspect or intervene.\n\nIn the interactive TUI, a running foreground command can be folded into a background job by pressing the fold chord twice (default `Alt+Shift+B`, plus `Cmd+B` on macOS; remappable via `app.tool.backgroundFold`), or automatically when the user sends a steer after the command has run for at least 2 seconds (with `busyPromptMode=steer` (`toolInterruptPolicy` is not a gate)). This covers managed non-PTY bash, editor/ACP client-terminal commands, and PTY-mode commands. A folded job keeps its original deadline, is never killed or restarted by folding, and its result wakes a later turn automatically; a folded PTY continues output-only and no longer accepts input. A `job` await also folds after the same 2-second grace window when a steer arrives, returning immediately with the still-running job id(s) while those jobs keep their original deadlines and wake a later turn with their results. Job listings expose `foldReason` (`steer`, `chord`, `timer`, or `sdk_control`) beside `backgrounded` so you can tell why a job left the foreground. `task`/`subagent` waits are not foldable. Raw shell `Ctrl+Z`/`bg` is not the supported path inside GJC because it bypasses job ownership and output-routing contracts.\n\n# Operations\n\n## `list: true`\nUse to inspect what's running.\n\n## `tail: [id, …]`\nShow the retained output buffer for one or more background jobs without waiting.\n- Use this to reopen/tail a backgrounded long-running bash/tool output after folding it away.\n- Output is bounded by the manager retention window; stale cursors may report that only the retained tail is available.\n- Prefer `tail` over polling when you only need to peek at progress, so the conversation can continue without flooding the TUI.\n\n## `poll: [id, …]`\nBlock until the specified jobs finish or the wait window (~30 s, not configurable) elapses.\n- Use when you are genuinely blocked on a result and have no other work to do.\n- Returns the current snapshot when the timer elapses; running jobs remain running.\n- Completed jobs include their final output in the returned snapshot.\n\n## `cancel: [id, …]`\nStop running jobs.\n- Use when a job is stalled, hung, or no longer needed.\n- Returns immediately after cancelling.",
|
|
2330
2357
|
"parameters": {
|
|
2331
2358
|
"type": "object",
|
|
2332
2359
|
"properties": {
|
package/src/tools/tool-errors.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { markDesignedError } from "@gajae-code/utils/error-classification";
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* Standardized error types for tool execution.
|
|
3
5
|
*
|
|
@@ -16,6 +18,7 @@ export class ToolError extends Error {
|
|
|
16
18
|
) {
|
|
17
19
|
super(message);
|
|
18
20
|
this.name = "ToolError";
|
|
21
|
+
markDesignedError(this);
|
|
19
22
|
}
|
|
20
23
|
|
|
21
24
|
/** Render error for LLM consumption. Override for custom formatting. */
|
|
@@ -33,6 +36,7 @@ export class ToolAbortError extends Error {
|
|
|
33
36
|
constructor(message: string = ToolAbortError.MESSAGE) {
|
|
34
37
|
super(message);
|
|
35
38
|
this.name = "ToolAbortError";
|
|
39
|
+
markDesignedError(this);
|
|
36
40
|
}
|
|
37
41
|
}
|
|
38
42
|
|