@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
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { logger } from "@gajae-code/utils";
|
|
2
|
+
import type { SessionManager } from "../session/session-manager";
|
|
3
|
+
import { DEFAULT_ARTIFACT_MAX_BYTES, truncateHeadBytes } from "../session/streaming-output";
|
|
4
|
+
|
|
5
|
+
const ASYNC_INLINE_RESULT_MAX_CHARS = 12_000;
|
|
6
|
+
const ASYNC_PREVIEW_MAX_CHARS = 4_000;
|
|
7
|
+
|
|
8
|
+
export async function formatAsyncResultForFollowUp(
|
|
9
|
+
sessionManager: SessionManager,
|
|
10
|
+
result: string,
|
|
11
|
+
allowArtifact = true,
|
|
12
|
+
): Promise<string> {
|
|
13
|
+
if (result.length <= ASYNC_INLINE_RESULT_MAX_CHARS) return result;
|
|
14
|
+
const preview = `${result.slice(0, ASYNC_PREVIEW_MAX_CHARS)}\n\n[Output truncated. Showing first ${ASYNC_PREVIEW_MAX_CHARS.toLocaleString()} characters.]`;
|
|
15
|
+
// Denied owned deliveries must not leave unreferenced output artifacts.
|
|
16
|
+
if (!allowArtifact) return preview;
|
|
17
|
+
try {
|
|
18
|
+
const artifactId = await sessionManager.saveArtifact(result, "async");
|
|
19
|
+
if (artifactId) {
|
|
20
|
+
const bytes = Buffer.byteLength(result, "utf8");
|
|
21
|
+
const omittedBytes =
|
|
22
|
+
bytes > DEFAULT_ARTIFACT_MAX_BYTES
|
|
23
|
+
? bytes - truncateHeadBytes(result, DEFAULT_ARTIFACT_MAX_BYTES).bytes
|
|
24
|
+
: 0;
|
|
25
|
+
const notice = omittedBytes
|
|
26
|
+
? `Saved completion output is incomplete; omitted ${omittedBytes} UTF-8 bytes`
|
|
27
|
+
: "Saved completion output";
|
|
28
|
+
return `${preview}\n${notice}: artifact://${artifactId}`;
|
|
29
|
+
}
|
|
30
|
+
} catch (error) {
|
|
31
|
+
try {
|
|
32
|
+
logger.warn("Failed to persist async follow-up artifact", {
|
|
33
|
+
error: error instanceof Error ? error.message : String(error),
|
|
34
|
+
});
|
|
35
|
+
} catch {
|
|
36
|
+
// Diagnostics must not discard a completed job's preview.
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return `${preview}\n[Completion output storage unavailable; only the preview is available here.]`;
|
|
40
|
+
}
|
package/src/autoresearch/git.ts
CHANGED
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
*/
|
|
14
14
|
import * as fs from "node:fs";
|
|
15
15
|
import * as path from "node:path";
|
|
16
|
+
import { isEnoent } from "@gajae-code/utils";
|
|
16
17
|
import * as git from "../utils/git";
|
|
17
18
|
import { HARNESS_FILENAME, normalizePathSpec } from "./harness";
|
|
18
19
|
|
|
@@ -49,10 +50,16 @@ export function getCurrentAutoresearchBranch(workDir: string): Promise<string |
|
|
|
49
50
|
* Mission state under `.gjc/**` is intentionally NOT listed here: it is
|
|
50
51
|
* runtime-owned and only the sanctioned `gjc autoresearch` CLI writes it.
|
|
51
52
|
*/
|
|
52
|
-
export function isAutoresearchAuthorizedResearchPath(
|
|
53
|
+
export function isAutoresearchAuthorizedResearchPath(cwd: string, rawPath: string): boolean {
|
|
53
54
|
const normalized = normalizePathSpec(rawPath);
|
|
54
55
|
if (normalized === ".") return false;
|
|
55
|
-
|
|
56
|
+
if (normalized !== HARNESS_FILENAME) return false;
|
|
57
|
+
try {
|
|
58
|
+
// Writes follow an existing harness symlink into its referent.
|
|
59
|
+
return !fs.lstatSync(path.join(cwd, HARNESS_FILENAME)).isSymbolicLink();
|
|
60
|
+
} catch (error) {
|
|
61
|
+
return isEnoent(error);
|
|
62
|
+
}
|
|
56
63
|
}
|
|
57
64
|
|
|
58
65
|
/**
|
|
@@ -32,7 +32,7 @@ Do not edit product source, manifests, or dependencies. The harness must use exi
|
|
|
32
32
|
### Steps
|
|
33
33
|
|
|
34
34
|
1. Inspect the target. Read source, identify what to measure, decide on the workload.
|
|
35
|
-
2. Write `autoresearch.sh` only when it is an explicitly approved research artifact;
|
|
35
|
+
2. Write or edit `autoresearch.sh` with a direct file tool only when it is an explicitly approved research artifact; do not use Bash, Python, or eval to change the harness. Otherwise use an existing benchmark command.
|
|
36
36
|
3. Validate it: invoke `bash autoresearch.sh` through the regular `bash` tool. Confirm it exits 0 and emits at least one `METRIC` line. Iterate on the harness until it does.
|
|
37
37
|
4. Start the mission with `gjc autoresearch` (cold intake: goal, constraints, deliverables, and an explicit mode) or `gjc autoresearch --spec <spec>` (handoff intake). Record the primary metric name and its direction (lower/higher is better) with the mission. The harness baseline is snapshotted at mission start and Phase 2 (the iteration loop) begins.
|
|
38
38
|
|
package/src/capability/fs.ts
CHANGED
|
@@ -9,8 +9,8 @@ const dirCache = new Map<string, fs.Dirent[]>();
|
|
|
9
9
|
export type ReadScope = "project" | "user" | "native";
|
|
10
10
|
|
|
11
11
|
export interface FileIdentity {
|
|
12
|
-
readonly dev:
|
|
13
|
-
readonly ino:
|
|
12
|
+
readonly dev: bigint;
|
|
13
|
+
readonly ino: bigint;
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
export interface ReadFileOptions {
|
|
@@ -45,7 +45,7 @@ function isWithinOrEqual(root: string, candidate: string): boolean {
|
|
|
45
45
|
async function isHomeIdentityStable(options?: ReadFileOptions): Promise<boolean> {
|
|
46
46
|
if (!options?.isolatedHome || !options.home || !options.homeIdentity) return true;
|
|
47
47
|
try {
|
|
48
|
-
const current = await fs.promises.stat(options.home);
|
|
48
|
+
const current = await fs.promises.stat(options.home, { bigint: true });
|
|
49
49
|
return current.dev === options.homeIdentity.dev && current.ino === options.homeIdentity.ino;
|
|
50
50
|
} catch {
|
|
51
51
|
return false;
|
|
@@ -69,7 +69,7 @@ async function isUserAgentIdentityStable(options?: ReadFileOptions): Promise<boo
|
|
|
69
69
|
}
|
|
70
70
|
}
|
|
71
71
|
try {
|
|
72
|
-
const current = await fs.promises.stat(options.userAgentDir);
|
|
72
|
+
const current = await fs.promises.stat(options.userAgentDir, { bigint: true });
|
|
73
73
|
return current.dev === options.userAgentIdentity.dev && current.ino === options.userAgentIdentity.ino;
|
|
74
74
|
} catch {
|
|
75
75
|
return false;
|
|
@@ -79,7 +79,7 @@ async function isUserAgentIdentityStable(options?: ReadFileOptions): Promise<boo
|
|
|
79
79
|
async function isContainmentRootIdentityStable(options?: ReadFileOptions): Promise<boolean> {
|
|
80
80
|
if (!options?.isolatedHome || !options.containmentRoot || !options.containmentRootIdentity) return true;
|
|
81
81
|
try {
|
|
82
|
-
const current = await fs.promises.stat(options.containmentRoot);
|
|
82
|
+
const current = await fs.promises.stat(options.containmentRoot, { bigint: true });
|
|
83
83
|
return sameFileIdentity(current, options.containmentRootIdentity);
|
|
84
84
|
} catch {
|
|
85
85
|
return false;
|
|
@@ -152,8 +152,8 @@ async function isCurrentIsolatedPath(abs: string, options?: ReadFileOptions): Pr
|
|
|
152
152
|
}
|
|
153
153
|
}
|
|
154
154
|
|
|
155
|
-
function isSingleLinkRegularFile(stat: fs.Stats): boolean {
|
|
156
|
-
return stat.isFile() && !stat.isSymbolicLink() && stat.nlink === 1;
|
|
155
|
+
function isSingleLinkRegularFile(stat: fs.Stats | fs.BigIntStats): boolean {
|
|
156
|
+
return stat.isFile() && !stat.isSymbolicLink() && (stat.nlink === 1n || stat.nlink === 1);
|
|
157
157
|
}
|
|
158
158
|
|
|
159
159
|
/** Reject hard-linked executable or configuration files in isolated discovery. */
|
|
@@ -165,8 +165,16 @@ export async function isSingleLinkRegularFileAt(filePath: string): Promise<boole
|
|
|
165
165
|
}
|
|
166
166
|
}
|
|
167
167
|
|
|
168
|
-
function sameFileIdentity(
|
|
169
|
-
|
|
168
|
+
function sameFileIdentity(
|
|
169
|
+
left: Pick<fs.Stats, "dev" | "ino"> | Pick<fs.BigIntStats, "dev" | "ino">,
|
|
170
|
+
right: Pick<fs.Stats, "dev" | "ino"> | Pick<fs.BigIntStats, "dev" | "ino"> | FileIdentity,
|
|
171
|
+
): boolean {
|
|
172
|
+
// Convert to bigint if needed for comparison
|
|
173
|
+
const leftDev = typeof left.dev === "bigint" ? left.dev : BigInt(left.dev);
|
|
174
|
+
const leftIno = typeof left.ino === "bigint" ? left.ino : BigInt(left.ino);
|
|
175
|
+
const rightDev = typeof right.dev === "bigint" ? right.dev : BigInt(right.dev);
|
|
176
|
+
const rightIno = typeof right.ino === "bigint" ? right.ino : BigInt(right.ino);
|
|
177
|
+
return leftDev === rightDev && leftIno === rightIno;
|
|
170
178
|
}
|
|
171
179
|
|
|
172
180
|
/**
|
|
@@ -174,13 +182,13 @@ function sameFileIdentity(left: Pick<fs.Stats, "dev" | "ino">, right: Pick<fs.St
|
|
|
174
182
|
* Stat first and canonicalize second so a parent replacement in either seam
|
|
175
183
|
* is detected by the descriptor identity check below.
|
|
176
184
|
*/
|
|
177
|
-
async function statAuthorizedPath(abs: string, options?: ReadFileOptions): Promise<fs.Stats | null> {
|
|
185
|
+
async function statAuthorizedPath(abs: string, options?: ReadFileOptions): Promise<fs.Stats | fs.BigIntStats | null> {
|
|
178
186
|
if (!options?.isolatedHome) return null;
|
|
179
187
|
if (!(await isHomeIdentityStable(options))) return null;
|
|
180
188
|
if (!(await isUserAgentIdentityStable(options))) return null;
|
|
181
|
-
let authorized: fs.Stats;
|
|
189
|
+
let authorized: fs.Stats | fs.BigIntStats;
|
|
182
190
|
try {
|
|
183
|
-
authorized = await fs.promises.stat(abs);
|
|
191
|
+
authorized = await fs.promises.stat(abs, { bigint: true });
|
|
184
192
|
} catch {
|
|
185
193
|
return null;
|
|
186
194
|
}
|
|
@@ -191,18 +199,18 @@ async function validateOpenedPath(
|
|
|
191
199
|
handle: fsPromises.FileHandle,
|
|
192
200
|
abs: string,
|
|
193
201
|
options: ReadFileOptions | undefined,
|
|
194
|
-
authorized: fs.Stats | null,
|
|
202
|
+
authorized: fs.Stats | fs.BigIntStats | null,
|
|
195
203
|
kind: "file" | "directory",
|
|
196
204
|
): Promise<boolean> {
|
|
197
205
|
if (!options?.isolatedHome) return true;
|
|
198
206
|
if (!(await isHomeIdentityStable(options))) return false;
|
|
199
207
|
if (!(await isUserAgentIdentityStable(options))) return false;
|
|
200
208
|
try {
|
|
201
|
-
const opened = await handle.stat();
|
|
209
|
+
const opened = await handle.stat({ bigint: true });
|
|
202
210
|
const validKind =
|
|
203
211
|
kind === "file" ? isSingleLinkRegularFile(opened) : opened.isDirectory() && !opened.isSymbolicLink();
|
|
204
212
|
if (!validKind || authorized === null || !sameFileIdentity(opened, authorized)) return false;
|
|
205
|
-
const current = await fs.promises.stat(abs);
|
|
213
|
+
const current = await fs.promises.stat(abs, { bigint: true });
|
|
206
214
|
if (!sameFileIdentity(opened, current)) return false;
|
|
207
215
|
return await isCurrentIsolatedPath(abs, options);
|
|
208
216
|
} catch {
|
|
@@ -212,7 +220,7 @@ async function validateOpenedPath(
|
|
|
212
220
|
|
|
213
221
|
interface OpenedPath {
|
|
214
222
|
handle: fsPromises.FileHandle;
|
|
215
|
-
authorized: fs.Stats | null;
|
|
223
|
+
authorized: fs.Stats | fs.BigIntStats | null;
|
|
216
224
|
}
|
|
217
225
|
|
|
218
226
|
async function openIsolatedFile(abs: string, options?: ReadFileOptions): Promise<OpenedPath | null> {
|
|
@@ -233,6 +241,28 @@ async function openIsolatedFile(abs: string, options?: ReadFileOptions): Promise
|
|
|
233
241
|
}
|
|
234
242
|
}
|
|
235
243
|
|
|
244
|
+
/**
|
|
245
|
+
* A lexical path outside the boundary keeps the historical ancestor read.
|
|
246
|
+
* A path inside the boundary must still be inside after realpath, so a
|
|
247
|
+
* repository symlink cannot be opened as its external target.
|
|
248
|
+
*/
|
|
249
|
+
async function containedReadPath(abs: string, options?: ReadFileOptions): Promise<string | null> {
|
|
250
|
+
const root = options?.containmentRoot;
|
|
251
|
+
if (!root || options?.isolatedHome) return abs;
|
|
252
|
+
const lexical = path.resolve(abs);
|
|
253
|
+
const boundary = path.resolve(root);
|
|
254
|
+
if (!isWithinOrEqual(boundary, lexical)) return lexical;
|
|
255
|
+
try {
|
|
256
|
+
const [boundaryReal, targetReal] = await Promise.all([
|
|
257
|
+
fs.promises.realpath(boundary),
|
|
258
|
+
fs.promises.realpath(lexical),
|
|
259
|
+
]);
|
|
260
|
+
return isWithinOrEqual(boundaryReal, targetReal) ? targetReal : null;
|
|
261
|
+
} catch {
|
|
262
|
+
return null;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
236
266
|
export async function readFile(filePath: string, options?: ReadFileOptions): Promise<string | null> {
|
|
237
267
|
let abs: string | null;
|
|
238
268
|
try {
|
|
@@ -242,13 +272,19 @@ export async function readFile(filePath: string, options?: ReadFileOptions): Pro
|
|
|
242
272
|
}
|
|
243
273
|
if (abs === null) return null;
|
|
244
274
|
if (!(await isCurrentIsolatedPath(abs, options))) return null;
|
|
245
|
-
|
|
275
|
+
// A contained read must not share the lexical cache with a different boundary.
|
|
276
|
+
const useCache = !options?.bypassCache && !options?.isolatedHome && !options?.containmentRoot;
|
|
246
277
|
if (useCache && contentCache.has(abs)) {
|
|
247
278
|
return contentCache.get(abs) ?? null;
|
|
248
279
|
}
|
|
249
280
|
if (!options?.isolatedHome) {
|
|
281
|
+
const readable = await containedReadPath(abs, options);
|
|
282
|
+
if (readable === null) {
|
|
283
|
+
if (useCache) contentCache.set(abs, null);
|
|
284
|
+
return null;
|
|
285
|
+
}
|
|
250
286
|
try {
|
|
251
|
-
const content = await Bun.file(
|
|
287
|
+
const content = await Bun.file(readable).text();
|
|
252
288
|
if (useCache) contentCache.set(abs, content);
|
|
253
289
|
return content;
|
|
254
290
|
} catch {
|
|
@@ -279,7 +315,7 @@ export async function readFile(filePath: string, options?: ReadFileOptions): Pro
|
|
|
279
315
|
/** Capture a stable physical identity for an authorized containment root. */
|
|
280
316
|
export async function capturePathIdentity(filePath: string): Promise<FileIdentity | null> {
|
|
281
317
|
try {
|
|
282
|
-
const stat = await fs.promises.stat(path.resolve(filePath));
|
|
318
|
+
const stat = await fs.promises.stat(path.resolve(filePath), { bigint: true });
|
|
283
319
|
return { dev: stat.dev, ino: stat.ino };
|
|
284
320
|
} catch {
|
|
285
321
|
return null;
|
|
@@ -342,14 +378,14 @@ export async function readFileSize(filePath: string, options?: ReadFileOptions):
|
|
|
342
378
|
async function validateDirectoryPath(
|
|
343
379
|
abs: string,
|
|
344
380
|
options: ReadFileOptions | undefined,
|
|
345
|
-
authorized: fs.Stats | null,
|
|
381
|
+
authorized: fs.Stats | fs.BigIntStats | null,
|
|
346
382
|
): Promise<boolean> {
|
|
347
383
|
if (!options?.isolatedHome) return true;
|
|
348
384
|
if (authorized === null) return false;
|
|
349
385
|
if (!(await isHomeIdentityStable(options))) return false;
|
|
350
386
|
if (!(await isUserAgentIdentityStable(options))) return false;
|
|
351
387
|
try {
|
|
352
|
-
const current = await fs.promises.stat(abs);
|
|
388
|
+
const current = await fs.promises.stat(abs, { bigint: true });
|
|
353
389
|
return (
|
|
354
390
|
current.isDirectory() && sameFileIdentity(current, authorized) && (await isCurrentIsolatedPath(abs, options))
|
|
355
391
|
);
|
|
@@ -452,7 +488,7 @@ export async function findRepoRoot(
|
|
|
452
488
|
let current = resolvePath(startDir);
|
|
453
489
|
const stop = stopAt ? resolvePath(stopAt) : undefined;
|
|
454
490
|
while (true) {
|
|
455
|
-
const entries = await readDirEntries(current, options);
|
|
491
|
+
const entries = await readDirEntries(current, { ...options, bypassCache: true });
|
|
456
492
|
if (entries.some(e => e.name === ".git")) {
|
|
457
493
|
return current;
|
|
458
494
|
}
|
package/src/capability/index.ts
CHANGED
|
@@ -482,7 +482,7 @@ export async function loadCapabilityForHome<T>(
|
|
|
482
482
|
// read another profile's SYSTEM/RULES/AGENTS, skills, commands, hooks,
|
|
483
483
|
// settings, or executable descriptors.
|
|
484
484
|
const canonicalHome = await canonicalizeThroughExistingAncestor(resolvedHome);
|
|
485
|
-
const homeStats = await fs.stat(canonicalHome);
|
|
485
|
+
const homeStats = await fs.stat(canonicalHome, { bigint: true });
|
|
486
486
|
const homeIdentity = { dev: homeStats.dev, ino: homeStats.ino };
|
|
487
487
|
const trustedHome = await canonicalizeThroughExistingAncestor(getTrustedHomeDir());
|
|
488
488
|
const isolatedHome = canonicalHome !== trustedHome;
|
|
@@ -510,7 +510,7 @@ export async function loadCapabilityForHome<T>(
|
|
|
510
510
|
"user agent directory",
|
|
511
511
|
)
|
|
512
512
|
: path.join(canonicalHome, getConfigDirName(), "agent");
|
|
513
|
-
const userAgentStats = await fs.stat(userAgentDir).catch(() => null);
|
|
513
|
+
const userAgentStats = await fs.stat(userAgentDir, { bigint: true }).catch(() => null);
|
|
514
514
|
const userAgentIdentity = userAgentStats ? { dev: userAgentStats.dev, ino: userAgentStats.ino } : null;
|
|
515
515
|
await assertExplicitHomeRoots(canonicalHome, cwd, Boolean(options.agentDir), effectiveProviderIds, isolatedHome);
|
|
516
516
|
const repoRootCandidate = await findRepoRoot(cwd, canonicalHome, {
|
package/src/capability/rule.ts
CHANGED
|
@@ -18,6 +18,8 @@ export interface RuleFrontmatter {
|
|
|
18
18
|
alwaysApply?: boolean;
|
|
19
19
|
/** New key for TTSR match conditions. */
|
|
20
20
|
condition?: string | string[];
|
|
21
|
+
/** AST-grep pattern(s) for TTSR AST matching (evaluated on completed tool args). */
|
|
22
|
+
astCondition?: string | string[];
|
|
21
23
|
/** New key for TTSR stream scope. */
|
|
22
24
|
scope?: string | string[];
|
|
23
25
|
/** Per-rule TTSR interrupt mode override. */
|
|
@@ -48,6 +50,8 @@ export interface Rule {
|
|
|
48
50
|
description?: string;
|
|
49
51
|
/** Regex condition(s) that can trigger TTSR interruption. */
|
|
50
52
|
condition?: string[];
|
|
53
|
+
/** AST-grep pattern(s) for TTSR AST matching (evaluated on completed tool args). */
|
|
54
|
+
astCondition?: string[];
|
|
51
55
|
/** Optional stream scope tokens (for example: text, thinking, tool:edit(*.ts)). */
|
|
52
56
|
scope?: string[];
|
|
53
57
|
/** Per-rule TTSR interrupt mode override (falls back to global ttsr.interruptMode). */
|
|
@@ -182,17 +186,21 @@ function isLikelyFileGlob(value: string): boolean {
|
|
|
182
186
|
}
|
|
183
187
|
|
|
184
188
|
/**
|
|
185
|
-
* Parse `condition`
|
|
189
|
+
* Parse `condition`, `astCondition`, and `scope` from rule frontmatter.
|
|
186
190
|
*
|
|
187
191
|
* - `condition` accepts string or string[]
|
|
192
|
+
* - `astCondition` accepts string or string[] (ast-grep patterns)
|
|
188
193
|
* - `scope` accepts string or string[]
|
|
189
194
|
* - legacy `ttsr_trigger` / `ttsrTrigger` are accepted as a `condition` fallback
|
|
190
195
|
* - condition tokens that look like file globs become scope shorthands:
|
|
191
196
|
* `*.rs` => `tool:edit(*.rs)`, `tool:write(*.rs)` and a catch-all condition `.*`
|
|
192
197
|
*/
|
|
193
|
-
export function parseRuleConditionAndScope(
|
|
198
|
+
export function parseRuleConditionAndScope(
|
|
199
|
+
frontmatter: RuleFrontmatter,
|
|
200
|
+
): Pick<Rule, "condition" | "astCondition" | "scope"> {
|
|
194
201
|
const rawCondition = frontmatter.condition ?? frontmatter.ttsr_trigger ?? frontmatter.ttsrTrigger;
|
|
195
202
|
const parsedCondition = normalizeRuleField(rawCondition);
|
|
203
|
+
const parsedAstCondition = normalizeRuleField(frontmatter.astCondition);
|
|
196
204
|
const parsedScope = normalizeScopeField(frontmatter.scope);
|
|
197
205
|
|
|
198
206
|
const inferredScope: string[] = [];
|
|
@@ -214,6 +222,7 @@ export function parseRuleConditionAndScope(frontmatter: RuleFrontmatter): Pick<R
|
|
|
214
222
|
const scope = [...(parsedScope ?? []), ...inferredScope];
|
|
215
223
|
return {
|
|
216
224
|
condition: condition.length > 0 ? Array.from(new Set(condition)) : undefined,
|
|
225
|
+
astCondition: parsedAstCondition ? Array.from(new Set(parsedAstCondition)) : undefined,
|
|
217
226
|
scope: scope.length > 0 ? Array.from(new Set(scope)) : undefined,
|
|
218
227
|
};
|
|
219
228
|
}
|
package/src/cli/accounts-cli.ts
CHANGED
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
OAuthCredentialSelectorError,
|
|
15
15
|
type OAuthPinTarget,
|
|
16
16
|
resolveOAuthStorageProvider,
|
|
17
|
+
selectOrganizationMatches,
|
|
17
18
|
} from "@gajae-code/ai/core";
|
|
18
19
|
import { getAgentDir } from "@gajae-code/utils";
|
|
19
20
|
import { ModelRegistry } from "../config/model-registry";
|
|
@@ -25,15 +26,21 @@ import {
|
|
|
25
26
|
clearPersistentPinForRemovedRows,
|
|
26
27
|
} from "../session/account-inventory";
|
|
27
28
|
import { CREDENTIAL_STORE_UNREADABLE_MESSAGE } from "../session/credential-store-errors";
|
|
28
|
-
import {
|
|
29
|
+
import {
|
|
30
|
+
type CredentialPreferenceEntryValue,
|
|
31
|
+
isValidCredentialPreferenceCap,
|
|
32
|
+
parsePreferenceAccount,
|
|
33
|
+
resolveStartupAuthConfig,
|
|
34
|
+
type StartupAuthConfigSnapshot,
|
|
35
|
+
} from "../session/startup-auth-config";
|
|
29
36
|
|
|
30
|
-
export const ACCOUNTS_ACTIONS = ["list", "check", "pin", "logout"] as const;
|
|
37
|
+
export const ACCOUNTS_ACTIONS = ["list", "check", "pin", "prefer", "logout"] as const;
|
|
31
38
|
export type AccountsAction = (typeof ACCOUNTS_ACTIONS)[number];
|
|
32
39
|
|
|
33
40
|
export interface AccountsCommandArgs {
|
|
34
41
|
action: AccountsAction;
|
|
35
42
|
provider?: string;
|
|
36
|
-
|
|
43
|
+
selectors?: readonly string[];
|
|
37
44
|
flags: {
|
|
38
45
|
json?: boolean;
|
|
39
46
|
persistent?: boolean;
|
|
@@ -92,9 +99,10 @@ function selectorFromInput(raw: string | undefined): AuthCredentialSelector {
|
|
|
92
99
|
if (/^id:[1-9]\d*$/.test(value)) return { kind: "id", value: value.slice(3) };
|
|
93
100
|
if (/^email:[^@\s]+@[^@\s]+$/.test(value)) return { kind: "email", value: value.slice(6) };
|
|
94
101
|
if (/^account:\S+$/.test(value)) return { kind: "account", value: value.slice(8) };
|
|
102
|
+
if (/^org:\S/.test(value)) return { kind: "org", value: value.slice(4).trim() };
|
|
95
103
|
if (/^[^@\s]+@[^@\s]+$/.test(value)) return { kind: "email", value };
|
|
96
104
|
throw new AccountsCommandError(
|
|
97
|
-
"Pin target must be a bare email or an id:<row-id>, email:<email>,
|
|
105
|
+
"Pin target must be a bare email or an id:<row-id>, email:<email>, account:<account-id>, or org:<organization id or name> selector.",
|
|
98
106
|
);
|
|
99
107
|
}
|
|
100
108
|
|
|
@@ -286,15 +294,171 @@ async function runPin(
|
|
|
286
294
|
});
|
|
287
295
|
}
|
|
288
296
|
|
|
297
|
+
/**
|
|
298
|
+
* Split `<account>=<percent>` into the account and its usage cap. The cap follows the whole account, so an `=` before
|
|
299
|
+
* the last `@` belongs to an email local part (`user=2@example.com`), and a suffix that does not start with a digit is
|
|
300
|
+
* part of the account (account ids may end in `=`); a digit-led suffix must be a valid cap.
|
|
301
|
+
*/
|
|
302
|
+
function parsePreferArgument(raw: string): { account: string; cap: number | undefined } {
|
|
303
|
+
const separator = raw.lastIndexOf("=");
|
|
304
|
+
const rawCap = separator < 0 || separator < raw.lastIndexOf("@") ? "" : raw.slice(separator + 1).trim();
|
|
305
|
+
if (!/^\d/.test(rawCap)) return { account: raw.trim(), cap: undefined };
|
|
306
|
+
const cap = /^\d+(\.\d+)?$/.test(rawCap) ? Number(rawCap) : Number.NaN;
|
|
307
|
+
if (!isValidCredentialPreferenceCap(cap)) {
|
|
308
|
+
throw new AccountsCommandError(
|
|
309
|
+
`Usage cap in ${JSON.stringify(raw)} must be a percent above 0 and at most 100, e.g. me@example.com=75; no preference was written.`,
|
|
310
|
+
);
|
|
311
|
+
}
|
|
312
|
+
return { account: raw.slice(0, separator).trim(), cap };
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* Portable config text for a resolved OAuth row: its email, else its account id, whichever no other OAuth row of the
|
|
317
|
+
* provider shares. Numeric row ids are never persisted as preferences; a row with neither unique value is refused.
|
|
318
|
+
* Each candidate text is written only when {@link parsePreferenceAccount} reads it back as the same selector, so a
|
|
319
|
+
* successful write neither fails the next startup nor retargets the row: the email is written bare when that
|
|
320
|
+
* round-trips, else as `email:<address>` (an address such as `account:team@example.com` would otherwise parse as an
|
|
321
|
+
* account id, and `email:team@example.com` would lose its prefix).
|
|
322
|
+
*/
|
|
323
|
+
function portablePreferenceAccount(
|
|
324
|
+
row: CredentialInventoryRecord,
|
|
325
|
+
providerRows: readonly CredentialInventoryRecord[],
|
|
326
|
+
): string | undefined {
|
|
327
|
+
const oauthRows = providerRows.filter(other => other.credentialKind === "oauth");
|
|
328
|
+
const email = row.email?.trim().toLowerCase();
|
|
329
|
+
if (email && oauthRows.filter(other => other.email?.trim().toLowerCase() === email).length === 1) {
|
|
330
|
+
const selector: AuthCredentialSelector = { kind: "email", value: row.email!.trim() };
|
|
331
|
+
const written = [selector.value, `email:${selector.value}`].find(text => readsBackAs(text, selector));
|
|
332
|
+
if (written) return written;
|
|
333
|
+
}
|
|
334
|
+
const accountId = row.accountId?.trim();
|
|
335
|
+
if (accountId && oauthRows.filter(other => other.accountId?.trim() === accountId).length === 1) {
|
|
336
|
+
const written = `account:${accountId}`;
|
|
337
|
+
if (readsBackAs(written, { kind: "account", value: accountId })) return written;
|
|
338
|
+
}
|
|
339
|
+
return undefined;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
/** Whether startup parses `text` as exactly `selector`, kind and value. */
|
|
343
|
+
function readsBackAs(text: string, selector: AuthCredentialSelector): boolean {
|
|
344
|
+
const parsed = parsePreferenceAccount(text);
|
|
345
|
+
return parsed?.kind === selector.kind && parsed.value === selector.value;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
async function writePersistentPreferences(
|
|
349
|
+
provider: string,
|
|
350
|
+
entries: readonly CredentialPreferenceEntryValue[] | undefined,
|
|
351
|
+
): Promise<void> {
|
|
352
|
+
const settings = await Settings.init();
|
|
353
|
+
await settings.commitAtomicBatchWithCurrent(current => {
|
|
354
|
+
const currentAuth = current.auth === undefined ? {} : asRecord(current.auth);
|
|
355
|
+
const rawPreferences = currentAuth?.credentialPreferences;
|
|
356
|
+
const preferences = rawPreferences === undefined ? {} : asRecord(rawPreferences);
|
|
357
|
+
if (!currentAuth || !preferences) {
|
|
358
|
+
throw new AccountsCommandError("Global auth.credentialPreferences is malformed; no preference was written.");
|
|
359
|
+
}
|
|
360
|
+
const next: Record<string, unknown> = { ...preferences };
|
|
361
|
+
if (entries) next[provider] = entries;
|
|
362
|
+
else delete next[provider];
|
|
363
|
+
return [{ path: "auth.credentialPreferences", op: "set", value: next }];
|
|
364
|
+
});
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
async function runPrefer(
|
|
368
|
+
providerArg: string | undefined,
|
|
369
|
+
accountArgs: readonly string[] | undefined,
|
|
370
|
+
flags: AccountsCommandArgs["flags"],
|
|
371
|
+
): Promise<void> {
|
|
372
|
+
const provider = canonicalProvider(providerArg);
|
|
373
|
+
const accounts = accountArgs ?? [];
|
|
374
|
+
if (flags.clear && accounts.length > 0) {
|
|
375
|
+
throw new AccountsCommandError("Use either preferred accounts or --clear, not both.");
|
|
376
|
+
}
|
|
377
|
+
if (!flags.clear && accounts.length === 0) {
|
|
378
|
+
throw new AccountsCommandError(
|
|
379
|
+
"Prefer requires one or more accounts in order (e.g. me@example.com shared@example.com=75), or --clear.",
|
|
380
|
+
);
|
|
381
|
+
}
|
|
382
|
+
if (flags.clear) {
|
|
383
|
+
await writePersistentPreferences(provider, undefined);
|
|
384
|
+
if (flags.json) writeJson({ ok: true, provider, cleared: true });
|
|
385
|
+
else writeText([`Cleared preferred accounts for ${provider}; selection uses normal ranking.`]);
|
|
386
|
+
return;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
const requested = accounts.map(parsePreferArgument);
|
|
390
|
+
await withAuthStorageAndModels(async (storage, modelRegistry, startupAuth) => {
|
|
391
|
+
if (
|
|
392
|
+
!modelRegistry.getConfiguredProviderIds().includes(provider) &&
|
|
393
|
+
modelRegistry.getAll().every(model => model.provider !== provider)
|
|
394
|
+
) {
|
|
395
|
+
throw new AccountsCommandError(`Provider ${provider} is not configured; no preference was written.`);
|
|
396
|
+
}
|
|
397
|
+
const providerRows = storage.listCredentialInventory(provider);
|
|
398
|
+
const seenIds = new Set<number>();
|
|
399
|
+
const resolved = requested.map(({ account, cap }) => {
|
|
400
|
+
let target: OAuthPinTarget;
|
|
401
|
+
try {
|
|
402
|
+
target = storage.resolveOAuthPinTarget(provider, selectorFromInput(account));
|
|
403
|
+
} catch (error) {
|
|
404
|
+
throw toAccountsCommandError(error) ?? error;
|
|
405
|
+
}
|
|
406
|
+
if (seenIds.has(target.credentialId)) {
|
|
407
|
+
throw new AccountsCommandError(`${account} is listed more than once; no preference was written.`);
|
|
408
|
+
}
|
|
409
|
+
seenIds.add(target.credentialId);
|
|
410
|
+
const row = providerRows.find(candidate => candidate.id === target.credentialId);
|
|
411
|
+
const portable = row ? portablePreferenceAccount(row, providerRows) : undefined;
|
|
412
|
+
if (!portable) {
|
|
413
|
+
throw new AccountsCommandError(
|
|
414
|
+
`${account} has no unique email or account id to store as a preference; no preference was written.`,
|
|
415
|
+
);
|
|
416
|
+
}
|
|
417
|
+
return { account: portable, cap: cap === 100 ? undefined : cap };
|
|
418
|
+
});
|
|
419
|
+
const entries: CredentialPreferenceEntryValue[] = resolved.map(({ account, cap }) =>
|
|
420
|
+
cap === undefined ? account : { [account]: cap },
|
|
421
|
+
);
|
|
422
|
+
await writePersistentPreferences(provider, entries);
|
|
423
|
+
if (flags.json) {
|
|
424
|
+
writeJson({ ok: true, provider, preferences: entries });
|
|
425
|
+
return;
|
|
426
|
+
}
|
|
427
|
+
writeText([
|
|
428
|
+
`Preferred accounts for ${provider}, tried in order:`,
|
|
429
|
+
...resolved.map(
|
|
430
|
+
({ account, cap }, index) =>
|
|
431
|
+
` ${index + 1}. ${account}${cap === undefined ? " (until rate-limited)" : ` (until ${cap}% used)`}`,
|
|
432
|
+
),
|
|
433
|
+
`Then: remaining accounts by ${startupAuth.credentialRankingMode} ranking. Takes effect for new sessions.`,
|
|
434
|
+
]);
|
|
435
|
+
});
|
|
436
|
+
}
|
|
437
|
+
|
|
289
438
|
function accountMatches(row: CredentialInventoryRecord, rawAccount: string): boolean {
|
|
290
439
|
const value = rawAccount.trim();
|
|
291
440
|
if (value.length === 0) return false;
|
|
292
441
|
if (/^[1-9]\d*$/.test(value) && row.id === Number(value)) return true;
|
|
293
442
|
if (/^id:[1-9]\d*$/i.test(value) && row.id === Number(value.slice(3))) return true;
|
|
443
|
+
// Labels carry the organization name, so a bare email matches every organization of that account.
|
|
444
|
+
if (row.email?.toLowerCase() === value.toLowerCase()) return true;
|
|
294
445
|
const identity = inventoryIdentity(row);
|
|
295
446
|
return identity?.toLowerCase() === value.toLowerCase();
|
|
296
447
|
}
|
|
297
448
|
|
|
449
|
+
/**
|
|
450
|
+
* Rows a `--account` value designates. `org:` prefers an exact organization-id match over display-name
|
|
451
|
+
* matches; more than one result is reported as ambiguous by the caller, never resolved by guessing.
|
|
452
|
+
*/
|
|
453
|
+
function selectLogoutTargets<T extends { row: CredentialInventoryRecord }>(
|
|
454
|
+
targets: readonly T[],
|
|
455
|
+
rawAccount: string,
|
|
456
|
+
): T[] {
|
|
457
|
+
const value = rawAccount.trim();
|
|
458
|
+
if (/^org:/i.test(value)) return selectOrganizationMatches(targets, value.slice(4), target => target.row);
|
|
459
|
+
return targets.filter(target => accountMatches(target.row, value));
|
|
460
|
+
}
|
|
461
|
+
|
|
298
462
|
function removalCandidates(
|
|
299
463
|
inventory: readonly CredentialInventoryRecord[],
|
|
300
464
|
targets: readonly CredentialRemovalTarget[],
|
|
@@ -335,9 +499,7 @@ async function runLogout(providerArg: string | undefined, flags: AccountsCommand
|
|
|
335
499
|
try {
|
|
336
500
|
const inventory = storage.listCredentialInventory(provider);
|
|
337
501
|
const targets = removalCandidates(inventory, storage.listCredentialRemovalTargets(provider));
|
|
338
|
-
const selected = flags.all
|
|
339
|
-
? targets
|
|
340
|
-
: targets.filter(candidate => accountMatches(candidate.row, flags.account ?? ""));
|
|
502
|
+
const selected = flags.all ? targets : selectLogoutTargets(targets, flags.account ?? "");
|
|
341
503
|
if (selected.length !== 1 && !flags.all) throwLogoutResolutionError(provider, flags.account, targets);
|
|
342
504
|
if (selected.length === 0) throwLogoutResolutionError(provider, flags.account, targets);
|
|
343
505
|
const result = storage.removeAuthCredentialsHard(
|
|
@@ -383,7 +545,13 @@ export async function runAccountsCommand(cmd: AccountsCommandArgs): Promise<void
|
|
|
383
545
|
await runCheck(cmd.provider, cmd.flags);
|
|
384
546
|
return;
|
|
385
547
|
case "pin":
|
|
386
|
-
|
|
548
|
+
if ((cmd.selectors?.length ?? 0) > 1) {
|
|
549
|
+
throw new AccountsCommandError("Pin takes one selector; use `gjc accounts prefer` for an ordered list.");
|
|
550
|
+
}
|
|
551
|
+
await runPin(cmd.provider, cmd.selectors?.[0], cmd.flags);
|
|
552
|
+
return;
|
|
553
|
+
case "prefer":
|
|
554
|
+
await runPrefer(cmd.provider, cmd.selectors, cmd.flags);
|
|
387
555
|
return;
|
|
388
556
|
case "logout":
|
|
389
557
|
await runLogout(cmd.provider, cmd.flags);
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export interface AppendSystemPromptCandidate {
|
|
2
|
+
level: "user" | "project";
|
|
3
|
+
path: string;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
/** First user file. A repository copy is untrusted project text and is never selected. */
|
|
7
|
+
export function selectAppendSystemPromptFile(candidates: readonly AppendSystemPromptCandidate[]): string | undefined {
|
|
8
|
+
for (const candidate of candidates) {
|
|
9
|
+
if (candidate.level === "user") return candidate.path;
|
|
10
|
+
}
|
|
11
|
+
return undefined;
|
|
12
|
+
}
|
package/src/cli/args.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import * as path from "node:path";
|
|
5
5
|
import { ThinkingLevel } from "@gajae-code/agent-core";
|
|
6
|
-
import { logger } from "@gajae-code/utils";
|
|
6
|
+
import { $pickenv, logger } from "@gajae-code/utils";
|
|
7
7
|
import { CliParseError } from "@gajae-code/utils/cli";
|
|
8
8
|
import { parseThinkingLevel } from "../thinking";
|
|
9
9
|
import { BUILTIN_TOOLS } from "../tools";
|
|
@@ -46,8 +46,6 @@ export interface Args {
|
|
|
46
46
|
allowHome?: boolean;
|
|
47
47
|
provider?: string;
|
|
48
48
|
model?: string;
|
|
49
|
-
smol?: string;
|
|
50
|
-
slow?: string;
|
|
51
49
|
plan?: string;
|
|
52
50
|
mpreset?: string;
|
|
53
51
|
default?: boolean;
|
|
@@ -228,10 +226,6 @@ export function parseArgs(args: string[], authority: ParseArgsAuthority = "local
|
|
|
228
226
|
result.provider = takeFlagValue(args, i++, "--provider");
|
|
229
227
|
} else if (arg === "--model") {
|
|
230
228
|
result.model = takeFlagValue(args, i++, "--model");
|
|
231
|
-
} else if (arg === "--smol") {
|
|
232
|
-
result.smol = takeFlagValue(args, i++, "--smol");
|
|
233
|
-
} else if (arg === "--slow") {
|
|
234
|
-
result.slow = takeFlagValue(args, i++, "--slow");
|
|
235
229
|
} else if (arg === "--plan") {
|
|
236
230
|
result.plan = takeFlagValue(args, i++, "--plan");
|
|
237
231
|
} else if (arg === "--mpreset") {
|
|
@@ -427,3 +421,13 @@ export function assertLocalLaunchArgs(parsed: Args): void {
|
|
|
427
421
|
if (!launchFlagIsOwnedBy(name, "local")) throw new CliParseError(`Unknown option: ${token}`);
|
|
428
422
|
}
|
|
429
423
|
}
|
|
424
|
+
|
|
425
|
+
/**
|
|
426
|
+
* Resolve the ephemeral `plan` model-role override that plan mode switches to.
|
|
427
|
+
*
|
|
428
|
+
* Precedence is CLI `--plan` > `GJC_PLAN_MODEL` > legacy `PI_PLAN_MODEL`.
|
|
429
|
+
* `$pickenv` trims values and treats empty/whitespace as unset.
|
|
430
|
+
*/
|
|
431
|
+
export function resolvePlanModelOverride(parsed: Pick<Args, "plan">): string | undefined {
|
|
432
|
+
return parsed.plan ?? $pickenv("GJC_PLAN_MODEL", "PI_PLAN_MODEL");
|
|
433
|
+
}
|