@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/cli/update-cli.ts
CHANGED
|
@@ -10,8 +10,8 @@ import { randomUUID } from "node:crypto";
|
|
|
10
10
|
import * as fs from "node:fs";
|
|
11
11
|
import * as os from "node:os";
|
|
12
12
|
import * as path from "node:path";
|
|
13
|
-
import { exactReplaceRetained, exactUnlink } from "@gajae-code/natives";
|
|
14
|
-
import { $which, APP_NAME, isCompiledBinary, isEnoent, redactCrashSecrets, VERSION } from "@gajae-code/utils";
|
|
13
|
+
import { exactReplaceRetained, exactUnlink, exactUnlinkDirect } from "@gajae-code/natives";
|
|
14
|
+
import { $which, APP_NAME, isCompiledBinary, isEnoent, logger, redactCrashSecrets, VERSION } from "@gajae-code/utils";
|
|
15
15
|
import { $ } from "bun";
|
|
16
16
|
import chalk from "chalk";
|
|
17
17
|
import { acquireFileLock } from "../config/file-lock";
|
|
@@ -38,6 +38,7 @@ import {
|
|
|
38
38
|
createActivationRecordFile,
|
|
39
39
|
type DirectoryIdentity,
|
|
40
40
|
type FileIdentity,
|
|
41
|
+
type ReconcileResult,
|
|
41
42
|
readActivationRecord,
|
|
42
43
|
reconcileActivationRecord,
|
|
43
44
|
sameDirectoryIdentity,
|
|
@@ -108,6 +109,60 @@ export interface BinaryReplacementOptions {
|
|
|
108
109
|
verifyInstalledVersion: (expectedVersion: string) => Promise<InstalledVersionVerification>;
|
|
109
110
|
}
|
|
110
111
|
|
|
112
|
+
function formatRetainedActivationPaths(
|
|
113
|
+
paths:
|
|
114
|
+
| Pick<
|
|
115
|
+
ReconcileResult,
|
|
116
|
+
"detachedPath" | "retainedSuccessorPath" | "retainedPlaceholderPath" | "retainedUnknownPath"
|
|
117
|
+
>
|
|
118
|
+
| undefined,
|
|
119
|
+
): string {
|
|
120
|
+
if (!paths) return "";
|
|
121
|
+
const entries = [
|
|
122
|
+
paths.detachedPath !== undefined ? `detached=${paths.detachedPath}` : undefined,
|
|
123
|
+
paths.retainedSuccessorPath !== undefined ? `successor=${paths.retainedSuccessorPath}` : undefined,
|
|
124
|
+
paths.retainedPlaceholderPath !== undefined ? `placeholder=${paths.retainedPlaceholderPath}` : undefined,
|
|
125
|
+
paths.retainedUnknownPath !== undefined ? `unknown=${paths.retainedUnknownPath}` : undefined,
|
|
126
|
+
].filter((entry): entry is string => entry !== undefined);
|
|
127
|
+
return entries.length > 0 ? `; retained paths: ${entries.join("|")}` : "";
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export class InstallationAccessDeniedError extends Error {
|
|
131
|
+
readonly code = "access_denied";
|
|
132
|
+
|
|
133
|
+
constructor(
|
|
134
|
+
readonly targetPath: string,
|
|
135
|
+
readonly windowsErrorCode?: string,
|
|
136
|
+
readonly stagingCleanupWarning?: string,
|
|
137
|
+
readonly retainedPaths?: Pick<
|
|
138
|
+
ReconcileResult,
|
|
139
|
+
"detachedPath" | "retainedSuccessorPath" | "retainedPlaceholderPath" | "retainedUnknownPath"
|
|
140
|
+
>,
|
|
141
|
+
) {
|
|
142
|
+
super(
|
|
143
|
+
`access_denied during exact replacement of ${targetPath}${windowsErrorCode ? ` (${windowsErrorCode})` : ""}${stagingCleanupWarning ? `; ${stagingCleanupWarning}` : ""}${formatRetainedActivationPaths(retainedPaths)}`,
|
|
144
|
+
);
|
|
145
|
+
this.name = "InstallationAccessDeniedError";
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export class ActivationRetryCandidateRepointError extends Error {
|
|
150
|
+
readonly reasonCode: string;
|
|
151
|
+
|
|
152
|
+
constructor(
|
|
153
|
+
readonly candidatePath: string,
|
|
154
|
+
cause: unknown,
|
|
155
|
+
) {
|
|
156
|
+
const reasonCode =
|
|
157
|
+
cause instanceof Error && /^[a-z][a-z0-9_]{0,63}$/.test(cause.message)
|
|
158
|
+
? cause.message
|
|
159
|
+
: "activation_record_repoint_failed";
|
|
160
|
+
super(`${reasonCode}; retained retry candidate: ${candidatePath}`, { cause });
|
|
161
|
+
this.name = "ActivationRetryCandidateRepointError";
|
|
162
|
+
this.reasonCode = reasonCode;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
111
166
|
/**
|
|
112
167
|
* Parse update subcommand arguments.
|
|
113
168
|
* Returns undefined if not an update command.
|
|
@@ -232,6 +287,40 @@ function resolveNpmManagedTarget(
|
|
|
232
287
|
return undefined;
|
|
233
288
|
}
|
|
234
289
|
|
|
290
|
+
/**
|
|
291
|
+
* Detect a project-local package install: `<...>/node_modules/.bin/<name>` with
|
|
292
|
+
* or without an executable extension.
|
|
293
|
+
*
|
|
294
|
+
* On Windows a dependency-installed CLI is a real `.exe` copied into
|
|
295
|
+
* `node_modules/.bin`, so the `.cmd`/`.ps1` shim rules never match it and
|
|
296
|
+
* `gjc update` used to treat it as a standalone binary and try to replace it
|
|
297
|
+
* (#6842). That file belongs to the package manager and to the project's
|
|
298
|
+
* dependency tree: replacing it breaks the lockfile contract and, on Windows,
|
|
299
|
+
* fails on the file's ACL. The caller must redirect to the package manager
|
|
300
|
+
* instead of downloading a replacement.
|
|
301
|
+
*/
|
|
302
|
+
function resolveProjectLocalPackageInstall(
|
|
303
|
+
ompPath: string,
|
|
304
|
+
platform: PathPlatform = process.platform,
|
|
305
|
+
): { packageRoot: string } | undefined {
|
|
306
|
+
const pathApi = pathApiForPlatform(platform);
|
|
307
|
+
const resolved = pathApi.normalize(ompPath);
|
|
308
|
+
const binDir = pathApi.dirname(resolved);
|
|
309
|
+
if (pathApi.basename(binDir).toLowerCase() !== ".bin") return undefined;
|
|
310
|
+
if (pathApi.basename(pathApi.dirname(binDir)).toLowerCase() !== "node_modules") return undefined;
|
|
311
|
+
const extension = pathApi.extname(resolved).toLowerCase();
|
|
312
|
+
const basename = extension ? pathApi.basename(resolved, extension) : pathApi.basename(resolved);
|
|
313
|
+
if (basename.toLowerCase() !== APP_NAME.toLowerCase()) return undefined;
|
|
314
|
+
return { packageRoot: pathApi.dirname(binDir) };
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
export function resolveProjectLocalPackageInstallForTest(
|
|
318
|
+
ompPath: string,
|
|
319
|
+
platform: PathPlatform,
|
|
320
|
+
): { packageRoot: string } | undefined {
|
|
321
|
+
return resolveProjectLocalPackageInstall(ompPath, platform);
|
|
322
|
+
}
|
|
323
|
+
|
|
235
324
|
function resolveUpdateMethod(ompPath: string, bunBinDir: string | undefined): "bun" | "npm" | "binary" {
|
|
236
325
|
if (resolveNpmManagedTarget(ompPath)) return "npm";
|
|
237
326
|
if (!bunBinDir) return "binary";
|
|
@@ -346,6 +435,15 @@ async function resolveUpdateTarget(): Promise<UpdateTarget> {
|
|
|
346
435
|
);
|
|
347
436
|
}
|
|
348
437
|
|
|
438
|
+
const projectLocalInstall = ompPath ? resolveProjectLocalPackageInstall(ompPath) : undefined;
|
|
439
|
+
if (ompPath && projectLocalInstall) {
|
|
440
|
+
throw new Error(
|
|
441
|
+
formatUnsupportedTargetMessage(
|
|
442
|
+
`${APP_NAME} at ${ompPath} is installed by a package manager in this project's node_modules; update the dependency instead of replacing that file`,
|
|
443
|
+
),
|
|
444
|
+
);
|
|
445
|
+
}
|
|
446
|
+
|
|
349
447
|
if (ompPath && isShimPath(ompPath, bunBinDir)) {
|
|
350
448
|
if (isProtectedSourcePath(userPath)) {
|
|
351
449
|
throw new Error(formatUnsupportedTargetMessage(`Refusing to install over a source checkout at ${userPath}`));
|
|
@@ -782,6 +880,60 @@ export async function recoverWindowsUpdateJournal(journalPath: string): Promise<
|
|
|
782
880
|
throw new Error("legacy_update_journal_requires_manual_review");
|
|
783
881
|
}
|
|
784
882
|
|
|
883
|
+
async function removeExactStagingFile(filePath: string, identity: FileIdentity): Promise<void> {
|
|
884
|
+
const quarantineName = `.${path.basename(filePath)}.cleanup.${randomUUID()}`;
|
|
885
|
+
const removed = exactUnlink(filePath, toNativeIdentity(identity, quarantineName));
|
|
886
|
+
let pathStillExists = false;
|
|
887
|
+
let snapshotFailure: string | undefined;
|
|
888
|
+
try {
|
|
889
|
+
pathStillExists = (await snapshotRegularFile(filePath)) !== undefined;
|
|
890
|
+
} catch (error) {
|
|
891
|
+
snapshotFailure = error instanceof Error ? error.message : String(error);
|
|
892
|
+
}
|
|
893
|
+
const recoveryPaths = [
|
|
894
|
+
removed.detachedPath ? `detached=${removed.detachedPath}` : undefined,
|
|
895
|
+
removed.retainedPlaceholderPath ? `placeholder=${removed.retainedPlaceholderPath}` : undefined,
|
|
896
|
+
removed.retainedUnknownPath ? `unknown=${removed.retainedUnknownPath}` : undefined,
|
|
897
|
+
removed.retainedSuccessorPath ? `successor=${removed.retainedSuccessorPath}` : undefined,
|
|
898
|
+
].filter((value): value is string => value !== undefined);
|
|
899
|
+
if (
|
|
900
|
+
(!removed.ok && removed.code !== "not_found") ||
|
|
901
|
+
pathStillExists ||
|
|
902
|
+
snapshotFailure !== undefined ||
|
|
903
|
+
recoveryPaths.length > 0
|
|
904
|
+
) {
|
|
905
|
+
const reason =
|
|
906
|
+
removed.code ?? (pathStillExists ? "path_still_exists" : snapshotFailure ? "snapshot_failed" : "unverified");
|
|
907
|
+
const details = [
|
|
908
|
+
`path=${filePath}`,
|
|
909
|
+
...recoveryPaths,
|
|
910
|
+
...(snapshotFailure !== undefined ? [`snapshot=${snapshotFailure}`] : []),
|
|
911
|
+
].join("|");
|
|
912
|
+
throw new Error(`activation_retry_staging_cleanup_failed:${reason}:${details}`);
|
|
913
|
+
}
|
|
914
|
+
}
|
|
915
|
+
|
|
916
|
+
async function tryRemoveExactStagingFile(filePath: string, identity: FileIdentity): Promise<string | undefined> {
|
|
917
|
+
try {
|
|
918
|
+
await removeExactStagingFile(filePath, identity);
|
|
919
|
+
return undefined;
|
|
920
|
+
} catch (error) {
|
|
921
|
+
return error instanceof Error ? error.message : String(error);
|
|
922
|
+
}
|
|
923
|
+
}
|
|
924
|
+
|
|
925
|
+
async function repointActivationRecordToRetryCandidate(
|
|
926
|
+
record: ActivationRecordV1,
|
|
927
|
+
recordIdentity: FileIdentity,
|
|
928
|
+
candidatePath: string,
|
|
929
|
+
): Promise<FileIdentity> {
|
|
930
|
+
try {
|
|
931
|
+
return await updateActivationRecordFile(record, recordIdentity);
|
|
932
|
+
} catch (error) {
|
|
933
|
+
throw new ActivationRetryCandidateRepointError(candidatePath, error);
|
|
934
|
+
}
|
|
935
|
+
}
|
|
936
|
+
|
|
785
937
|
/**
|
|
786
938
|
* Atomically replace the installed binary and roll back if version verification fails.
|
|
787
939
|
*/
|
|
@@ -830,6 +982,7 @@ export async function replaceBinaryForUpdate(options: BinaryReplacementOptions):
|
|
|
830
982
|
}
|
|
831
983
|
let record: ActivationRecordV1;
|
|
832
984
|
let recordIdentity: FileIdentity;
|
|
985
|
+
let redundantStage: { readonly path: string; readonly identity: FileIdentity } | undefined;
|
|
833
986
|
if (existing.status === "valid" && existing.record.phase !== "verified" && existing.record.phase !== "rolled_back") {
|
|
834
987
|
if (
|
|
835
988
|
existing.record.candidate.digest !== (options.candidate?.sha256.toLowerCase() ?? staged?.identity.sha256) ||
|
|
@@ -839,8 +992,43 @@ export async function replaceBinaryForUpdate(options: BinaryReplacementOptions):
|
|
|
839
992
|
existing.record.candidate.channel !== options.candidate.channel))
|
|
840
993
|
)
|
|
841
994
|
throw new Error("activation_transaction_requires_reconciliation");
|
|
842
|
-
|
|
843
|
-
|
|
995
|
+
const currentStagingPath = path.resolve(options.tempPath);
|
|
996
|
+
const recordedStagingPath = path.resolve(existing.record.stagingPath);
|
|
997
|
+
if (currentStagingPath !== recordedStagingPath) {
|
|
998
|
+
const retryCandidate = staged ?? (await snapshotRegularFile(currentStagingPath));
|
|
999
|
+
if (retryCandidate && retryCandidate.identity.sha256 !== existing.record.candidate.digest)
|
|
1000
|
+
throw new Error("activation_retry_candidate_digest_mismatch");
|
|
1001
|
+
const activationNeedsStagedCandidate =
|
|
1002
|
+
existing.record.phase === "staged" || existing.record.phase === "pending_activation";
|
|
1003
|
+
const recordedStage = activationNeedsStagedCandidate
|
|
1004
|
+
? await snapshotRegularFile(recordedStagingPath)
|
|
1005
|
+
: undefined;
|
|
1006
|
+
const recordedStageIsValid =
|
|
1007
|
+
recordedStage &&
|
|
1008
|
+
existing.record.stagingIdentity &&
|
|
1009
|
+
sameFileIdentity(existing.record.stagingIdentity, recordedStage.identity) &&
|
|
1010
|
+
recordedStage.identity.sha256 === existing.record.candidate.digest;
|
|
1011
|
+
if (activationNeedsStagedCandidate && !recordedStageIsValid) {
|
|
1012
|
+
if (!retryCandidate) throw new Error("activation_retry_candidate_missing");
|
|
1013
|
+
record = {
|
|
1014
|
+
...existing.record,
|
|
1015
|
+
stagingPath: currentStagingPath,
|
|
1016
|
+
stagingIdentity: retryCandidate.identity,
|
|
1017
|
+
};
|
|
1018
|
+
recordIdentity = await repointActivationRecordToRetryCandidate(
|
|
1019
|
+
record,
|
|
1020
|
+
existing.identity,
|
|
1021
|
+
currentStagingPath,
|
|
1022
|
+
);
|
|
1023
|
+
} else {
|
|
1024
|
+
if (retryCandidate) redundantStage = { path: currentStagingPath, identity: retryCandidate.identity };
|
|
1025
|
+
record = existing.record;
|
|
1026
|
+
recordIdentity = existing.identity;
|
|
1027
|
+
}
|
|
1028
|
+
} else {
|
|
1029
|
+
record = existing.record;
|
|
1030
|
+
recordIdentity = existing.identity;
|
|
1031
|
+
}
|
|
844
1032
|
} else {
|
|
845
1033
|
if (!staged) throw new Error("installation_staging_unavailable");
|
|
846
1034
|
record = {
|
|
@@ -873,26 +1061,97 @@ export async function replaceBinaryForUpdate(options: BinaryReplacementOptions):
|
|
|
873
1061
|
allowPromotion: true,
|
|
874
1062
|
expectedRecordIdentity: recordIdentity,
|
|
875
1063
|
});
|
|
876
|
-
if (promoted.
|
|
1064
|
+
if (promoted.reason === "candidate_changed" && redundantStage) {
|
|
1065
|
+
logger.warn("Activation staging path changed during reconciliation; leaving it untouched", {
|
|
1066
|
+
changedStagingPath: record.stagingPath,
|
|
1067
|
+
retryCandidatePath: redundantStage.path,
|
|
1068
|
+
});
|
|
1069
|
+
await repointActivationRecordToRetryCandidate(
|
|
1070
|
+
{
|
|
1071
|
+
...record,
|
|
1072
|
+
phase: "pending_activation",
|
|
1073
|
+
stagingPath: redundantStage.path,
|
|
1074
|
+
stagingIdentity: redundantStage.identity,
|
|
1075
|
+
},
|
|
1076
|
+
recordIdentity,
|
|
1077
|
+
redundantStage.path,
|
|
1078
|
+
);
|
|
1079
|
+
throw new Error("installation_pending_activation");
|
|
1080
|
+
}
|
|
1081
|
+
if (promoted.status === "pending_activation") {
|
|
1082
|
+
if (promoted.reason === "candidate_missing" && redundantStage) {
|
|
1083
|
+
await repointActivationRecordToRetryCandidate(
|
|
1084
|
+
{
|
|
1085
|
+
...record,
|
|
1086
|
+
phase: "pending_activation",
|
|
1087
|
+
stagingPath: redundantStage.path,
|
|
1088
|
+
stagingIdentity: redundantStage.identity,
|
|
1089
|
+
},
|
|
1090
|
+
recordIdentity,
|
|
1091
|
+
redundantStage.path,
|
|
1092
|
+
);
|
|
1093
|
+
} else {
|
|
1094
|
+
const stagingCleanupWarning = redundantStage
|
|
1095
|
+
? await tryRemoveExactStagingFile(redundantStage.path, redundantStage.identity)
|
|
1096
|
+
: undefined;
|
|
1097
|
+
if (stagingCleanupWarning)
|
|
1098
|
+
logger.warn("Update staging cleanup failed while activation is pending", {
|
|
1099
|
+
warning: stagingCleanupWarning,
|
|
1100
|
+
});
|
|
1101
|
+
}
|
|
1102
|
+
throw new Error("installation_pending_activation");
|
|
1103
|
+
}
|
|
877
1104
|
if (
|
|
878
1105
|
(promoted.status !== "reconciled" &&
|
|
879
1106
|
promoted.status !== "applied_unverified" &&
|
|
880
1107
|
promoted.status !== "verified") ||
|
|
881
1108
|
!promoted.record ||
|
|
882
1109
|
!promoted.recordIdentity
|
|
883
|
-
)
|
|
1110
|
+
) {
|
|
1111
|
+
const stagingCleanupWarning = redundantStage
|
|
1112
|
+
? await tryRemoveExactStagingFile(redundantStage.path, redundantStage.identity)
|
|
1113
|
+
: undefined;
|
|
1114
|
+
if (promoted.reason === "access_denied")
|
|
1115
|
+
throw new InstallationAccessDeniedError(targetPath, promoted.windowsErrorCode, stagingCleanupWarning, {
|
|
1116
|
+
detachedPath: promoted.detachedPath,
|
|
1117
|
+
retainedSuccessorPath: promoted.retainedSuccessorPath,
|
|
1118
|
+
retainedPlaceholderPath: promoted.retainedPlaceholderPath,
|
|
1119
|
+
retainedUnknownPath: promoted.retainedUnknownPath,
|
|
1120
|
+
});
|
|
1121
|
+
if (stagingCleanupWarning)
|
|
1122
|
+
logger.warn("Update staging cleanup failed after activation reconciliation failed", {
|
|
1123
|
+
reason: promoted.reason ?? promoted.status,
|
|
1124
|
+
warning: stagingCleanupWarning,
|
|
1125
|
+
});
|
|
884
1126
|
throw new Error(promoted.reason ?? "installation_publication_unverified");
|
|
1127
|
+
}
|
|
885
1128
|
record = promoted.record;
|
|
886
1129
|
recordIdentity = promoted.recordIdentity;
|
|
1130
|
+
const cleanupWarnings: string[] = [];
|
|
887
1131
|
try {
|
|
888
|
-
|
|
1132
|
+
let verification = await options.verifyInstalledVersion(options.expectedVersion);
|
|
889
1133
|
if (!verification.ok) throw new Error(formatVerificationFailure(verification, options.expectedVersion));
|
|
890
1134
|
const after = await snapshotRegularFile(targetPath);
|
|
891
1135
|
if (!after || !record.targetIdentity || !sameFileIdentity(record.targetIdentity, after.identity))
|
|
892
1136
|
throw new Error("installation_postcheck_identity_changed");
|
|
1137
|
+
if (redundantStage) {
|
|
1138
|
+
const cleanupWarning = await tryRemoveExactStagingFile(redundantStage.path, redundantStage.identity);
|
|
1139
|
+
if (cleanupWarning) {
|
|
1140
|
+
cleanupWarnings.push(cleanupWarning);
|
|
1141
|
+
verification = { ...verification, cleanupWarning };
|
|
1142
|
+
}
|
|
1143
|
+
}
|
|
893
1144
|
await updateActivationRecordFile({ ...record, phase: "verified" }, recordIdentity);
|
|
894
1145
|
return verification;
|
|
895
1146
|
} catch (primary) {
|
|
1147
|
+
const rollbackCleanupWarning = redundantStage
|
|
1148
|
+
? await tryRemoveExactStagingFile(redundantStage.path, redundantStage.identity)
|
|
1149
|
+
: undefined;
|
|
1150
|
+
if (rollbackCleanupWarning) cleanupWarnings.push(rollbackCleanupWarning);
|
|
1151
|
+
if (cleanupWarnings.length > 0)
|
|
1152
|
+
logger.warn("Update staging cleanup failed before activation rollback", {
|
|
1153
|
+
warning: cleanupWarnings.join("\n"),
|
|
1154
|
+
});
|
|
896
1155
|
try {
|
|
897
1156
|
const after = await snapshotRegularFile(targetPath);
|
|
898
1157
|
if (!after || !record.targetIdentity || !sameFileIdentity(record.targetIdentity, after.identity))
|
|
@@ -1135,14 +1394,179 @@ function binaryStagingPath(targetPath: string, stamp: string): string {
|
|
|
1135
1394
|
return `${targetPath.slice(0, -extension.length)}.new.${stamp}${extension}`;
|
|
1136
1395
|
}
|
|
1137
1396
|
|
|
1397
|
+
const UPDATE_FILE_ID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/u;
|
|
1398
|
+
const EXACT_REPLACE_DESTINATION_NAME = /^\.gjc-exact-replace-destination-([0-9a-f]+)-([0-9a-f]+)$/u;
|
|
1399
|
+
// Do not race a separate process that is still completing a native replacement in this directory.
|
|
1400
|
+
const EXACT_REPLACE_CLEANUP_GRACE_MS = 10 * 60 * 1000;
|
|
1401
|
+
|
|
1402
|
+
function isUpdateStagingName(targetPath: string, name: string): boolean {
|
|
1403
|
+
const extension = path.extname(targetPath);
|
|
1404
|
+
const prefix = `${path.basename(targetPath, extension)}.new.`;
|
|
1405
|
+
if (!name.startsWith(prefix)) return false;
|
|
1406
|
+
const suffix = name.slice(prefix.length);
|
|
1407
|
+
if (extension && !suffix.endsWith(extension)) return false;
|
|
1408
|
+
const id = extension ? suffix.slice(0, -extension.length) : suffix;
|
|
1409
|
+
return UPDATE_FILE_ID.test(id);
|
|
1410
|
+
}
|
|
1411
|
+
|
|
1412
|
+
function isUpdateBackupName(targetPath: string, name: string): boolean {
|
|
1413
|
+
const prefix = `${path.basename(targetPath)}.bak.`;
|
|
1414
|
+
if (name.startsWith(prefix) && UPDATE_FILE_ID.test(name.slice(prefix.length))) return true;
|
|
1415
|
+
const legacyPrefix = `${path.basename(targetPath)}-`;
|
|
1416
|
+
if (!name.startsWith(legacyPrefix)) return false;
|
|
1417
|
+
const legacyName = name.slice(legacyPrefix.length);
|
|
1418
|
+
const marker = legacyName.indexOf(".bak.");
|
|
1419
|
+
if (marker < 0) return false;
|
|
1420
|
+
const version = legacyName.slice(0, marker);
|
|
1421
|
+
const timestamp = legacyName.slice(marker + ".bak.".length);
|
|
1422
|
+
return (
|
|
1423
|
+
/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/u.test(version) && /^[0-9A-Za-z._-]+$/u.test(timestamp)
|
|
1424
|
+
);
|
|
1425
|
+
}
|
|
1426
|
+
|
|
1427
|
+
export function isUpdateStagingNameForTest(targetPath: string, name: string): boolean {
|
|
1428
|
+
return isUpdateStagingName(targetPath, name);
|
|
1429
|
+
}
|
|
1430
|
+
|
|
1431
|
+
export function isUpdateBackupNameForTest(targetPath: string, name: string): boolean {
|
|
1432
|
+
return isUpdateBackupName(targetPath, name);
|
|
1433
|
+
}
|
|
1434
|
+
|
|
1435
|
+
async function removeExactUpdateArtifact(filePath: string, parent: DirectoryIdentity): Promise<string | undefined> {
|
|
1436
|
+
try {
|
|
1437
|
+
const snapshot = await snapshotRegularFile(filePath);
|
|
1438
|
+
if (!snapshot) return undefined;
|
|
1439
|
+
if (snapshot.identity.parentDev !== parent.dev || snapshot.identity.parentIno !== parent.ino)
|
|
1440
|
+
return `parent_identity_changed:${filePath}`;
|
|
1441
|
+
const removed = exactUnlinkDirect(
|
|
1442
|
+
filePath,
|
|
1443
|
+
toNativeIdentity(snapshot.identity, `.gjc-update-cleanup-${randomUUID()}`),
|
|
1444
|
+
);
|
|
1445
|
+
if (removed.ok || removed.code === "not_found") return undefined;
|
|
1446
|
+
const retained = [
|
|
1447
|
+
removed.detachedPath ? `detached=${removed.detachedPath}` : undefined,
|
|
1448
|
+
removed.retainedPlaceholderPath ? `placeholder=${removed.retainedPlaceholderPath}` : undefined,
|
|
1449
|
+
removed.retainedUnknownPath ? `unknown=${removed.retainedUnknownPath}` : undefined,
|
|
1450
|
+
removed.retainedSuccessorPath ? `successor=${removed.retainedSuccessorPath}` : undefined,
|
|
1451
|
+
].filter((entry): entry is string => entry !== undefined);
|
|
1452
|
+
return `exact_unlink_${removed.code ?? "failed"}:${filePath}${retained.length ? `|${retained.join("|")}` : ""}`;
|
|
1453
|
+
} catch (error) {
|
|
1454
|
+
return `snapshot_failed:${filePath}:${error instanceof Error ? error.message : String(error)}`;
|
|
1455
|
+
}
|
|
1456
|
+
}
|
|
1457
|
+
|
|
1458
|
+
/**
|
|
1459
|
+
* Reclaim only generated update artifacts while holding the install lock.
|
|
1460
|
+
* Keep a stage only while an active activation record owns it, and retain at
|
|
1461
|
+
* most the rollback backup the record can still need.
|
|
1462
|
+
*/
|
|
1463
|
+
async function cleanupBinaryUpdateArtifacts(
|
|
1464
|
+
targetPath: string,
|
|
1465
|
+
originalParent: DirectoryIdentity,
|
|
1466
|
+
retainedRetryCandidatePath?: string,
|
|
1467
|
+
): Promise<string | undefined> {
|
|
1468
|
+
const parentPath = path.dirname(path.resolve(targetPath));
|
|
1469
|
+
const warnings: string[] = [];
|
|
1470
|
+
try {
|
|
1471
|
+
const currentParent = await snapshotDirectory(parentPath);
|
|
1472
|
+
if (!sameDirectoryIdentity(originalParent, currentParent)) return "installation_parent_changed";
|
|
1473
|
+
const entries = await fs.promises.readdir(parentPath, { withFileTypes: true });
|
|
1474
|
+
const activation = await readActivationRecord(targetPath);
|
|
1475
|
+
if (activation.status === "malformed" || activation.status === "foreign") {
|
|
1476
|
+
warnings.push(`activation_record_untrusted:${activation.reason}`);
|
|
1477
|
+
} else {
|
|
1478
|
+
const record = activation.status === "valid" ? activation.record : undefined;
|
|
1479
|
+
const terminal = record?.phase === "verified" || record?.phase === "rolled_back";
|
|
1480
|
+
const pinnedStages = new Set<string>();
|
|
1481
|
+
if (record && !terminal) pinnedStages.add(path.resolve(record.stagingPath));
|
|
1482
|
+
if (retainedRetryCandidatePath) pinnedStages.add(path.resolve(retainedRetryCandidatePath));
|
|
1483
|
+
for (const entry of entries) {
|
|
1484
|
+
if (!isUpdateStagingName(targetPath, entry.name)) continue;
|
|
1485
|
+
const stagingPath = path.join(parentPath, entry.name);
|
|
1486
|
+
if (pinnedStages.has(stagingPath)) continue;
|
|
1487
|
+
const warning = await removeExactUpdateArtifact(stagingPath, originalParent);
|
|
1488
|
+
if (warning) warnings.push(warning);
|
|
1489
|
+
}
|
|
1490
|
+
|
|
1491
|
+
const recognizedBackup = record && isUpdateBackupName(targetPath, record.backupName);
|
|
1492
|
+
let preserveBackup: string | undefined;
|
|
1493
|
+
let mayPruneBackups = activation.status === "missing";
|
|
1494
|
+
if (record && recognizedBackup) {
|
|
1495
|
+
if (record.phase === "rolled_back" || !record.baseline.exists) {
|
|
1496
|
+
mayPruneBackups = true;
|
|
1497
|
+
} else if (record.phase === "verified" || record.phase === "published" || record.phase === "uncertain") {
|
|
1498
|
+
if (record.backupIdentity) {
|
|
1499
|
+
const baseline = await snapshotRegularFile(path.join(parentPath, record.backupName));
|
|
1500
|
+
if (baseline && sameFileIdentity(record.backupIdentity, baseline.identity)) {
|
|
1501
|
+
preserveBackup = record.backupName;
|
|
1502
|
+
mayPruneBackups = true;
|
|
1503
|
+
}
|
|
1504
|
+
}
|
|
1505
|
+
} else if (record.phase === "staged" || record.phase === "pending_activation") {
|
|
1506
|
+
const current = await snapshotRegularFile(targetPath);
|
|
1507
|
+
const baselineUnchanged = record.baseline.exists
|
|
1508
|
+
? !!current && !!record.targetIdentity && sameFileIdentity(record.targetIdentity, current.identity)
|
|
1509
|
+
: current === undefined;
|
|
1510
|
+
if (baselineUnchanged) {
|
|
1511
|
+
const transactionBackup = await snapshotRegularFile(path.join(parentPath, record.backupName));
|
|
1512
|
+
if (!transactionBackup) mayPruneBackups = true;
|
|
1513
|
+
else if (
|
|
1514
|
+
record.backupIdentity &&
|
|
1515
|
+
sameFileIdentity(record.backupIdentity, transactionBackup.identity)
|
|
1516
|
+
) {
|
|
1517
|
+
preserveBackup = record.backupName;
|
|
1518
|
+
mayPruneBackups = true;
|
|
1519
|
+
}
|
|
1520
|
+
}
|
|
1521
|
+
}
|
|
1522
|
+
}
|
|
1523
|
+
if (mayPruneBackups) {
|
|
1524
|
+
for (const entry of entries) {
|
|
1525
|
+
if (!isUpdateBackupName(targetPath, entry.name) || entry.name === preserveBackup) continue;
|
|
1526
|
+
const warning = await removeExactUpdateArtifact(path.join(parentPath, entry.name), originalParent);
|
|
1527
|
+
if (warning) warnings.push(warning);
|
|
1528
|
+
}
|
|
1529
|
+
}
|
|
1530
|
+
}
|
|
1531
|
+
|
|
1532
|
+
if (process.platform !== "win32") {
|
|
1533
|
+
const staleBefore = BigInt(Date.now() - EXACT_REPLACE_CLEANUP_GRACE_MS) * 1_000_000n;
|
|
1534
|
+
for (const entry of entries) {
|
|
1535
|
+
const match = EXACT_REPLACE_DESTINATION_NAME.exec(entry.name);
|
|
1536
|
+
if (!match) continue;
|
|
1537
|
+
const placeholderPath = path.join(parentPath, entry.name);
|
|
1538
|
+
const placeholder = await snapshotRegularFile(placeholderPath);
|
|
1539
|
+
if (!placeholder) continue;
|
|
1540
|
+
const { identity } = placeholder;
|
|
1541
|
+
if (
|
|
1542
|
+
identity.parentDev !== originalParent.dev ||
|
|
1543
|
+
identity.parentIno !== originalParent.ino ||
|
|
1544
|
+
identity.size !== "0" ||
|
|
1545
|
+
identity.nlink !== "1" ||
|
|
1546
|
+
BigInt(identity.ino).toString(16) !== match[1]
|
|
1547
|
+
)
|
|
1548
|
+
continue;
|
|
1549
|
+
// The current flow has returned from its synchronous native calls; older
|
|
1550
|
+
// process IDs get the grace window above before their residue is reaped.
|
|
1551
|
+
if (match[2] !== process.pid.toString(16) && BigInt(identity.mtimeNs) >= staleBefore) continue;
|
|
1552
|
+
const warning = await removeExactUpdateArtifact(placeholderPath, originalParent);
|
|
1553
|
+
if (warning) warnings.push(warning);
|
|
1554
|
+
}
|
|
1555
|
+
}
|
|
1556
|
+
} catch (error) {
|
|
1557
|
+
warnings.push(error instanceof Error ? error.message : String(error));
|
|
1558
|
+
}
|
|
1559
|
+
return warnings.length > 0 ? warnings.join("\n") : undefined;
|
|
1560
|
+
}
|
|
1561
|
+
|
|
1138
1562
|
/**
|
|
1139
1563
|
* Orchestrate download → fsync → replace → verify with a strict ordering
|
|
1140
1564
|
* contract: the downloaded temp binary MUST be flushed to stable storage
|
|
1141
1565
|
* before it is published (renamed into place) or exec'd for verification.
|
|
1142
1566
|
*
|
|
1143
1567
|
* If fsync fails the temp bytes are not durable, so we abort before
|
|
1144
|
-
* replacement/verification
|
|
1145
|
-
*
|
|
1568
|
+
* replacement/verification. Final cleanup removes the exact staging identity
|
|
1569
|
+
* only when no activation record still owns it.
|
|
1146
1570
|
*/
|
|
1147
1571
|
export async function runBinaryUpdateFlow(
|
|
1148
1572
|
targetPath: string,
|
|
@@ -1154,15 +1578,19 @@ export async function runBinaryUpdateFlow(
|
|
|
1154
1578
|
const tempPath = binaryStagingPath(targetPath, stamp);
|
|
1155
1579
|
const backupPath = `${targetPath}.bak.${stamp}`;
|
|
1156
1580
|
const releaseLock = await acquireBinaryUpdateLock(targetPath);
|
|
1581
|
+
let originalParent: DirectoryIdentity | undefined;
|
|
1582
|
+
let verification: InstalledVersionVerification | undefined;
|
|
1583
|
+
let failed = false;
|
|
1584
|
+
let failure: unknown;
|
|
1157
1585
|
try {
|
|
1158
1586
|
const originalTarget = await snapshotRegularFile(targetPath);
|
|
1159
|
-
|
|
1587
|
+
originalParent = await snapshotDirectory(path.dirname(targetPath));
|
|
1160
1588
|
if (!originalParent) throw new Error("installation_parent_unavailable");
|
|
1161
1589
|
await flow.download(url, tempPath);
|
|
1162
1590
|
await flow.fsync(tempPath);
|
|
1163
1591
|
|
|
1164
1592
|
flow.beforeReplace?.();
|
|
1165
|
-
|
|
1593
|
+
verification = await flow.replace({
|
|
1166
1594
|
targetPath,
|
|
1167
1595
|
tempPath,
|
|
1168
1596
|
backupPath,
|
|
@@ -1171,9 +1599,38 @@ export async function runBinaryUpdateFlow(
|
|
|
1171
1599
|
originalParent,
|
|
1172
1600
|
verifyInstalledVersion: flow.verifyInstalledVersion,
|
|
1173
1601
|
});
|
|
1174
|
-
}
|
|
1602
|
+
} catch (error) {
|
|
1603
|
+
failed = true;
|
|
1604
|
+
failure = error;
|
|
1605
|
+
}
|
|
1606
|
+
const retainedRetryCandidatePath =
|
|
1607
|
+
failure instanceof ActivationRetryCandidateRepointError ? failure.candidatePath : undefined;
|
|
1608
|
+
const cleanupWarning = originalParent
|
|
1609
|
+
? await cleanupBinaryUpdateArtifacts(targetPath, originalParent, retainedRetryCandidatePath)
|
|
1610
|
+
: undefined;
|
|
1611
|
+
try {
|
|
1175
1612
|
await releaseLock();
|
|
1613
|
+
} catch (error) {
|
|
1614
|
+
if (!failed) {
|
|
1615
|
+
failed = true;
|
|
1616
|
+
failure = error;
|
|
1617
|
+
} else {
|
|
1618
|
+
logger.warn("Update install lock cleanup failed", {
|
|
1619
|
+
warning: error instanceof Error ? error.message : String(error),
|
|
1620
|
+
});
|
|
1621
|
+
}
|
|
1176
1622
|
}
|
|
1623
|
+
if (failed) {
|
|
1624
|
+
if (cleanupWarning)
|
|
1625
|
+
logger.warn("Update artifact cleanup failed after unsuccessful update", { warning: cleanupWarning });
|
|
1626
|
+
throw failure;
|
|
1627
|
+
}
|
|
1628
|
+
if (!verification) throw new Error("binary_update_verification_missing");
|
|
1629
|
+
if (!cleanupWarning) return verification;
|
|
1630
|
+
return {
|
|
1631
|
+
...verification,
|
|
1632
|
+
cleanupWarning: [verification.cleanupWarning, cleanupWarning].filter(Boolean).join("\n"),
|
|
1633
|
+
};
|
|
1177
1634
|
}
|
|
1178
1635
|
|
|
1179
1636
|
export async function acquireBinaryUpdateLock(targetPath: string): Promise<() => Promise<void>> {
|
|
@@ -1286,7 +1743,8 @@ async function updateViaBinaryAt(
|
|
|
1286
1743
|
beforeReplace: () => console.log(chalk.dim("Installing update...")),
|
|
1287
1744
|
});
|
|
1288
1745
|
|
|
1289
|
-
if (verification.cleanupWarning)
|
|
1746
|
+
if (verification.cleanupWarning)
|
|
1747
|
+
logger.warn("Update staging cleanup failed", { warning: verification.cleanupWarning });
|
|
1290
1748
|
return verification;
|
|
1291
1749
|
}
|
|
1292
1750
|
|
package/src/cli-ordinary.ts
CHANGED
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
* This module owns the effectful bootstrap (startup timing, stderr drainer,
|
|
4
4
|
* bash-shell worker protocol) plus the unchanged ordinary routing order:
|
|
5
5
|
* bash-shell workers -> darwin malloc re-exec -> internal smoke -> tmux owner
|
|
6
|
-
* isolation -> managed-owner supervisor/admission/recovery ->
|
|
7
|
-
* stderr drainer -> public family dispatch -> community app -> doctor worker /
|
|
6
|
+
* isolation -> managed-owner supervisor/admission/recovery -> broker hop ->
|
|
7
|
+
* private sdk stderr drainer -> public family dispatch -> community app -> doctor worker /
|
|
8
8
|
* probe / cli -> full registry. `cli.ts` imports it lazily, only after argv has
|
|
9
9
|
* been proven not to be a `sdk diagnostics` observation. */
|
|
10
10
|
|
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
BASH_SHELL_SUPERVISOR_ARG,
|
|
17
17
|
BASH_SHELL_WORKER_ARG,
|
|
18
18
|
} from "./exec/bash-shell-worker-protocol";
|
|
19
|
+
import { runBrokerHopFromArgv } from "./sdk/broker/hop";
|
|
19
20
|
import { runSdkStderrDrainerFromArgv } from "./sdk/broker/stderr-drainer";
|
|
20
21
|
|
|
21
22
|
export { commands };
|
|
@@ -128,6 +129,10 @@ export async function runOrdinaryCli(argv: string[]): Promise<void> {
|
|
|
128
129
|
return;
|
|
129
130
|
}
|
|
130
131
|
}
|
|
132
|
+
if (argv.length >= 2 && argv[0] === "internal" && argv[1] === "broker-hop") {
|
|
133
|
+
await runBrokerHopFromArgv(argv.slice(2));
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
131
136
|
if (argv[0] === "sdk" && argv[1] === "stderr-drain-internal") {
|
|
132
137
|
// Private lifecycle stderr drainer: must stay ahead of the public sdk family
|
|
133
138
|
// dispatcher, which would otherwise reject the private argv as a usage error.
|
package/src/commands/accounts.ts
CHANGED
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
} from "../cli/accounts-cli";
|
|
9
9
|
|
|
10
10
|
export default class Accounts extends Command {
|
|
11
|
-
static description = "List, check, pin, and remove stored OAuth accounts";
|
|
11
|
+
static description = "List, check, pin, prefer, and remove stored OAuth accounts";
|
|
12
12
|
static strict = true;
|
|
13
13
|
|
|
14
14
|
static args = {
|
|
@@ -22,16 +22,18 @@ export default class Accounts extends Command {
|
|
|
22
22
|
required: false,
|
|
23
23
|
}),
|
|
24
24
|
selector: Args.string({
|
|
25
|
-
description:
|
|
25
|
+
description:
|
|
26
|
+
"Account selector (bare email, id:, email:, or account:); prefer takes several in order, each optionally =<max used percent>",
|
|
26
27
|
required: false,
|
|
28
|
+
multiple: true,
|
|
27
29
|
}),
|
|
28
30
|
};
|
|
29
31
|
|
|
30
32
|
static flags = {
|
|
31
33
|
json: Flags.boolean({ description: "Output safe machine-readable JSON" }),
|
|
32
34
|
persistent: Flags.boolean({ description: "Required for persistent pin changes" }),
|
|
33
|
-
clear: Flags.boolean({ description: "Clear the persistent pin" }),
|
|
34
|
-
account: Flags.string({ description: "Logout one account by row id or
|
|
35
|
+
clear: Flags.boolean({ description: "Clear the persistent pin or preferred accounts" }),
|
|
36
|
+
account: Flags.string({ description: "Logout one account by row id, email, or org:<organization id or name>" }),
|
|
35
37
|
all: Flags.boolean({ description: "Logout every OAuth account for the provider" }),
|
|
36
38
|
};
|
|
37
39
|
|
|
@@ -42,7 +44,10 @@ export default class Accounts extends Command {
|
|
|
42
44
|
"gjc accounts check anthropic --json",
|
|
43
45
|
"gjc accounts pin anthropic me@example.com --persistent",
|
|
44
46
|
"gjc accounts pin anthropic id:42 --persistent",
|
|
47
|
+
"gjc accounts pin anthropic 'org:Acme Team' --persistent",
|
|
45
48
|
"gjc accounts pin anthropic --clear --persistent",
|
|
49
|
+
"gjc accounts prefer anthropic cheap@example.com shared@example.com=75",
|
|
50
|
+
"gjc accounts prefer anthropic --clear",
|
|
46
51
|
"gjc accounts logout anthropic --account me@example.com",
|
|
47
52
|
"gjc accounts logout anthropic --all",
|
|
48
53
|
];
|
|
@@ -57,7 +62,7 @@ export default class Accounts extends Command {
|
|
|
57
62
|
const cmd: AccountsCommandArgs = {
|
|
58
63
|
action,
|
|
59
64
|
provider: args.provider,
|
|
60
|
-
selector: args.selector,
|
|
65
|
+
selectors: Array.isArray(args.selector) ? args.selector : args.selector ? [args.selector] : undefined,
|
|
61
66
|
flags: {
|
|
62
67
|
json: flags.json,
|
|
63
68
|
persistent: flags.persistent,
|