@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
|
@@ -3,7 +3,6 @@ import * as os from "node:os";
|
|
|
3
3
|
import * as path from "node:path";
|
|
4
4
|
|
|
5
5
|
import { untilAborted } from "@gajae-code/utils/abortable";
|
|
6
|
-
import { Snowflake } from "@gajae-code/utils/snowflake";
|
|
7
6
|
import type { HTMLElement } from "linkedom";
|
|
8
7
|
import type {
|
|
9
8
|
Browser,
|
|
@@ -414,6 +413,31 @@ interface ActiveRun {
|
|
|
414
413
|
pendingTools: Map<string, { resolve(value: unknown): void; reject(error: Error): void }>;
|
|
415
414
|
}
|
|
416
415
|
|
|
416
|
+
async function writePrivateScreenshot(bytes: Uint8Array): Promise<string> {
|
|
417
|
+
// Random directory plus an exclusive create: a preexisting symlink under the
|
|
418
|
+
// shared temp directory is not opened, and the screenshot is not left world-readable.
|
|
419
|
+
// Create the temp root first: base created it with a recursive mkdir, and
|
|
420
|
+
// mkdtemp fails when that root does not exist yet. Do not change the root's mode.
|
|
421
|
+
const root = os.tmpdir();
|
|
422
|
+
await fs.promises.mkdir(root, { recursive: true });
|
|
423
|
+
const tempDir = await fs.promises.mkdtemp(path.join(root, "gjc-sshots-"));
|
|
424
|
+
try {
|
|
425
|
+
if (process.platform !== "win32") await fs.promises.chmod(tempDir, 0o700);
|
|
426
|
+
const dest = path.join(tempDir, "screenshot.png");
|
|
427
|
+
const handle = await fs.promises.open(dest, "wx", 0o600);
|
|
428
|
+
try {
|
|
429
|
+
await handle.writeFile(bytes);
|
|
430
|
+
} finally {
|
|
431
|
+
await handle.close();
|
|
432
|
+
}
|
|
433
|
+
if (process.platform !== "win32") await fs.promises.chmod(dest, 0o600);
|
|
434
|
+
return dest;
|
|
435
|
+
} catch (error) {
|
|
436
|
+
await fs.promises.rm(tempDir, { recursive: true, force: true });
|
|
437
|
+
throw error;
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
|
|
417
441
|
export class WorkerCore {
|
|
418
442
|
#transport: Transport;
|
|
419
443
|
#browser?: Browser;
|
|
@@ -852,20 +876,22 @@ export class WorkerCore {
|
|
|
852
876
|
{ maxWidth: 1024, maxHeight: 1024, maxBytes: 150 * 1024, jpegQuality: 70 },
|
|
853
877
|
);
|
|
854
878
|
const explicitPath = opts.save ? resolveTabWorkerPath(opts.save, session.cwd) : undefined;
|
|
855
|
-
|
|
856
|
-
const
|
|
857
|
-
explicitPath ??
|
|
858
|
-
(session.browserScreenshotDir
|
|
859
|
-
? path.join(
|
|
860
|
-
session.browserScreenshotDir,
|
|
861
|
-
`screenshot-${new Date().toISOString().replace(/[:.]/g, "-").slice(0, -1)}.png`,
|
|
862
|
-
)
|
|
863
|
-
: path.join(os.tmpdir(), `gjc-sshots-${Snowflake.next()}.png`));
|
|
864
|
-
await fs.promises.mkdir(path.dirname(dest), { recursive: true });
|
|
865
|
-
const saveFullRes = !!(explicitPath || session.browserScreenshotDir);
|
|
879
|
+
const configuredDir = session.browserScreenshotDir;
|
|
880
|
+
const saveFullRes = !!(explicitPath || configuredDir);
|
|
866
881
|
const savedBuffer = saveFullRes ? buffer : resized.buffer;
|
|
867
882
|
const savedMimeType = saveFullRes ? "image/png" : resized.mimeType;
|
|
868
|
-
|
|
883
|
+
// Caller-chosen destinations stay as they were. Only the shared temp
|
|
884
|
+
// fallback is an exclusive private file: a preexisting symlink at the old
|
|
885
|
+
// predictable name is not opened, and the bytes are not left world-readable.
|
|
886
|
+
const dest = explicitPath
|
|
887
|
+
? explicitPath
|
|
888
|
+
: configuredDir
|
|
889
|
+
? path.join(configuredDir, `screenshot-${new Date().toISOString().replace(/[:.]/g, "-").slice(0, -1)}.png`)
|
|
890
|
+
: await writePrivateScreenshot(savedBuffer);
|
|
891
|
+
if (explicitPath || configuredDir) {
|
|
892
|
+
await fs.promises.mkdir(path.dirname(dest), { recursive: true });
|
|
893
|
+
await Bun.write(dest, savedBuffer);
|
|
894
|
+
}
|
|
869
895
|
const info: ScreenshotResult = {
|
|
870
896
|
dest,
|
|
871
897
|
mimeType: savedMimeType,
|
package/src/tools/browser.ts
CHANGED
|
@@ -10,8 +10,9 @@ import { assertLoopbackCdpUrl } from "./browser/cdp-url";
|
|
|
10
10
|
import { isChromeProfileExecutableForLaunch, isEdgeExecutable, resolveSystemChromeForProfile } from "./browser/launch";
|
|
11
11
|
import { chromeUserDataRoots, type DiscoveryEnv, defaultDiscoveryEnv } from "./browser/profile-discovery";
|
|
12
12
|
import { acquireBrowser, type BrowserHandle, type BrowserKind, type BrowserKindTag } from "./browser/registry";
|
|
13
|
-
import type { Observation, ScreenshotResult } from "./browser/tab-protocol";
|
|
13
|
+
import type { Observation, RunResultOk, ScreenshotResult } from "./browser/tab-protocol";
|
|
14
14
|
import { acquireTab, dropHeadlessTabs, getTab, releaseAllTabs, releaseTab, runInTab } from "./browser/tab-supervisor";
|
|
15
|
+
import { wrapUntrustedContent } from "./fetch";
|
|
15
16
|
import type { OutputMeta } from "./output-meta";
|
|
16
17
|
import { resolveToCwd } from "./path-utils";
|
|
17
18
|
import { ToolAbortError, ToolError, throwIfAborted } from "./tool-errors";
|
|
@@ -392,8 +393,8 @@ export class BrowserTool implements AgentTool<typeof browserSchema, BrowserToolD
|
|
|
392
393
|
const verb = result.created ? "Opened" : "Reused";
|
|
393
394
|
const lines = [
|
|
394
395
|
`${verb} tab ${JSON.stringify(name)} on ${describeBrowser(browser)}`,
|
|
395
|
-
`URL: ${url}`,
|
|
396
|
-
title ? `Title: ${title}` : null,
|
|
396
|
+
`URL: ${frameBrowserPageText(url)}`,
|
|
397
|
+
title ? `Title: ${frameBrowserPageText(title)}` : null,
|
|
397
398
|
].filter((l): l is string => typeof l === "string");
|
|
398
399
|
details.result = lines.join("\n");
|
|
399
400
|
return toolResult(details).text(lines.join("\n")).done();
|
|
@@ -446,9 +447,9 @@ export class BrowserTool implements AgentTool<typeof browserSchema, BrowserToolD
|
|
|
446
447
|
|
|
447
448
|
if (screenshots.length) details.screenshots = screenshots;
|
|
448
449
|
|
|
449
|
-
const content =
|
|
450
|
+
const content = frameBrowserPageDisplays(displays);
|
|
450
451
|
if (returnValue !== undefined) {
|
|
451
|
-
content.push({ type: "text", text: stringifyReturnValue(returnValue) });
|
|
452
|
+
content.push({ type: "text", text: frameBrowserPageText(stringifyReturnValue(returnValue)) });
|
|
452
453
|
}
|
|
453
454
|
if (!content.length) {
|
|
454
455
|
content.push({ type: "text", text: `Ran code on tab ${JSON.stringify(name)}` });
|
|
@@ -496,9 +497,9 @@ export class BrowserTool implements AgentTool<typeof browserSchema, BrowserToolD
|
|
|
496
497
|
});
|
|
497
498
|
|
|
498
499
|
if (screenshots.length) details.screenshots = screenshots;
|
|
499
|
-
const content =
|
|
500
|
+
const content = frameBrowserPageDisplays(displays);
|
|
500
501
|
if (returnValue !== undefined) {
|
|
501
|
-
content.push({ type: "text", text: stringifyReturnValue(returnValue) });
|
|
502
|
+
content.push({ type: "text", text: frameBrowserPageText(stringifyReturnValue(returnValue)) });
|
|
502
503
|
}
|
|
503
504
|
if (!content.length) {
|
|
504
505
|
content.push({ type: "text", text: `Ran ${steps.length} action(s) on tab ${JSON.stringify(name)}` });
|
|
@@ -549,6 +550,15 @@ function sameBrowserKind(a: BrowserKind, b: BrowserKind): boolean {
|
|
|
549
550
|
return false;
|
|
550
551
|
}
|
|
551
552
|
|
|
553
|
+
/** Page URL, title, extract/observe text, and script return values are external content. */
|
|
554
|
+
function frameBrowserPageText(text: string): string {
|
|
555
|
+
return wrapUntrustedContent(text);
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
function frameBrowserPageDisplays(displays: RunResultOk["displays"]): RunResultOk["displays"] {
|
|
559
|
+
return displays.map(block => (block.type === "text" ? { ...block, text: frameBrowserPageText(block.text) } : block));
|
|
560
|
+
}
|
|
561
|
+
|
|
552
562
|
/** Max chars of a browser return value surfaced into the tool result (F22). */
|
|
553
563
|
const MAX_BROWSER_RETURN_CHARS = 256 * 1024;
|
|
554
564
|
|
package/src/tools/cron.ts
CHANGED
|
@@ -443,7 +443,16 @@ function toCronListJobDetails(snapshot: CronJobSnapshot): CronListJobDetails {
|
|
|
443
443
|
}
|
|
444
444
|
|
|
445
445
|
function formatCronFireContent(snapshot: CronJobSnapshot): string {
|
|
446
|
-
|
|
446
|
+
// A fired cron prompt is an instruction to execute, not a notification to
|
|
447
|
+
// acknowledge. Wrapping it in a notification-only envelope made models
|
|
448
|
+
// restate the prompt and end the turn without calling a tool.
|
|
449
|
+
return [
|
|
450
|
+
"<system-reminder>",
|
|
451
|
+
`Scheduled task ${snapshot.id} fired (${snapshot.humanSchedule}). The text below is this turn's task instruction: execute it now, using tools as needed. Do not merely restate, summarize, or acknowledge it.`,
|
|
452
|
+
"</system-reminder>",
|
|
453
|
+
"",
|
|
454
|
+
snapshot.prompt,
|
|
455
|
+
].join("\n");
|
|
447
456
|
}
|
|
448
457
|
|
|
449
458
|
function deliverCronFire(record: CronScheduleRecord): Promise<void> | undefined {
|
package/src/tools/debug.ts
CHANGED
|
@@ -466,8 +466,8 @@ function buildOutcomeText(outcome: DapContinueOutcome, timeoutSec: number, verb:
|
|
|
466
466
|
return lines.join("\n");
|
|
467
467
|
}
|
|
468
468
|
|
|
469
|
-
function getConfiguredAdapters(cwd: string): string {
|
|
470
|
-
const adapters = getAvailableAdapters(cwd).map(adapter => adapter.name);
|
|
469
|
+
function getConfiguredAdapters(cwd: string, trustCwds?: readonly string[]): string {
|
|
470
|
+
const adapters = getAvailableAdapters(cwd, trustCwds).map(adapter => adapter.name);
|
|
471
471
|
return adapters.length > 0 ? adapters.join(", ") : "none";
|
|
472
472
|
}
|
|
473
473
|
async function validateLaunchProgram(program: string, cwd: string): Promise<void> {
|
|
@@ -637,6 +637,7 @@ export class DebugTool implements AgentTool<typeof debugSchema, DebugToolDetails
|
|
|
637
637
|
const combinedSignal = signal ? AbortSignal.any([signal, timeoutSignal]) : timeoutSignal;
|
|
638
638
|
const details: DebugToolDetails = { action: params.action, success: true };
|
|
639
639
|
const result = toolResult(details);
|
|
640
|
+
const trustCwds = [this.session.cwd];
|
|
640
641
|
switch (params.action) {
|
|
641
642
|
case "launch": {
|
|
642
643
|
if (!params.program) {
|
|
@@ -645,14 +646,14 @@ export class DebugTool implements AgentTool<typeof debugSchema, DebugToolDetails
|
|
|
645
646
|
const commandCwd = params.cwd ? resolveToCwd(params.cwd, this.session.cwd) : this.session.cwd;
|
|
646
647
|
const program = resolveToCwd(params.program, commandCwd);
|
|
647
648
|
await validateLaunchProgram(program, commandCwd);
|
|
648
|
-
const adapter = selectLaunchAdapter(program, commandCwd, params.adapter);
|
|
649
|
+
const adapter = selectLaunchAdapter(program, commandCwd, params.adapter, trustCwds);
|
|
649
650
|
if (!adapter) {
|
|
650
651
|
throw new ToolError(
|
|
651
|
-
`No debugger adapter available. Installed adapters: ${getConfiguredAdapters(commandCwd)}`,
|
|
652
|
+
`No debugger adapter available. Installed adapters: ${getConfiguredAdapters(commandCwd, trustCwds)}`,
|
|
652
653
|
);
|
|
653
654
|
}
|
|
654
655
|
const snapshot = await dapSessionManager.launch(
|
|
655
|
-
{ adapter, program, args: params.args, cwd: commandCwd },
|
|
656
|
+
{ adapter, program, args: params.args, cwd: commandCwd, workspace: this.session.cwd },
|
|
656
657
|
combinedSignal,
|
|
657
658
|
timeoutSec * 1000,
|
|
658
659
|
);
|
|
@@ -665,14 +666,21 @@ export class DebugTool implements AgentTool<typeof debugSchema, DebugToolDetails
|
|
|
665
666
|
throw new ToolError("attach requires pid or port");
|
|
666
667
|
}
|
|
667
668
|
const commandCwd = params.cwd ? resolveToCwd(params.cwd, this.session.cwd) : this.session.cwd;
|
|
668
|
-
const adapter = selectAttachAdapter(commandCwd, params.adapter, params.port);
|
|
669
|
+
const adapter = selectAttachAdapter(commandCwd, params.adapter, params.port, trustCwds);
|
|
669
670
|
if (!adapter) {
|
|
670
671
|
throw new ToolError(
|
|
671
|
-
`No debugger adapter available. Installed adapters: ${getConfiguredAdapters(commandCwd)}`,
|
|
672
|
+
`No debugger adapter available. Installed adapters: ${getConfiguredAdapters(commandCwd, trustCwds)}`,
|
|
672
673
|
);
|
|
673
674
|
}
|
|
674
675
|
const snapshot = await dapSessionManager.attach(
|
|
675
|
-
{
|
|
676
|
+
{
|
|
677
|
+
adapter,
|
|
678
|
+
cwd: commandCwd,
|
|
679
|
+
workspace: this.session.cwd,
|
|
680
|
+
pid: params.pid,
|
|
681
|
+
port: params.port,
|
|
682
|
+
host: params.host,
|
|
683
|
+
},
|
|
676
684
|
combinedSignal,
|
|
677
685
|
timeoutSec * 1000,
|
|
678
686
|
);
|
|
@@ -35,6 +35,7 @@ function makeSession(overrides: Record<string, unknown> = {}): any {
|
|
|
35
35
|
"astGrep.enabled": true,
|
|
36
36
|
"astEdit.enabled": true,
|
|
37
37
|
"renderMermaid.enabled": true,
|
|
38
|
+
"reportIssue.enabled": true,
|
|
38
39
|
"web_search.enabled": true,
|
|
39
40
|
"calc.enabled": true,
|
|
40
41
|
"skill.enabled": true,
|
|
@@ -128,6 +129,7 @@ describe("tool descriptor compatibility gate", () => {
|
|
|
128
129
|
"skill",
|
|
129
130
|
"goal",
|
|
130
131
|
"move_session",
|
|
132
|
+
"report_issue",
|
|
131
133
|
];
|
|
132
134
|
expect(Object.keys(BUILTIN_TOOLS)).toEqual(expectedBuiltin);
|
|
133
135
|
expect(Object.keys(BUILTIN_TOOL_DESCRIPTORS)).toEqual(expectedBuiltin);
|
|
@@ -291,6 +293,7 @@ describe("tool descriptor compatibility gate", () => {
|
|
|
291
293
|
"astGrep.enabled": false,
|
|
292
294
|
"astEdit.enabled": false,
|
|
293
295
|
"renderMermaid.enabled": false,
|
|
296
|
+
"reportIssue.enabled": false,
|
|
294
297
|
"web_search.enabled": false,
|
|
295
298
|
"calc.enabled": false,
|
|
296
299
|
"skill.enabled": false,
|
|
@@ -313,6 +316,7 @@ describe("tool descriptor compatibility gate", () => {
|
|
|
313
316
|
"ast_grep",
|
|
314
317
|
"ast_edit",
|
|
315
318
|
"render_mermaid",
|
|
319
|
+
"report_issue",
|
|
316
320
|
"web_search",
|
|
317
321
|
"calc",
|
|
318
322
|
"skill",
|
|
@@ -961,6 +965,46 @@ describe("tool descriptor compatibility gate", () => {
|
|
|
961
965
|
expect((normalizedBisect.parameters as any).properties?._i).toBeUndefined();
|
|
962
966
|
expect((normalizedWrite.parameters as any).properties?._i).toBeDefined();
|
|
963
967
|
});
|
|
968
|
+
for (const staleOutcome of ["resolved", "rejected"] as const) {
|
|
969
|
+
test(`session move invalidation keeps a newer load when the old load ${staleOutcome}`, async () => {
|
|
970
|
+
const first = Promise.withResolvers<AgentTool>();
|
|
971
|
+
const second = Promise.withResolvers<AgentTool>();
|
|
972
|
+
const oldTool = syntheticTool();
|
|
973
|
+
const newTool = syntheticTool();
|
|
974
|
+
let loads = 0;
|
|
975
|
+
const session = makeSession();
|
|
976
|
+
const descriptor: ToolDescriptor = {
|
|
977
|
+
metadata: { name: "synthetic" },
|
|
978
|
+
presentation: { label: "Synthetic" },
|
|
979
|
+
isAvailable: () => true,
|
|
980
|
+
load: () => (++loads === 1 ? first.promise : second.promise),
|
|
981
|
+
};
|
|
982
|
+
const lazy = new LazyAgentTool(descriptor, undefined, () => descriptor.load(session), session);
|
|
983
|
+
const oldLoad = lazy.materialize().then(
|
|
984
|
+
tool => ({ tool, error: undefined }),
|
|
985
|
+
error => ({ tool: undefined, error }),
|
|
986
|
+
);
|
|
987
|
+
await Promise.resolve();
|
|
988
|
+
expect(loads).toBe(1);
|
|
989
|
+
lazy.invalidateForSessionMove();
|
|
990
|
+
const newLoad = lazy.materialize();
|
|
991
|
+
await Promise.resolve();
|
|
992
|
+
expect(loads).toBe(2);
|
|
993
|
+
if (staleOutcome === "resolved") first.resolve(oldTool);
|
|
994
|
+
else first.reject(new Error("old context load failed"));
|
|
995
|
+
const oldResult = await oldLoad;
|
|
996
|
+
if (staleOutcome === "resolved") expect(oldResult.tool).toBe(oldTool);
|
|
997
|
+
else expect(String(oldResult.error)).toContain("old context load failed");
|
|
998
|
+
const joinedLoad = lazy.materialize();
|
|
999
|
+
await Promise.resolve();
|
|
1000
|
+
expect(loads).toBe(2);
|
|
1001
|
+
second.resolve(newTool);
|
|
1002
|
+
expect(await newLoad).toBe(newTool);
|
|
1003
|
+
expect(await joinedLoad).toBe(newTool);
|
|
1004
|
+
expect(await lazy.materialize()).toBe(newTool);
|
|
1005
|
+
});
|
|
1006
|
+
}
|
|
1007
|
+
|
|
964
1008
|
test("concurrent lazy first use shares one load and cleanup registration", async () => {
|
|
965
1009
|
let loads = 0;
|
|
966
1010
|
let cleanupRegistrations = 0;
|
package/src/tools/descriptors.ts
CHANGED
|
@@ -84,6 +84,9 @@ export class LazyAgentTool implements AgentTool<any, any, any> {
|
|
|
84
84
|
#loader?: () => ToolDescriptorLoadResult;
|
|
85
85
|
#session?: ToolSession;
|
|
86
86
|
#loadPromise?: Promise<AgentTool<any, any, any>>;
|
|
87
|
+
#loadGeneration = 0;
|
|
88
|
+
#loadToken?: object;
|
|
89
|
+
#expectedCwd?: string;
|
|
87
90
|
|
|
88
91
|
constructor(
|
|
89
92
|
descriptor: ToolDescriptor,
|
|
@@ -97,9 +100,31 @@ export class LazyAgentTool implements AgentTool<any, any, any> {
|
|
|
97
100
|
this.#session = session;
|
|
98
101
|
}
|
|
99
102
|
|
|
103
|
+
/** Clear a session-bound materialization after its owning session moves. */
|
|
104
|
+
invalidateForSessionMove(expectedCwd?: string): void {
|
|
105
|
+
this.#loadGeneration++;
|
|
106
|
+
this.#tool = undefined;
|
|
107
|
+
this.#loadPromise = undefined;
|
|
108
|
+
this.#loadToken = undefined;
|
|
109
|
+
this.#expectedCwd = expectedCwd;
|
|
110
|
+
if (!this.#loader && this.#session) {
|
|
111
|
+
const session = this.#session;
|
|
112
|
+
this.#loader = () => this.descriptor.load(session);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
100
116
|
async #get(): Promise<AgentTool<any, any, any>> {
|
|
117
|
+
const sessionCwd = this.#session?.cwd;
|
|
118
|
+
if (this.#expectedCwd !== undefined && sessionCwd !== this.#expectedCwd) {
|
|
119
|
+
throw new ToolError(
|
|
120
|
+
`Tool "${this.descriptor.metadata.name}" session cwd changed outside a committed move (expected ${this.#expectedCwd}, got ${sessionCwd ?? "unknown"})`,
|
|
121
|
+
);
|
|
122
|
+
}
|
|
101
123
|
if (this.#tool) return this.#tool;
|
|
102
124
|
if (!this.#loadPromise) {
|
|
125
|
+
const generation = this.#loadGeneration;
|
|
126
|
+
const token = {};
|
|
127
|
+
this.#loadToken = token;
|
|
103
128
|
const load = this.#loader;
|
|
104
129
|
this.#loadPromise = Promise.resolve()
|
|
105
130
|
.then(() => {
|
|
@@ -108,11 +133,14 @@ export class LazyAgentTool implements AgentTool<any, any, any> {
|
|
|
108
133
|
})
|
|
109
134
|
.then(tool => {
|
|
110
135
|
if (!tool) throw new ToolError(`Tool "${this.descriptor.metadata.name}" failed to load`);
|
|
111
|
-
this.#tool = tool;
|
|
136
|
+
if (generation === this.#loadGeneration && this.#loadToken === token) this.#tool = tool;
|
|
112
137
|
return tool;
|
|
113
138
|
})
|
|
114
139
|
.catch(error => {
|
|
115
|
-
this.#
|
|
140
|
+
if (generation === this.#loadGeneration && this.#loadToken === token) {
|
|
141
|
+
this.#loadPromise = undefined;
|
|
142
|
+
this.#loadToken = undefined;
|
|
143
|
+
}
|
|
116
144
|
if (error instanceof ToolError) throw error;
|
|
117
145
|
throw new ToolError(
|
|
118
146
|
`Tool "${this.descriptor.metadata.name}" failed to load implementation: ${error instanceof Error ? error.message : String(error)}`,
|
|
@@ -123,10 +151,14 @@ export class LazyAgentTool implements AgentTool<any, any, any> {
|
|
|
123
151
|
return this.#loadPromise;
|
|
124
152
|
}
|
|
125
153
|
|
|
126
|
-
/**
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
154
|
+
/**
|
|
155
|
+
* Resolve the underlying implementation. Pre-dispatch hooks (TTSR `astCondition`
|
|
156
|
+
* matching) call this to read implementation-only members such as the matcher
|
|
157
|
+
* hooks before the first `execute` would otherwise load the tool.
|
|
158
|
+
* An arrow property (like `execute`) so session guard proxies can forward it
|
|
159
|
+
* without losing access to the private loader state.
|
|
160
|
+
*/
|
|
161
|
+
readonly materialize = async (): Promise<AgentTool<any, any, any>> => await this.#get();
|
|
130
162
|
|
|
131
163
|
get name(): string {
|
|
132
164
|
return this.#tool?.name ?? this.descriptor.metadata.name;
|
|
@@ -219,6 +251,14 @@ export class LazyAgentTool implements AgentTool<any, any, any> {
|
|
|
219
251
|
get mode() {
|
|
220
252
|
return (this.#tool as any)?.mode;
|
|
221
253
|
}
|
|
254
|
+
get matcherDigest() {
|
|
255
|
+
const digest = this.#tool?.matcherDigest;
|
|
256
|
+
return digest ? digest.bind(this.#tool) : undefined;
|
|
257
|
+
}
|
|
258
|
+
get matcherEntries() {
|
|
259
|
+
const entries = this.#tool?.matcherEntries;
|
|
260
|
+
return entries ? entries.bind(this.#tool) : undefined;
|
|
261
|
+
}
|
|
222
262
|
|
|
223
263
|
readonly execute: AgentTool<any, any, any>["execute"] = async (...args) => {
|
|
224
264
|
const tool = await this.#get();
|
|
@@ -270,6 +310,7 @@ function availableFor(name: string, session: ToolSession, context = defaultAvail
|
|
|
270
310
|
if (name === "ast_grep") return Boolean(session.settings.get("astGrep.enabled"));
|
|
271
311
|
if (name === "ast_edit") return Boolean(session.settings.get("astEdit.enabled"));
|
|
272
312
|
if (name === "render_mermaid") return Boolean(session.settings.get("renderMermaid.enabled"));
|
|
313
|
+
if (name === "report_issue") return Boolean(session.settings.get("reportIssue.enabled"));
|
|
273
314
|
if (name === "web_search") return Boolean(session.settings.get("web_search.enabled"));
|
|
274
315
|
if (name === "search_tool_bm25") return context.discoveryActive ?? resolveDiscoveryActive(session);
|
|
275
316
|
if (name === "calc") return Boolean(session.settings.get("calc.enabled"));
|
|
@@ -389,6 +430,8 @@ const loaders: Record<string, Loader> = {
|
|
|
389
430
|
cached("goal", () => import("../goals/tools/goal-tool")).then(module => new module.GoalTool(session)),
|
|
390
431
|
move_session: session =>
|
|
391
432
|
cached("move_session", () => import("./move-session")).then(module => new module.MoveSessionTool(session)),
|
|
433
|
+
report_issue: session =>
|
|
434
|
+
cached("report_issue", () => import("./report-issue")).then(module => new module.ReportIssueTool(session)),
|
|
392
435
|
yield: session => cached("yield", () => import("./yield")).then(module => new module.YieldTool(session)),
|
|
393
436
|
report_finding: _session => cached("review", () => import("./review")).then(module => module.reportFindingTool),
|
|
394
437
|
resolve: session => cached("resolve", () => import("./resolve")).then(module => new module.ResolveTool(session)),
|
|
@@ -481,6 +524,7 @@ const names: Array<[name: string, label: string, summary: string | undefined, lo
|
|
|
481
524
|
["skill", "Skill", "Chain into another available skill", "essential"],
|
|
482
525
|
["goal", "Goal", undefined, "essential"],
|
|
483
526
|
["move_session", "Move Session", undefined, "essential"],
|
|
527
|
+
["report_issue", "Report Issue", "Record a tool that behaved inconsistently with its description", "essential"],
|
|
484
528
|
];
|
|
485
529
|
|
|
486
530
|
const sessionDescriptions: Readonly<Record<string, ToolDescriptorMetadata["descriptionForSession"]>> = {
|
package/src/tools/eval.ts
CHANGED
|
@@ -139,6 +139,18 @@ export interface EvalToolOptions {
|
|
|
139
139
|
proxyExecutor?: EvalProxyExecutor;
|
|
140
140
|
}
|
|
141
141
|
|
|
142
|
+
/**
|
|
143
|
+
* In-process eval is not a shell command, so a bash allowlist cannot authorize it.
|
|
144
|
+
* The same flags that turn the bash tool's allowlist on refuse eval outright.
|
|
145
|
+
* Blank prefixes match `checkBashAllowedPrefixes`: they do not restrict.
|
|
146
|
+
*/
|
|
147
|
+
function bashRestrictionBlocksEval(
|
|
148
|
+
session: Pick<ToolSession, "bashAllowedPrefixes" | "bashRestrictionProfile">,
|
|
149
|
+
): boolean {
|
|
150
|
+
if (session.bashRestrictionProfile === "read-only") return true;
|
|
151
|
+
return (session.bashAllowedPrefixes ?? []).some(prefix => prefix.trim().length > 0);
|
|
152
|
+
}
|
|
153
|
+
|
|
142
154
|
interface ResolvedBackend {
|
|
143
155
|
backend: ExecutorBackend;
|
|
144
156
|
notice?: string;
|
|
@@ -225,6 +237,9 @@ export class EvalTool implements AgentTool<typeof evalSchema> {
|
|
|
225
237
|
onUpdate?: AgentToolUpdateCallback,
|
|
226
238
|
_ctx?: AgentToolContext,
|
|
227
239
|
): Promise<AgentToolResult<EvalToolDetails | undefined>> {
|
|
240
|
+
if (this.session && bashRestrictionBlocksEval(this.session)) {
|
|
241
|
+
throw new ToolError("Eval is unavailable while this session's bash allowlist or read-only profile is active.");
|
|
242
|
+
}
|
|
228
243
|
if (this.#proxyExecutor) {
|
|
229
244
|
return this.#proxyExecutor(params, signal);
|
|
230
245
|
}
|
package/src/tools/fetch.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import * as nodeFs from "node:fs";
|
|
1
3
|
import * as fs from "node:fs/promises";
|
|
2
4
|
import * as path from "node:path";
|
|
3
5
|
import type { AgentToolResult } from "@gajae-code/agent-core";
|
|
@@ -677,12 +679,13 @@ async function handleSpecialUrls(
|
|
|
677
679
|
timeout: number,
|
|
678
680
|
signal: AbortSignal | undefined,
|
|
679
681
|
storage: AgentStorage | null,
|
|
682
|
+
settings: Settings,
|
|
680
683
|
): Promise<FetchRenderResult | null> {
|
|
681
684
|
for (const handler of getSpecialHandlers()) {
|
|
682
685
|
if (signal?.aborted) {
|
|
683
686
|
throw new ToolAbortError();
|
|
684
687
|
}
|
|
685
|
-
const result = await handler(url, timeout, signal, storage);
|
|
688
|
+
const result = await handler(url, timeout, signal, storage, settings);
|
|
686
689
|
if (result) return result;
|
|
687
690
|
}
|
|
688
691
|
return null;
|
|
@@ -766,7 +769,7 @@ async function renderUrl(
|
|
|
766
769
|
|
|
767
770
|
// Step 1: Try special handlers for known sites (unless raw mode)
|
|
768
771
|
if (!raw) {
|
|
769
|
-
const specialResult = await handleSpecialUrls(url, timeout, signal, storage);
|
|
772
|
+
const specialResult = await handleSpecialUrls(url, timeout, signal, storage, settings);
|
|
770
773
|
if (specialResult) return specialResult;
|
|
771
774
|
}
|
|
772
775
|
|
|
@@ -1384,10 +1387,93 @@ async function materializeReadUrlCacheEntry(
|
|
|
1384
1387
|
return null;
|
|
1385
1388
|
}
|
|
1386
1389
|
|
|
1390
|
+
const ARTIFACT_NOFOLLOW = process.platform === "win32" ? 0 : (nodeFs.constants.O_NOFOLLOW ?? 0);
|
|
1391
|
+
|
|
1392
|
+
async function unlinkOwnedStaging(staging: string, dev: bigint, ino: bigint): Promise<void> {
|
|
1393
|
+
try {
|
|
1394
|
+
const stat = await fs.lstat(staging, { bigint: true });
|
|
1395
|
+
if (!stat.isFile() || stat.isSymbolicLink() || stat.dev !== dev || stat.ino !== ino) return;
|
|
1396
|
+
await fs.unlink(staging);
|
|
1397
|
+
} catch (error) {
|
|
1398
|
+
if ((error as NodeJS.ErrnoException).code !== "ENOENT") throw error;
|
|
1399
|
+
}
|
|
1400
|
+
}
|
|
1401
|
+
|
|
1402
|
+
/**
|
|
1403
|
+
* Publish text at an explicit artifact path the way `ArtifactManager.save` does:
|
|
1404
|
+
* a temp file opened with `O_NOFOLLOW` and mode 0600, then rename. A symlink is
|
|
1405
|
+
* not followed. A pre-existing regular file is replaced, so mode 0644 is not kept.
|
|
1406
|
+
*/
|
|
1407
|
+
export async function publishExplicitArtifactText(destination: string, text: string): Promise<void> {
|
|
1408
|
+
const bytes = Buffer.from(text, "utf8");
|
|
1409
|
+
const parent = path.dirname(destination);
|
|
1410
|
+
// Bun.write created a missing parent. Match that when the allocated path's
|
|
1411
|
+
// directory does not exist yet.
|
|
1412
|
+
try {
|
|
1413
|
+
await fs.lstat(parent);
|
|
1414
|
+
} catch (error) {
|
|
1415
|
+
if ((error as NodeJS.ErrnoException).code !== "ENOENT") throw error;
|
|
1416
|
+
await fs.mkdir(parent, { recursive: true, mode: 0o700 });
|
|
1417
|
+
}
|
|
1418
|
+
// Keep the staging component a fixed length. A destination basename near NAME_MAX
|
|
1419
|
+
// plus a UUID would make the temp name longer than the filesystem allows.
|
|
1420
|
+
const staging = path.join(parent, `.artifact-${randomUUID()}.staging`);
|
|
1421
|
+
let handle: fs.FileHandle | undefined;
|
|
1422
|
+
let stagedDev: bigint | undefined;
|
|
1423
|
+
let stagedIno: bigint | undefined;
|
|
1424
|
+
let published = false;
|
|
1425
|
+
try {
|
|
1426
|
+
handle = await fs.open(
|
|
1427
|
+
staging,
|
|
1428
|
+
nodeFs.constants.O_CREAT | nodeFs.constants.O_EXCL | nodeFs.constants.O_WRONLY | ARTIFACT_NOFOLLOW,
|
|
1429
|
+
0o600,
|
|
1430
|
+
);
|
|
1431
|
+
const opened = await handle.stat({ bigint: true });
|
|
1432
|
+
stagedDev = opened.dev;
|
|
1433
|
+
stagedIno = opened.ino;
|
|
1434
|
+
if (process.platform !== "win32") await handle.chmod(0o600);
|
|
1435
|
+
await handle.writeFile(bytes);
|
|
1436
|
+
await handle.sync();
|
|
1437
|
+
const staged = await handle.stat({ bigint: true });
|
|
1438
|
+
if (process.platform !== "win32" && (Number(staged.mode) & 0o777) !== 0o600) {
|
|
1439
|
+
throw new Error("artifact_publish_mode");
|
|
1440
|
+
}
|
|
1441
|
+
await handle.close();
|
|
1442
|
+
handle = undefined;
|
|
1443
|
+
|
|
1444
|
+
let existing: nodeFs.Stats | undefined;
|
|
1445
|
+
try {
|
|
1446
|
+
existing = await fs.lstat(destination);
|
|
1447
|
+
} catch (error) {
|
|
1448
|
+
if ((error as NodeJS.ErrnoException).code !== "ENOENT") throw error;
|
|
1449
|
+
}
|
|
1450
|
+
if (existing?.isSymbolicLink()) throw new Error("artifact_destination_symlink");
|
|
1451
|
+
if (existing && !existing.isFile()) throw new Error("artifact_destination_not_file");
|
|
1452
|
+
|
|
1453
|
+
await fs.rename(staging, destination);
|
|
1454
|
+
const named = await fs.lstat(destination, { bigint: true });
|
|
1455
|
+
if (
|
|
1456
|
+
!named.isFile() ||
|
|
1457
|
+
named.isSymbolicLink() ||
|
|
1458
|
+
named.dev !== stagedDev ||
|
|
1459
|
+
named.ino !== stagedIno ||
|
|
1460
|
+
(process.platform !== "win32" && (Number(named.mode) & 0o777) !== 0o600)
|
|
1461
|
+
) {
|
|
1462
|
+
throw new Error("artifact_destination_identity_changed");
|
|
1463
|
+
}
|
|
1464
|
+
published = true;
|
|
1465
|
+
} finally {
|
|
1466
|
+
if (handle) await handle.close().catch(() => undefined);
|
|
1467
|
+
if (!published && stagedDev !== undefined && stagedIno !== undefined) {
|
|
1468
|
+
await unlinkOwnedStaging(staging, stagedDev, stagedIno);
|
|
1469
|
+
}
|
|
1470
|
+
}
|
|
1471
|
+
}
|
|
1472
|
+
|
|
1387
1473
|
async function persistReadUrlArtifact(session: ToolSession, output: string): Promise<string | undefined> {
|
|
1388
1474
|
const { path: artifactPath, id } = (await session.allocateOutputArtifact?.("read")) ?? {};
|
|
1389
1475
|
if (!artifactPath) return undefined;
|
|
1390
|
-
await
|
|
1476
|
+
await publishExplicitArtifactText(artifactPath, wrapUntrustedContent(output));
|
|
1391
1477
|
return id;
|
|
1392
1478
|
}
|
|
1393
1479
|
|
|
@@ -1474,7 +1560,10 @@ export const WRAP_PREFIX_CHARS = `${UNTRUSTED_CONTENT_OPEN}\n`.length;
|
|
|
1474
1560
|
const WRAP_SUFFIX = `\n${UNTRUSTED_CONTENT_CLOSE}`;
|
|
1475
1561
|
|
|
1476
1562
|
export function escapeUntrustedContent(content: string): string {
|
|
1477
|
-
|
|
1563
|
+
// HTML and XML accept whitespace between the tag name and ">".
|
|
1564
|
+
return content
|
|
1565
|
+
.replace(/<\/untrusted-content>/gi, "</untrusted-content>")
|
|
1566
|
+
.replace(/<\/untrusted-content[ \t\n\r\f]+>/gi, "</untrusted-content>");
|
|
1478
1567
|
}
|
|
1479
1568
|
|
|
1480
1569
|
export function wrapUntrustedContent(content: string): string {
|