@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
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { Database, type Statement } from "bun:sqlite";
|
|
2
2
|
import * as fs from "node:fs";
|
|
3
|
-
import * as fsPromises from "node:fs/promises";
|
|
4
3
|
import * as path from "node:path";
|
|
5
4
|
import { getHistoryDbPath, logger } from "@gajae-code/utils";
|
|
6
5
|
|
|
@@ -26,6 +25,155 @@ function escapeLikePattern(text: string): string {
|
|
|
26
25
|
return text.replace(/[\\%_]/g, "\\$&");
|
|
27
26
|
}
|
|
28
27
|
|
|
28
|
+
const HISTORY_SUFFIXES = ["", "-wal", "-shm"] as const;
|
|
29
|
+
let openingHistory: Database | undefined;
|
|
30
|
+
|
|
31
|
+
function secureHistoryPath(pathname: string, kind: "directory" | "file"): void {
|
|
32
|
+
if (process.platform !== "win32") return;
|
|
33
|
+
const native = require("@gajae-code/natives") as typeof import("@gajae-code/natives");
|
|
34
|
+
const applied = native.applyOwnerOnlyPathSecurity(pathname, kind);
|
|
35
|
+
const verified = native.verifyOwnerOnlyPathSecurity(pathname, kind);
|
|
36
|
+
if (applied?.ok !== true || verified?.ok !== true) {
|
|
37
|
+
throw new Error("history database owner-only protection could not be established");
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function historyErrno(error: unknown): string | undefined {
|
|
42
|
+
return (error as NodeJS.ErrnoException | undefined)?.code;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function historyLeaf(dbPath: string, suffix: string): fs.BigIntStats | undefined {
|
|
46
|
+
try {
|
|
47
|
+
return fs.lstatSync(`${dbPath}${suffix}`, { bigint: true });
|
|
48
|
+
} catch (error) {
|
|
49
|
+
if (historyErrno(error) !== "ENOENT") throw error;
|
|
50
|
+
return undefined;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function assertRegularHistoryLeaf(existing: fs.BigIntStats | undefined): void {
|
|
55
|
+
if (existing && (existing.isSymbolicLink() || !existing.isFile())) {
|
|
56
|
+
throw new Error("history database path is not a regular file");
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function assertHistoryLeaves(dbPath: string): Set<string> {
|
|
61
|
+
const present = new Set<string>();
|
|
62
|
+
for (const suffix of HISTORY_SUFFIXES) {
|
|
63
|
+
const existing = historyLeaf(dbPath, suffix);
|
|
64
|
+
assertRegularHistoryLeaf(existing);
|
|
65
|
+
if (existing) present.add(suffix);
|
|
66
|
+
}
|
|
67
|
+
return present;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function removeNewHistoryLeaves(): void {
|
|
71
|
+
// A WAL reader can stay live while BEGIN EXCLUSIVE succeeds, so failure
|
|
72
|
+
// cleanup must not unlink a database another opener has adopted.
|
|
73
|
+
try {
|
|
74
|
+
openingHistory?.close();
|
|
75
|
+
} catch {
|
|
76
|
+
// The caller reports the original open failure.
|
|
77
|
+
}
|
|
78
|
+
openingHistory = undefined;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function readHistoryDirectory(dir: string): fs.Stats | undefined {
|
|
82
|
+
let existing: fs.Stats;
|
|
83
|
+
try {
|
|
84
|
+
existing = fs.lstatSync(dir);
|
|
85
|
+
} catch (error) {
|
|
86
|
+
if (historyErrno(error) !== "ENOENT") throw error;
|
|
87
|
+
return undefined;
|
|
88
|
+
}
|
|
89
|
+
if (existing.isSymbolicLink() || !existing.isDirectory()) {
|
|
90
|
+
throw new Error("history database directory is not a real directory");
|
|
91
|
+
}
|
|
92
|
+
return existing;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function tightenHistoryDirectory(dir: string): void {
|
|
96
|
+
let existing = readHistoryDirectory(dir);
|
|
97
|
+
if (!existing) {
|
|
98
|
+
try {
|
|
99
|
+
fs.mkdirSync(dir, { recursive: true, mode: 0o700 });
|
|
100
|
+
} catch (error) {
|
|
101
|
+
if (historyErrno(error) !== "EEXIST") throw error;
|
|
102
|
+
}
|
|
103
|
+
existing = readHistoryDirectory(dir);
|
|
104
|
+
if (!existing) throw new Error("history database directory is not a real directory");
|
|
105
|
+
}
|
|
106
|
+
secureHistoryPath(dir, "directory");
|
|
107
|
+
if (process.platform === "win32") return;
|
|
108
|
+
// 0300/0700 are already owner-only. Sticky directories are shared and stay as they are.
|
|
109
|
+
// chmod(2) needs no read bit, so mode 0333 becomes 0700. A symlink swap is
|
|
110
|
+
// followed by chmod; the recheck then refuses and no database file is created.
|
|
111
|
+
if ((existing.mode & 0o077) === 0 || (existing.mode & 0o1000) !== 0) return;
|
|
112
|
+
try {
|
|
113
|
+
fs.chmodSync(dir, 0o700);
|
|
114
|
+
} catch (error) {
|
|
115
|
+
if (historyErrno(error) === "ELOOP") throw new Error("history database directory is not a real directory");
|
|
116
|
+
throw error;
|
|
117
|
+
}
|
|
118
|
+
const after = readHistoryDirectory(dir);
|
|
119
|
+
if (!after || (after.mode & 0o077) !== 0) {
|
|
120
|
+
throw new Error("history database directory could not be tightened to mode 0700");
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function sameHistoryFile(left: fs.BigIntStats, right: fs.BigIntStats): boolean {
|
|
125
|
+
return left.dev === right.dev && left.ino === right.ino && left.nlink === right.nlink;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function restrictHistoryFiles(dbPath: string, createMissingDb: boolean): void {
|
|
129
|
+
const noFollow = process.platform === "win32" ? 0 : (fs.constants.O_NOFOLLOW ?? 0);
|
|
130
|
+
for (const suffix of HISTORY_SUFFIXES) {
|
|
131
|
+
const target = `${dbPath}${suffix}`;
|
|
132
|
+
let existing = historyLeaf(dbPath, suffix);
|
|
133
|
+
assertRegularHistoryLeaf(existing);
|
|
134
|
+
if (!existing && suffix === "" && createMissingDb) {
|
|
135
|
+
let created: number | undefined;
|
|
136
|
+
try {
|
|
137
|
+
created = fs.openSync(
|
|
138
|
+
target,
|
|
139
|
+
fs.constants.O_WRONLY | fs.constants.O_CREAT | fs.constants.O_EXCL | noFollow,
|
|
140
|
+
0o600,
|
|
141
|
+
);
|
|
142
|
+
fs.fchmodSync(created, 0o600);
|
|
143
|
+
} catch (error) {
|
|
144
|
+
if (historyErrno(error) === "ELOOP") throw new Error("history database path is not a regular file");
|
|
145
|
+
if (historyErrno(error) !== "EEXIST") throw error;
|
|
146
|
+
} finally {
|
|
147
|
+
if (created !== undefined) fs.closeSync(created);
|
|
148
|
+
}
|
|
149
|
+
existing = historyLeaf(dbPath, suffix);
|
|
150
|
+
assertRegularHistoryLeaf(existing);
|
|
151
|
+
if (!existing) throw new Error("history database path is not a regular file");
|
|
152
|
+
}
|
|
153
|
+
if (!existing) continue;
|
|
154
|
+
secureHistoryPath(target, "file");
|
|
155
|
+
if (process.platform === "win32" || (Number(existing.mode) & 0o077) === 0) continue;
|
|
156
|
+
let fd: number | undefined;
|
|
157
|
+
try {
|
|
158
|
+
fd = fs.openSync(target, fs.constants.O_RDONLY | noFollow);
|
|
159
|
+
const opened = fs.fstatSync(fd, { bigint: true });
|
|
160
|
+
const again = historyLeaf(dbPath, suffix);
|
|
161
|
+
if (!again || !opened.isFile() || !sameHistoryFile(existing, opened) || !sameHistoryFile(opened, again)) {
|
|
162
|
+
throw new Error("history database path is not a regular file");
|
|
163
|
+
}
|
|
164
|
+
fs.fchmodSync(fd, 0o600);
|
|
165
|
+
if ((Number(fs.fstatSync(fd, { bigint: true }).mode) & 0o777) !== 0o600) {
|
|
166
|
+
throw new Error("history database path could not be tightened to mode 0600");
|
|
167
|
+
}
|
|
168
|
+
} catch (error) {
|
|
169
|
+
if (historyErrno(error) === "ELOOP") throw new Error("history database path is not a regular file");
|
|
170
|
+
throw error;
|
|
171
|
+
} finally {
|
|
172
|
+
if (fd !== undefined) fs.closeSync(fd);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
29
177
|
class AsyncDrain<T> {
|
|
30
178
|
#queue?: T[];
|
|
31
179
|
#promise = Promise.resolve();
|
|
@@ -83,6 +231,7 @@ export class HistoryStorage {
|
|
|
83
231
|
|
|
84
232
|
private constructor(dbPath: string) {
|
|
85
233
|
this.#db = new Database(dbPath);
|
|
234
|
+
openingHistory = this.#db;
|
|
86
235
|
|
|
87
236
|
const hasFts = this.#db.prepare("SELECT 1 FROM sqlite_master WHERE type='table' AND name='history_fts'").get();
|
|
88
237
|
|
|
@@ -137,12 +286,32 @@ CREATE TRIGGER IF NOT EXISTS history_ai AFTER INSERT ON history BEGIN
|
|
|
137
286
|
|
|
138
287
|
const last = this.#lastPromptStmt.get() as { prompt?: string } | undefined;
|
|
139
288
|
this.#lastPromptCache = last?.prompt ?? null;
|
|
289
|
+
try {
|
|
290
|
+
restrictHistoryFiles(dbPath, false);
|
|
291
|
+
} catch (error) {
|
|
292
|
+
this.#db.close();
|
|
293
|
+
throw error;
|
|
294
|
+
}
|
|
295
|
+
openingHistory = undefined;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
static #openNew(dbPath: string): HistoryStorage {
|
|
299
|
+
openingHistory = undefined;
|
|
300
|
+
assertHistoryLeaves(dbPath);
|
|
301
|
+
try {
|
|
302
|
+
tightenHistoryDirectory(path.dirname(dbPath));
|
|
303
|
+
assertHistoryLeaves(dbPath);
|
|
304
|
+
restrictHistoryFiles(dbPath, true);
|
|
305
|
+
return new HistoryStorage(dbPath);
|
|
306
|
+
} catch (error) {
|
|
307
|
+
removeNewHistoryLeaves();
|
|
308
|
+
throw error;
|
|
309
|
+
}
|
|
140
310
|
}
|
|
141
311
|
|
|
142
312
|
static open(dbPath: string = getHistoryDbPath()): HistoryStorage {
|
|
143
313
|
if (!HistoryStorage.#instance) {
|
|
144
|
-
|
|
145
|
-
HistoryStorage.#instance = new HistoryStorage(dbPath);
|
|
314
|
+
HistoryStorage.#instance = HistoryStorage.#openNew(dbPath);
|
|
146
315
|
}
|
|
147
316
|
return HistoryStorage.#instance;
|
|
148
317
|
}
|
|
@@ -150,16 +319,17 @@ CREATE TRIGGER IF NOT EXISTS history_ai AFTER INSERT ON history BEGIN
|
|
|
150
319
|
static async openAsync(dbPath: string = getHistoryDbPath()): Promise<HistoryStorage> {
|
|
151
320
|
if (HistoryStorage.#instance) return HistoryStorage.#instance;
|
|
152
321
|
if (!HistoryStorage.#opening) {
|
|
153
|
-
HistoryStorage.#opening = (
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
322
|
+
HistoryStorage.#opening = Promise.resolve()
|
|
323
|
+
.then(() => {
|
|
324
|
+
const storage = HistoryStorage.#openNew(dbPath);
|
|
325
|
+
HistoryStorage.#instance ??= storage;
|
|
326
|
+
return HistoryStorage.#instance;
|
|
327
|
+
})
|
|
328
|
+
.finally(() => {
|
|
329
|
+
HistoryStorage.#opening = undefined;
|
|
330
|
+
});
|
|
161
331
|
}
|
|
162
|
-
return
|
|
332
|
+
return HistoryStorage.#opening;
|
|
163
333
|
}
|
|
164
334
|
|
|
165
335
|
/** @internal Reset the singleton — test-only. */
|
|
@@ -228,14 +228,22 @@ function bindManagedWriteAuthority(scope: ManagedScope, authority: ManagedCandid
|
|
|
228
228
|
function assertRetainedManagedDirectoryIdentity(scope: ManagedScope): void {
|
|
229
229
|
const expected = managedDirectoryIdentities.get(scope);
|
|
230
230
|
if (!expected) return;
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
231
|
+
assertSameManagedDirectoryIdentity(fs.lstatSync(scope.directoryPath, { bigint: true }), expected);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Fence a managed directory to the identity it was bound with. The decision is
|
|
236
|
+
* the same for every mismatch; only the reported reason differs. The same
|
|
237
|
+
* inode on a different `dev` is the shape a remount, drive-letter change or
|
|
238
|
+
* volume-serial change leaves behind (#6750). It is still refused, because an
|
|
239
|
+
* inode is only unique within one volume, but it is named so an operator is
|
|
240
|
+
* not sent looking for a corrupted binding.
|
|
241
|
+
*/
|
|
242
|
+
function assertSameManagedDirectoryIdentity(current: fs.BigIntStats, expected: { dev: bigint; ino: bigint }): void {
|
|
243
|
+
if (!current.isDirectory() || current.isSymbolicLink()) throw new Error("Managed session directory changed");
|
|
244
|
+
if (current.dev === expected.dev && current.ino === expected.ino) return;
|
|
245
|
+
if (current.ino === expected.ino) throw new Error("volume_identity_changed");
|
|
246
|
+
throw new Error("Managed session directory changed");
|
|
239
247
|
}
|
|
240
248
|
|
|
241
249
|
export function managedDirectoryAuthorityForScope(scope: ManagedScope): RecoveryFsRoot | undefined {
|
|
@@ -299,6 +307,7 @@ export type ManagedScopeErrorCode =
|
|
|
299
307
|
| "managed_gc_journal_capacity_exceeded"
|
|
300
308
|
| "atomic_unavailable"
|
|
301
309
|
| "invalid_request"
|
|
310
|
+
| "permission_denied"
|
|
302
311
|
| "durability_failed"
|
|
303
312
|
| "durability_not_provable"
|
|
304
313
|
| "capacity_exceeded";
|
|
@@ -318,7 +327,22 @@ export type ManagedScopeResolution =
|
|
|
318
327
|
* propagating them keeps startup redaction intact while telling the operator
|
|
319
328
|
* which invariant actually failed instead of a blanket `binding_invalid`.
|
|
320
329
|
*/
|
|
321
|
-
const managedScopeIdentityFailureCodes = new Set([
|
|
330
|
+
const managedScopeIdentityFailureCodes = new Set([
|
|
331
|
+
"identity_mismatch",
|
|
332
|
+
"volume_identity_changed",
|
|
333
|
+
"reparse_point",
|
|
334
|
+
"not_directory",
|
|
335
|
+
"not_file",
|
|
336
|
+
]);
|
|
337
|
+
|
|
338
|
+
/**
|
|
339
|
+
* Fixed, path-free tokens from the replacement-cleanup receipt preflight that
|
|
340
|
+
* runs before every no-replace publish. A stale receipt the preflight refuses
|
|
341
|
+
* (for example one whose filename encodes a device number that changed after a
|
|
342
|
+
* reboot, #6820) blocks an otherwise healthy binding publish; reporting it as
|
|
343
|
+
* `binding_invalid` sends operators to the binding instead of the receipt.
|
|
344
|
+
*/
|
|
345
|
+
const managedScopeReceiptFailureCodes = new Set(["managed_replace_cleanup_receipt_invalid"]);
|
|
322
346
|
|
|
323
347
|
/**
|
|
324
348
|
* Fixed, path-free errno tokens. A managed scope that fails to prepare because
|
|
@@ -342,6 +366,7 @@ const managedScopeErrnoFailureCodes = new Set([
|
|
|
342
366
|
const managedScopeFailureCodes = new Set<ManagedScopeErrorCode>([
|
|
343
367
|
"atomic_unavailable",
|
|
344
368
|
"invalid_request",
|
|
369
|
+
"permission_denied",
|
|
345
370
|
"durability_failed",
|
|
346
371
|
"durability_not_provable",
|
|
347
372
|
"migration_busy",
|
|
@@ -374,6 +399,7 @@ function isManagedScopeCapacityMessage(message: string): boolean {
|
|
|
374
399
|
function managedScopeResidualClassification(error: unknown): string | undefined {
|
|
375
400
|
if (!(error instanceof Error)) return undefined;
|
|
376
401
|
if (managedScopeIdentityFailureCodes.has(error.message)) return error.message;
|
|
402
|
+
if (managedScopeReceiptFailureCodes.has(error.message)) return error.message;
|
|
377
403
|
const errno = (error as NodeJS.ErrnoException).code;
|
|
378
404
|
return errno !== undefined && managedScopeErrnoFailureCodes.has(errno) ? errno : undefined;
|
|
379
405
|
}
|
|
@@ -441,6 +467,7 @@ export type ManagedOpenFailure =
|
|
|
441
467
|
| "managed_gc_journal_capacity_exceeded"
|
|
442
468
|
| "durability_failed"
|
|
443
469
|
| "atomic_unavailable"
|
|
470
|
+
| "permission_denied"
|
|
444
471
|
| "invalid_request"
|
|
445
472
|
| "durability_not_provable"
|
|
446
473
|
| "migration_retired"
|
|
@@ -505,6 +532,7 @@ type NativeIdentity =
|
|
|
505
532
|
type CanonicalNativeIdentity = Extract<NativeIdentity, { ok: true }>;
|
|
506
533
|
|
|
507
534
|
type NativeIdentityFailureCode =
|
|
535
|
+
| "access_denied"
|
|
508
536
|
| "not_found"
|
|
509
537
|
| "not_directory"
|
|
510
538
|
| "not_utf8"
|
|
@@ -615,6 +643,12 @@ function nativeFailure(code: NativeIdentityFailureCode): ManagedScopeResolution
|
|
|
615
643
|
return { kind: "error", code: "cwd_missing", message: "The workspace directory does not exist." };
|
|
616
644
|
if (code === "not_directory")
|
|
617
645
|
return { kind: "error", code: "cwd_not_directory", message: "The workspace path is not a directory." };
|
|
646
|
+
if (code === "access_denied")
|
|
647
|
+
return {
|
|
648
|
+
kind: "error",
|
|
649
|
+
code: "permission_denied",
|
|
650
|
+
message: "Permission was denied while identifying the workspace directory.",
|
|
651
|
+
};
|
|
618
652
|
if (code === "network_unsupported") {
|
|
619
653
|
return {
|
|
620
654
|
kind: "error",
|
|
@@ -625,6 +659,10 @@ function nativeFailure(code: NativeIdentityFailureCode): ManagedScopeResolution
|
|
|
625
659
|
return { kind: "error", code: "identity_unavailable", message: "Workspace directory identity is unavailable." };
|
|
626
660
|
}
|
|
627
661
|
|
|
662
|
+
function nativeIdentityDiagnosticCode(code: NativeIdentityFailureCode): string {
|
|
663
|
+
return code === "access_denied" ? "permission_denied" : `cwd_${code}`;
|
|
664
|
+
}
|
|
665
|
+
|
|
628
666
|
function bindingFor(scope: ManagedScope): Binding {
|
|
629
667
|
return {
|
|
630
668
|
schemaVersion: 1,
|
|
@@ -1104,7 +1142,7 @@ function inspectCandidate(
|
|
|
1104
1142
|
if (header.type !== "session" || typeof header.id !== "string" || typeof header.cwd !== "string")
|
|
1105
1143
|
return { code: "invalid_header" };
|
|
1106
1144
|
const cwdIdentity = identityFor(header.cwd);
|
|
1107
|
-
if (!cwdIdentity.ok) return { code:
|
|
1145
|
+
if (!cwdIdentity.ok) return { code: nativeIdentityDiagnosticCode(cwdIdentity.code) };
|
|
1108
1146
|
const named = fs.lstatSync(filePath, { bigint: true });
|
|
1109
1147
|
if (
|
|
1110
1148
|
!named.isFile() ||
|
|
@@ -1166,7 +1204,8 @@ function listDirectoryCandidates(
|
|
|
1166
1204
|
try {
|
|
1167
1205
|
directoryStat = fs.lstatSync(directory);
|
|
1168
1206
|
} catch (error) {
|
|
1169
|
-
|
|
1207
|
+
const code = (error as NodeJS.ErrnoException).code;
|
|
1208
|
+
if (code === "ENOENT" || (provenance === "legacy" && code === "ENAMETOOLONG")) return [];
|
|
1170
1209
|
throw error;
|
|
1171
1210
|
}
|
|
1172
1211
|
if (!directoryStat.isDirectory() || directoryStat.isSymbolicLink()) throw new Error("unsafe directory");
|
|
@@ -1868,13 +1907,7 @@ export function prepareManagedSessionScopeForWriteSync(
|
|
|
1868
1907
|
managedDirectoryAuthorities.set(scope, retainedAuthority);
|
|
1869
1908
|
stage = "scope_revalidate";
|
|
1870
1909
|
const directoryStat = fs.lstatSync(scope.directoryPath, { bigint: true });
|
|
1871
|
-
|
|
1872
|
-
!directoryStat.isDirectory() ||
|
|
1873
|
-
directoryStat.isSymbolicLink() ||
|
|
1874
|
-
directoryStat.dev !== preparedDirectory.dev ||
|
|
1875
|
-
directoryStat.ino !== preparedDirectory.ino
|
|
1876
|
-
)
|
|
1877
|
-
throw new Error("Managed session directory changed");
|
|
1910
|
+
assertSameManagedDirectoryIdentity(directoryStat, preparedDirectory);
|
|
1878
1911
|
managedDirectoryIdentities.set(scope, { dev: preparedDirectory.dev, ino: preparedDirectory.ino });
|
|
1879
1912
|
if (validated) {
|
|
1880
1913
|
if (validated.kind !== "error") throw new Error("Unexpected managed scope binding result");
|
|
@@ -1950,7 +1983,7 @@ export function listManagedCandidates(scope: ManagedScope): ManagedCandidateList
|
|
|
1950
1983
|
const candidateIdentity = identityFor(candidate.cwd);
|
|
1951
1984
|
if (!candidateIdentity.ok) {
|
|
1952
1985
|
invalid.push({
|
|
1953
|
-
code:
|
|
1986
|
+
code: nativeIdentityDiagnosticCode(candidateIdentity.code),
|
|
1954
1987
|
...(safeManagedCandidateSessionId(candidate.sessionId) === undefined
|
|
1955
1988
|
? {}
|
|
1956
1989
|
: { sessionId: candidate.sessionId }),
|
|
@@ -1970,7 +2003,8 @@ export function listManagedCandidates(scope: ManagedScope): ManagedCandidateList
|
|
|
1970
2003
|
}
|
|
1971
2004
|
}
|
|
1972
2005
|
}
|
|
1973
|
-
const
|
|
2006
|
+
const retired = owned.length > 0 ? retiredTargetKeys(scope) : new Set<string>();
|
|
2007
|
+
const visible = owned.filter(candidate => !isRetiredIn(retired, candidate));
|
|
1974
2008
|
const active = visible.filter(
|
|
1975
2009
|
candidate =>
|
|
1976
2010
|
candidate.provenance === "v2" ||
|
|
@@ -2041,6 +2075,7 @@ function expectedFailure(error: unknown): ManagedOpenFailure {
|
|
|
2041
2075
|
message === "managed_gc_journal_capacity_exceeded" ||
|
|
2042
2076
|
message === "durability_failed" ||
|
|
2043
2077
|
message === "atomic_unavailable" ||
|
|
2078
|
+
message === "permission_denied" ||
|
|
2044
2079
|
message === "invalid_request" ||
|
|
2045
2080
|
message === "durability_not_provable" ||
|
|
2046
2081
|
message === "migration_retired"
|
|
@@ -2692,11 +2727,14 @@ function managedGcStreamDirectoryInventory(
|
|
|
2692
2727
|
countEntry: (entry: fs.Dirent) => boolean,
|
|
2693
2728
|
retain: (entry: fs.Dirent) => boolean,
|
|
2694
2729
|
operation: ManagedGcInventoryOperation,
|
|
2730
|
+
verificationBaseline?: ManagedGcDirectoryInventory,
|
|
2695
2731
|
): ManagedGcDirectoryInventory {
|
|
2696
2732
|
managedGcReserveInventoryWork(operation);
|
|
2697
2733
|
const beforeStat = fs.lstatSync(pathname, { bigint: true });
|
|
2698
2734
|
if (!beforeStat.isDirectory() || beforeStat.isSymbolicLink()) throw new Error("managed_gc_scope_authority_mismatch");
|
|
2699
2735
|
const before = managedGcDirectoryEvidence(beforeStat);
|
|
2736
|
+
if (verificationBaseline && !managedGcSameDirectoryEvidence(before, verificationBaseline.evidence))
|
|
2737
|
+
throw new Error("managed_gc_scope_authority_mismatch");
|
|
2700
2738
|
const scan = (collect: boolean): { count: number; physicalEntryCount: number; entries: fs.Dirent[] } => {
|
|
2701
2739
|
managedGcReserveInventoryWork(operation);
|
|
2702
2740
|
const entries: fs.Dirent[] = [];
|
|
@@ -2742,7 +2780,9 @@ function managedGcStreamDirectoryInventory(
|
|
|
2742
2780
|
managedGcReserveInventoryWork(operation);
|
|
2743
2781
|
return { count, physicalEntryCount, entries };
|
|
2744
2782
|
};
|
|
2745
|
-
const counted =
|
|
2783
|
+
const counted = verificationBaseline
|
|
2784
|
+
? { count: verificationBaseline.entryCount, physicalEntryCount: verificationBaseline.physicalEntryCount }
|
|
2785
|
+
: scan(false);
|
|
2746
2786
|
managedGcReserveInventoryWork(operation);
|
|
2747
2787
|
const afterCount = fs.lstatSync(pathname, { bigint: true });
|
|
2748
2788
|
if (
|
|
@@ -2776,6 +2816,7 @@ function managedGcReceiptDirectoryInventory(
|
|
|
2776
2816
|
scope: ManagedScope,
|
|
2777
2817
|
store: ManagedSessionDescendantStore,
|
|
2778
2818
|
operation: ManagedGcInventoryOperation,
|
|
2819
|
+
verificationBaseline?: ManagedGcDirectoryInventory,
|
|
2779
2820
|
): ManagedGcDirectoryInventory {
|
|
2780
2821
|
const relativePath = MANAGED_GC_RETIREMENT_RECEIPTS;
|
|
2781
2822
|
managedGcReserveInventoryWork(
|
|
@@ -2789,6 +2830,7 @@ function managedGcReceiptDirectoryInventory(
|
|
|
2789
2830
|
entry => entry.name.startsWith(MANAGED_GC_RETIREMENT_PREFIX),
|
|
2790
2831
|
entry => entry.name.startsWith(MANAGED_GC_RETIREMENT_PREFIX),
|
|
2791
2832
|
operation,
|
|
2833
|
+
verificationBaseline,
|
|
2792
2834
|
);
|
|
2793
2835
|
managedGcReserveInventoryWork(operation);
|
|
2794
2836
|
const after = store.captureDirectoryIdentity(relativePath);
|
|
@@ -3606,14 +3648,20 @@ export async function readManagedGcSessionRetirementReceiptReadOnly(
|
|
|
3606
3648
|
if (!hasFsCode(error, "ENOENT")) throw error;
|
|
3607
3649
|
receiptDirectoryPresent = false;
|
|
3608
3650
|
}
|
|
3609
|
-
if (receiptDirectoryPresent)
|
|
3651
|
+
if (receiptDirectoryPresent) {
|
|
3652
|
+
const inventory = managedGcReceiptDirectoryInventory(scope, store, operation);
|
|
3610
3653
|
receipt = readManagedGcSessionRetirementReceiptUnlocked(
|
|
3611
3654
|
scope,
|
|
3612
3655
|
transcriptPath,
|
|
3613
3656
|
trusted,
|
|
3614
3657
|
store,
|
|
3615
3658
|
operation,
|
|
3659
|
+
inventory,
|
|
3616
3660
|
).receipt;
|
|
3661
|
+
const after = managedGcReceiptDirectoryInventory(scope, store, operation, inventory);
|
|
3662
|
+
if (!managedGcSameReceiptDirectoryInventory(inventory, after))
|
|
3663
|
+
throw new Error("task_artifact_owner_continuation_authority_mismatch");
|
|
3664
|
+
}
|
|
3617
3665
|
} catch (error) {
|
|
3618
3666
|
primaryError = error;
|
|
3619
3667
|
hasPrimaryError = true;
|
|
@@ -6282,8 +6330,96 @@ function tombstonePathContaining(scope: ManagedScope, candidate: ManagedCandidat
|
|
|
6282
6330
|
return undefined;
|
|
6283
6331
|
}
|
|
6284
6332
|
|
|
6285
|
-
|
|
6333
|
+
/**
|
|
6334
|
+
* Identity fields a retired tombstone target must match exactly for a candidate
|
|
6335
|
+
* to be hidden. `dev`, `ino`, and `mtimeNs` are persisted as decimal strings.
|
|
6336
|
+
*/
|
|
6337
|
+
type RetiredTargetIdentity = readonly [
|
|
6338
|
+
path: string,
|
|
6339
|
+
sessionId: string,
|
|
6340
|
+
cwd: string,
|
|
6341
|
+
canonicalPath: string,
|
|
6342
|
+
dev: string,
|
|
6343
|
+
ino: string,
|
|
6344
|
+
size: number,
|
|
6345
|
+
mtimeNs: string,
|
|
6346
|
+
sha256: string,
|
|
6347
|
+
];
|
|
6348
|
+
|
|
6349
|
+
function candidateRetiredIdentity(candidate: ManagedCandidate): RetiredTargetIdentity {
|
|
6350
|
+
return [
|
|
6351
|
+
candidate.path,
|
|
6352
|
+
candidate.sessionId,
|
|
6353
|
+
candidate.cwd,
|
|
6354
|
+
candidate.identity.canonicalPath,
|
|
6355
|
+
String(candidate.identity.dev),
|
|
6356
|
+
String(candidate.identity.ino),
|
|
6357
|
+
candidate.identity.size,
|
|
6358
|
+
String(candidate.identity.mtimeNs),
|
|
6359
|
+
candidate.identity.sha256,
|
|
6360
|
+
];
|
|
6361
|
+
}
|
|
6362
|
+
|
|
6363
|
+
/** Returns the target's identity only when every field has the type a candidate can carry. */
|
|
6364
|
+
function parseRetiredTargetIdentity(target: unknown): RetiredTargetIdentity | undefined {
|
|
6365
|
+
if (!target || typeof target !== "object") return undefined;
|
|
6366
|
+
const value = target as { path?: unknown; sessionId?: unknown; cwd?: unknown; identity?: unknown };
|
|
6367
|
+
if (!value.identity || typeof value.identity !== "object") return undefined;
|
|
6368
|
+
const identity = value.identity as {
|
|
6369
|
+
canonicalPath?: unknown;
|
|
6370
|
+
dev?: unknown;
|
|
6371
|
+
ino?: unknown;
|
|
6372
|
+
size?: unknown;
|
|
6373
|
+
mtimeNs?: unknown;
|
|
6374
|
+
sha256?: unknown;
|
|
6375
|
+
};
|
|
6376
|
+
if (
|
|
6377
|
+
typeof value.path !== "string" ||
|
|
6378
|
+
typeof value.sessionId !== "string" ||
|
|
6379
|
+
typeof value.cwd !== "string" ||
|
|
6380
|
+
typeof identity.canonicalPath !== "string" ||
|
|
6381
|
+
typeof identity.dev !== "string" ||
|
|
6382
|
+
typeof identity.ino !== "string" ||
|
|
6383
|
+
typeof identity.size !== "number" ||
|
|
6384
|
+
typeof identity.mtimeNs !== "string" ||
|
|
6385
|
+
typeof identity.sha256 !== "string"
|
|
6386
|
+
)
|
|
6387
|
+
return undefined;
|
|
6388
|
+
return [
|
|
6389
|
+
value.path,
|
|
6390
|
+
value.sessionId,
|
|
6391
|
+
value.cwd,
|
|
6392
|
+
identity.canonicalPath,
|
|
6393
|
+
identity.dev,
|
|
6394
|
+
identity.ino,
|
|
6395
|
+
identity.size,
|
|
6396
|
+
identity.mtimeNs,
|
|
6397
|
+
identity.sha256,
|
|
6398
|
+
];
|
|
6399
|
+
}
|
|
6400
|
+
|
|
6401
|
+
/**
|
|
6402
|
+
* Key equality matches strict equality of every member: strings and finite
|
|
6403
|
+
* numbers encode distinctly. A non-finite tombstone `size` encodes as `null`,
|
|
6404
|
+
* which no candidate produces.
|
|
6405
|
+
*/
|
|
6406
|
+
function retiredIdentityKey(identity: RetiredTargetIdentity): string {
|
|
6407
|
+
return JSON.stringify(identity);
|
|
6408
|
+
}
|
|
6409
|
+
|
|
6410
|
+
/**
|
|
6411
|
+
* Snapshot of the identities retired by a scope's tombstones.
|
|
6412
|
+
*
|
|
6413
|
+
* Tombstones are visited in directory order and the walk stops at the first
|
|
6414
|
+
* unreadable or malformed one: it grants no retirement authority, and neither do
|
|
6415
|
+
* the tombstones after it. Listing builds this once and checks every candidate
|
|
6416
|
+
* against it, so its cost is linear in tombstones rather than in
|
|
6417
|
+
* candidates x tombstones.
|
|
6418
|
+
*/
|
|
6419
|
+
function retiredTargetKeys(scope: ManagedScope): ReadonlySet<string> {
|
|
6420
|
+
const keys = new Set<string>();
|
|
6286
6421
|
const directory = path.join(managedInternalDirectory(scope), MANAGED_TOMBSTONES_DIRECTORY);
|
|
6422
|
+
const digest = scopeDigest(scope.platform, scope.canonicalCwd);
|
|
6287
6423
|
try {
|
|
6288
6424
|
for (const name of fs.readdirSync(directory)) {
|
|
6289
6425
|
const pathname = path.join(directory, name);
|
|
@@ -6293,47 +6429,27 @@ function isRetired(scope: ManagedScope, candidate: ManagedCandidate): boolean {
|
|
|
6293
6429
|
if (
|
|
6294
6430
|
record.schemaVersion !== 2 ||
|
|
6295
6431
|
record.state !== "retired" ||
|
|
6296
|
-
record.scope !==
|
|
6432
|
+
record.scope !== digest ||
|
|
6297
6433
|
!Array.isArray(record.targets)
|
|
6298
6434
|
)
|
|
6299
6435
|
continue;
|
|
6300
|
-
|
|
6301
|
-
|
|
6302
|
-
|
|
6303
|
-
|
|
6304
|
-
path?: unknown;
|
|
6305
|
-
sessionId?: unknown;
|
|
6306
|
-
cwd?: unknown;
|
|
6307
|
-
identity?: {
|
|
6308
|
-
canonicalPath?: unknown;
|
|
6309
|
-
dev?: unknown;
|
|
6310
|
-
ino?: unknown;
|
|
6311
|
-
size?: unknown;
|
|
6312
|
-
mtimeNs?: unknown;
|
|
6313
|
-
sha256?: unknown;
|
|
6314
|
-
};
|
|
6315
|
-
};
|
|
6316
|
-
const identity = value.identity;
|
|
6317
|
-
if (!identity) return false;
|
|
6318
|
-
return (
|
|
6319
|
-
value.path === candidate.path &&
|
|
6320
|
-
value.sessionId === candidate.sessionId &&
|
|
6321
|
-
value.cwd === candidate.cwd &&
|
|
6322
|
-
identity.canonicalPath === candidate.identity.canonicalPath &&
|
|
6323
|
-
identity.dev === String(candidate.identity.dev) &&
|
|
6324
|
-
identity.ino === String(candidate.identity.ino) &&
|
|
6325
|
-
identity.size === candidate.identity.size &&
|
|
6326
|
-
identity.mtimeNs === String(candidate.identity.mtimeNs) &&
|
|
6327
|
-
identity.sha256 === candidate.identity.sha256
|
|
6328
|
-
);
|
|
6329
|
-
})
|
|
6330
|
-
)
|
|
6331
|
-
return true;
|
|
6436
|
+
for (const target of record.targets) {
|
|
6437
|
+
const identity = parseRetiredTargetIdentity(target);
|
|
6438
|
+
if (identity) keys.add(retiredIdentityKey(identity));
|
|
6439
|
+
}
|
|
6332
6440
|
}
|
|
6333
6441
|
} catch {
|
|
6334
6442
|
/* a missing/malformed tombstone grants no retirement authority */
|
|
6335
6443
|
}
|
|
6336
|
-
return
|
|
6444
|
+
return keys;
|
|
6445
|
+
}
|
|
6446
|
+
|
|
6447
|
+
function isRetiredIn(retired: ReadonlySet<string>, candidate: ManagedCandidate): boolean {
|
|
6448
|
+
return retired.has(retiredIdentityKey(candidateRetiredIdentity(candidate)));
|
|
6449
|
+
}
|
|
6450
|
+
|
|
6451
|
+
function isRetired(scope: ManagedScope, candidate: ManagedCandidate): boolean {
|
|
6452
|
+
return isRetiredIn(retiredTargetKeys(scope), candidate);
|
|
6337
6453
|
}
|
|
6338
6454
|
|
|
6339
6455
|
type ArtifactManifestEntry =
|