@gajae-code/coding-agent 0.18.11 → 0.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +760 -0
- package/dist/types/async/job-manager.d.ts +7 -3
- package/dist/types/async/result-formatting.d.ts +2 -0
- package/dist/types/autoresearch/git.d.ts +1 -1
- package/dist/types/capability/fs.d.ts +2 -2
- package/dist/types/capability/rule.d.ts +7 -2
- package/dist/types/cli/accounts-cli.d.ts +2 -2
- package/dist/types/cli/append-system-prompt.d.ts +6 -0
- package/dist/types/cli/args.d.ts +7 -2
- package/dist/types/cli/customize-doctor-precedence.d.ts +16 -0
- package/dist/types/cli/customize-doctor.d.ts +4 -3
- package/dist/types/cli/doctor/install-repairs.d.ts +4 -1
- package/dist/types/cli/install-activation.d.ts +10 -4
- package/dist/types/cli/public-command-errors.d.ts +2 -0
- package/dist/types/cli/root-flags.d.ts +0 -12
- package/dist/types/cli/update-cli.d.ts +21 -3
- package/dist/types/cli-main.d.ts +0 -6
- package/dist/types/cli-ordinary.d.ts +2 -2
- package/dist/types/commands/accounts.d.ts +2 -1
- package/dist/types/commands/launch.d.ts +0 -6
- package/dist/types/commands/migrate.d.ts +1 -1
- package/dist/types/commands/sdk.d.ts +13 -0
- package/dist/types/commit/changelog/index.d.ts +1 -0
- package/dist/types/commit/map-reduce/index.d.ts +4 -4
- package/dist/types/config/autorouting-tier-map.d.ts +53 -1
- package/dist/types/config/config-hot-reload-trust.d.ts +12 -0
- package/dist/types/config/config-hot-reload.d.ts +44 -0
- package/dist/types/config/file-lock.d.ts +52 -3
- package/dist/types/config/model-discovery-manager.d.ts +2 -0
- package/dist/types/config/model-profile-activation.d.ts +79 -9
- package/dist/types/config/model-registry.d.ts +72 -8
- package/dist/types/config/model-resolver.d.ts +7 -4
- package/dist/types/config/models-config-schema.d.ts +10 -0
- package/dist/types/config/settings-schema.d.ts +57 -4
- package/dist/types/config/settings.d.ts +33 -1
- package/dist/types/coordinator-mcp/server.d.ts +4 -0
- package/dist/types/customization/import.d.ts +2 -0
- package/dist/types/customization/types.d.ts +1 -1
- package/dist/types/dap/config.d.ts +8 -4
- package/dist/types/dap/session.d.ts +11 -2
- package/dist/types/debug/system-info.d.ts +6 -1
- package/dist/types/discovery/helpers.d.ts +7 -2
- package/dist/types/discovery/mcp-compat.d.ts +3 -1
- package/dist/types/edit/index.d.ts +5 -0
- package/dist/types/edit/modes/patch.d.ts +14 -0
- package/dist/types/edit/modes/replace.d.ts +3 -0
- package/dist/types/eval/py/runtime.d.ts +9 -0
- package/dist/types/exec/bash-executor.d.ts +12 -0
- package/dist/types/export/ttsr.d.ts +19 -0
- package/dist/types/extensibility/extensions/types.d.ts +5 -1
- package/dist/types/extensibility/slash-commands.d.ts +1 -0
- package/dist/types/gjc-runtime/managed-owner-admission.d.ts +3 -0
- package/dist/types/gjc-runtime/ralplan-current-final.d.ts +54 -0
- package/dist/types/gjc-runtime/ralplan-runtime.d.ts +5 -0
- package/dist/types/gjc-runtime/session-state-lock.d.ts +2 -0
- package/dist/types/gjc-runtime/state-runtime.d.ts +0 -2
- package/dist/types/gjc-runtime/tmux-sessions.d.ts +24 -0
- package/dist/types/gjc-runtime/ultragoal-change-set.d.ts +6 -0
- package/dist/types/gjc-runtime/ultragoal-runtime.d.ts +1 -0
- package/dist/types/harness-control-plane/receipt-spool.d.ts +4 -0
- package/dist/types/hindsight/content.d.ts +5 -6
- package/dist/types/internal-urls/types.d.ts +2 -0
- package/dist/types/lsp/clients/swiftlint-client.d.ts +0 -4
- package/dist/types/main.d.ts +23 -18
- package/dist/types/migrate/action-planner.d.ts +10 -1
- package/dist/types/migrate/adapters/codex.d.ts +2 -1
- package/dist/types/migrate/adapters/index.d.ts +42 -1
- package/dist/types/migrate/executor.d.ts +3 -1
- package/dist/types/migrate/hook-module.d.ts +23 -0
- package/dist/types/migrate/report.d.ts +2 -1
- package/dist/types/migrate/types.d.ts +82 -3
- package/dist/types/modes/components/agent-dashboard.d.ts +1 -1
- package/dist/types/modes/components/model-selector.d.ts +43 -0
- package/dist/types/modes/components/settings-selector.d.ts +6 -0
- package/dist/types/modes/controllers/selector-controller.d.ts +2 -0
- package/dist/types/modes/prompt-suggestion-controller.d.ts +1 -1
- package/dist/types/modes/theme/theme.d.ts +1 -0
- package/dist/types/modes/utils/host-status.d.ts +15 -11
- package/dist/types/runtime-mcp/content-limits.d.ts +4 -0
- package/dist/types/runtime-mcp/manager.d.ts +7 -0
- package/dist/types/runtime-mcp/plugin-mcp-name-filter.d.ts +22 -0
- package/dist/types/runtime-mcp/project-host-values.d.ts +11 -5
- package/dist/types/runtime-mcp/tool-bridge.d.ts +4 -0
- package/dist/types/sdk/broker/broker-exit.d.ts +3 -3
- package/dist/types/sdk/broker/broker.d.ts +29 -2
- package/dist/types/sdk/broker/daemon-entry.d.ts +11 -1
- package/dist/types/sdk/broker/doctor-restart.d.ts +1 -1
- package/dist/types/sdk/broker/ensure.d.ts +141 -9
- package/dist/types/sdk/broker/hop.d.ts +45 -0
- package/dist/types/sdk/broker/lifecycle-ledger.d.ts +18 -10
- package/dist/types/sdk/broker/lifecycle.d.ts +22 -0
- package/dist/types/sdk/broker/lock-host-identity.d.ts +5 -0
- package/dist/types/sdk/broker/session-index.d.ts +6 -1
- package/dist/types/sdk/broker/transport.d.ts +2 -0
- package/dist/types/sdk/bus/chat-command-policy.d.ts +2 -1
- package/dist/types/sdk/bus/index.d.ts +44 -0
- package/dist/types/sdk/bus/reconciliation-store.d.ts +10 -0
- package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +9 -1
- package/dist/types/sdk/bus/telegram-daemon.d.ts +2 -0
- package/dist/types/sdk/bus/threaded-render.d.ts +2 -1
- package/dist/types/sdk/cli/session-cli.d.ts +0 -1
- package/dist/types/sdk/host/control/operations.d.ts +5 -1
- package/dist/types/sdk/host/events.d.ts +4 -0
- package/dist/types/sdk/host/host.d.ts +2 -1
- package/dist/types/sdk/host/prompt-image-upload.d.ts +43 -0
- package/dist/types/sdk/host/session-runtime.d.ts +40 -8
- package/dist/types/sdk/host/surface-policy.d.ts +2 -1
- package/dist/types/sdk/lifecycle/service.d.ts +12 -0
- package/dist/types/sdk/prompt-deadline-flush.d.ts +42 -4
- package/dist/types/sdk/prompt-deadline-manager.d.ts +2 -0
- package/dist/types/sdk/prompt-status.d.ts +11 -4
- package/dist/types/sdk/protocol/adapter-validation.d.ts +10 -4
- package/dist/types/sdk/reconciliation-extensions.d.ts +1 -1
- package/dist/types/sdk/session-list.d.ts +1 -1
- package/dist/types/session/agent-session.d.ts +107 -9
- package/dist/types/session/internal/managed-session-scope.d.ts +2 -2
- package/dist/types/session/internal/managed-session-storage.d.ts +1 -1
- package/dist/types/session/internal/native-publish-outcome.d.ts +1 -1
- package/dist/types/session/messages.d.ts +5 -1
- package/dist/types/session/session-manager.d.ts +29 -3
- package/dist/types/session/startup-auth-config.d.ts +24 -2
- package/dist/types/session/streaming-output.d.ts +11 -0
- package/dist/types/session/terminal-abort.d.ts +3 -1
- package/dist/types/session-import/service.d.ts +1 -1
- package/dist/types/skill-state/workflow-mutation-guard.d.ts +20 -4
- package/dist/types/slash-commands/helpers/mcp.d.ts +5 -0
- package/dist/types/slash-commands/helpers/provider-auth-status.d.ts +8 -0
- package/dist/types/ssh/ssh-executor.d.ts +8 -1
- package/dist/types/system-prompt.d.ts +3 -2
- package/dist/types/task/executor.d.ts +19 -2
- package/dist/types/task/role-body.d.ts +11 -0
- package/dist/types/tools/ast-edit-write-path.d.ts +10 -0
- package/dist/types/tools/atomic-file-write.d.ts +9 -7
- package/dist/types/tools/awaiting-input.d.ts +28 -0
- package/dist/types/tools/bash-allowed-prefixes.d.ts +17 -0
- package/dist/types/tools/bash-skill-urls.d.ts +4 -0
- package/dist/types/tools/bash.d.ts +7 -0
- package/dist/types/tools/descriptors.d.ts +15 -2
- package/dist/types/tools/fetch.d.ts +7 -1
- package/dist/types/tools/index.d.ts +2 -0
- package/dist/types/tools/irc.d.ts +5 -3
- package/dist/types/tools/python.d.ts +1 -1
- package/dist/types/tools/report-issue.d.ts +41 -0
- package/dist/types/tools/subagent.d.ts +23 -0
- package/dist/types/tools/tmux-self-injection-guard.d.ts +2 -0
- package/dist/types/tools/ultragoal-ask-guard.d.ts +1 -1
- package/dist/types/tools/write.d.ts +2 -0
- package/dist/types/utils/file-mentions.d.ts +5 -0
- package/dist/types/utils/image-limits.d.ts +7 -0
- package/dist/types/utils/image-loading.d.ts +0 -1
- package/dist/types/utils/open.d.ts +0 -1
- package/dist/types/utils/pasted-image-loading.d.ts +1 -5
- package/dist/types/utils/prompt-suggestion.d.ts +2 -2
- package/dist/types/utils/shell-snapshot.d.ts +2 -0
- package/dist/types/utils/title-generator.d.ts +13 -3
- package/dist/types/web/scrapers/types.d.ts +8 -1
- package/dist/types/web/search/providers/codex.d.ts +6 -7
- package/dist/types/web/search/providers/insane.d.ts +7 -1
- package/dist/types/web/search/providers/searxng.d.ts +5 -0
- package/package.json +10 -9
- package/scripts/acp-conformance-agent.ts +12 -2
- package/scripts/build-binary.ts +2 -1
- package/scripts/capture-subagent-compact-panel-showcase.ts +105 -0
- package/scripts/generate-docs-index.ts +8 -1
- package/scripts/generate-sdk-operation-inventory.ts +38 -4
- package/src/async/job-manager.ts +356 -56
- package/src/async/result-formatting.ts +40 -0
- package/src/autoresearch/git.ts +9 -2
- package/src/autoresearch/prompts/prompt-setup.md +1 -1
- package/src/capability/fs.ts +58 -22
- package/src/capability/index.ts +2 -2
- package/src/capability/rule.ts +11 -2
- package/src/cli/accounts-cli.ts +176 -8
- package/src/cli/append-system-prompt.ts +12 -0
- package/src/cli/args.ts +11 -7
- package/src/cli/auth-broker-cli.ts +21 -11
- package/src/cli/customize-doctor-precedence.ts +31 -0
- package/src/cli/customize-doctor.ts +49 -39
- package/src/cli/doctor/install-repairs.ts +35 -5
- package/src/cli/fast-help.ts +1 -3
- package/src/cli/install-activation.ts +47 -4
- package/src/cli/migrate-cli.ts +19 -6
- package/src/cli/public-command-errors.ts +6 -0
- package/src/cli/root-flags.ts +2 -4
- package/src/cli/stats-cli.ts +9 -5
- package/src/cli/update-cli.ts +471 -13
- package/src/cli-ordinary.ts +7 -2
- package/src/commands/accounts.ts +10 -5
- package/src/commands/harness.ts +25 -15
- package/src/commands/migrate.ts +3 -3
- package/src/commands/sdk.ts +277 -50
- package/src/commands/ultragoal.ts +16 -8
- package/src/commit/agentic/agent.ts +6 -6
- package/src/commit/analysis/conventional.ts +14 -5
- package/src/commit/changelog/index.ts +39 -1
- package/src/commit/map-reduce/index.ts +7 -7
- package/src/commit/message.ts +10 -3
- package/src/commit/pipeline.ts +12 -12
- package/src/config/autorouting-tier-map.ts +50 -1
- package/src/config/config-file.ts +106 -3
- package/src/config/config-hot-reload-trust.ts +148 -0
- package/src/config/config-hot-reload.ts +742 -0
- package/src/config/file-lock.ts +1615 -328
- package/src/config/keybindings.ts +154 -7
- package/src/config/model-bindings-applier.ts +85 -47
- package/src/config/model-discovery-manager.ts +10 -0
- package/src/config/model-profile-activation.ts +889 -148
- package/src/config/model-registry.ts +1706 -205
- package/src/config/model-resolver.ts +52 -22
- package/src/config/models-config-schema.ts +6 -0
- package/src/config/settings-schema.ts +73 -4
- package/src/config/settings.ts +460 -24
- package/src/coordinator-mcp/policy.ts +26 -1
- package/src/coordinator-mcp/server.ts +37 -2
- package/src/crash/sanitize.ts +7 -1
- package/src/customization/import.ts +279 -8
- package/src/customization/inventory.ts +24 -8
- package/src/customization/types.ts +1 -0
- package/src/dap/client.ts +3 -2
- package/src/dap/config.ts +31 -11
- package/src/dap/defaults.json +1 -1
- package/src/dap/session.ts +161 -36
- package/src/debug/crash-diagnostics.ts +89 -6
- package/src/debug/report-bundle.ts +90 -2
- package/src/debug/system-info.ts +8 -3
- package/src/defaults/gjc/extensions/grok-cli-vendor/src/shared/base-url.ts +21 -7
- package/src/discovery/agents-md.ts +1 -1
- package/src/discovery/builtin.ts +4 -3
- package/src/discovery/helpers.ts +25 -16
- package/src/discovery/mcp-compat.ts +120 -17
- package/src/edit/apply-patch/index.ts +12 -1
- package/src/edit/index.ts +204 -22
- package/src/edit/modes/patch.ts +214 -8
- package/src/edit/modes/replace.ts +136 -0
- package/src/eval/js/context-manager.ts +29 -3
- package/src/eval/js/worker-entry.ts +51 -18
- package/src/eval/py/executor.ts +14 -4
- package/src/eval/py/runner.py +144 -5
- package/src/eval/py/runtime.ts +44 -4
- package/src/exec/bash-executor.ts +81 -4
- package/src/exec/bash-shell-guardian.ts +4 -1
- package/src/exec/bash-shell-supervisor.ts +8 -4
- package/src/exec/isolated-shell.ts +5 -1
- package/src/exec/non-interactive-env.ts +0 -1
- package/src/export/html/index.ts +86 -6
- package/src/export/ttsr.ts +176 -1
- package/src/extensibility/custom-tools/wrapper.ts +3 -1
- package/src/extensibility/extensions/loader.ts +10 -1
- package/src/extensibility/extensions/ouroboros-ooo-bridge.ts +63 -1
- package/src/extensibility/extensions/types.ts +5 -1
- package/src/extensibility/hooks/loader.ts +10 -2
- package/src/extensibility/plugins/marketplace/fetcher.ts +2 -2
- package/src/extensibility/plugins/marketplace/manager.ts +36 -10
- package/src/extensibility/slash-commands.ts +5 -0
- package/src/gjc-runtime/gc-runtime.ts +37 -7
- package/src/gjc-runtime/launch-tmux.ts +30 -15
- package/src/gjc-runtime/launch-worktree.test.ts +148 -0
- package/src/gjc-runtime/launch-worktree.ts +101 -14
- package/src/gjc-runtime/managed-owner-admission.ts +46 -2
- package/src/gjc-runtime/ralplan-current-final.ts +294 -0
- package/src/gjc-runtime/ralplan-runtime.ts +95 -1
- package/src/gjc-runtime/session-resolution.ts +38 -6
- package/src/gjc-runtime/session-state-lock.ts +59 -14
- package/src/gjc-runtime/state-runtime.ts +23 -19
- package/src/gjc-runtime/state-writer.ts +109 -5
- package/src/gjc-runtime/tmux-sessions.ts +93 -13
- package/src/gjc-runtime/ultragoal-change-set.ts +90 -11
- package/src/gjc-runtime/ultragoal-guard.ts +9 -1
- package/src/gjc-runtime/ultragoal-runtime.ts +15 -5
- package/src/harness-control-plane/finalize.ts +11 -0
- package/src/harness-control-plane/receipt-spool.ts +105 -28
- package/src/harness-control-plane/storage.ts +124 -6
- package/src/hindsight/content.ts +116 -16
- package/src/hindsight/mental-models.ts +22 -10
- package/src/hindsight/state.ts +7 -4
- package/src/hooks/native-skill-hook.ts +17 -12
- package/src/internal-urls/artifact-protocol.ts +3 -1
- package/src/internal-urls/docs-index.generated.ts +23 -22
- package/src/internal-urls/local-protocol.ts +167 -8
- package/src/internal-urls/types.ts +2 -0
- package/src/lsp/client.ts +2 -1
- package/src/lsp/clients/biome-client.ts +2 -0
- package/src/lsp/clients/swiftlint-client.ts +2 -0
- package/src/lsp/index.ts +29 -0
- package/src/lsp/workspace-path.ts +23 -0
- package/src/main.ts +330 -92
- package/src/memories/index.ts +25 -2
- package/src/migrate/action-planner.ts +574 -2
- package/src/migrate/adapters/claude-code.ts +162 -5
- package/src/migrate/adapters/codex.ts +77 -7
- package/src/migrate/adapters/index.ts +258 -5
- package/src/migrate/adapters/opencode.ts +10 -1
- package/src/migrate/executor.ts +222 -7
- package/src/migrate/hook-module.ts +193 -0
- package/src/migrate/report.ts +63 -2
- package/src/migrate/types.ts +84 -4
- package/src/modes/DESIGN.md +51 -0
- package/src/modes/acp/acp-agent.ts +10 -1
- package/src/modes/acp/permission-mode.ts +18 -1
- package/src/modes/components/agent-dashboard.ts +20 -4
- package/src/modes/components/assistant-message.ts +55 -1
- package/src/modes/components/customization/customization-dashboard.ts +7 -0
- package/src/modes/components/customization/import-wizard.ts +29 -3
- package/src/modes/components/model-selector.ts +157 -103
- package/src/modes/components/settings-selector.ts +103 -37
- package/src/modes/components/welcome.ts +3 -1
- package/src/modes/controllers/command-controller.ts +300 -9
- package/src/modes/controllers/event-controller.ts +1 -1
- package/src/modes/controllers/extension-ui-controller.ts +31 -1
- package/src/modes/controllers/goal-mode-controller.ts +79 -10
- package/src/modes/controllers/input-controller.ts +2 -0
- package/src/modes/controllers/plan-mode-controller.ts +66 -11
- package/src/modes/controllers/runtime-mcp-command-controller.ts +7 -6
- package/src/modes/controllers/selector-controller.ts +76 -7
- package/src/modes/interactive-mode.ts +123 -4
- package/src/modes/prompt-suggestion-controller.ts +1 -1
- package/src/modes/runtime-init.ts +5 -1
- package/src/modes/theme/theme.ts +15 -0
- package/src/modes/utils/host-status.ts +21 -10
- package/src/prompts/agent-fragments/browser-aside.md +2 -2
- package/src/prompts/system/irc-incoming.md +3 -1
- package/src/prompts/tools/debug.md +1 -1
- package/src/prompts/tools/irc.md +1 -1
- package/src/prompts/tools/read.md +1 -1
- package/src/prompts/tools/report-issue.md +13 -0
- package/src/prompts/tools/task.md +1 -1
- package/src/runtime-credential-selector.ts +2 -2
- package/src/runtime-mcp/content-limits.ts +7 -0
- package/src/runtime-mcp/manager-pool.test.ts +5 -4
- package/src/runtime-mcp/manager.ts +40 -3
- package/src/runtime-mcp/oauth-flow.ts +32 -6
- package/src/runtime-mcp/plugin-mcp-name-filter.ts +62 -0
- package/src/runtime-mcp/project-host-values.ts +12 -8
- package/src/runtime-mcp/render.ts +16 -1
- package/src/runtime-mcp/tool-bridge.ts +194 -21
- package/src/runtime-mcp/transports/stdio.ts +2 -1
- package/src/sdk/broker/broker-exit.ts +10 -5
- package/src/sdk/broker/broker.ts +484 -85
- package/src/sdk/broker/daemon-entry.ts +338 -116
- package/src/sdk/broker/doctor-restart.ts +2 -1
- package/src/sdk/broker/ensure.ts +1214 -268
- package/src/sdk/broker/hop.ts +286 -0
- package/src/sdk/broker/lifecycle-ledger.ts +38 -11
- package/src/sdk/broker/lifecycle.ts +102 -6
- package/src/sdk/broker/lock-host-identity.ts +29 -0
- package/src/sdk/broker/session-index.ts +91 -16
- package/src/sdk/broker/transport.ts +17 -1
- package/src/sdk/bus/chat-command-policy.ts +16 -3
- package/src/sdk/bus/chat-daemon-control.ts +2 -2
- package/src/sdk/bus/index.ts +460 -35
- package/src/sdk/bus/reconciliation-store.ts +201 -8
- package/src/sdk/bus/session-control.ts +2 -0
- package/src/sdk/bus/slack-daemon.ts +105 -9
- package/src/sdk/bus/telegram-daemon-contract.ts +9 -1
- package/src/sdk/bus/telegram-daemon.ts +10 -2
- package/src/sdk/bus/threaded-render.ts +20 -1
- package/src/sdk/cli/session-cli.ts +23 -6
- package/src/sdk/host/control/dispatch.ts +13 -1
- package/src/sdk/host/control/operations.ts +10 -1
- package/src/sdk/host/events.ts +67 -4
- package/src/sdk/host/host.ts +8 -2
- package/src/sdk/host/prompt-image-upload.ts +438 -0
- package/src/sdk/host/sdk-surface-parity.test.ts +15 -0
- package/src/sdk/host/session-runtime.test.ts +1737 -129
- package/src/sdk/host/session-runtime.ts +684 -83
- package/src/sdk/host/surface-policy.ts +4 -0
- package/src/sdk/host/websocket-transport.lifecycle.test.ts +90 -2
- package/src/sdk/host/websocket-transport.ts +19 -7
- package/src/sdk/lifecycle/service.ts +15 -1
- package/src/sdk/prompt-deadline-flush.ts +78 -5
- package/src/sdk/prompt-deadline-manager.ts +68 -14
- package/src/sdk/prompt-status.ts +6 -4
- package/src/sdk/protocol/adapter-validation.ts +81 -12
- package/src/sdk/protocol/operation-inventory.generated.json +261 -2
- package/src/sdk/protocol/operation-registry.ts +33 -3
- package/src/sdk/reconciliation-extensions.ts +1 -0
- package/src/sdk/session-list.ts +1 -0
- package/src/sdk/session.ts +237 -97
- package/src/session/agent-session.ts +2726 -479
- package/src/session/history-storage.ts +182 -12
- package/src/session/internal/managed-session-scope.ts +173 -57
- package/src/session/internal/managed-session-storage.ts +85 -12
- package/src/session/internal/native-publish-outcome.ts +5 -1
- package/src/session/messages.ts +47 -6
- package/src/session/session-manager.ts +326 -19
- package/src/session/startup-auth-config.ts +116 -8
- package/src/session/streaming-output.ts +262 -4
- package/src/session/terminal-abort.ts +14 -2
- package/src/session-import/redact.ts +53 -2
- package/src/session-import/service.ts +1 -0
- package/src/skill-state/workflow-mutation-guard.ts +2145 -206
- package/src/slash-commands/builtin-registry.ts +21 -6
- package/src/slash-commands/helpers/credential-switch.ts +10 -5
- package/src/slash-commands/helpers/mcp.ts +11 -3
- package/src/slash-commands/helpers/provider-auth-status.ts +456 -0
- package/src/ssh/ssh-executor.ts +9 -2
- package/src/stt/recorder.ts +26 -9
- package/src/system-prompt.ts +61 -16
- package/src/task/executor.ts +115 -20
- package/src/task/index.ts +389 -175
- package/src/task/role-body.ts +84 -0
- package/src/task/token-log-mode.test.ts +162 -0
- package/src/task/token-log.ts +91 -2
- package/src/tools/archive-reader.ts +650 -37
- package/src/tools/ask.ts +1 -0
- package/src/tools/ast-edit-write-path.ts +64 -0
- package/src/tools/ast-edit.ts +18 -1
- package/src/tools/ast-grep.ts +7 -2
- package/src/tools/atomic-file-write.ts +46 -15
- package/src/tools/auto-generated-guard.ts +15 -3
- package/src/tools/awaiting-input.ts +114 -0
- package/src/tools/bash-allowed-prefixes.ts +214 -6
- package/src/tools/bash-interactive.ts +14 -2
- package/src/tools/bash-skill-urls.ts +58 -5
- package/src/tools/bash.ts +113 -26
- package/src/tools/browser/tab-worker.ts +39 -13
- package/src/tools/browser.ts +17 -7
- package/src/tools/cron.ts +10 -1
- package/src/tools/debug.ts +16 -8
- package/src/tools/descriptors.test.ts +44 -0
- package/src/tools/descriptors.ts +50 -6
- package/src/tools/eval.ts +15 -0
- package/src/tools/fetch.ts +93 -4
- package/src/tools/gh.ts +27 -12
- package/src/tools/image-gen.ts +20 -6
- package/src/tools/index.ts +2 -0
- package/src/tools/irc.ts +5 -3
- package/src/tools/monitor.ts +9 -1
- package/src/tools/output-meta.ts +74 -5
- package/src/tools/python.ts +1 -1
- package/src/tools/read.ts +26 -37
- package/src/tools/recipe/runner.ts +23 -12
- package/src/tools/render-utils.ts +2 -2
- package/src/tools/report-issue.ts +184 -0
- package/src/tools/search.ts +3 -2
- package/src/tools/ssh.ts +274 -8
- package/src/tools/subagent-render.ts +114 -12
- package/src/tools/subagent.ts +118 -5
- package/src/tools/tmux-self-injection-guard.ts +3301 -122
- package/src/tools/todo-write.ts +41 -3
- package/src/tools/tool-catalog.generated.ts +32 -4
- package/src/tools/ultragoal-ask-guard.ts +13 -1
- package/src/tools/write.ts +42 -3
- package/src/tui/tree-list.ts +15 -1
- package/src/utils/commit-message-generator.ts +12 -4
- package/src/utils/external-editor.ts +21 -7
- package/src/utils/file-mentions.ts +75 -7
- package/src/utils/git.ts +38 -7
- package/src/utils/image-limits.ts +8 -0
- package/src/utils/image-loading.ts +1 -1
- package/src/utils/open.ts +73 -4
- package/src/utils/pasted-image-loading.ts +9 -8
- package/src/utils/prompt-suggestion.ts +5 -4
- package/src/utils/shell-snapshot.ts +9 -1
- package/src/utils/title-generator.ts +72 -22
- package/src/web/scrapers/index.ts +3 -1
- package/src/web/scrapers/types.ts +8 -0
- package/src/web/scrapers/youtube.ts +3 -2
- package/src/web/search/index.ts +2 -1
- package/src/web/search/providers/codex.ts +29 -32
- package/src/web/search/providers/insane.ts +91 -28
- package/src/web/search/providers/searxng.ts +62 -10
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `report_issue` — local auto-QA for tool behavior.
|
|
3
|
+
*
|
|
4
|
+
* The model records a tool whose output contradicts that tool's own
|
|
5
|
+
* description. Reports are appended to a local SQLite database
|
|
6
|
+
* (`~/.gjc/tool-issues.db`); nothing leaves the machine. Inspect them with the
|
|
7
|
+
* read tool's SQLite support, e.g.
|
|
8
|
+
* `gjc read '~/.gjc/tool-issues.db?q=SELECT tool, count(*) FROM tool_issues GROUP BY tool'`.
|
|
9
|
+
*
|
|
10
|
+
* Inspired by oh-my-pi's `xd://report_issue` auto-QA device; collector push
|
|
11
|
+
* and consent prompts are intentionally not part of this local-only port.
|
|
12
|
+
*/
|
|
13
|
+
import { Database } from "bun:sqlite";
|
|
14
|
+
import * as fs from "node:fs/promises";
|
|
15
|
+
import * as path from "node:path";
|
|
16
|
+
import type { AgentTool, AgentToolResult } from "@gajae-code/agent-core";
|
|
17
|
+
import { getToolIssuesDbPath, logger, prompt, VERSION } from "@gajae-code/utils";
|
|
18
|
+
import * as z from "zod/v4";
|
|
19
|
+
import reportIssueDescription from "../prompts/tools/report-issue.md" with { type: "text" };
|
|
20
|
+
import type { ToolSession } from "./index";
|
|
21
|
+
import { ToolError } from "./tool-errors";
|
|
22
|
+
|
|
23
|
+
/** Upper bound on the stored tool name, in UTF-8 bytes. */
|
|
24
|
+
const MAX_TOOL_BYTES = 128;
|
|
25
|
+
/** Upper bound on the stored report body, in characters. */
|
|
26
|
+
const MAX_REPORT_CHARS = 4_000;
|
|
27
|
+
|
|
28
|
+
const reportIssueSchema = z.object({
|
|
29
|
+
tool: z.string().describe("name of the tool that misbehaved"),
|
|
30
|
+
report: z.string().describe("what was called, what was expected, what happened"),
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
export type ReportIssueToolInput = z.infer<typeof reportIssueSchema>;
|
|
34
|
+
|
|
35
|
+
export interface ReportIssueToolDetails {
|
|
36
|
+
recorded: boolean;
|
|
37
|
+
tool: string;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface ToolIssueEntry {
|
|
41
|
+
tool: string;
|
|
42
|
+
report: string;
|
|
43
|
+
model: string | null;
|
|
44
|
+
sessionId: string | null;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Open the database for a single report. Reports are rare, so no connection is cached: a file the
|
|
49
|
+
* user deleted is recreated on the next report, and no handle keeps the file locked between reports.
|
|
50
|
+
*/
|
|
51
|
+
async function openToolIssueDb(dbPath: string): Promise<Database> {
|
|
52
|
+
// Only a directory created here gets 0700; an existing shared config/data root keeps its mode.
|
|
53
|
+
await fs.mkdir(path.dirname(dbPath), { recursive: true, mode: 0o700 });
|
|
54
|
+
// Create (or tighten) the database file before SQLite opens it. SQLite's unix VFS creates the
|
|
55
|
+
// WAL/SHM sidecars with the main file's permissions, so they are private from their first byte.
|
|
56
|
+
const handle = await fs.open(dbPath, "a", 0o600);
|
|
57
|
+
try {
|
|
58
|
+
if (process.platform !== "win32") await handle.chmod(0o600);
|
|
59
|
+
} finally {
|
|
60
|
+
await handle.close();
|
|
61
|
+
}
|
|
62
|
+
const db = new Database(dbPath);
|
|
63
|
+
try {
|
|
64
|
+
db.run(`
|
|
65
|
+
PRAGMA busy_timeout=5000;
|
|
66
|
+
PRAGMA journal_mode=WAL;
|
|
67
|
+
CREATE TABLE IF NOT EXISTS tool_issues (
|
|
68
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
69
|
+
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
|
|
70
|
+
tool TEXT NOT NULL,
|
|
71
|
+
report TEXT NOT NULL,
|
|
72
|
+
model TEXT,
|
|
73
|
+
gjc_version TEXT NOT NULL,
|
|
74
|
+
session_id TEXT
|
|
75
|
+
);
|
|
76
|
+
CREATE INDEX IF NOT EXISTS idx_tool_issues_tool_created ON tool_issues(tool, created_at);
|
|
77
|
+
`);
|
|
78
|
+
} catch (error) {
|
|
79
|
+
db.close();
|
|
80
|
+
throw error;
|
|
81
|
+
}
|
|
82
|
+
return db;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function truncateUtf8(text: string, maxBytes: number): string {
|
|
86
|
+
const bytes = Buffer.from(text, "utf8");
|
|
87
|
+
if (bytes.length <= maxBytes) return text;
|
|
88
|
+
// Decoding a cut multi-byte sequence yields U+FFFD; drop it.
|
|
89
|
+
return bytes
|
|
90
|
+
.subarray(0, maxBytes)
|
|
91
|
+
.toString("utf8")
|
|
92
|
+
.replace(/\uFFFD+$/u, "");
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Truncate text by complete Unicode code points, avoiding incomplete surrogate pairs.
|
|
97
|
+
* JavaScript strings use UTF-16 encoding, where astral characters use two code units
|
|
98
|
+
* (high surrogate + low surrogate). We must count actual code points, not UTF-16 code units.
|
|
99
|
+
*/
|
|
100
|
+
function truncateByCodePoint(text: string, maxChars: number): string {
|
|
101
|
+
let codePointCount = 0;
|
|
102
|
+
let result = "";
|
|
103
|
+
// Iterate through actual code points using for-of, which handles surrogate pairs correctly
|
|
104
|
+
for (const codePoint of text) {
|
|
105
|
+
if (codePointCount >= maxChars) break;
|
|
106
|
+
result += codePoint;
|
|
107
|
+
codePointCount++;
|
|
108
|
+
}
|
|
109
|
+
return result;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function codePointLength(text: string): number {
|
|
113
|
+
let count = 0;
|
|
114
|
+
for (const _ of text) count++;
|
|
115
|
+
return count;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/** Normalize and clamp a report to the stored field limits. */
|
|
119
|
+
export function normalizeToolIssue(tool: string, report: string): { tool: string; report: string } {
|
|
120
|
+
const normalizedTool = truncateUtf8(tool.trim(), MAX_TOOL_BYTES);
|
|
121
|
+
const trimmedReport = report.trim();
|
|
122
|
+
const normalizedReport =
|
|
123
|
+
codePointLength(trimmedReport) > MAX_REPORT_CHARS
|
|
124
|
+
? `${truncateByCodePoint(trimmedReport, MAX_REPORT_CHARS - 1)}…`
|
|
125
|
+
: trimmedReport;
|
|
126
|
+
return { tool: normalizedTool, report: normalizedReport };
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/** Append one report to the tool-issue database at `dbPath`. */
|
|
130
|
+
export async function recordToolIssue(dbPath: string, entry: ToolIssueEntry): Promise<void> {
|
|
131
|
+
const db = await openToolIssueDb(dbPath);
|
|
132
|
+
try {
|
|
133
|
+
db.run("INSERT INTO tool_issues (tool, report, model, gjc_version, session_id) VALUES (?, ?, ?, ?, ?)", [
|
|
134
|
+
entry.tool,
|
|
135
|
+
entry.report,
|
|
136
|
+
entry.model,
|
|
137
|
+
VERSION,
|
|
138
|
+
entry.sessionId,
|
|
139
|
+
]);
|
|
140
|
+
} finally {
|
|
141
|
+
db.close();
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
export class ReportIssueTool implements AgentTool<typeof reportIssueSchema, ReportIssueToolDetails> {
|
|
146
|
+
readonly name = "report_issue";
|
|
147
|
+
readonly label = "Report Issue";
|
|
148
|
+
readonly loadMode = "essential" as const;
|
|
149
|
+
readonly description = prompt.render(reportIssueDescription);
|
|
150
|
+
readonly parameters = reportIssueSchema;
|
|
151
|
+
readonly strict = true;
|
|
152
|
+
readonly intent = "omit" as const;
|
|
153
|
+
readonly #session: ToolSession;
|
|
154
|
+
readonly #dbPath: string;
|
|
155
|
+
|
|
156
|
+
constructor(session: ToolSession, dbPath: string = getToolIssuesDbPath()) {
|
|
157
|
+
this.#session = session;
|
|
158
|
+
this.#dbPath = dbPath;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
async execute(_toolCallId: string, params: ReportIssueToolInput): Promise<AgentToolResult<ReportIssueToolDetails>> {
|
|
162
|
+
const { tool, report } = normalizeToolIssue(params.tool, params.report);
|
|
163
|
+
if (!tool) throw new ToolError("tool must name the tool that misbehaved.");
|
|
164
|
+
if (!report) throw new ToolError("report must describe the unexpected behavior.");
|
|
165
|
+
try {
|
|
166
|
+
await recordToolIssue(this.#dbPath, {
|
|
167
|
+
tool,
|
|
168
|
+
report,
|
|
169
|
+
model: this.#session.getActiveModelString?.() ?? null,
|
|
170
|
+
sessionId: this.#session.getSessionId?.() ?? null,
|
|
171
|
+
});
|
|
172
|
+
} catch (error) {
|
|
173
|
+
logger.warn("report_issue: failed to record tool issue", { error: String(error), dbPath: this.#dbPath });
|
|
174
|
+
return {
|
|
175
|
+
content: [{ type: "text", text: "Report could not be stored locally; continue the task." }],
|
|
176
|
+
details: { recorded: false, tool },
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
return {
|
|
180
|
+
content: [{ type: "text", text: "Recorded. Continue the task." }],
|
|
181
|
+
details: { recorded: true, tool },
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
}
|
package/src/tools/search.ts
CHANGED
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
openArchive,
|
|
20
20
|
parseArchivePathCandidates,
|
|
21
21
|
} from "./archive-reader";
|
|
22
|
+
import { wrapUntrustedContent } from "./fetch";
|
|
22
23
|
import { createFileRecorder, formatResultPath } from "./file-recorder";
|
|
23
24
|
import { formatGroupedFiles } from "./grouped-file-output";
|
|
24
25
|
import { formatMatchLine } from "./match-line-format";
|
|
@@ -552,7 +553,7 @@ export class SearchTool implements AgentTool<typeof searchSchema, SearchToolDeta
|
|
|
552
553
|
missingPaths: missingPaths.length > 0 ? missingPaths : undefined,
|
|
553
554
|
};
|
|
554
555
|
const text = warningNote ? `No matches found\n${warningNote}` : "No matches found";
|
|
555
|
-
const noMatchResult = toolResult(details).text(text).done();
|
|
556
|
+
const noMatchResult = toolResult(details).text(wrapUntrustedContent(text)).done();
|
|
556
557
|
await cleanupArchiveScratch();
|
|
557
558
|
throwIfSearchCancelled();
|
|
558
559
|
return noMatchResult;
|
|
@@ -676,7 +677,7 @@ export class SearchTool implements AgentTool<typeof searchSchema, SearchToolDeta
|
|
|
676
677
|
if (truncation.truncated) details.truncation = truncation;
|
|
677
678
|
if (linesTruncated) details.linesTruncated = true;
|
|
678
679
|
const resultBuilder = toolResult(details)
|
|
679
|
-
.text(output)
|
|
680
|
+
.text(wrapUntrustedContent(output))
|
|
680
681
|
.limits({ columnMax: linesTruncated ? DEFAULT_MAX_COLUMN : undefined });
|
|
681
682
|
if (truncation.truncated) {
|
|
682
683
|
resultBuilder.truncation(truncation, { direction: "head" });
|
package/src/tools/ssh.ts
CHANGED
|
@@ -5,18 +5,35 @@ import { prompt } from "@gajae-code/utils";
|
|
|
5
5
|
import * as z from "zod/v4";
|
|
6
6
|
import type { SSHHost } from "../capability/ssh";
|
|
7
7
|
import { sshCapability } from "../capability/ssh";
|
|
8
|
+
import { getDefault, type Settings } from "../config/settings";
|
|
8
9
|
import { loadCapability } from "../discovery";
|
|
9
10
|
import type { RenderResultOptions } from "../extensibility/custom-tools/types";
|
|
10
11
|
import type { Theme } from "../modes/theme/theme";
|
|
11
12
|
import sshDescriptionBase from "../prompts/tools/ssh.md" with { type: "text" };
|
|
12
|
-
import {
|
|
13
|
+
import type { SessionArtifactPublication } from "../session/session-manager";
|
|
14
|
+
import {
|
|
15
|
+
DEFAULT_MAX_BYTES,
|
|
16
|
+
streamTailUpdates,
|
|
17
|
+
TailBuffer,
|
|
18
|
+
type TerminalArtifactPublisher,
|
|
19
|
+
type TruncationResult,
|
|
20
|
+
truncateMiddle,
|
|
21
|
+
truncateMiddleWindows,
|
|
22
|
+
truncateTail,
|
|
23
|
+
} from "../session/streaming-output";
|
|
13
24
|
import type { SSHHostInfo } from "../ssh/connection-manager";
|
|
14
25
|
import { ensureHostInfo, getHostInfoForHost } from "../ssh/connection-manager";
|
|
15
|
-
import { executeSSH } from "../ssh/ssh-executor";
|
|
26
|
+
import { executeSSH, type SSHResult } from "../ssh/ssh-executor";
|
|
16
27
|
import { renderStatusLine } from "../tui";
|
|
17
28
|
import { CachedOutputBlock } from "../tui/output-block";
|
|
18
29
|
import type { ToolSession } from ".";
|
|
19
|
-
import {
|
|
30
|
+
import { wrapUntrustedContent } from "./fetch";
|
|
31
|
+
import {
|
|
32
|
+
formatArtifactReference,
|
|
33
|
+
formatStyledTruncationWarning,
|
|
34
|
+
type OutputMeta,
|
|
35
|
+
stripOutputNotice,
|
|
36
|
+
} from "./output-meta";
|
|
20
37
|
import { ToolError } from "./tool-errors";
|
|
21
38
|
import { toolResult } from "./tool-result";
|
|
22
39
|
import { clampTimeout } from "./tool-timeouts";
|
|
@@ -89,6 +106,181 @@ function quoteCmdPath(value: string): string {
|
|
|
89
106
|
return `"${escaped}"`;
|
|
90
107
|
}
|
|
91
108
|
|
|
109
|
+
const FENCE_OPEN = "<untrusted-content>\n";
|
|
110
|
+
const FENCE_CLOSE = "\n</untrusted-content>";
|
|
111
|
+
const UNTRUSTED_FENCE_OVERHEAD = Buffer.byteLength(`${FENCE_OPEN}${FENCE_CLOSE}`, "utf-8");
|
|
112
|
+
const INLINE_MARKER_RESERVE = 256;
|
|
113
|
+
/** `</untrusted-content>` grows when `wrapUntrustedContent` escapes it. */
|
|
114
|
+
const CLOSER_RAW_BYTES = "</untrusted-content>".length;
|
|
115
|
+
const CLOSER_ESCAPED_BYTES = "</untrusted-content>".length;
|
|
116
|
+
|
|
117
|
+
type OutputBudgetPath = "tools.maxInlineResultBytes" | "tools.artifactHeadBytes" | "tools.artifactTailLines";
|
|
118
|
+
|
|
119
|
+
function settingNumber(settings: Settings | undefined, path: OutputBudgetPath): number {
|
|
120
|
+
return settings?.get(path) ?? getDefault(path);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
type RemoteFrame = {
|
|
124
|
+
text: string;
|
|
125
|
+
truncation?: TruncationResult;
|
|
126
|
+
direction?: "head" | "tail" | "middle";
|
|
127
|
+
/** True only after the full remote text is already stored. */
|
|
128
|
+
shrunk: boolean;
|
|
129
|
+
visibleLines: number;
|
|
130
|
+
visibleBytes: number;
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
function inlineCapBytes(settings: Settings | undefined): number {
|
|
134
|
+
return settingNumber(settings, "tools.maxInlineResultBytes") * 1024;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function exceedsInlineCap(output: string, settings: Settings | undefined): boolean {
|
|
138
|
+
const cap = inlineCapBytes(settings);
|
|
139
|
+
if (cap <= 0) return false;
|
|
140
|
+
// Measure the escaped frame. A raw-size check misses closer expansion.
|
|
141
|
+
return Buffer.byteLength(wrapUntrustedContent(output), "utf-8") > cap;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function frameFullRemoteOutput(output: string): RemoteFrame {
|
|
145
|
+
return { text: wrapUntrustedContent(output), shrunk: false, visibleLines: 0, visibleBytes: 0 };
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Cut to the inline window, then frame that excerpt. Call only when the full
|
|
150
|
+
* remote text is already stored, so a tail cut cannot drop the opening fence
|
|
151
|
+
* or the only copy of the output.
|
|
152
|
+
*/
|
|
153
|
+
function frameShrunkRemoteOutput(output: string, settings: Settings | undefined): RemoteFrame {
|
|
154
|
+
const cap = inlineCapBytes(settings);
|
|
155
|
+
const escapedRoom = Math.max(1, cap - UNTRUSTED_FENCE_OVERHEAD - INLINE_MARKER_RESERVE);
|
|
156
|
+
// Budget in remote-output bytes so framing and closer escaping cannot distort
|
|
157
|
+
// the source coordinates reported by truncation metadata.
|
|
158
|
+
let budget = Math.max(1, Math.floor((escapedRoom * CLOSER_RAW_BYTES) / CLOSER_ESCAPED_BYTES));
|
|
159
|
+
const headBytes = settingNumber(settings, "tools.artifactHeadBytes") * 1024;
|
|
160
|
+
const tailLines = settingNumber(settings, "tools.artifactTailLines");
|
|
161
|
+
const headBudget = Math.max(
|
|
162
|
+
1,
|
|
163
|
+
Math.floor((Math.min(headBytes, escapedRoom / 2) * CLOSER_RAW_BYTES) / CLOSER_ESCAPED_BYTES),
|
|
164
|
+
);
|
|
165
|
+
const cutRemoteOutput = (maxBytes: number) => {
|
|
166
|
+
if (headBytes > 0) {
|
|
167
|
+
const options = {
|
|
168
|
+
maxBytes,
|
|
169
|
+
maxLines: tailLines * 2,
|
|
170
|
+
maxHeadBytes: Math.min(headBudget, Math.floor(maxBytes / 2)),
|
|
171
|
+
maxHeadLines: tailLines,
|
|
172
|
+
};
|
|
173
|
+
return {
|
|
174
|
+
truncation: truncateMiddle(output, options),
|
|
175
|
+
windows: truncateMiddleWindows(output, options),
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
const options = { maxBytes, maxLines: tailLines };
|
|
179
|
+
return {
|
|
180
|
+
truncation: truncateTail(output, options),
|
|
181
|
+
windows: truncateMiddleWindows(output, { ...options, maxHeadBytes: 0, maxHeadLines: 0 }),
|
|
182
|
+
};
|
|
183
|
+
};
|
|
184
|
+
let { truncation, windows } = cutRemoteOutput(budget);
|
|
185
|
+
let excerpt = truncation.truncated ? truncation.content : output;
|
|
186
|
+
let text = wrapUntrustedContent(excerpt);
|
|
187
|
+
let textBytes = Buffer.byteLength(text, "utf-8");
|
|
188
|
+
while (textBytes > cap && budget > 1) {
|
|
189
|
+
const scaledBudget = Math.floor((budget * cap) / textBytes);
|
|
190
|
+
budget = Math.max(1, Math.min(budget - 1, scaledBudget));
|
|
191
|
+
({ truncation, windows } = cutRemoteOutput(budget));
|
|
192
|
+
excerpt = truncation.truncated ? truncation.content : output;
|
|
193
|
+
text = wrapUntrustedContent(excerpt);
|
|
194
|
+
textBytes = Buffer.byteLength(text, "utf-8");
|
|
195
|
+
}
|
|
196
|
+
if (text === wrapUntrustedContent(output)) return frameFullRemoteOutput(output);
|
|
197
|
+
if (windows.kind === "full") return frameFullRemoteOutput(output);
|
|
198
|
+
const direction = windows.kind === "middle" ? "middle" : windows.kind === "head-only" ? "head" : "tail";
|
|
199
|
+
return {
|
|
200
|
+
text,
|
|
201
|
+
truncation,
|
|
202
|
+
direction,
|
|
203
|
+
shrunk: true,
|
|
204
|
+
visibleLines: (windows.head?.lines ?? 0) + (windows.tail?.lines ?? 0),
|
|
205
|
+
visibleBytes: (windows.head?.bytes ?? 0) + (windows.tail?.bytes ?? 0),
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/** Store the full remote text. Never write an already-shortened excerpt.
|
|
210
|
+
* The command has already finished, so a rejected reservation must not discard it.
|
|
211
|
+
*/
|
|
212
|
+
async function storeFullRemoteOutput(
|
|
213
|
+
session: ToolSession,
|
|
214
|
+
output: string,
|
|
215
|
+
artifactPublication: SessionArtifactPublication | undefined,
|
|
216
|
+
): Promise<string | undefined> {
|
|
217
|
+
let allocated: { id?: string; path?: string } | undefined;
|
|
218
|
+
try {
|
|
219
|
+
allocated = await session.allocateOutputArtifact?.("ssh");
|
|
220
|
+
} catch {
|
|
221
|
+
allocated = undefined;
|
|
222
|
+
}
|
|
223
|
+
if (allocated?.path && allocated.id) {
|
|
224
|
+
try {
|
|
225
|
+
await Bun.write(allocated.path, output);
|
|
226
|
+
return allocated.id;
|
|
227
|
+
} catch {
|
|
228
|
+
// Fall through to the session-owned publisher when a writable path failed.
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
if (!artifactPublication) return undefined;
|
|
232
|
+
try {
|
|
233
|
+
return await artifactPublication(output, "ssh");
|
|
234
|
+
} catch {
|
|
235
|
+
return undefined;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
function createSshArtifactPublisher(
|
|
240
|
+
artifactPublication: SessionArtifactPublication | undefined,
|
|
241
|
+
): TerminalArtifactPublisher | undefined {
|
|
242
|
+
if (!artifactPublication) return undefined;
|
|
243
|
+
return async (content, info) => {
|
|
244
|
+
try {
|
|
245
|
+
const artifactId = await artifactPublication(content, "ssh");
|
|
246
|
+
if (!artifactId) return { status: "unavailable" };
|
|
247
|
+
return info.omittedBytes > 0
|
|
248
|
+
? { status: "published", artifactId, omittedBytes: info.omittedBytes }
|
|
249
|
+
: { status: "published", artifactId };
|
|
250
|
+
} catch (error) {
|
|
251
|
+
return { status: "failed", diagnostic: error instanceof Error ? error.message : String(error) };
|
|
252
|
+
}
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
function hasCompleteRemoteOutput(result: SSHResult, output: string): boolean {
|
|
257
|
+
return (
|
|
258
|
+
!result.truncated &&
|
|
259
|
+
result.outputBytes === result.totalBytes &&
|
|
260
|
+
Buffer.byteLength(output, "utf-8") === result.outputBytes
|
|
261
|
+
);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
function incompleteOutputNotice(result: SSHResult, artifactId: string | undefined): string | undefined {
|
|
265
|
+
const retainedWindowIncomplete = result.truncated || result.outputBytes < result.totalBytes;
|
|
266
|
+
if (!retainedWindowIncomplete) return undefined;
|
|
267
|
+
|
|
268
|
+
const artifactIncomplete =
|
|
269
|
+
(result.artifactTruncatedBytes ?? 0) > 0 || result.artifactFailureDiagnostic !== undefined;
|
|
270
|
+
const completeArtifactAvailable = artifactId !== undefined && !artifactIncomplete;
|
|
271
|
+
if (completeArtifactAvailable) return undefined;
|
|
272
|
+
|
|
273
|
+
const notes = [`SSH output is incomplete: retained ${result.outputBytes} of ${result.totalBytes} bytes`];
|
|
274
|
+
notes.push("no complete output artifact is available");
|
|
275
|
+
if ((result.artifactTruncatedBytes ?? 0) > 0) {
|
|
276
|
+
notes.push(`artifact storage omitted at least ${result.artifactTruncatedBytes} bytes`);
|
|
277
|
+
}
|
|
278
|
+
if (result.artifactFailureDiagnostic) {
|
|
279
|
+
notes.push(`Artifact storage failed: ${result.artifactFailureDiagnostic}`);
|
|
280
|
+
}
|
|
281
|
+
return notes.join("; ");
|
|
282
|
+
}
|
|
283
|
+
|
|
92
284
|
function buildRemoteCommand(command: string, cwd: string | undefined, info: SSHHostInfo): string {
|
|
93
285
|
if (!cwd) return command;
|
|
94
286
|
|
|
@@ -161,6 +353,8 @@ export class SshTool implements AgentTool<typeof sshSchema, SSHToolDetails> {
|
|
|
161
353
|
// Clamp to reasonable range: 1s - 3600s (1 hour)
|
|
162
354
|
const timeoutSec = clampTimeout("ssh", rawTimeout);
|
|
163
355
|
const timeoutMs = timeoutSec * 1000;
|
|
356
|
+
const outputSettings = _ctx?.settings ?? this.session.settings;
|
|
357
|
+
const artifactPublication = this.session.captureArtifactPublication?.();
|
|
164
358
|
|
|
165
359
|
const tailBuffer = new TailBuffer(DEFAULT_MAX_BYTES);
|
|
166
360
|
const { path: artifactPath, id: artifactId } = (await this.session.allocateOutputArtifact?.("ssh")) ?? {};
|
|
@@ -172,22 +366,94 @@ export class SshTool implements AgentTool<typeof sshSchema, SSHToolDetails> {
|
|
|
172
366
|
compatEnabled: hostInfo.compatEnabled,
|
|
173
367
|
artifactPath,
|
|
174
368
|
artifactId,
|
|
369
|
+
artifactPublisher: createSshArtifactPublisher(artifactPublication),
|
|
175
370
|
onChunk: streamTailUpdates(tailBuffer, onUpdate),
|
|
176
371
|
});
|
|
177
372
|
|
|
373
|
+
const remote = result.output ?? "";
|
|
374
|
+
let storedArtifactId = result.artifactId;
|
|
375
|
+
let framed: RemoteFrame | undefined;
|
|
376
|
+
if (!remote) {
|
|
377
|
+
framed = undefined;
|
|
378
|
+
} else if (!exceedsInlineCap(remote, outputSettings)) {
|
|
379
|
+
framed = frameFullRemoteOutput(remote);
|
|
380
|
+
} else {
|
|
381
|
+
if (!storedArtifactId && hasCompleteRemoteOutput(result, remote)) {
|
|
382
|
+
storedArtifactId = await storeFullRemoteOutput(this.session, remote, artifactPublication);
|
|
383
|
+
}
|
|
384
|
+
framed = storedArtifactId ? frameShrunkRemoteOutput(remote, outputSettings) : frameFullRemoteOutput(remote);
|
|
385
|
+
}
|
|
386
|
+
// The artifact id stays in truncation metadata. A line after the closing
|
|
387
|
+
// fence makes the inline backstop drop the opening fence.
|
|
388
|
+
const modelText = framed?.text;
|
|
389
|
+
const storedNote =
|
|
390
|
+
(framed?.shrunk || result.truncated) && storedArtifactId
|
|
391
|
+
? `\n\n${formatArtifactReference(storedArtifactId, result.artifactTruncatedBytes)}`
|
|
392
|
+
: "";
|
|
393
|
+
const incompleteNote = incompleteOutputNotice(result, storedArtifactId);
|
|
394
|
+
const artifactFailureNote =
|
|
395
|
+
result.artifactFailureDiagnostic && !incompleteNote
|
|
396
|
+
? `SSH streaming artifact storage failed: ${result.artifactFailureDiagnostic}`
|
|
397
|
+
: undefined;
|
|
178
398
|
if (result.cancelled) {
|
|
179
|
-
|
|
399
|
+
const message = [
|
|
400
|
+
modelText || "Command aborted",
|
|
401
|
+
storedNote.trim() || undefined,
|
|
402
|
+
incompleteNote,
|
|
403
|
+
artifactFailureNote,
|
|
404
|
+
]
|
|
405
|
+
.filter((part): part is string => part !== undefined)
|
|
406
|
+
.join("\n\n");
|
|
407
|
+
throw new ToolError(message);
|
|
180
408
|
}
|
|
181
409
|
|
|
182
|
-
const outputText =
|
|
410
|
+
const outputText = modelText ?? "(no output)";
|
|
183
411
|
const details: SSHToolDetails = {};
|
|
184
|
-
const resultBuilder = toolResult(details).text(outputText)
|
|
412
|
+
const resultBuilder = toolResult(details).text(outputText);
|
|
413
|
+
if (framed?.shrunk && framed.truncation && storedArtifactId) {
|
|
414
|
+
resultBuilder.truncation(framed.truncation, {
|
|
415
|
+
direction: framed.direction ?? "tail",
|
|
416
|
+
artifactId: storedArtifactId,
|
|
417
|
+
});
|
|
418
|
+
} else {
|
|
419
|
+
resultBuilder.truncationFromSummary(result, { direction: "tail" });
|
|
420
|
+
}
|
|
185
421
|
|
|
186
422
|
if (result.exitCode !== 0 && result.exitCode !== undefined) {
|
|
187
|
-
|
|
423
|
+
const message = [
|
|
424
|
+
outputText,
|
|
425
|
+
storedNote.trim() || undefined,
|
|
426
|
+
incompleteNote,
|
|
427
|
+
artifactFailureNote,
|
|
428
|
+
`Command exited with code ${result.exitCode}`,
|
|
429
|
+
]
|
|
430
|
+
.filter((part): part is string => part !== undefined)
|
|
431
|
+
.join("\n\n");
|
|
432
|
+
throw new ToolError(message);
|
|
188
433
|
}
|
|
189
434
|
|
|
190
|
-
|
|
435
|
+
const built = resultBuilder.done();
|
|
436
|
+
const truncation = built.details?.meta?.truncation;
|
|
437
|
+
if (framed?.shrunk && truncation) {
|
|
438
|
+
// The cut is a window of an earlier excerpt. Keep the stream totals
|
|
439
|
+
// and do not present that window's coordinates as the original range.
|
|
440
|
+
truncation.totalLines = result.totalLines;
|
|
441
|
+
truncation.totalBytes = result.totalBytes;
|
|
442
|
+
truncation.outputLines = framed.visibleLines;
|
|
443
|
+
truncation.outputBytes = framed.visibleBytes;
|
|
444
|
+
truncation.artifactId = storedArtifactId;
|
|
445
|
+
truncation.artifactTruncatedBytes = result.artifactTruncatedBytes;
|
|
446
|
+
truncation.artifactFailureDiagnostic = result.artifactFailureDiagnostic;
|
|
447
|
+
delete truncation.shownRange;
|
|
448
|
+
delete truncation.headRange;
|
|
449
|
+
delete truncation.tailRange;
|
|
450
|
+
delete truncation.nextOffset;
|
|
451
|
+
if (framed.direction === "middle") {
|
|
452
|
+
truncation.elidedLines = Math.max(0, result.totalLines - framed.visibleLines);
|
|
453
|
+
truncation.elidedBytes = Math.max(0, result.totalBytes - framed.visibleBytes);
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
return built;
|
|
191
457
|
}
|
|
192
458
|
}
|
|
193
459
|
|