@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
package/src/tools/todo-write.ts
CHANGED
|
@@ -268,6 +268,14 @@ function removeTasks(phases: TodoPhase[], entry: TodoOpEntryValue, errors: strin
|
|
|
268
268
|
return phases;
|
|
269
269
|
}
|
|
270
270
|
|
|
271
|
+
/** Type of a value that was expected to be a string, with its article. */
|
|
272
|
+
function describeArgument(value: unknown): string {
|
|
273
|
+
if (value === null) return "null";
|
|
274
|
+
if (Array.isArray(value)) return "an array";
|
|
275
|
+
if (typeof value === "object") return "an object";
|
|
276
|
+
return `a ${typeof value}`;
|
|
277
|
+
}
|
|
278
|
+
|
|
271
279
|
function applyEntry(phases: TodoPhase[], entry: TodoOpEntryValue, errors: string[]): TodoPhase[] {
|
|
272
280
|
switch (entry.op) {
|
|
273
281
|
case "init":
|
|
@@ -302,6 +310,16 @@ function applyEntry(phases: TodoPhase[], entry: TodoOpEntryValue, errors: string
|
|
|
302
310
|
case "note": {
|
|
303
311
|
const hit = resolveTaskOrError(phases, entry.task, errors);
|
|
304
312
|
if (!hit) return phases;
|
|
313
|
+
// The schema types `text`, but bridges and replays reach `execute` without
|
|
314
|
+
// raw validation, and `.replace` on a non-string body used to crash the
|
|
315
|
+
// call. Reject it with a correction instead of storing it.
|
|
316
|
+
if (entry.text !== undefined && typeof entry.text !== "string") {
|
|
317
|
+
errors.push(
|
|
318
|
+
`Note text must be a string; got ${describeArgument(entry.text)}. ` +
|
|
319
|
+
'Pass the note body as "text", for example { op: "note", task: "<task content>", text: "why" }.',
|
|
320
|
+
);
|
|
321
|
+
return phases;
|
|
322
|
+
}
|
|
305
323
|
const text = (entry.text ?? "").replace(/\s+$/u, "");
|
|
306
324
|
if (!text) {
|
|
307
325
|
errors.push("Missing text for note operation");
|
|
@@ -337,6 +355,26 @@ export function applyOpsToPhases(
|
|
|
337
355
|
// Markdown round-trip
|
|
338
356
|
// =============================================================================
|
|
339
357
|
|
|
358
|
+
/**
|
|
359
|
+
* Notes are declared as `string[]`, but the array is only typed, never validated:
|
|
360
|
+
* a session persisted elsewhere or a bridged caller that reached `execute`
|
|
361
|
+
* without raw validation can hand over any element, and every render path splits
|
|
362
|
+
* each note on newlines. One non-string element used to crash the call with a
|
|
363
|
+
* minified `.split is not a function` TypeError instead of a tool result. String
|
|
364
|
+
* notes render exactly as before.
|
|
365
|
+
*/
|
|
366
|
+
function noteDisplayText(note: unknown): string {
|
|
367
|
+
if (typeof note === "string") return note;
|
|
368
|
+
if (note !== null && typeof note === "object") {
|
|
369
|
+
try {
|
|
370
|
+
return JSON.stringify(note) ?? "[unrenderable note]";
|
|
371
|
+
} catch {
|
|
372
|
+
return "[unrenderable note]";
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
return String(note);
|
|
376
|
+
}
|
|
377
|
+
|
|
340
378
|
const STATUS_TO_MARKER: Record<TodoStatus, string> = {
|
|
341
379
|
pending: " ",
|
|
342
380
|
in_progress: "/",
|
|
@@ -356,7 +394,7 @@ export function phasesToMarkdown(phases: TodoPhase[]): string {
|
|
|
356
394
|
if (task.notes && task.notes.length > 0) {
|
|
357
395
|
for (let j = 0; j < task.notes.length; j++) {
|
|
358
396
|
if (j > 0) out.push(" >");
|
|
359
|
-
for (const noteLine of task.notes[j].split("\n")) {
|
|
397
|
+
for (const noteLine of noteDisplayText(task.notes[j]).split("\n")) {
|
|
360
398
|
out.push(noteLine === "" ? " >" : ` > ${noteLine}`);
|
|
361
399
|
}
|
|
362
400
|
}
|
|
@@ -504,7 +542,7 @@ function formatSummary(phases: TodoPhase[], errors: string[]): string {
|
|
|
504
542
|
if (task.status === "in_progress" && task.notes && task.notes.length > 0) {
|
|
505
543
|
for (let j = 0; j < task.notes.length; j++) {
|
|
506
544
|
if (j > 0) lines.push(" ---");
|
|
507
|
-
for (const noteLine of task.notes[j].split("\n")) {
|
|
545
|
+
for (const noteLine of noteDisplayText(task.notes[j]).split("\n")) {
|
|
508
546
|
lines.push(` ${noteLine}`);
|
|
509
547
|
}
|
|
510
548
|
}
|
|
@@ -685,7 +723,7 @@ function renderNoteAttachments(phases: TodoPhase[], uiTheme: Theme): string[] {
|
|
|
685
723
|
lines.push(` ${title}`);
|
|
686
724
|
for (let j = 0; j < task.notes.length; j++) {
|
|
687
725
|
if (j > 0) lines.push(` ${bar}`);
|
|
688
|
-
for (const noteLine of task.notes[j].split("\n")) {
|
|
726
|
+
for (const noteLine of noteDisplayText(task.notes[j]).split("\n")) {
|
|
689
727
|
lines.push(` ${bar} ${uiTheme.fg("dim", noteLine)}`);
|
|
690
728
|
}
|
|
691
729
|
}
|
|
@@ -27,7 +27,7 @@ export const TOOL_CATALOG: Readonly<Record<string, ToolCatalogEntry>> = {
|
|
|
27
27
|
"read": {
|
|
28
28
|
"name": "read",
|
|
29
29
|
"label": "Read",
|
|
30
|
-
"description": "Read files, directories, archives, SQLite databases, images, documents, internal resources, and web URLs through a single `path` string.\n\n<instruction>\n- One tool for filesystem, archives, SQLite, images, documents (PDF/DOCX/PPTX/XLSX/RTF/EPUB/ipynb), internal URIs, and web URLs (reader-mode by default).\n- You SHOULD parallelize independent reads when exploring related files.\n- You SHOULD reach for `read` — not a browser/puppeteer tool — for fetching web content.\n</instruction>\n\n## Parameters\n\n- `path` — required. Local path, internal URI (`agent://`, `artifact://`, `rule://`, `local://`), or URL. Append `:<sel>` for line ranges, raw mode, or special modes (e.g. `src/foo.ts:50-200`, `src/foo.ts:raw`, `db.sqlite:users:42`).\n- `truncation` — optional `head` | `last` | `both`; selects which end of an over-budget result to retain. Configured default: last (factory default: `last`); structural summaries and routes such as URLs, directories and converted documents default to `head`. A line-range selector still bounds the selection — this only picks which end of that selection survives the byte/line cap. SQLite row queries page via their own `limit`/`offset` and ignore it.\n## Selectors\nAppend `:<sel>` to `path`. The bare path falls back to the default mode.\n\n- _(none)_ — parseable code → structural summary (signatures kept, bodies elided); a plain text file → a bounded receipt of about 50 lines or 10 KiB, whichever is smaller; the configured truncation direction is last (factory default: last). Line+hash anchors keep their real file line numbers and a footer names the omitted range. Archive members use the larger 3000-line / 50 KiB budget. Converted documents, notebooks, URLs and directory listings still start from the beginning.\n- `:50` / `:50-` — read from line 50 onward.\n- `:50-200` — lines 50–200 inclusive.\n- `:50+150` — 150 lines starting at line 50.\n- `:20+1` — exactly one line.\n- `:5-16,960-973` — multiple ranges in one call (sorted, overlaps merged).\n- `:raw` — verbatim text; no anchors, no summary, no line prefixes.\n- `:2-4:raw` or `:raw:2-4` — range AND verbatim; the two compose in either order.\n- `:conflicts` — one-line-per-block index of every unresolved git merge conflict.\n\n# Files\n\n- Reading a directory path returns a depth-limited dirent listing.\n- Parseable code without a selector returns a **structural summary**: declarations kept, large bodies collapsed to `..` (merged brace pair) or `…` (standalone). Summarized output ends with a footer of the form:\n\n `[NN lines across MM elided regions; read <path>:raw or a line range like <path>:1-9999 for verbatim content]`\n\n If the elided body is what you actually need, re-issue the **exact selector the footer names**. NEVER guess what's inside `..` / `…` — those markers carry no content.\n- Structural summaries honor explicit `truncation`: `head` retains the beginning, `last` the end, and `both` both ends within the summary byte budget. Complete summary units and their original source anchors stay intact and in source order; budget omissions are marked where they occur. This does not switch to raw source. Summaries that fit are unchanged by direction.\n- Directional windows identify the retained first/last lines and the omitted range; use the `re-read <path>:1-<total>` or `:raw` hint in the footer to recover the full content.\n\n# Documents & Notebooks\n\nExtracts text from PDF, Word, PowerPoint, Excel, RTF, and EPUB. Notebooks (`.ipynb`) are shown as editable `# %% [type] cell:N` text; edits round-trip back to the underlying JSON preserving notebook metadata. Add `:raw` to a notebook to bypass the converter and read the JSON directly.\n\n# Images\n\nReading an image path returns the image itself for visual inspection by a vision-capable model.\n\n# Archives\n\nSupports `.tar`, `.tar.gz`, `.tgz`, `.zip`. Use `archive.ext:path/inside/archive` to read a member, and append a normal selector to the inner path: `archive.zip:dir/file.ts:50-60`.\n\n# SQLite\n\nFor `.sqlite`, `.sqlite3`, `.db`, `.db3`:\n- `file.db` — list tables with row counts\n- `file.db:table` — schema + sample rows\n- `file.db:table:key` — single row by primary key\n- `file.db:table?limit=50&offset=100` — paginated rows\n- `file.db:table?where=status='active'&order=created:desc` — filtered rows\n- `file.db?q=SELECT …` — read-only SELECT query\n\n# URLs\n\n- Default reader-mode: HTML pages, GitHub issues/PRs, Stack Overflow, Wikipedia, Reddit, NPM, arXiv, RSS/Atom, JSON endpoints, PDFs → clean text/markdown.\n- `:raw` returns untouched HTML; line selectors (`:50`, `:50-100`, `:50+150`) paginate the cached fetched output.\n- Bare `host:port` URLs collide with the selector grammar — add a trailing slash before the selector: `https://example.com/:80`.\n\n# Internal URIs\n\n`agent://<id>`, `artifact://<id>`, `rule://<name>`, and `local://<name>.md` resolve transparently and accept the same line selectors as filesystem paths. Use `artifact://<id>` to recover full output that a previous bash/eval/tool result spilled or truncated.\n\nBundled skills have no filesystem home, so the skill tool and skill discovery report them as `embedded:gjc/skills/<name>/SKILL.md`; read that identifier verbatim. Bundled skill fragments, when surfaced, likewise use `embedded:gjc/skill-fragments
|
|
30
|
+
"description": "Read files, directories, archives, SQLite databases, images, documents, internal resources, and web URLs through a single `path` string.\n\n<instruction>\n- One tool for filesystem, archives, SQLite, images, documents (PDF/DOCX/PPTX/XLSX/RTF/EPUB/ipynb), internal URIs, and web URLs (reader-mode by default).\n- You SHOULD parallelize independent reads when exploring related files.\n- You SHOULD reach for `read` — not a browser/puppeteer tool — for fetching web content.\n</instruction>\n\n## Parameters\n\n- `path` — required. Local path, internal URI (`agent://`, `artifact://`, `rule://`, `local://`), or URL. Append `:<sel>` for line ranges, raw mode, or special modes (e.g. `src/foo.ts:50-200`, `src/foo.ts:raw`, `db.sqlite:users:42`).\n- `truncation` — optional `head` | `last` | `both`; selects which end of an over-budget result to retain. Configured default: last (factory default: `last`); structural summaries and routes such as URLs, directories and converted documents default to `head`. A line-range selector still bounds the selection — this only picks which end of that selection survives the byte/line cap. SQLite row queries page via their own `limit`/`offset` and ignore it.\n## Selectors\nAppend `:<sel>` to `path`. The bare path falls back to the default mode.\n\n- _(none)_ — parseable code → structural summary (signatures kept, bodies elided); a plain text file → a bounded receipt of about 50 lines or 10 KiB, whichever is smaller; the configured truncation direction is last (factory default: last). Line+hash anchors keep their real file line numbers and a footer names the omitted range. Archive members use the larger 3000-line / 50 KiB budget. Converted documents, notebooks, URLs and directory listings still start from the beginning.\n- `:50` / `:50-` — read from line 50 onward.\n- `:50-200` — lines 50–200 inclusive.\n- `:50+150` — 150 lines starting at line 50.\n- `:20+1` — exactly one line.\n- `:5-16,960-973` — multiple ranges in one call (sorted, overlaps merged).\n- `:raw` — verbatim text; no anchors, no summary, no line prefixes.\n- `:2-4:raw` or `:raw:2-4` — range AND verbatim; the two compose in either order.\n- `:conflicts` — one-line-per-block index of every unresolved git merge conflict.\n\n# Files\n\n- Reading a directory path returns a depth-limited dirent listing.\n- Parseable code without a selector returns a **structural summary**: declarations kept, large bodies collapsed to `..` (merged brace pair) or `…` (standalone). Summarized output ends with a footer of the form:\n\n `[NN lines across MM elided regions; read <path>:raw or a line range like <path>:1-9999 for verbatim content]`\n\n If the elided body is what you actually need, re-issue the **exact selector the footer names**. NEVER guess what's inside `..` / `…` — those markers carry no content.\n- Structural summaries honor explicit `truncation`: `head` retains the beginning, `last` the end, and `both` both ends within the summary byte budget. Complete summary units and their original source anchors stay intact and in source order; budget omissions are marked where they occur. This does not switch to raw source. Summaries that fit are unchanged by direction.\n- Directional windows identify the retained first/last lines and the omitted range; use the `re-read <path>:1-<total>` or `:raw` hint in the footer to recover the full content.\n\n# Documents & Notebooks\n\nExtracts text from PDF, Word, PowerPoint, Excel, RTF, and EPUB. Notebooks (`.ipynb`) are shown as editable `# %% [type] cell:N` text; edits round-trip back to the underlying JSON preserving notebook metadata. Add `:raw` to a notebook to bypass the converter and read the JSON directly.\n\n# Images\n\nReading an image path returns the image itself for visual inspection by a vision-capable model.\n\n# Archives\n\nSupports `.tar`, `.tar.gz`, `.tgz`, `.zip`. Use `archive.ext:path/inside/archive` to read a member, and append a normal selector to the inner path: `archive.zip:dir/file.ts:50-60`.\n\n# SQLite\n\nFor `.sqlite`, `.sqlite3`, `.db`, `.db3`:\n- `file.db` — list tables with row counts\n- `file.db:table` — schema + sample rows\n- `file.db:table:key` — single row by primary key\n- `file.db:table?limit=50&offset=100` — paginated rows\n- `file.db:table?where=status='active'&order=created:desc` — filtered rows\n- `file.db?q=SELECT …` — read-only SELECT query\n\n# URLs\n\n- Default reader-mode: HTML pages, GitHub issues/PRs, Stack Overflow, Wikipedia, Reddit, NPM, arXiv, RSS/Atom, JSON endpoints, PDFs → clean text/markdown.\n- `:raw` returns untouched HTML; line selectors (`:50`, `:50-100`, `:50+150`) paginate the cached fetched output.\n- Bare `host:port` URLs collide with the selector grammar — add a trailing slash before the selector: `https://example.com/:80`.\n\n# Internal URIs\n\n`agent://<id>`, `artifact://<id>`, `rule://<name>`, and `local://<name>.md` resolve transparently and accept the same line selectors as filesystem paths. Use `artifact://<id>` to recover full output that a previous bash/eval/tool result spilled or truncated.\n\nBundled skills have no filesystem home, so the skill tool and skill discovery report them as `embedded:gjc/skills/<name>/SKILL.md`; read that identifier verbatim. Bundled skill fragments, when surfaced, likewise use `embedded:gjc/skill-fragments/…` identifiers.\n\n<critical>\n- Always include `path`; never call `read` with `{}`.\n- For line ranges, append the selector to `path`.\n- Re-issue the selector named by a summary footer before relying on elided content.\n</critical>",
|
|
31
31
|
"parameters": {
|
|
32
32
|
"type": "object",
|
|
33
33
|
"properties": {
|
|
@@ -407,7 +407,7 @@ export const TOOL_CATALOG: Readonly<Record<string, ToolCatalogEntry>> = {
|
|
|
407
407
|
"debug": {
|
|
408
408
|
"name": "debug",
|
|
409
409
|
"label": "Debug",
|
|
410
|
-
"description": "Provides debugger access through the Debug Adapter Protocol (DAP).\nUse for launching or attaching debuggers, setting breakpoints, stepping through execution, inspecting threads/stack/variables, evaluating expressions, capturing output, and interrupting hung programs.\n\n<instruction>\n- Prefer over bash for program state, breakpoints, stepping, thread inspection, or interrupting a running process.\n- `action: \"launch\"` starts a session; `program` is required, `adapter` optional (auto-selected from target path and workspace).\n For Python, set `adapter: \"debugpy\"` and `program` to the target `.py` file; put interpreter/script flags in `args`.\n- `action: \"attach\"` connects to an existing process: `pid` for local attach, `port` for remote attach (where the adapter supports it), `adapter` to force a specific debugger.\n- **Breakpoints**: `set_breakpoint`/`remove_breakpoint` with source (`file`+`line`) or function (`function`); optional `condition` for conditional breakpoints.\n- **Flow control**: `continue` (resumes; briefly waits to observe whether the program stops or keeps running), `step_over`/`step_in`/`step_out` (single-step), `pause` (interrupt a running program so you can inspect state).\n- **Inspect**: `threads` (list), `stack_trace` (frames for current stopped thread), `scopes` (needs `frame_id` or a current stopped frame), `variables` (needs `variable_ref` or `scope_id`), `evaluate` (needs `expression`; `context: \"repl\"` for raw debugger commands when the adapter supports them), `output` (captured stdout/stderr/console), `sessions` (tracked debug sessions), `terminate`.\n- Timeouts apply per-request, not to the full session lifetime.\n</instruction>\n\n<caution>\n- Only one active debug session is supported at a time.\n- Some adapters require a launched session to receive `configurationDone` before the target actually runs; if the tool says configuration is pending, set breakpoints and then call `continue`.\n- Adapter availability depends on local binaries. Common built-ins: `gdb`, `lldb-dap`, `python -m debugpy.adapter`, `dlv dap`.\n- `program` must be an executable file or debug target, not a directory or interpreter name that resolves to a workspace directory.\n</caution>\n\n<examples>\n# Launch and inspect hang\n1. `debug(action: \"launch\", program: \"./my_app\")`\n2. `debug(action: \"set_breakpoint\", file: \"src/main.c\", line: 42)`\n3. `debug(action: \"continue\")`\n4. If the program appears hung: `debug(action: \"pause\")`\n5. Inspect state with `threads`, `stack_trace`, `scopes`, and `variables`\n# Launch a Python script with debugpy\n`debug(action: \"launch\", adapter: \"debugpy\", program: \"scripts/job.py\", args: [\"--flag\"])`\n# Raw debugger command through repl\n`debug(action: \"evaluate\", expression: \"info registers\", context: \"repl\")`\n</examples>",
|
|
410
|
+
"description": "Provides debugger access through the Debug Adapter Protocol (DAP).\nUse for launching or attaching debuggers, setting breakpoints, stepping through execution, inspecting threads/stack/variables, evaluating expressions, capturing output, and interrupting hung programs.\n\n<instruction>\n- Prefer over bash for program state, breakpoints, stepping, thread inspection, or interrupting a running process.\n- `action: \"launch\"` starts a session; `program` is required, `adapter` optional (auto-selected from target path and workspace).\n For Python, set `adapter: \"debugpy\"` and `program` to the target `.py` file; put interpreter/script flags in `args`.\n- `action: \"attach\"` connects to an existing process: `pid` for local attach, `port` for remote attach (where the adapter supports it), `adapter` to force a specific debugger.\n- **Breakpoints**: `set_breakpoint`/`remove_breakpoint` with source (`file`+`line`) or function (`function`); optional `condition` for conditional breakpoints.\n- **Flow control**: `continue` (resumes; briefly waits to observe whether the program stops or keeps running), `step_over`/`step_in`/`step_out` (single-step), `pause` (interrupt a running program so you can inspect state).\n- **Inspect**: `threads` (list), `stack_trace` (frames for current stopped thread), `scopes` (needs `frame_id` or a current stopped frame), `variables` (needs `variable_ref` or `scope_id`), `evaluate` (needs `expression`; `context: \"repl\"` for raw debugger commands when the adapter supports them), `output` (captured stdout/stderr/console), `sessions` (tracked debug sessions), `terminate`.\n- Timeouts apply per-request, not to the full session lifetime.\n</instruction>\n\n<caution>\n- Only one active debug session is supported at a time.\n- Some adapters require a launched session to receive `configurationDone` before the target actually runs; if the tool says configuration is pending, set breakpoints and then call `continue`.\n- Adapter availability depends on local binaries. Common built-ins: `gdb`, `lldb-dap`, `python -I -m debugpy.adapter`, `dlv dap`.\n- `program` must be an executable file or debug target, not a directory or interpreter name that resolves to a workspace directory.\n</caution>\n\n<examples>\n# Launch and inspect hang\n1. `debug(action: \"launch\", program: \"./my_app\")`\n2. `debug(action: \"set_breakpoint\", file: \"src/main.c\", line: 42)`\n3. `debug(action: \"continue\")`\n4. If the program appears hung: `debug(action: \"pause\")`\n5. Inspect state with `threads`, `stack_trace`, `scopes`, and `variables`\n# Launch a Python script with debugpy\n`debug(action: \"launch\", adapter: \"debugpy\", program: \"scripts/job.py\", args: [\"--flag\"])`\n# Raw debugger command through repl\n`debug(action: \"evaluate\", expression: \"info registers\", context: \"repl\")`\n</examples>",
|
|
411
411
|
"parameters": {
|
|
412
412
|
"type": "object",
|
|
413
413
|
"properties": {
|
|
@@ -2096,7 +2096,7 @@ export const TOOL_CATALOG: Readonly<Record<string, ToolCatalogEntry>> = {
|
|
|
2096
2096
|
"task": {
|
|
2097
2097
|
"name": "task",
|
|
2098
2098
|
"label": "Task",
|
|
2099
|
-
"description": "Launches subagents to parallelize workflows.\n\n- Results are delivered automatically when complete.\n- The tool result lists the assigned task ids (e.g. `0-AuthLoader`) — those are the live agent ids.\n- Coordinate with running tasks via `irc` when available, using those ids. `subagent` cancel terminates a task and **cannot carry a message**.\n- Use `subagent` action `inspect` or `list` to snapshot manager state.\n- To wait or cancel, use the `subagent` tool; its await/cancel doctrine is authoritative.\n\nSubagents have no conversation history. Every fact, file path, and direction they need MUST be explicit in `context` or `assignment`.\nUse `irc` for live coordination when it is available.\n\n<parameters>\n- `agent`: agent type for all tasks\n- `tasks`: tasks to execute in parallel\n - `.id`: filesystem-safe, ≤48 chars, matching `[A-Za-z0-9][A-Za-z0-9_-]*`; prefer CamelCase\n - `.description`: UI label only — subagent never sees it\n - `.assignment`: complete self-contained instructions; one-liners and missing acceptance criteria are PROHIBITED\n- `context`: shared background prepended to every assignment; session-specific only\n- `.inheritContext` (optional): fork-context mode for seeding the subagent with sanitized parent conversation. Omit it or set `\"none\"` for no copied context. `\"receipt\"` copies a minimal receipt-sized snapshot, `\"last-turn\"` copies only the latest exchange, `\"bounded\"` copies the bounded default snapshot, and `\"full\"` copies a larger snapshot up to the configured/model token cap. Non-`none` modes work only when global `task.forkContext.enabled` is true and the target agent declares `forkContext: allowed`; otherwise the call is rejected. Bundled agents that support it: `executor`, `architect`. Use inherited context only when the subagent's value depends on parent context; cloned tokens are billed to the child as fresh input and surfaced in task receipts as fork-context cloned-token accounting.\n\n- `schema`: JTD schema for expected structured output (do not put format rules in assignments)\n- `spawnPlan` (optional): required before any batch with more than 4 tasks; include whyParallel, whyNotLocal, independence, expectedReceiptShape, and maxInlineTokens.\n\n- `repositoryBinding`: optional authoritative repository/worktree identity. It must match the
|
|
2099
|
+
"description": "Launches subagents to parallelize workflows.\n\n- Results are delivered automatically when complete.\n- The tool result lists the assigned task ids (e.g. `0-AuthLoader`) — those are the live agent ids.\n- Coordinate with running tasks via `irc` when available, using those ids. `subagent` cancel terminates a task and **cannot carry a message**.\n- Use `subagent` action `inspect` or `list` to snapshot manager state.\n- To wait or cancel, use the `subagent` tool; its await/cancel doctrine is authoritative.\n\nSubagents have no conversation history. Every fact, file path, and direction they need MUST be explicit in `context` or `assignment`.\nUse `irc` for live coordination when it is available.\n\n<parameters>\n- `agent`: agent type for all tasks\n- `tasks`: tasks to execute in parallel\n - `.id`: filesystem-safe, ≤48 chars, matching `[A-Za-z0-9][A-Za-z0-9_-]*`; prefer CamelCase\n - `.description`: UI label only — subagent never sees it\n - `.assignment`: complete self-contained instructions; one-liners and missing acceptance criteria are PROHIBITED\n- `context`: shared background prepended to every assignment; session-specific only\n- `.inheritContext` (optional): fork-context mode for seeding the subagent with sanitized parent conversation. Omit it or set `\"none\"` for no copied context. `\"receipt\"` copies a minimal receipt-sized snapshot, `\"last-turn\"` copies only the latest exchange, `\"bounded\"` copies the bounded default snapshot, and `\"full\"` copies a larger snapshot up to the configured/model token cap. Non-`none` modes work only when global `task.forkContext.enabled` is true and the target agent declares `forkContext: allowed`; otherwise the call is rejected. Bundled agents that support it: `executor`, `architect`. Use inherited context only when the subagent's value depends on parent context; cloned tokens are billed to the child as fresh input and surfaced in task receipts as fork-context cloned-token accounting.\n\n- `schema`: JTD schema for expected structured output (do not put format rules in assignments)\n- `spawnPlan` (optional): required before any batch with more than 4 tasks; include whyParallel, whyNotLocal, independence, expectedReceiptShape, and maxInlineTokens.\n\n- `repositoryBinding`: optional authoritative repository/worktree identity. It must match the session's committed cwd and cannot itself authorize relocation. After an authorized session move, new work uses the committed target; already-admitted work retains its source execution inputs. Verified storage relocation of that same parent does not change child execution cwd; a logical session switch does not substitute successor storage. For another approved repository, start a fresh session rooted there (for example, `gjc --cwd <approved-worktree>`) or use the existing user-authorized relocation surface; `move_session` still only narrows to a descendant directory.\n</parameters>\n\n<rules>\n- HARD runtime gate: calls with more than 4 tasks are rejected before any child launches unless `spawnPlan` is complete.\n- NEVER assign tasks to run project-wide build/test/lint. Caller verifies after the batch.\n- **Subagents do not verify, lint, or format.** Every assignment MUST instruct the subagent to skip all gates and formatters. You run them once at the end across the union of changed files — avoids redundant runs and racing formatter passes.\n- Each task: ≤3–5 explicit files. No globs, no \"update all\", no package-wide scope. Use `irc` to coordinate overlapping work when it is available; otherwise keep file sets independent.\n- Pass large payloads via `local://<path>` URIs, not inline.\n- Put shared constraints in `context` once; do not duplicate across assignments.\n- Prefer agents that investigate **and** edit in one pass; only spin a read-only discovery step when affected files are genuinely unknown.\n</rules>\n\n<parallelization>\nTest: can task B run correctly without seeing A's output? If no, sequence A → B, unless `irc` is available and A can answer a small clarification.\nStill sequence when one task produces a large, evolving contract (generated types, schema migration, core module API); live coordination is not a substitute for a finished contract.\nParallel when tasks touch disjoint files, are independent refactors/tests, or need only occasional peer clarification.\n</parallelization>\n\n<context-fmt>\n# Goal ← one sentence: what the batch accomplishes\n# Constraints ← MUST/NEVER rules and session decisions\n# Contract ← exact types/signatures if tasks share an interface\n</context-fmt>\n\n<assignment-fmt>\n# Target ← exact files and symbols; explicit non-goals\n# Change ← step-by-step add/remove/rename; APIs and patterns\n# Acceptance ← observable result; no project-wide commands\n</assignment-fmt>\n\n<agents>\nBundled role names: executor, architect, planner, critic. A configured agent may override a bundled role name and takes precedence.\nOther configured agents (project, user, plugin) may also be available; calling with an unknown `agent` lists the agents callable in this session.\n</agents>",
|
|
2100
2100
|
"parameters": {
|
|
2101
2101
|
"type": "object",
|
|
2102
2102
|
"properties": {
|
|
@@ -2509,7 +2509,7 @@ export const TOOL_CATALOG: Readonly<Record<string, ToolCatalogEntry>> = {
|
|
|
2509
2509
|
"irc": {
|
|
2510
2510
|
"name": "irc",
|
|
2511
2511
|
"label": "IRC",
|
|
2512
|
-
"description": "Sends short text messages to other live agents in this process and receives their prose replies.\n\n<instruction>\n- The main agent is addressable as `0-Main`. Subagents reuse their task id (e.g. `0-AuthLoader`).\n- `op: \"list\"` returns the current set of visible peers. Use it before sending if you are not sure who is live.\n- `op: \"send\"` delivers `message` to `to`. `to` may be a specific id or `\"all\"` to broadcast.\n- `awaitReply` (optional): wait for a prose reply. Defaults to `true` for DMs and `false` for `to: \"all\"` broadcasts.\n- The recipient generates the reply via an ephemeral side-channel turn that uses their current model, system prompt, and history — it does **not** wait for the recipient's main loop to be free, so it is safe to IRC an agent that is currently inside a long-running tool call.\n- The exchange (incoming question + auto-reply) is
|
|
2512
|
+
"description": "Sends short text messages to other live agents in this process and receives their prose replies.\n\n<instruction>\n- The main agent is addressable as `0-Main`. Subagents reuse their task id (e.g. `0-AuthLoader`).\n- `op: \"list\"` returns the current set of visible peers. Use it before sending if you are not sure who is live.\n- `op: \"send\"` delivers `message` to `to`. `to` may be a specific id or `\"all\"` to broadcast.\n- `awaitReply` (optional): wait for a prose reply. Defaults to `true` for DMs and `false` for `to: \"all\"` broadcasts.\n- The recipient generates the reply via an ephemeral side-channel turn that uses their current model, system prompt, and history — it does **not** wait for the recipient's main loop to be free, so it is safe to IRC an agent that is currently inside a long-running tool call.\n- The exchange (incoming question + auto-reply) is injected into the recipient's persisted history as soon as it can be delivered: an idle recipient gets it immediately, and a recipient that is already streaming receives it at its next step boundary instead of at the end of its turn. The recipient can then follow up if needed.\n</instruction>\n\n<conditions>\nYou SHOULD reach for `irc` proactively when continuing alone is wasteful or wrong. When in doubt, prefer messaging.\n- **Unexpected state.** You hit something the original task did not describe — a missing file, a config that contradicts the assignment, an API behaving differently than you were told, a tool failing in a way that suggests the spec is wrong. DM `0-Main` (or the spawning agent) for guidance instead of guessing.\n- **Blocked by another agent.** A peer holds the file/branch/resource you need, has already started the change you are about to make, or owns a decision you depend on. DM that peer (or broadcast to discover who) before duplicating or stepping on work.\n- **Decision points outside your scope.** A genuine fork in the road that the assignment did not pre-decide (e.g. which of two viable APIs to use, whether to refactor adjacent code). Ask the requester rather than picking unilaterally.\n- **Coordination opportunities.** Before editing a shared file or relying on another agent's in-flight API, message the relevant peer; proactively share state that affects their work.\n\nDo **not** use `irc` for: routine progress updates, things you can verify with a tool call, or questions whose answer is already in your assignment / repo / docs.\n</conditions>\n\n<etiquette>\nThese rules apply to both sending and replying.\n- **Plain prose only.** Do not send structured JSON status payloads (e.g. `{\"type\":\"task_completed\",…}`). Write a normal sentence: \"Done with the auth refactor — left a TODO in `src/server/auth.ts` for the rate limiter.\"\n- **Do not quote the message you are replying to.** The sender already saw it; the TUI already renders it. Lead with the answer.\n- **Use IRC, not terminal tools, to learn about peers.** Do not `search` artifacts, read other sessions' JSONL files, or shell-poke around to figure out what another agent is doing. DM them — they have the live answer and you do not.\n- **One round-trip is enough.** Replies arrive synchronously when the recipient is reachable. Do not follow up with \"did you get my message?\" — they did. If `delivered` is empty or the result was `failed`, the peer is unavailable; move on or report the blocker, do not retry in a loop.\n- **Stay terse.** A DM is a chat message, not a memo. One question per send when you can. Share file paths and artifacts via `local://` / `artifact://` URLs instead of pasting blobs.\n- **Address peers by id.** Use the exact id from `op: \"list\"` (e.g. `0-AuthLoader`, `0-Main`). Do not invent friendly names.\n- **Do not IRC for things a tool would answer.** If a `read`, `search`, or build command would resolve the question, do that first.\n- **When you receive an IRC message, answer it before continuing.** The recipient injects the question + your auto-reply into your history; address it directly, do not repeat it back to the user.\n</etiquette>\n\n<output>\n- `send`: returns each recipient that received the message and any prose replies that arrived.\n- `list`: returns peers and channels visible to the caller.\n</output>\n\n<examples>\n# List peers\n`{\"op\": \"list\"}`\n# Direct message to the main agent (waits for prose reply)\n`{\"op\": \"send\", \"to\": \"0-Main\", \"message\": \"Should I prefer JWT or session cookies for the auth flow?\"}`\n# Unexpected state — ask the originator\n`{\"op\": \"send\", \"to\": \"0-Main\", \"message\": \"Assignment says edit src/auth/jwt.ts but the file does not exist. Is the new path src/server/auth/jwt.ts?\"}`\n# Blocked by a peer — ask them directly\n`{\"op\": \"send\", \"to\": \"0-AuthLoader\", \"message\": \"Are you still touching src/server/auth.ts? I need to add a 401 path; OK to proceed or should I wait?\"}`\n# Broadcast to discover who owns something (no replies, just informs them)\n`{\"op\": \"send\", \"to\": \"all\", \"message\": \"About to refactor src/server/middleware/*. Anyone already in there?\", \"awaitReply\": false}`\n</examples>",
|
|
2513
2513
|
"parameters": {
|
|
2514
2514
|
"type": "object",
|
|
2515
2515
|
"properties": {
|
|
@@ -2955,6 +2955,34 @@ export const TOOL_CATALOG: Readonly<Record<string, ToolCatalogEntry>> = {
|
|
|
2955
2955
|
"concurrency": "exclusive",
|
|
2956
2956
|
"intent": "omit"
|
|
2957
2957
|
},
|
|
2958
|
+
"report_issue": {
|
|
2959
|
+
"name": "report_issue",
|
|
2960
|
+
"label": "Report Issue",
|
|
2961
|
+
"description": "Records a tool that behaved inconsistently with its own description, for local quality tracking.\n\n<instruction>\n- Call this when a tool's output contradicts what its description or parameters promise: ignored or misapplied parameters, wrong or truncated results, misleading errors, silent no-ops.\n- `tool` is the misbehaving tool's name; `report` states what you called, what you expected, and what actually happened, in one or two sentences.\n- Do not report your own mistakes, user-caused failures, or expected errors (missing files, failing tests, network outages).\n- Do not paste secrets, tokens, credentials, or raw tool output into report; summarize it instead.\n- False positives are acceptable. Report once per distinct problem, then continue the task; this never fixes or retries anything.\n</instruction>\n\n<output>\nConfirms whether the report was recorded. Reports stay on this machine.\n</output>",
|
|
2962
|
+
"parameters": {
|
|
2963
|
+
"type": "object",
|
|
2964
|
+
"properties": {
|
|
2965
|
+
"tool": {
|
|
2966
|
+
"type": "string",
|
|
2967
|
+
"description": "name of the tool that misbehaved"
|
|
2968
|
+
},
|
|
2969
|
+
"report": {
|
|
2970
|
+
"type": "string",
|
|
2971
|
+
"description": "what was called, what was expected, what happened"
|
|
2972
|
+
}
|
|
2973
|
+
},
|
|
2974
|
+
"required": [
|
|
2975
|
+
"tool",
|
|
2976
|
+
"report"
|
|
2977
|
+
],
|
|
2978
|
+
"additionalProperties": false
|
|
2979
|
+
},
|
|
2980
|
+
"strict": true,
|
|
2981
|
+
"deferrable": false,
|
|
2982
|
+
"loadMode": "essential",
|
|
2983
|
+
"summary": "Record a tool that behaved inconsistently with its description",
|
|
2984
|
+
"intent": "omit"
|
|
2985
|
+
},
|
|
2958
2986
|
"yield": {
|
|
2959
2987
|
"name": "yield",
|
|
2960
2988
|
"label": "Submit Result",
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { AgentTool } from "@gajae-code/agent-core";
|
|
2
|
+
import { resolveRalplanCurrentFinal, selectsRalplanApprovalGate } from "../gjc-runtime/ralplan-current-final";
|
|
2
3
|
import {
|
|
3
4
|
consumeUltragoalAskNudge,
|
|
4
5
|
isUltragoalAskBlocked,
|
|
@@ -33,6 +34,7 @@ export async function assertUltragoalAskAllowed(
|
|
|
33
34
|
cwd: string,
|
|
34
35
|
context: UltragoalAskGuardContext = {},
|
|
35
36
|
agentDir?: string,
|
|
37
|
+
pendingQuestions?: unknown,
|
|
36
38
|
): Promise<void> {
|
|
37
39
|
// Durable Ultragoal state is session-scoped, so only the caller's own run may
|
|
38
40
|
// block `ask`. An anonymous caller has no run attributable to it, so the guard
|
|
@@ -43,6 +45,16 @@ export async function assertUltragoalAskAllowed(
|
|
|
43
45
|
if (!sessionId) return;
|
|
44
46
|
const diagnostic = await isUltragoalAskBlocked(cwd, { sessionId });
|
|
45
47
|
if (!diagnostic.active) return;
|
|
48
|
+
// The one ask that may proceed while a goal is blocked is the execution
|
|
49
|
+
// approval for an authenticated current ralplan final (issue #6578). The
|
|
50
|
+
// request must select that gate and the final must validate on disk; a
|
|
51
|
+
// caller-declared tag alone never lifts the guard. AskTool still captures and
|
|
52
|
+
// authenticates the publication it records consent against.
|
|
53
|
+
if (selectsRalplanApprovalGate(pendingQuestions)) {
|
|
54
|
+
const resolution = await resolveRalplanCurrentFinal({ cwd, sessionId, agentDir });
|
|
55
|
+
if (resolution.ok) return;
|
|
56
|
+
throw new ToolError(`Ultragoal ask guard refused the ralplan execution approval: ${resolution.refusal.reason}`);
|
|
57
|
+
}
|
|
46
58
|
const nudge = await consumeUltragoalAskNudge(cwd, sessionId, agentDir);
|
|
47
59
|
if (nudge.nudged) throw new ToolError(nudge.message);
|
|
48
60
|
throw new ToolError(formatUltragoalAskBlockMessage(diagnostic));
|
|
@@ -66,7 +78,7 @@ export function guardToolForUltragoalAsk<T extends AgentTool>(
|
|
|
66
78
|
// budget against the SESSION profile, never the process-global one.
|
|
67
79
|
// The caller session id is the only binding, so a foreign session's
|
|
68
80
|
// durable run can never gate this ask.
|
|
69
|
-
await assertUltragoalAskAllowed(getCwd(), getContext(), getSessionAgentDir());
|
|
81
|
+
await assertUltragoalAskAllowed(getCwd(), getContext(), getSessionAgentDir(), args[1]);
|
|
70
82
|
return Reflect.apply(target.execute, target, args);
|
|
71
83
|
};
|
|
72
84
|
},
|
package/src/tools/write.ts
CHANGED
|
@@ -3,7 +3,7 @@ import * as fs from "node:fs/promises";
|
|
|
3
3
|
import type { AgentTool, AgentToolContext, AgentToolResult, AgentToolUpdateCallback } from "@gajae-code/agent-core";
|
|
4
4
|
import type { Component } from "@gajae-code/tui";
|
|
5
5
|
import { Text } from "@gajae-code/tui";
|
|
6
|
-
import { isEnoent, isRecord, prompt, untilAborted } from "@gajae-code/utils";
|
|
6
|
+
import { isEnoent, isRecord, pathIsWithin, prompt, untilAborted } from "@gajae-code/utils";
|
|
7
7
|
import * as z from "zod/v4";
|
|
8
8
|
import { stripHashlinePrefixes } from "../edit";
|
|
9
9
|
import type { RenderResultOptions } from "../extensibility/custom-tools/types";
|
|
@@ -177,6 +177,31 @@ function parseSqliteWriteTarget(subPath: string, queryString: string): { table:
|
|
|
177
177
|
return { table, key };
|
|
178
178
|
}
|
|
179
179
|
|
|
180
|
+
/**
|
|
181
|
+
* `new Database` follows symlinks. A lexical path inside the workspace can
|
|
182
|
+
* still name a file outside it, so open only the real path that stays inside
|
|
183
|
+
* the same session workspace an ordinary write resolves against.
|
|
184
|
+
*/
|
|
185
|
+
async function realSqliteDatabaseInWorkspace(
|
|
186
|
+
workspaceRoot: string,
|
|
187
|
+
absolutePath: string,
|
|
188
|
+
signal?: AbortSignal,
|
|
189
|
+
): Promise<string> {
|
|
190
|
+
signal?.throwIfAborted();
|
|
191
|
+
let realPath: string;
|
|
192
|
+
try {
|
|
193
|
+
realPath = await fs.realpath(absolutePath);
|
|
194
|
+
} catch (error) {
|
|
195
|
+
if (isEnoent(error)) throw error;
|
|
196
|
+
throw new ToolError(error instanceof Error ? error.message : String(error));
|
|
197
|
+
}
|
|
198
|
+
signal?.throwIfAborted();
|
|
199
|
+
if (!pathIsWithin(workspaceRoot, realPath)) {
|
|
200
|
+
throw new ToolError(`SQLite write target resolves outside the workspace: ${absolutePath}`);
|
|
201
|
+
}
|
|
202
|
+
return realPath;
|
|
203
|
+
}
|
|
204
|
+
|
|
180
205
|
/**
|
|
181
206
|
* Write tool implementation.
|
|
182
207
|
*
|
|
@@ -193,6 +218,13 @@ export class WriteTool implements AgentTool<typeof writeSchema, WriteToolDetails
|
|
|
193
218
|
readonly loadMode = "discoverable";
|
|
194
219
|
readonly summary = "Write content to a file (creates or overwrites)";
|
|
195
220
|
|
|
221
|
+
/** Stream matchers should see the real file content, not its JSON-escaped argument encoding. */
|
|
222
|
+
matcherDigest(args: unknown): string | undefined {
|
|
223
|
+
if (!args || typeof args !== "object" || Array.isArray(args)) return undefined;
|
|
224
|
+
const content = (args as Partial<WriteParams>).content;
|
|
225
|
+
return typeof content === "string" ? content : undefined;
|
|
226
|
+
}
|
|
227
|
+
|
|
196
228
|
readonly #writethrough: WritethroughCallback;
|
|
197
229
|
|
|
198
230
|
constructor(private readonly session: ToolSession) {
|
|
@@ -372,6 +404,7 @@ export class WriteTool implements AgentTool<typeof writeSchema, WriteToolDetails
|
|
|
372
404
|
displayPath: string,
|
|
373
405
|
content: string,
|
|
374
406
|
resolvedSqlitePath: ResolvedSqliteWritePath,
|
|
407
|
+
signal?: AbortSignal,
|
|
375
408
|
): Promise<AgentToolResult<WriteToolDetails>> {
|
|
376
409
|
let db: Database | null = null;
|
|
377
410
|
try {
|
|
@@ -379,7 +412,13 @@ export class WriteTool implements AgentTool<typeof writeSchema, WriteToolDetails
|
|
|
379
412
|
throw new ToolError(`SQLite database '${displayPath}' not found`);
|
|
380
413
|
}
|
|
381
414
|
|
|
382
|
-
|
|
415
|
+
const realDatabasePath = await realSqliteDatabaseInWorkspace(
|
|
416
|
+
this.session.cwd,
|
|
417
|
+
resolvedSqlitePath.absolutePath,
|
|
418
|
+
signal,
|
|
419
|
+
);
|
|
420
|
+
signal?.throwIfAborted();
|
|
421
|
+
db = new Database(realDatabasePath, { create: false, strict: true });
|
|
383
422
|
db.run("PRAGMA busy_timeout = 3000");
|
|
384
423
|
|
|
385
424
|
const trimmedContent = content.trim();
|
|
@@ -718,7 +757,7 @@ export class WriteTool implements AgentTool<typeof writeSchema, WriteToolDetails
|
|
|
718
757
|
if (resolvedSqlitePath) {
|
|
719
758
|
enforcePlanModeWrite(this.session, resolvedSqlitePath.sqlitePath, { op: "update" });
|
|
720
759
|
|
|
721
|
-
const sqliteResult = await this.#writeSqliteRow(path, cleanContent, resolvedSqlitePath);
|
|
760
|
+
const sqliteResult = await this.#writeSqliteRow(path, cleanContent, resolvedSqlitePath, signal);
|
|
722
761
|
if (stripped) {
|
|
723
762
|
const firstText = sqliteResult.content.find(
|
|
724
763
|
(block): block is { type: "text"; text: string } =>
|
package/src/tui/tree-list.ts
CHANGED
|
@@ -3,11 +3,25 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import { replaceTabs } from "@gajae-code/tui";
|
|
6
|
+
import { sanitizeText } from "@gajae-code/utils";
|
|
6
7
|
import type { Theme } from "../modes/theme/theme";
|
|
7
8
|
import { formatMoreItems } from "../tools/render-utils";
|
|
8
9
|
import type { TreeContext } from "./types";
|
|
9
10
|
import { getTreeBranch, getTreeContinuePrefix } from "./utils";
|
|
10
11
|
|
|
12
|
+
/**
|
|
13
|
+
* Tool renderers pass raw match lines or line groups into `renderItem`, which
|
|
14
|
+
* then applies theme colors. Strip controls first so OSC bytes are not wrapped
|
|
15
|
+
* into the styled string. Objects (file entries, todos) are left unchanged.
|
|
16
|
+
*/
|
|
17
|
+
function sanitizeTreeItem<T>(item: T): T {
|
|
18
|
+
if (typeof item === "string") return sanitizeText(item) as T;
|
|
19
|
+
if (Array.isArray(item)) {
|
|
20
|
+
return item.map(part => (typeof part === "string" ? sanitizeText(part) : part)) as T;
|
|
21
|
+
}
|
|
22
|
+
return item;
|
|
23
|
+
}
|
|
24
|
+
|
|
11
25
|
export interface TreeListOptions<T> {
|
|
12
26
|
items: T[];
|
|
13
27
|
expanded?: boolean;
|
|
@@ -32,7 +46,7 @@ export function renderTreeList<T>(options: TreeListOptions<T>, theme: Theme): st
|
|
|
32
46
|
// renderItem implementations MUST NOT vary line count based on isLast.
|
|
33
47
|
const preRendered: string[][] = [];
|
|
34
48
|
for (let i = 0; i < maxItems; i++) {
|
|
35
|
-
const rendered = renderItem(items[i], {
|
|
49
|
+
const rendered = renderItem(sanitizeTreeItem(items[i]!), {
|
|
36
50
|
index: i,
|
|
37
51
|
isLast: false,
|
|
38
52
|
depth: 0,
|
|
@@ -5,7 +5,8 @@
|
|
|
5
5
|
import type { ThinkingLevel } from "@gajae-code/agent-core";
|
|
6
6
|
import type { Api, Model } from "@gajae-code/ai/core";
|
|
7
7
|
import { completeSimple } from "@gajae-code/ai/core";
|
|
8
|
-
import { logger, prompt } from "@gajae-code/utils";
|
|
8
|
+
import { logger, prompt, redactCrashSecrets } from "@gajae-code/utils";
|
|
9
|
+
import { redactChangelogEntry } from "../commit/changelog";
|
|
9
10
|
import type { ModelRegistry } from "../config/model-registry";
|
|
10
11
|
import { resolveModelRoleValue } from "../config/model-resolver";
|
|
11
12
|
import type { Settings } from "../config/settings";
|
|
@@ -35,11 +36,17 @@ function filterDiffNoise(diff: string): string {
|
|
|
35
36
|
return filtered.join("\n");
|
|
36
37
|
}
|
|
37
38
|
|
|
39
|
+
/** Keep changelog patterns, then scrub known credential shapes on the whole string. */
|
|
40
|
+
function redactCommitMessageText(text: string): string {
|
|
41
|
+
return redactCrashSecrets(redactChangelogEntry(text));
|
|
42
|
+
}
|
|
43
|
+
|
|
38
44
|
function getModelCandidates(
|
|
39
45
|
registry: ModelRegistry,
|
|
40
46
|
settings: Settings,
|
|
47
|
+
credentialSessionId?: string,
|
|
41
48
|
): Array<{ model: Model<Api>; thinkingLevel?: ThinkingLevel }> {
|
|
42
|
-
const availableModels = registry.getAvailable();
|
|
49
|
+
const availableModels = registry.getAvailable(credentialSessionId);
|
|
43
50
|
if (availableModels.length === 0) return [];
|
|
44
51
|
|
|
45
52
|
const candidates: Array<{ model: Model<Api>; thinkingLevel?: ThinkingLevel }> = [];
|
|
@@ -74,7 +81,7 @@ export async function generateCommitMessage(
|
|
|
74
81
|
settings: Settings,
|
|
75
82
|
sessionId?: string,
|
|
76
83
|
): Promise<string | null> {
|
|
77
|
-
const candidates = getModelCandidates(registry, settings);
|
|
84
|
+
const candidates = getModelCandidates(registry, settings, sessionId);
|
|
78
85
|
if (candidates.length === 0) {
|
|
79
86
|
logger.debug("commit-msg-generator: no model found");
|
|
80
87
|
return null;
|
|
@@ -87,7 +94,7 @@ export async function generateCommitMessage(
|
|
|
87
94
|
logger.debug("commit-msg-generator: diff is empty after noise filtering");
|
|
88
95
|
return null;
|
|
89
96
|
}
|
|
90
|
-
const userMessage = `<diff>\n${truncatedDiff}\n</diff>`;
|
|
97
|
+
const userMessage = `<diff>\n${redactCommitMessageText(truncatedDiff)}\n</diff>`;
|
|
91
98
|
|
|
92
99
|
for (const candidate of candidates) {
|
|
93
100
|
const apiKey = await registry.getApiKey(candidate.model, sessionId);
|
|
@@ -120,6 +127,7 @@ export async function generateCommitMessage(
|
|
|
120
127
|
|
|
121
128
|
// Clean up: remove wrapping quotes, backticks, trailing period
|
|
122
129
|
msg = msg.replace(/^[`"']|[`"']$/g, "").replace(/\.$/, "");
|
|
130
|
+
msg = redactCommitMessageText(msg);
|
|
123
131
|
|
|
124
132
|
logger.debug("commit-msg-generator: generated", { model: candidate.model.id, msg });
|
|
125
133
|
return msg;
|
|
@@ -6,7 +6,7 @@ import { spawn } from "node:child_process";
|
|
|
6
6
|
import * as fs from "node:fs/promises";
|
|
7
7
|
import * as os from "node:os";
|
|
8
8
|
import * as path from "node:path";
|
|
9
|
-
import { $pickCredentialEnv
|
|
9
|
+
import { $pickCredentialEnv } from "@gajae-code/utils";
|
|
10
10
|
|
|
11
11
|
/** Returns the user's preferred editor command, or undefined if not configured. */
|
|
12
12
|
export function getEditorCommand(): string | undefined {
|
|
@@ -119,10 +119,22 @@ export async function openInEditor(
|
|
|
119
119
|
options?: OpenInEditorOptions,
|
|
120
120
|
): Promise<string | null> {
|
|
121
121
|
const ext = options?.extension ?? ".md";
|
|
122
|
-
|
|
122
|
+
// Random directory plus an exclusive create: a preexisting symlink under the
|
|
123
|
+
// temp directory is not opened, and the draft is not left world-readable.
|
|
124
|
+
let tempDir: string | undefined;
|
|
125
|
+
let tmpFile = "";
|
|
123
126
|
|
|
124
127
|
try {
|
|
125
|
-
await
|
|
128
|
+
tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "gjc-editor-"));
|
|
129
|
+
if (process.platform !== "win32") await fs.chmod(tempDir, 0o700);
|
|
130
|
+
tmpFile = path.join(tempDir, `draft${ext}`);
|
|
131
|
+
const handle = await fs.open(tmpFile, "wx", 0o600);
|
|
132
|
+
try {
|
|
133
|
+
await handle.writeFile(content);
|
|
134
|
+
} finally {
|
|
135
|
+
await handle.close();
|
|
136
|
+
}
|
|
137
|
+
if (process.platform !== "win32") await fs.chmod(tmpFile, 0o600);
|
|
126
138
|
|
|
127
139
|
const [editor, ...editorArgs] = editorCmd.split(" ");
|
|
128
140
|
const stdio = options?.stdio ?? ["inherit", "inherit", "inherit"];
|
|
@@ -147,10 +159,12 @@ export async function openInEditor(
|
|
|
147
159
|
}
|
|
148
160
|
return null;
|
|
149
161
|
} finally {
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
162
|
+
if (tempDir) {
|
|
163
|
+
try {
|
|
164
|
+
await fs.rm(tempDir, { recursive: true, force: true });
|
|
165
|
+
} catch {
|
|
166
|
+
// Ignore cleanup errors
|
|
167
|
+
}
|
|
154
168
|
}
|
|
155
169
|
}
|
|
156
170
|
}
|