@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/dap/session.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
import * as fs from "node:fs/promises";
|
|
1
2
|
import * as path from "node:path";
|
|
2
3
|
import * as timers from "node:timers/promises";
|
|
3
4
|
import { logger, untilAborted } from "@gajae-code/utils";
|
|
5
|
+
import { projectSafeSpawnEnv } from "@gajae-code/utils/shell-config";
|
|
4
6
|
import { NON_INTERACTIVE_ENV } from "../exec/non-interactive-env";
|
|
5
7
|
import { type OwnedProcess, spawnOwnedProcess } from "../runtime/process-lifecycle";
|
|
6
8
|
import { DapClient } from "./client";
|
|
@@ -106,9 +108,19 @@ interface DapSession {
|
|
|
106
108
|
initializedSeen: boolean;
|
|
107
109
|
needsConfigurationDone: boolean;
|
|
108
110
|
configurationDoneSent: boolean;
|
|
111
|
+
/** Containment root. Distinct from `cwd`, which is the caller execution directory. */
|
|
112
|
+
workspace: string;
|
|
113
|
+
closing: boolean;
|
|
114
|
+
disposed: boolean;
|
|
115
|
+
inflightTerminal: number;
|
|
116
|
+
terminalIdle?: Promise<void>;
|
|
117
|
+
resolveTerminalIdle?: () => void;
|
|
109
118
|
runInTerminalProcesses: Set<OwnedProcess>;
|
|
110
119
|
}
|
|
111
120
|
|
|
121
|
+
/** Test seam: pauses after cwd containment and before the debuggee spawn. */
|
|
122
|
+
export const runInTerminalTestHooks: { beforeSpawn?: () => Promise<void> } = {};
|
|
123
|
+
|
|
112
124
|
export interface DapOutputSnapshot {
|
|
113
125
|
snapshot: DapSessionSummary;
|
|
114
126
|
output: string;
|
|
@@ -210,6 +222,53 @@ function buildSummary(session: DapSession): DapSessionSummary {
|
|
|
210
222
|
};
|
|
211
223
|
}
|
|
212
224
|
|
|
225
|
+
function isEnoent(error: unknown): boolean {
|
|
226
|
+
return (error as NodeJS.ErrnoException | undefined)?.code === "ENOENT";
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/** Walk missing leaves so a not-yet-created debug cwd can still be contained. */
|
|
230
|
+
async function canonicalizeRunInTerminalCwd(absolute: string): Promise<string> {
|
|
231
|
+
try {
|
|
232
|
+
return await fs.realpath(absolute);
|
|
233
|
+
} catch (error) {
|
|
234
|
+
if (!isEnoent(error)) throw error;
|
|
235
|
+
}
|
|
236
|
+
const missing: string[] = [];
|
|
237
|
+
let cursor = absolute;
|
|
238
|
+
for (;;) {
|
|
239
|
+
const parent = path.dirname(cursor);
|
|
240
|
+
if (parent === cursor) throw new Error("runInTerminal cwd escapes the workspace");
|
|
241
|
+
missing.unshift(path.basename(cursor));
|
|
242
|
+
cursor = parent;
|
|
243
|
+
try {
|
|
244
|
+
return path.join(await fs.realpath(cursor), ...missing);
|
|
245
|
+
} catch (error) {
|
|
246
|
+
if (!isEnoent(error)) throw error;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
export function cwdEscapesWorkspace(root: string, candidate: string): boolean {
|
|
252
|
+
const relative = path.relative(root, candidate);
|
|
253
|
+
return relative === ".." || relative.startsWith(`..${path.sep}`) || path.isAbsolute(relative);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* The validated cwd is the cwd that is spawned. Relative paths resolve against
|
|
258
|
+
* the session workspace, then both sides are canonicalized before containment.
|
|
259
|
+
*/
|
|
260
|
+
async function resolveRunInTerminalCwd(workspace: string, requested: string | undefined): Promise<string> {
|
|
261
|
+
if (typeof requested === "string" && requested.trim() === "") {
|
|
262
|
+
throw new Error("runInTerminal cwd escapes the workspace");
|
|
263
|
+
}
|
|
264
|
+
const root = await fs.realpath(workspace);
|
|
265
|
+
const candidate = await canonicalizeRunInTerminalCwd(path.resolve(workspace, requested ?? workspace));
|
|
266
|
+
if (cwdEscapesWorkspace(root, candidate)) {
|
|
267
|
+
throw new Error("runInTerminal cwd escapes the workspace");
|
|
268
|
+
}
|
|
269
|
+
return candidate;
|
|
270
|
+
}
|
|
271
|
+
|
|
213
272
|
export class DapSessionManager {
|
|
214
273
|
#sessions = new Map<string, DapSession>();
|
|
215
274
|
#activeSessionId: string | null = null;
|
|
@@ -234,13 +293,19 @@ export class DapSessionManager {
|
|
|
234
293
|
}
|
|
235
294
|
|
|
236
295
|
async launch(
|
|
237
|
-
options: DapLaunchSessionOptions,
|
|
296
|
+
options: DapLaunchSessionOptions & { workspace?: string },
|
|
238
297
|
signal?: AbortSignal,
|
|
239
298
|
timeoutMs: number = 30_000,
|
|
240
299
|
): Promise<DapSessionSummary> {
|
|
241
300
|
await this.#ensureLaunchSlot();
|
|
242
301
|
const client = await DapClient.spawn({ adapter: options.adapter, cwd: options.cwd });
|
|
243
|
-
const session = this.#registerSession(
|
|
302
|
+
const session = this.#registerSession(
|
|
303
|
+
client,
|
|
304
|
+
options.adapter,
|
|
305
|
+
options.cwd,
|
|
306
|
+
options.workspace ?? options.cwd,
|
|
307
|
+
options.program,
|
|
308
|
+
);
|
|
244
309
|
try {
|
|
245
310
|
session.capabilities = await client.initialize(
|
|
246
311
|
this.#buildInitializeArguments(options.adapter),
|
|
@@ -299,13 +364,13 @@ export class DapSessionManager {
|
|
|
299
364
|
}
|
|
300
365
|
|
|
301
366
|
async attach(
|
|
302
|
-
options: DapAttachSessionOptions,
|
|
367
|
+
options: DapAttachSessionOptions & { workspace?: string },
|
|
303
368
|
signal?: AbortSignal,
|
|
304
369
|
timeoutMs: number = 30_000,
|
|
305
370
|
): Promise<DapSessionSummary> {
|
|
306
371
|
await this.#ensureLaunchSlot();
|
|
307
372
|
const client = await DapClient.spawn({ adapter: options.adapter, cwd: options.cwd });
|
|
308
|
-
const session = this.#registerSession(client, options.adapter, options.cwd);
|
|
373
|
+
const session = this.#registerSession(client, options.adapter, options.cwd, options.workspace ?? options.cwd);
|
|
309
374
|
try {
|
|
310
375
|
session.capabilities = await client.initialize(
|
|
311
376
|
this.#buildInitializeArguments(options.adapter),
|
|
@@ -886,24 +951,29 @@ export class DapSessionManager {
|
|
|
886
951
|
const session = this.#getActiveSessionOrNull();
|
|
887
952
|
if (!session) return null;
|
|
888
953
|
session.lastUsedAt = Date.now();
|
|
889
|
-
|
|
890
|
-
|
|
954
|
+
// Close before the first await. A runInTerminal paused on cwd resolution
|
|
955
|
+
// must not spawn while terminate or disconnect is still outstanding.
|
|
956
|
+
session.closing = true;
|
|
957
|
+
try {
|
|
958
|
+
if (session.status !== "terminated") {
|
|
959
|
+
if (session.capabilities?.supportsTerminateRequest) {
|
|
960
|
+
await untilAborted(
|
|
961
|
+
signal,
|
|
962
|
+
session.client.sendRequest("terminate", undefined, signal, timeoutMs).catch(() => undefined),
|
|
963
|
+
);
|
|
964
|
+
}
|
|
891
965
|
await untilAborted(
|
|
892
966
|
signal,
|
|
893
|
-
session.client
|
|
967
|
+
session.client
|
|
968
|
+
.sendRequest("disconnect", { terminateDebuggee: true }, signal, timeoutMs)
|
|
969
|
+
.catch(() => undefined),
|
|
894
970
|
);
|
|
895
971
|
}
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
.catch(() => undefined),
|
|
901
|
-
);
|
|
972
|
+
session.status = "terminated";
|
|
973
|
+
return buildSummary(session);
|
|
974
|
+
} finally {
|
|
975
|
+
await this.#disposeSession(session);
|
|
902
976
|
}
|
|
903
|
-
session.status = "terminated";
|
|
904
|
-
const summary = buildSummary(session);
|
|
905
|
-
await this.#disposeSession(session);
|
|
906
|
-
return summary;
|
|
907
977
|
}
|
|
908
978
|
|
|
909
979
|
#startCleanupTimer(): void {
|
|
@@ -945,12 +1015,22 @@ export class DapSessionManager {
|
|
|
945
1015
|
throw new Error(`Debug session ${active.id} is still active. Terminate it before launching another.`);
|
|
946
1016
|
}
|
|
947
1017
|
|
|
948
|
-
#registerSession(
|
|
1018
|
+
#registerSession(
|
|
1019
|
+
client: DapClient,
|
|
1020
|
+
adapter: DapResolvedAdapter,
|
|
1021
|
+
cwd: string,
|
|
1022
|
+
workspace: string,
|
|
1023
|
+
program?: string,
|
|
1024
|
+
): DapSession {
|
|
949
1025
|
const session: DapSession = {
|
|
950
1026
|
id: `debug-${++this.#nextId}`,
|
|
951
1027
|
adapter,
|
|
952
1028
|
cwd,
|
|
1029
|
+
workspace,
|
|
953
1030
|
program,
|
|
1031
|
+
closing: false,
|
|
1032
|
+
disposed: false,
|
|
1033
|
+
inflightTerminal: 0,
|
|
954
1034
|
client,
|
|
955
1035
|
status: "launching",
|
|
956
1036
|
launchedAt: Date.now(),
|
|
@@ -975,24 +1055,40 @@ export class DapSessionManager {
|
|
|
975
1055
|
if (!Array.isArray(args.args) || args.args.length === 0) {
|
|
976
1056
|
throw new Error("runInTerminal request did not include a command");
|
|
977
1057
|
}
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
1058
|
+
if (session.closing) throw new Error("runInTerminal session is closed");
|
|
1059
|
+
this.#beginTerminal(session);
|
|
1060
|
+
try {
|
|
1061
|
+
if (session.closing) throw new Error("runInTerminal session is closed");
|
|
1062
|
+
const env = Object.fromEntries(
|
|
1063
|
+
Object.entries(args.env ?? {}).filter((entry): entry is [string, string] => entry[1] !== null),
|
|
1064
|
+
);
|
|
1065
|
+
const cwd = await resolveRunInTerminalCwd(session.workspace, args.cwd ?? session.cwd);
|
|
1066
|
+
await runInTerminalTestHooks.beforeSpawn?.();
|
|
1067
|
+
if (session.closing || !this.#sessions.has(session.id)) {
|
|
1068
|
+
throw new Error("runInTerminal session is closed");
|
|
1069
|
+
}
|
|
1070
|
+
const owner = spawnOwnedProcess(args.args, {
|
|
1071
|
+
cwd,
|
|
1072
|
+
stdin: "ignore",
|
|
1073
|
+
env: {
|
|
1074
|
+
...projectSafeSpawnEnv(),
|
|
1075
|
+
...NON_INTERACTIVE_ENV,
|
|
1076
|
+
...env,
|
|
1077
|
+
},
|
|
1078
|
+
name: `dap:${session.id}:runInTerminal`,
|
|
1079
|
+
});
|
|
1080
|
+
if (session.closing || !this.#sessions.has(session.id)) {
|
|
1081
|
+
await owner.dispose();
|
|
1082
|
+
throw new Error("runInTerminal session is closed");
|
|
1083
|
+
}
|
|
1084
|
+
drainStream(owner.child.stdout);
|
|
1085
|
+
drainStream(owner.child.stderr);
|
|
1086
|
+
session.runInTerminalProcesses.add(owner);
|
|
1087
|
+
owner.exited.finally(() => session.runInTerminalProcesses.delete(owner));
|
|
1088
|
+
return { processId: owner.pid } satisfies DapRunInTerminalResponse;
|
|
1089
|
+
} finally {
|
|
1090
|
+
this.#endTerminal(session);
|
|
1091
|
+
}
|
|
996
1092
|
});
|
|
997
1093
|
client.onReverseRequest("startDebugging", async rawArgs => {
|
|
998
1094
|
const startArgs = (rawArgs ?? {}) as Partial<DapStartDebuggingArguments>;
|
|
@@ -1319,7 +1415,36 @@ export class DapSessionManager {
|
|
|
1319
1415
|
return session;
|
|
1320
1416
|
}
|
|
1321
1417
|
|
|
1418
|
+
#beginTerminal(session: DapSession): void {
|
|
1419
|
+
session.inflightTerminal++;
|
|
1420
|
+
if (session.inflightTerminal !== 1) return;
|
|
1421
|
+
session.terminalIdle = new Promise(resolve => {
|
|
1422
|
+
session.resolveTerminalIdle = resolve;
|
|
1423
|
+
});
|
|
1424
|
+
}
|
|
1425
|
+
|
|
1426
|
+
#endTerminal(session: DapSession): void {
|
|
1427
|
+
session.inflightTerminal = Math.max(0, session.inflightTerminal - 1);
|
|
1428
|
+
if (session.inflightTerminal !== 0) return;
|
|
1429
|
+
session.resolveTerminalIdle?.();
|
|
1430
|
+
session.resolveTerminalIdle = undefined;
|
|
1431
|
+
session.terminalIdle = undefined;
|
|
1432
|
+
}
|
|
1433
|
+
|
|
1434
|
+
async #waitTerminalIdle(session: DapSession): Promise<void> {
|
|
1435
|
+
while (session.inflightTerminal > 0 && session.terminalIdle) {
|
|
1436
|
+
await session.terminalIdle;
|
|
1437
|
+
}
|
|
1438
|
+
}
|
|
1439
|
+
|
|
1322
1440
|
async #disposeSession(session: DapSession) {
|
|
1441
|
+
session.closing = true;
|
|
1442
|
+
if (session.disposed) {
|
|
1443
|
+
await this.#waitTerminalIdle(session);
|
|
1444
|
+
return;
|
|
1445
|
+
}
|
|
1446
|
+
session.disposed = true;
|
|
1447
|
+
await this.#waitTerminalIdle(session);
|
|
1323
1448
|
if (this.#activeSessionId === session.id) {
|
|
1324
1449
|
this.#activeSessionId = null;
|
|
1325
1450
|
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
import { constants as fsConstants } from "node:fs";
|
|
1
2
|
import * as fs from "node:fs/promises";
|
|
2
3
|
import * as os from "node:os";
|
|
3
4
|
import * as path from "node:path";
|
|
5
|
+
import { redactCrashSecrets } from "@gajae-code/utils";
|
|
4
6
|
|
|
5
7
|
const CRASH_DIAGNOSTICS_ENV = "GJC_CRASH_DIAGNOSTICS";
|
|
6
8
|
const CRASH_DIAGNOSTICS_DIR_ENV = "GJC_CRASH_DIAGNOSTICS_DIR";
|
|
@@ -169,7 +171,7 @@ export async function writeCrashReport(
|
|
|
169
171
|
pid: process.pid,
|
|
170
172
|
cwd: options.cwd ?? process.cwd(),
|
|
171
173
|
...classification,
|
|
172
|
-
stderrPreview: input.stderr ? trimStartBytes(input.stderr, STDERR_PREVIEW_BYTES) : undefined,
|
|
174
|
+
stderrPreview: input.stderr ? trimStartBytes(redactCrashSecrets(input.stderr), STDERR_PREVIEW_BYTES) : undefined,
|
|
173
175
|
spawnError: input.spawnError === undefined ? undefined : stringifyError(input.spawnError),
|
|
174
176
|
};
|
|
175
177
|
const enabled = crashDiagnosticsEnabled(options.env);
|
|
@@ -179,10 +181,10 @@ export async function writeCrashReport(
|
|
|
179
181
|
}
|
|
180
182
|
|
|
181
183
|
try {
|
|
182
|
-
const
|
|
183
|
-
await ensurePrivateDiagnosticsDirectory(
|
|
184
|
+
const directory = diagnosticsDirectoryPath(getCrashDiagnosticsDirectory(options.env));
|
|
185
|
+
await ensurePrivateDiagnosticsDirectory(directory);
|
|
184
186
|
const filename = `${report.createdAt.replace(/[:.]/g, "-")}-${report.kind}-${report.class}-${process.pid}.json`;
|
|
185
|
-
const reportPath = path.join(
|
|
187
|
+
const reportPath = path.join(directory, filename);
|
|
186
188
|
await writePrivateCrashReport(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
187
189
|
return { report, path: reportPath, enabled };
|
|
188
190
|
} catch {
|
|
@@ -196,19 +198,100 @@ export function formatCrashDiagnosticNotice(result: CrashReportWriteResult): str
|
|
|
196
198
|
return `[crash:${result.report.kind}:${result.report.class}] ${result.report.reason}${location}`;
|
|
197
199
|
}
|
|
198
200
|
|
|
201
|
+
const POSIX_NOFOLLOW = process.platform === "win32" ? 0 : (fsConstants.O_NOFOLLOW ?? 0);
|
|
202
|
+
|
|
203
|
+
function isFilesystemRoot(dir: string): boolean {
|
|
204
|
+
const root = path.parse(dir).root;
|
|
205
|
+
return root.length > 0 && dir === root;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/** Drop trailing platform separators and "." segments. A backslash stays in the name on POSIX. ".." is unchanged. */
|
|
209
|
+
function diagnosticsDirectoryPath(dir: string): string {
|
|
210
|
+
const sep = path.sep;
|
|
211
|
+
let current = dir;
|
|
212
|
+
while (!isFilesystemRoot(current)) {
|
|
213
|
+
let end = current.length;
|
|
214
|
+
while (end > 0 && current[end - 1] === sep) end -= 1;
|
|
215
|
+
if (end !== current.length) {
|
|
216
|
+
const stripped = current.slice(0, end);
|
|
217
|
+
if (stripped === "" || isFilesystemRoot(stripped)) return path.parse(current).root || current;
|
|
218
|
+
if (process.platform === "win32" && /^[A-Za-z]:$/.test(stripped)) return `${stripped}\\`;
|
|
219
|
+
current = stripped;
|
|
220
|
+
continue;
|
|
221
|
+
}
|
|
222
|
+
const dotSuffix = `${sep}.`;
|
|
223
|
+
if (current.endsWith(dotSuffix) && !current.endsWith(`${sep}..`)) {
|
|
224
|
+
const parent = current.slice(0, -dotSuffix.length);
|
|
225
|
+
if (parent === "" || isFilesystemRoot(parent)) {
|
|
226
|
+
return parent === "" ? path.parse(current).root || current : parent;
|
|
227
|
+
}
|
|
228
|
+
if (process.platform === "win32" && /^[A-Za-z]:$/.test(parent)) return `${parent}\\`;
|
|
229
|
+
current = parent;
|
|
230
|
+
continue;
|
|
231
|
+
}
|
|
232
|
+
return current;
|
|
233
|
+
}
|
|
234
|
+
return current;
|
|
235
|
+
}
|
|
236
|
+
|
|
199
237
|
async function ensurePrivateDiagnosticsDirectory(dir: string): Promise<void> {
|
|
238
|
+
await assertRealDiagnosticsDirectory(dir);
|
|
200
239
|
await fs.mkdir(dir, { recursive: true, mode: DIRECTORY_MODE });
|
|
240
|
+
await assertRealDiagnosticsDirectory(dir);
|
|
241
|
+
await chmodRealDiagnosticsDirectory(dir);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
async function assertRealDiagnosticsDirectory(dir: string): Promise<void> {
|
|
245
|
+
let stat: Awaited<ReturnType<typeof fs.lstat>>;
|
|
246
|
+
try {
|
|
247
|
+
stat = await fs.lstat(dir);
|
|
248
|
+
} catch (error) {
|
|
249
|
+
if (typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT") return;
|
|
250
|
+
throw error;
|
|
251
|
+
}
|
|
252
|
+
if (stat.isSymbolicLink() || !stat.isDirectory()) {
|
|
253
|
+
throw new Error("crash diagnostics path is not a private directory");
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
async function chmodRealDiagnosticsDirectory(dir: string): Promise<void> {
|
|
258
|
+
const stat = await fs.lstat(dir);
|
|
259
|
+
if (stat.isSymbolicLink() || !stat.isDirectory()) {
|
|
260
|
+
throw new Error("crash diagnostics path is not a private directory");
|
|
261
|
+
}
|
|
262
|
+
// macOS lchmod sets the mode without read permission and without following a symlink.
|
|
263
|
+
// Linux has no equivalent here, so an unreadable directory is left unchanged. A path chmod would follow a swapped symlink.
|
|
264
|
+
if (process.platform === "darwin" && typeof fs.lchmod === "function") {
|
|
265
|
+
await fs.lchmod(dir, DIRECTORY_MODE);
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
268
|
+
const directoryFlag = fsConstants.O_DIRECTORY ?? 0;
|
|
269
|
+
const pathFlag = (fsConstants as { O_PATH?: number }).O_PATH ?? 0;
|
|
270
|
+
if (POSIX_NOFOLLOW !== 0 && directoryFlag !== 0) {
|
|
271
|
+
const openFlag = pathFlag !== 0 ? pathFlag : fsConstants.O_RDONLY;
|
|
272
|
+
const handle = await fs.open(dir, openFlag | directoryFlag | POSIX_NOFOLLOW);
|
|
273
|
+
try {
|
|
274
|
+
await handle.chmod(DIRECTORY_MODE);
|
|
275
|
+
} finally {
|
|
276
|
+
await handle.close();
|
|
277
|
+
}
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
201
280
|
await fs.chmod(dir, DIRECTORY_MODE);
|
|
202
281
|
}
|
|
203
282
|
|
|
204
283
|
async function writePrivateCrashReport(reportPath: string, contents: string): Promise<void> {
|
|
205
|
-
const file = await fs.open(
|
|
284
|
+
const file = await fs.open(
|
|
285
|
+
reportPath,
|
|
286
|
+
fsConstants.O_WRONLY | fsConstants.O_CREAT | fsConstants.O_EXCL | POSIX_NOFOLLOW,
|
|
287
|
+
REPORT_FILE_MODE,
|
|
288
|
+
);
|
|
206
289
|
try {
|
|
207
290
|
await file.writeFile(contents);
|
|
291
|
+
await file.chmod(REPORT_FILE_MODE);
|
|
208
292
|
} finally {
|
|
209
293
|
await file.close();
|
|
210
294
|
}
|
|
211
|
-
await fs.chmod(reportPath, REPORT_FILE_MODE);
|
|
212
295
|
}
|
|
213
296
|
|
|
214
297
|
function stringifyError(error: unknown): string {
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Creates a .tar.gz archive with session data, logs, system info, and optional profiling data.
|
|
5
5
|
*/
|
|
6
|
+
import { constants as fsConstants } from "node:fs";
|
|
6
7
|
import * as fs from "node:fs/promises";
|
|
7
8
|
import * as path from "node:path";
|
|
8
9
|
import type { WorkProfile } from "@gajae-code/natives";
|
|
@@ -13,7 +14,9 @@ import {
|
|
|
13
14
|
getLogPath,
|
|
14
15
|
getLogsDir,
|
|
15
16
|
getReportsDir,
|
|
17
|
+
hasFsCode,
|
|
16
18
|
isEnoent,
|
|
19
|
+
redactCrashSecrets,
|
|
17
20
|
resolveEquivalentPath,
|
|
18
21
|
} from "@gajae-code/utils";
|
|
19
22
|
import type { CpuProfile, HeapSnapshot } from "./profiler";
|
|
@@ -132,6 +135,72 @@ export interface DebugLogSource {
|
|
|
132
135
|
loadOlderLogs(limitDays?: number): Promise<string>;
|
|
133
136
|
}
|
|
134
137
|
|
|
138
|
+
/** Owner-only mode. umask cannot widen this, and fchmod repeats it after open. */
|
|
139
|
+
const REPORT_BUNDLE_MODE = 0o600;
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Open the archive path without following a final symlink.
|
|
143
|
+
* A missing path is created with O_EXCL. An existing regular file is replaced.
|
|
144
|
+
* A symlink fails the open (ELOOP, or EEXIST on macOS with O_EXCL) and is left in place.
|
|
145
|
+
*/
|
|
146
|
+
async function openReportBundleForWrite(outputPath: string): Promise<fs.FileHandle> {
|
|
147
|
+
const noFollow = process.platform === "win32" ? 0 : (fsConstants.O_NOFOLLOW ?? 0);
|
|
148
|
+
const createFlags = fsConstants.O_WRONLY | fsConstants.O_CREAT | fsConstants.O_EXCL | noFollow;
|
|
149
|
+
const replaceFlags = fsConstants.O_WRONLY | noFollow;
|
|
150
|
+
|
|
151
|
+
const openRegular = async (): Promise<fs.FileHandle> => {
|
|
152
|
+
const current = await fs.lstat(outputPath);
|
|
153
|
+
if (current.isSymbolicLink() || !current.isFile()) {
|
|
154
|
+
throw new Error("report bundle output is not a regular file");
|
|
155
|
+
}
|
|
156
|
+
try {
|
|
157
|
+
return await fs.open(outputPath, replaceFlags, REPORT_BUNDLE_MODE);
|
|
158
|
+
} catch (error) {
|
|
159
|
+
if (hasFsCode(error, "ELOOP")) throw new Error("report bundle output is not a regular file");
|
|
160
|
+
throw error;
|
|
161
|
+
}
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
let missing = false;
|
|
165
|
+
try {
|
|
166
|
+
await fs.lstat(outputPath);
|
|
167
|
+
} catch (error) {
|
|
168
|
+
if (!isEnoent(error)) throw error;
|
|
169
|
+
missing = true;
|
|
170
|
+
}
|
|
171
|
+
if (!missing) return openRegular();
|
|
172
|
+
|
|
173
|
+
try {
|
|
174
|
+
return await fs.open(outputPath, createFlags, REPORT_BUNDLE_MODE);
|
|
175
|
+
} catch (error) {
|
|
176
|
+
if (hasFsCode(error, "ELOOP")) throw new Error("report bundle output is not a regular file");
|
|
177
|
+
if (!hasFsCode(error, "EEXIST")) throw error;
|
|
178
|
+
}
|
|
179
|
+
return openRegular();
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
async function writeReportBundleFile(outputPath: string, bytes: Uint8Array): Promise<void> {
|
|
183
|
+
const handle = await openReportBundleForWrite(outputPath);
|
|
184
|
+
try {
|
|
185
|
+
const opened = await handle.stat();
|
|
186
|
+
const current = await fs.lstat(outputPath);
|
|
187
|
+
if (
|
|
188
|
+
current.isSymbolicLink() ||
|
|
189
|
+
!opened.isFile() ||
|
|
190
|
+
!current.isFile() ||
|
|
191
|
+
opened.dev !== current.dev ||
|
|
192
|
+
opened.ino !== current.ino
|
|
193
|
+
) {
|
|
194
|
+
throw new Error("report bundle output is not a regular file");
|
|
195
|
+
}
|
|
196
|
+
if (process.platform !== "win32") await handle.chmod(REPORT_BUNDLE_MODE);
|
|
197
|
+
await handle.truncate(0);
|
|
198
|
+
await handle.writeFile(bytes);
|
|
199
|
+
} finally {
|
|
200
|
+
await handle.close();
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
135
204
|
/**
|
|
136
205
|
* Create a debug report bundle.
|
|
137
206
|
*
|
|
@@ -228,12 +297,31 @@ export async function createReportBundle(options: ReportBundleOptions): Promise<
|
|
|
228
297
|
}
|
|
229
298
|
}
|
|
230
299
|
|
|
231
|
-
//
|
|
232
|
-
|
|
300
|
+
// Scrub the transcript copies only. Settings, system info, and profiles are
|
|
301
|
+
// separate payloads and stay as collected. Sources on disk are not rewritten.
|
|
302
|
+
for (const archivePath of Object.keys(data)) {
|
|
303
|
+
const content = data[archivePath];
|
|
304
|
+
if (content === undefined || !reportEntryCarriesUserText(archivePath)) continue;
|
|
305
|
+
data[archivePath] = redactCrashSecrets(content);
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
// Write archive. Bytes are built in memory so the path open can refuse a symlink.
|
|
309
|
+
const archiveBytes = await new Bun.Archive(data, { compress: "gzip" }).bytes();
|
|
310
|
+
await writeReportBundleFile(outputPath, archiveBytes);
|
|
233
311
|
|
|
234
312
|
return { path: outputPath, files };
|
|
235
313
|
}
|
|
236
314
|
|
|
315
|
+
/** Session, log, artifact, and subagent copies are the transcript the user may attach. */
|
|
316
|
+
function reportEntryCarriesUserText(archivePath: string): boolean {
|
|
317
|
+
return (
|
|
318
|
+
archivePath === "logs.txt" ||
|
|
319
|
+
archivePath === "session.jsonl" ||
|
|
320
|
+
archivePath.startsWith("artifacts/") ||
|
|
321
|
+
archivePath.startsWith("subagents/")
|
|
322
|
+
);
|
|
323
|
+
}
|
|
324
|
+
|
|
237
325
|
/** Add all files from a directory to the archive */
|
|
238
326
|
async function addDirectoryToArchive(
|
|
239
327
|
data: Record<string, string>,
|
package/src/debug/system-info.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import * as os from "node:os";
|
|
6
|
-
import { APP_NAME, formatBytes, getProjectDir, VERSION } from "@gajae-code/utils";
|
|
6
|
+
import { APP_NAME, formatBytes, getProjectDir, redactCrashSecrets, VERSION } from "@gajae-code/utils";
|
|
7
7
|
|
|
8
8
|
export interface SystemInfo {
|
|
9
9
|
os: string;
|
|
@@ -97,7 +97,12 @@ export function formatSystemInfo(info: SystemInfo): string {
|
|
|
97
97
|
return lines.join("\n");
|
|
98
98
|
}
|
|
99
99
|
|
|
100
|
-
/**
|
|
100
|
+
/**
|
|
101
|
+
* Sanitize environment variables for a debug report.
|
|
102
|
+
* Sensitive names stay fully replaced. Other values keep their text, with the
|
|
103
|
+
* same credential shapes removed that crash records already scrub before they
|
|
104
|
+
* are written to disk.
|
|
105
|
+
*/
|
|
101
106
|
export function sanitizeEnv(env: Record<string, string | undefined>): Record<string, string> {
|
|
102
107
|
const SENSITIVE_PATTERNS = [
|
|
103
108
|
/key/i,
|
|
@@ -117,7 +122,7 @@ export function sanitizeEnv(env: Record<string, string | undefined>): Record<str
|
|
|
117
122
|
for (const [k, v] of Object.entries(env)) {
|
|
118
123
|
if (v === undefined) continue;
|
|
119
124
|
const isSensitive = SENSITIVE_PATTERNS.some(p => p.test(k));
|
|
120
|
-
result[k] = isSensitive ? "[REDACTED]" : v;
|
|
125
|
+
result[k] = isSensitive ? "[REDACTED]" : redactCrashSecrets(v);
|
|
121
126
|
}
|
|
122
127
|
return result;
|
|
123
128
|
}
|
|
@@ -1,5 +1,8 @@
|
|
|
1
|
+
import { $credentialEnv, $pickCredentialEnv } from '@gajae-code/utils';
|
|
2
|
+
|
|
1
3
|
const DEFAULT_GROK_BUILD_BASE_URL = 'https://cli-chat-proxy.grok.com/v1';
|
|
2
4
|
const ALLOWED_GROK_BUILD_HOSTS = new Set(['cli-chat-proxy.grok.com']);
|
|
5
|
+
const UNSAFE_BASE_URL_ENV = 'GJC_GROK_CLI_ALLOW_UNSAFE_BASE_URL';
|
|
3
6
|
|
|
4
7
|
function normalizeBaseUrl(baseUrl: string): string {
|
|
5
8
|
return baseUrl.trim().replace(/\/+$/, '');
|
|
@@ -14,23 +17,34 @@ function isAllowedCredentialHost(baseUrl: string): boolean {
|
|
|
14
17
|
}
|
|
15
18
|
}
|
|
16
19
|
|
|
20
|
+
/**
|
|
21
|
+
* Operator environment only. `$credentialEnv` already drops a cwd dotenv value,
|
|
22
|
+
* including a dynamic declaration, using the snapshot taken when the env module
|
|
23
|
+
* loaded. A repository `.env` therefore cannot name the Grok Build base URL or
|
|
24
|
+
* approve that URL with the unsafe opt-in.
|
|
25
|
+
*/
|
|
26
|
+
function trustedConfiguredBaseUrl(): string | undefined {
|
|
27
|
+
return $pickCredentialEnv('GJC_GROK_CLI_BASE_URL', 'GROK_CLI_BASE_URL');
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function unsafeOverrideAllowed(): boolean {
|
|
31
|
+
return $credentialEnv(UNSAFE_BASE_URL_ENV) === '1';
|
|
32
|
+
}
|
|
33
|
+
|
|
17
34
|
export function getBaseUrl(): string {
|
|
18
|
-
const configured =
|
|
35
|
+
const configured = trustedConfiguredBaseUrl();
|
|
19
36
|
if (!configured) return DEFAULT_GROK_BUILD_BASE_URL;
|
|
20
37
|
|
|
21
38
|
const normalized = normalizeBaseUrl(configured);
|
|
22
39
|
if (isAllowedCredentialHost(normalized)) return normalized;
|
|
23
|
-
|
|
24
|
-
if (process.env.GJC_GROK_CLI_ALLOW_UNSAFE_BASE_URL === '1') return normalized;
|
|
40
|
+
if (unsafeOverrideAllowed()) return normalized;
|
|
25
41
|
|
|
26
42
|
return DEFAULT_GROK_BUILD_BASE_URL;
|
|
27
43
|
}
|
|
28
44
|
|
|
29
45
|
export function isGrokBuildBaseUrlOverrideIgnored(): boolean {
|
|
30
|
-
const configured =
|
|
46
|
+
const configured = trustedConfiguredBaseUrl();
|
|
31
47
|
if (!configured) return false;
|
|
32
48
|
const normalized = normalizeBaseUrl(configured);
|
|
33
|
-
return (
|
|
34
|
-
!isAllowedCredentialHost(normalized) && process.env.GJC_GROK_CLI_ALLOW_UNSAFE_BASE_URL !== '1'
|
|
35
|
-
);
|
|
49
|
+
return !isAllowedCredentialHost(normalized) && !unsafeOverrideAllowed();
|
|
36
50
|
}
|
|
@@ -43,7 +43,7 @@ async function readBoundedAgentsMdFile(
|
|
|
43
43
|
const homeIsStable = async (): Promise<boolean> => {
|
|
44
44
|
if (!options?.home || !options.homeIdentity) return true;
|
|
45
45
|
try {
|
|
46
|
-
const current = await fs.stat(options.home);
|
|
46
|
+
const current = await fs.stat(options.home, { bigint: true });
|
|
47
47
|
return current.dev === options.homeIdentity.dev && current.ino === options.homeIdentity.ino;
|
|
48
48
|
} catch {
|
|
49
49
|
return false;
|
package/src/discovery/builtin.ts
CHANGED
|
@@ -399,10 +399,11 @@ async function loadSlashCommands(ctx: LoadContext): Promise<LoadResult<SlashComm
|
|
|
399
399
|
const commandsDir = path.join(dir, "commands");
|
|
400
400
|
const result = await loadFilesFromDir<SlashCommand>(ctx, commandsDir, PROVIDER_ID, level, {
|
|
401
401
|
extensions: ["md"],
|
|
402
|
+
recursive: true,
|
|
402
403
|
scope: readScopeForLevel(level),
|
|
403
|
-
transform: (
|
|
404
|
-
name:
|
|
405
|
-
path,
|
|
404
|
+
transform: (_name, content, filePath, source) => ({
|
|
405
|
+
name: path.relative(commandsDir, filePath).replace(/\.md$/, "").split(path.sep).join(":"),
|
|
406
|
+
path: filePath,
|
|
406
407
|
content,
|
|
407
408
|
level,
|
|
408
409
|
_source: source,
|