@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
|
@@ -9,7 +9,11 @@
|
|
|
9
9
|
|
|
10
10
|
import * as os from "node:os";
|
|
11
11
|
import * as path from "node:path";
|
|
12
|
-
import type {
|
|
12
|
+
import type {
|
|
13
|
+
AuthCredentialSelector,
|
|
14
|
+
ConfiguredCredentialPreference,
|
|
15
|
+
CredentialRankingMode,
|
|
16
|
+
} from "@gajae-code/ai/core";
|
|
13
17
|
import { $credentialEnv, getAgentDbPath, getAgentDir, getConfigRootDir, isEnoent, logger } from "@gajae-code/utils";
|
|
14
18
|
import { YAML } from "bun";
|
|
15
19
|
import { readSecureTokenFile } from "./secure-token-file";
|
|
@@ -27,6 +31,8 @@ export interface StartupAuthConfigSnapshot {
|
|
|
27
31
|
credentialPinStoreIdentity?: string;
|
|
28
32
|
credentialRankingMode: CredentialRankingMode;
|
|
29
33
|
credentialPins: Readonly<Record<string, string>>;
|
|
34
|
+
/** Ordered soft per-provider preferences; only portable email/`account:` selectors are accepted. */
|
|
35
|
+
credentialPreferences: Readonly<Record<string, readonly ConfiguredCredentialPreference[]>>;
|
|
30
36
|
}
|
|
31
37
|
|
|
32
38
|
export type StartupAuthConfigErrorKind =
|
|
@@ -37,7 +43,8 @@ export type StartupAuthConfigErrorKind =
|
|
|
37
43
|
| "invalid-broker"
|
|
38
44
|
| "invalid-gateway"
|
|
39
45
|
| "invalid-ranking-mode"
|
|
40
|
-
| "invalid-credential-pins"
|
|
46
|
+
| "invalid-credential-pins"
|
|
47
|
+
| "invalid-credential-preferences";
|
|
41
48
|
|
|
42
49
|
/** A malformed startup-auth config aborts resolution rather than downgrading to local authority. */
|
|
43
50
|
export class StartupAuthConfigError extends Error {
|
|
@@ -64,6 +71,7 @@ const LEGACY_LITERAL_AUTH_KEYS = new Set([
|
|
|
64
71
|
"auth.broker.token",
|
|
65
72
|
"auth.credentialRankingMode",
|
|
66
73
|
"auth.credentialPins",
|
|
74
|
+
"auth.credentialPreferences",
|
|
67
75
|
]);
|
|
68
76
|
|
|
69
77
|
function configPathForAgentDir(agentDir: string): string {
|
|
@@ -78,7 +86,7 @@ function shortenConfigPath(configPath: string): string {
|
|
|
78
86
|
return absolutePath.startsWith(homePrefix) ? `~${absolutePath.slice(homePath.length)}` : absolutePath;
|
|
79
87
|
}
|
|
80
88
|
|
|
81
|
-
function startupAuthConfigError(agentDir: string, kind: StartupAuthConfigErrorKind): StartupAuthConfigError {
|
|
89
|
+
export function startupAuthConfigError(agentDir: string, kind: StartupAuthConfigErrorKind): StartupAuthConfigError {
|
|
82
90
|
return new StartupAuthConfigError(kind, configPathForAgentDir(agentDir));
|
|
83
91
|
}
|
|
84
92
|
|
|
@@ -100,6 +108,69 @@ export function isValidPersistedCredentialSelector(value: string): boolean {
|
|
|
100
108
|
return parsePersistedCredentialSelector(value) !== undefined;
|
|
101
109
|
}
|
|
102
110
|
|
|
111
|
+
/** JSON-schema pattern for a preference account; shared with {@link parsePreferenceAccount}. */
|
|
112
|
+
export const PREFERENCE_ACCOUNT_PATTERN = "^(email:[^@\\s]+@[^@\\s]+|account:\\S+|[^@\\s:]+@[^@\\s]+)$";
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Parse one `auth.credentialPreferences` account: a bare email, `email:<address>`, or `account:<id>`. Numeric row ids
|
|
116
|
+
* are excluded: they are bound to one credential store, and a preference has no store-identity binding to detect a
|
|
117
|
+
* retargeted row.
|
|
118
|
+
*/
|
|
119
|
+
export function parsePreferenceAccount(value: string): AuthCredentialSelector | undefined {
|
|
120
|
+
const trimmed = value.trim();
|
|
121
|
+
const selector = parsePersistedCredentialSelector(trimmed);
|
|
122
|
+
if (selector) return selector.kind === "id" ? undefined : selector;
|
|
123
|
+
return /^[^@\s:]+@[^@\s]+$/.test(trimmed) ? { kind: "email", value: trimmed } : undefined;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/** A preference cap accepts (0, 100]; 100 moves past the account only once it is blocked. */
|
|
127
|
+
export function isValidCredentialPreferenceCap(value: unknown): value is number {
|
|
128
|
+
return typeof value === "number" && Number.isFinite(value) && value > 0 && value <= 100;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/** Canonical config text for one preference entry: the account, or a one-key `{ account: cap }` mapping. */
|
|
132
|
+
export type CredentialPreferenceEntryValue = string | Readonly<Record<string, number>>;
|
|
133
|
+
|
|
134
|
+
function parseCredentialPreferenceEntry(value: unknown): ConfiguredCredentialPreference | undefined {
|
|
135
|
+
if (typeof value === "string") {
|
|
136
|
+
const selector = parsePreferenceAccount(value);
|
|
137
|
+
return selector ? { selector } : undefined;
|
|
138
|
+
}
|
|
139
|
+
const entry = asRecord(value);
|
|
140
|
+
const fields = entry ? Object.entries(entry) : [];
|
|
141
|
+
if (fields.length !== 1) return undefined;
|
|
142
|
+
const [account, cap] = fields[0]!;
|
|
143
|
+
const selector = parsePreferenceAccount(account);
|
|
144
|
+
if (!selector || !isValidCredentialPreferenceCap(cap)) return undefined;
|
|
145
|
+
return cap === 100 ? { selector } : { selector, maxUsedFraction: cap / 100 };
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function preferenceSelectorKey(selector: AuthCredentialSelector): string {
|
|
149
|
+
return `${selector.kind}:${selector.kind === "email" ? selector.value.toLowerCase() : selector.value}`;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Parse one provider's `auth.credentialPreferences` value: a single account, or a non-empty ordered list whose
|
|
154
|
+
* entries are an account (`me@example.com`) or an account with a usage cap in percent (`me@example.com: 75`).
|
|
155
|
+
* Returns undefined for any invalid value, including an account listed twice, so a typo fails closed instead of
|
|
156
|
+
* silently changing the order.
|
|
157
|
+
*/
|
|
158
|
+
export function parseCredentialPreferenceList(value: unknown): ConfiguredCredentialPreference[] | undefined {
|
|
159
|
+
const rawEntries = Array.isArray(value) ? value : [value];
|
|
160
|
+
if (rawEntries.length === 0) return undefined;
|
|
161
|
+
const entries: ConfiguredCredentialPreference[] = [];
|
|
162
|
+
const seen = new Set<string>();
|
|
163
|
+
for (const rawEntry of rawEntries) {
|
|
164
|
+
const entry = parseCredentialPreferenceEntry(rawEntry);
|
|
165
|
+
if (!entry) return undefined;
|
|
166
|
+
const key = preferenceSelectorKey(entry.selector);
|
|
167
|
+
if (seen.has(key)) return undefined;
|
|
168
|
+
seen.add(key);
|
|
169
|
+
entries.push(entry);
|
|
170
|
+
}
|
|
171
|
+
return entries;
|
|
172
|
+
}
|
|
173
|
+
|
|
103
174
|
function asRecord(value: unknown): Record<string, unknown> | undefined {
|
|
104
175
|
return value !== null && typeof value === "object" && !Array.isArray(value)
|
|
105
176
|
? (value as Record<string, unknown>)
|
|
@@ -145,6 +216,10 @@ function throwLegacyLiteralKeyError(keys: string[]): never {
|
|
|
145
216
|
" credentialRankingMode: balanced\n" +
|
|
146
217
|
" credentialPins:\n" +
|
|
147
218
|
" <provider>: id:<positive-id>\n" +
|
|
219
|
+
" credentialPreferences:\n" +
|
|
220
|
+
" <provider>:\n" +
|
|
221
|
+
" - <email>\n" +
|
|
222
|
+
" - <email>: <max used percent, above 0 and at most 100>\n" +
|
|
148
223
|
"Do not copy secret values into command output; rewrite the file by hand.",
|
|
149
224
|
);
|
|
150
225
|
}
|
|
@@ -172,23 +247,53 @@ function readCredentialPins(
|
|
|
172
247
|
return result;
|
|
173
248
|
}
|
|
174
249
|
|
|
250
|
+
function readCredentialPreferences(
|
|
251
|
+
auth: Record<string, unknown> | undefined,
|
|
252
|
+
configPath: string,
|
|
253
|
+
): Readonly<Record<string, readonly ConfiguredCredentialPreference[]>> {
|
|
254
|
+
const rawPreferences = auth?.credentialPreferences;
|
|
255
|
+
if (rawPreferences === undefined) return {};
|
|
256
|
+
const preferences = asRecord(rawPreferences);
|
|
257
|
+
if (!preferences) throw new StartupAuthConfigError("invalid-credential-preferences", configPath);
|
|
258
|
+
|
|
259
|
+
const result: Record<string, readonly ConfiguredCredentialPreference[]> = {};
|
|
260
|
+
for (const [provider, rawList] of Object.entries(preferences)) {
|
|
261
|
+
const normalizedProvider = provider.trim();
|
|
262
|
+
if (normalizedProvider.length === 0 || /[\u0000-\u001f\u007f]/u.test(normalizedProvider)) {
|
|
263
|
+
throw new StartupAuthConfigError("invalid-credential-preferences", configPath);
|
|
264
|
+
}
|
|
265
|
+
const entries = parseCredentialPreferenceList(rawList);
|
|
266
|
+
if (!entries) throw new StartupAuthConfigError("invalid-credential-preferences", configPath);
|
|
267
|
+
result[normalizedProvider] = entries;
|
|
268
|
+
}
|
|
269
|
+
return result;
|
|
270
|
+
}
|
|
271
|
+
|
|
175
272
|
interface GlobalStartupAuthYaml {
|
|
176
273
|
auth: Record<string, unknown> | undefined;
|
|
177
274
|
credentialPinStoreIdentity: string | undefined;
|
|
178
275
|
credentialPins: Readonly<Record<string, string>>;
|
|
276
|
+
credentialPreferences: Readonly<Record<string, readonly ConfiguredCredentialPreference[]>>;
|
|
179
277
|
}
|
|
180
278
|
|
|
279
|
+
const EMPTY_GLOBAL_STARTUP_AUTH: GlobalStartupAuthYaml = {
|
|
280
|
+
auth: undefined,
|
|
281
|
+
credentialPinStoreIdentity: undefined,
|
|
282
|
+
credentialPins: {},
|
|
283
|
+
credentialPreferences: {},
|
|
284
|
+
};
|
|
285
|
+
|
|
181
286
|
async function readGlobalStartupAuthYaml(agentDir: string): Promise<GlobalStartupAuthYaml> {
|
|
182
287
|
const configPath = configPathForAgentDir(agentDir);
|
|
183
288
|
let raw: string;
|
|
184
289
|
try {
|
|
185
290
|
raw = await Bun.file(configPath).text();
|
|
186
291
|
} catch (error) {
|
|
187
|
-
if (isEnoent(error)) return
|
|
292
|
+
if (isEnoent(error)) return EMPTY_GLOBAL_STARTUP_AUTH;
|
|
188
293
|
logger.warn("startup auth config.yml unreadable", { path: shortenConfigPath(configPath), cause: "unreadable" });
|
|
189
294
|
throw startupAuthConfigError(agentDir, "unreadable");
|
|
190
295
|
}
|
|
191
|
-
if (raw.trim() === "") return
|
|
296
|
+
if (raw.trim() === "") return EMPTY_GLOBAL_STARTUP_AUTH;
|
|
192
297
|
|
|
193
298
|
let parsed: unknown;
|
|
194
299
|
try {
|
|
@@ -211,7 +316,7 @@ async function readGlobalStartupAuthYaml(agentDir: string): Promise<GlobalStartu
|
|
|
211
316
|
const legacyKeys = Object.keys(root).filter(key => LEGACY_LITERAL_AUTH_KEYS.has(key));
|
|
212
317
|
if (legacyKeys.length > 0) throwLegacyLiteralKeyError(legacyKeys);
|
|
213
318
|
|
|
214
|
-
if (root.auth === undefined) return
|
|
319
|
+
if (root.auth === undefined) return EMPTY_GLOBAL_STARTUP_AUTH;
|
|
215
320
|
const auth = asRecord(root.auth);
|
|
216
321
|
if (!auth) throw startupAuthConfigError(agentDir, "invalid-auth");
|
|
217
322
|
|
|
@@ -234,12 +339,13 @@ async function readGlobalStartupAuthYaml(agentDir: string): Promise<GlobalStartu
|
|
|
234
339
|
throw startupAuthConfigError(agentDir, "invalid-ranking-mode");
|
|
235
340
|
}
|
|
236
341
|
const credentialPins = readCredentialPins(auth, configPath);
|
|
342
|
+
const credentialPreferences = readCredentialPreferences(auth, configPath);
|
|
237
343
|
const rawCredentialPinStoreIdentity = auth.credentialPinStoreIdentity;
|
|
238
344
|
const credentialPinStoreIdentity =
|
|
239
345
|
typeof rawCredentialPinStoreIdentity === "string" && rawCredentialPinStoreIdentity.trim().length > 0
|
|
240
346
|
? rawCredentialPinStoreIdentity.trim()
|
|
241
347
|
: undefined;
|
|
242
|
-
return { auth, credentialPinStoreIdentity, credentialPins };
|
|
348
|
+
return { auth, credentialPinStoreIdentity, credentialPins, credentialPreferences };
|
|
243
349
|
}
|
|
244
350
|
|
|
245
351
|
async function readTokenFile(): Promise<string | undefined> {
|
|
@@ -253,7 +359,8 @@ async function readTokenFile(): Promise<string | undefined> {
|
|
|
253
359
|
* cannot influence credential selection.
|
|
254
360
|
*/
|
|
255
361
|
export async function resolveStartupAuthConfig(agentDir: string = getAgentDir()): Promise<StartupAuthConfigSnapshot> {
|
|
256
|
-
const { auth, credentialPinStoreIdentity, credentialPins } =
|
|
362
|
+
const { auth, credentialPinStoreIdentity, credentialPins, credentialPreferences } =
|
|
363
|
+
await readGlobalStartupAuthYaml(agentDir);
|
|
257
364
|
const broker = asRecord(auth?.broker);
|
|
258
365
|
const envUrl = $credentialEnv("GJC_AUTH_BROKER_URL")?.trim();
|
|
259
366
|
const envToken = $credentialEnv("GJC_AUTH_BROKER_TOKEN")?.trim();
|
|
@@ -293,5 +400,6 @@ export async function resolveStartupAuthConfig(agentDir: string = getAgentDir())
|
|
|
293
400
|
...(credentialPinStoreIdentity ? { credentialPinStoreIdentity } : {}),
|
|
294
401
|
credentialRankingMode: rankingMode,
|
|
295
402
|
credentialPins,
|
|
403
|
+
credentialPreferences,
|
|
296
404
|
};
|
|
297
405
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
1
2
|
import type { AgentToolUpdateCallback } from "@gajae-code/agent-core";
|
|
2
3
|
|
|
3
4
|
import { sanitizeText } from "@gajae-code/utils";
|
|
@@ -28,6 +29,78 @@ const NL = "\n";
|
|
|
28
29
|
|
|
29
30
|
const ELLIPSIS = "…";
|
|
30
31
|
|
|
32
|
+
// Bound the temporary parts/join allocation in #applyColumnCap for a single
|
|
33
|
+
// large callback. Keep batches small enough to avoid per-line retention work.
|
|
34
|
+
const COLUMN_CAP_BATCH_CHARS = 64 * 1024;
|
|
35
|
+
|
|
36
|
+
const outputPlainSecrets = new AsyncLocalStorage<readonly string[]>();
|
|
37
|
+
|
|
38
|
+
/** Run `fn` while column caps can see these plain secrets. */
|
|
39
|
+
export function runWithOutputRedaction<T>(plainSecrets: readonly string[], fn: () => T): T {
|
|
40
|
+
return outputPlainSecrets.run(plainSecrets, fn);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** Get the active plain secrets from AsyncLocalStorage, if any are set. */
|
|
44
|
+
function activePlainSecrets(): readonly string[] | undefined {
|
|
45
|
+
const secrets = outputPlainSecrets.getStore();
|
|
46
|
+
return secrets && secrets.length > 0 ? secrets : undefined;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Pull a column-cap end back to the start of any plain secret that crosses it.
|
|
51
|
+
* A secret that ends on the cut stays, so a later whole-string obfuscator can match it.
|
|
52
|
+
*/
|
|
53
|
+
function columnHeadEndOutsideSecrets(segment: string, end: number, secrets: readonly string[]): number {
|
|
54
|
+
let cut = end;
|
|
55
|
+
while (cut > 0) {
|
|
56
|
+
let earliest = cut;
|
|
57
|
+
for (const secret of secrets) {
|
|
58
|
+
const secretChars = secret.length;
|
|
59
|
+
if (secretChars === 0) continue;
|
|
60
|
+
const windowStart = Math.max(0, cut - secretChars);
|
|
61
|
+
const window = segment.slice(windowStart, Math.min(segment.length, cut + secretChars));
|
|
62
|
+
const limit = cut - windowStart;
|
|
63
|
+
let from = 0;
|
|
64
|
+
while (from < limit) {
|
|
65
|
+
const rel = window.indexOf(secret, from);
|
|
66
|
+
if (rel === -1 || rel >= limit) break;
|
|
67
|
+
if (rel + secretChars > limit) earliest = Math.min(earliest, windowStart + rel);
|
|
68
|
+
from = rel + 1;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
if (earliest === cut) break;
|
|
72
|
+
cut = earliest;
|
|
73
|
+
}
|
|
74
|
+
return cut;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Pull a column-cap end off a proper prefix of a plain secret.
|
|
79
|
+
* Each candidate's continuation is checked from longest to shortest.
|
|
80
|
+
* A longer candidate that is already finished or contradicted does not hide
|
|
81
|
+
* a shorter open prefix. A secret that already ends on the cut stays.
|
|
82
|
+
*/
|
|
83
|
+
function columnHeadEndBeforeOpenPrefix(text: string, end: number, secrets: readonly string[]): number {
|
|
84
|
+
let cut = end;
|
|
85
|
+
const after = text.slice(end);
|
|
86
|
+
for (const secret of secrets) {
|
|
87
|
+
const secretChars = secret.length;
|
|
88
|
+
if (secretChars < 2 || end < 1) continue;
|
|
89
|
+
if (end >= secretChars && text.slice(end - secretChars, end) === secret) continue;
|
|
90
|
+
const maxPrefix = Math.min(secretChars - 1, end);
|
|
91
|
+
for (let k = maxPrefix; k >= 1; k--) {
|
|
92
|
+
if (text.slice(end - k, end) !== secret.slice(0, k)) continue;
|
|
93
|
+
const rest = secret.slice(k);
|
|
94
|
+
const have = Math.min(after.length, rest.length);
|
|
95
|
+
if (have === rest.length) continue;
|
|
96
|
+
if (after.slice(0, have) !== rest.slice(0, have)) continue;
|
|
97
|
+
cut = Math.min(cut, end - k);
|
|
98
|
+
break;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return cut;
|
|
102
|
+
}
|
|
103
|
+
|
|
31
104
|
const ARTIFACT_WRITER_DIAGNOSTIC_MAX_BYTES = 256;
|
|
32
105
|
|
|
33
106
|
/**
|
|
@@ -314,6 +387,72 @@ export function truncateLine(
|
|
|
314
387
|
return { text: `${line.slice(0, maxChars)}…`, wasTruncated: true };
|
|
315
388
|
}
|
|
316
389
|
|
|
390
|
+
/**
|
|
391
|
+
* Truncate each line to max characters without leaving a configured plain-secret
|
|
392
|
+
* prefix in the retained text. Prior uncapped lines remain in scope because a
|
|
393
|
+
* later line's cut can split a multiline secret.
|
|
394
|
+
*/
|
|
395
|
+
export function truncateTextWithSecretAwareness(
|
|
396
|
+
text: string,
|
|
397
|
+
maxChars: number = DEFAULT_MAX_COLUMN,
|
|
398
|
+
secrets?: readonly string[],
|
|
399
|
+
): { text: string; wasTruncated: boolean } {
|
|
400
|
+
const lines = text.split(NL);
|
|
401
|
+
const activeSecrets = secrets ?? activePlainSecrets();
|
|
402
|
+
if (!activeSecrets?.some(secret => secret.length > 0)) {
|
|
403
|
+
let wasTruncated = false;
|
|
404
|
+
const truncated = lines.map(line => {
|
|
405
|
+
const result = truncateLine(line, maxChars);
|
|
406
|
+
wasTruncated ||= result.wasTruncated;
|
|
407
|
+
return result.text;
|
|
408
|
+
});
|
|
409
|
+
return { text: truncated.join(NL), wasTruncated };
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
const contextChars = activeSecrets.reduce((max, secret) => Math.max(max, secret.length), 0);
|
|
413
|
+
let context = "";
|
|
414
|
+
const outputParts: string[] = [];
|
|
415
|
+
let wasTruncated = false;
|
|
416
|
+
for (let index = 0; index < lines.length; index++) {
|
|
417
|
+
const line = lines[index] ?? "";
|
|
418
|
+
const newline = index < lines.length - 1 ? NL : "";
|
|
419
|
+
if (line.length <= maxChars) {
|
|
420
|
+
outputParts.push(line, newline);
|
|
421
|
+
context = (context + line + newline).slice(-contextChars);
|
|
422
|
+
continue;
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
wasTruncated = true;
|
|
426
|
+
const combined = context + line;
|
|
427
|
+
let end = context.length + maxChars;
|
|
428
|
+
while (end > 0) {
|
|
429
|
+
const next = Math.min(
|
|
430
|
+
columnHeadEndOutsideSecrets(combined, end, activeSecrets),
|
|
431
|
+
columnHeadEndBeforeOpenPrefix(combined, end, activeSecrets),
|
|
432
|
+
);
|
|
433
|
+
if (next >= end) break;
|
|
434
|
+
end = next;
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
if (end < context.length) {
|
|
438
|
+
const retract = context.slice(end);
|
|
439
|
+
let output = outputParts.join("");
|
|
440
|
+
if (output.endsWith(retract)) {
|
|
441
|
+
output = output.slice(0, output.length - retract.length);
|
|
442
|
+
output += NL.repeat(countNewlines(retract));
|
|
443
|
+
} else {
|
|
444
|
+
output = NL.repeat(countNewlines(output));
|
|
445
|
+
}
|
|
446
|
+
outputParts.length = 0;
|
|
447
|
+
outputParts.push(output);
|
|
448
|
+
}
|
|
449
|
+
const keptChars = Math.max(0, end - context.length);
|
|
450
|
+
outputParts.push(line.slice(0, keptChars), ELLIPSIS, newline);
|
|
451
|
+
context = "";
|
|
452
|
+
}
|
|
453
|
+
return { text: outputParts.join(""), wasTruncated };
|
|
454
|
+
}
|
|
455
|
+
|
|
317
456
|
// =============================================================================
|
|
318
457
|
// Content truncation (line + byte aware, no full Buffer allocation)
|
|
319
458
|
// =============================================================================
|
|
@@ -952,6 +1091,13 @@ export class OutputSink {
|
|
|
952
1091
|
#columnEllipsisAdded = false;
|
|
953
1092
|
#columnDroppedBytes = 0;
|
|
954
1093
|
#columnTruncatedLines = 0;
|
|
1094
|
+
/** Plain secrets captured when this sink is built inside an active redaction scope. */
|
|
1095
|
+
readonly #plainSecrets?: readonly string[];
|
|
1096
|
+
readonly #plainSecretContextChars: number;
|
|
1097
|
+
/** Text already retained for the open line. A later chunk may shorten it. */
|
|
1098
|
+
#columnLine = "";
|
|
1099
|
+
/** Bounded suffix of prior uncapped output, preserved for multiline secrets. */
|
|
1100
|
+
#columnLineContext = "";
|
|
955
1101
|
#file?: {
|
|
956
1102
|
path: string;
|
|
957
1103
|
artifactId?: string;
|
|
@@ -1010,6 +1156,8 @@ export class OutputSink {
|
|
|
1010
1156
|
this.#artifactMaxBytes = Math.max(0, artifactMaxBytes);
|
|
1011
1157
|
this.#artifactPublisher = artifactPublisher;
|
|
1012
1158
|
this.#coalesceSanitize = coalesceSanitize;
|
|
1159
|
+
this.#plainSecrets = activePlainSecrets();
|
|
1160
|
+
this.#plainSecretContextChars = this.#plainSecrets?.reduce((max, secret) => Math.max(max, secret.length), 0) ?? 0;
|
|
1013
1161
|
}
|
|
1014
1162
|
|
|
1015
1163
|
#headText(): string {
|
|
@@ -1104,10 +1252,45 @@ export class OutputSink {
|
|
|
1104
1252
|
|
|
1105
1253
|
if (rawBytes === 0) return;
|
|
1106
1254
|
|
|
1107
|
-
const visibleChunk = this.#maxColumns > 0 ? this.#applyColumnCap(sanitizedChunk) : sanitizedChunk;
|
|
1108
1255
|
if (this.#artifactPath && this.#maxColumns > 0) this.#enqueueFileWrite(rawChunk, rawBytes);
|
|
1109
1256
|
else if (this.#artifactPublisher && this.#maxColumns > 0) this.#enqueueTerminalArtifactChunk(rawChunk, rawBytes);
|
|
1257
|
+
|
|
1258
|
+
if (this.#maxColumns > 0) {
|
|
1259
|
+
if (this.#plainSecrets) {
|
|
1260
|
+
// Secret-aware cuts can retract text from an earlier line, so retain
|
|
1261
|
+
// each line before processing the next one.
|
|
1262
|
+
let cursor = 0;
|
|
1263
|
+
while (cursor < sanitizedChunk.length) {
|
|
1264
|
+
const newline = sanitizedChunk.indexOf(NL, cursor);
|
|
1265
|
+
const end = newline === -1 ? sanitizedChunk.length : newline + 1;
|
|
1266
|
+
this.#retainVisible(this.#applyColumnCap(sanitizedChunk.slice(cursor, end)));
|
|
1267
|
+
cursor = end;
|
|
1268
|
+
}
|
|
1269
|
+
} else {
|
|
1270
|
+
// Batch ordinary output while bounding #applyColumnCap's temporary
|
|
1271
|
+
// parts/join allocation for large single callbacks. Retaining each
|
|
1272
|
+
// line separately repeats accounting millions of times.
|
|
1273
|
+
let cursor = 0;
|
|
1274
|
+
while (cursor < sanitizedChunk.length) {
|
|
1275
|
+
let end = Math.min(cursor + COLUMN_CAP_BATCH_CHARS, sanitizedChunk.length);
|
|
1276
|
+
// UTF-8 byte accounting must not observe a surrogate pair as two
|
|
1277
|
+
// replacement characters when a batch boundary lands between them.
|
|
1278
|
+
if (end < sanitizedChunk.length) {
|
|
1279
|
+
const before = sanitizedChunk.charCodeAt(end - 1);
|
|
1280
|
+
const after = sanitizedChunk.charCodeAt(end);
|
|
1281
|
+
if (before >= 0xd800 && before <= 0xdbff && after >= 0xdc00 && after <= 0xdfff) end--;
|
|
1282
|
+
}
|
|
1283
|
+
this.#retainVisible(this.#applyColumnCap(sanitizedChunk.slice(cursor, end)));
|
|
1284
|
+
cursor = end;
|
|
1285
|
+
}
|
|
1286
|
+
}
|
|
1287
|
+
} else {
|
|
1288
|
+
this.#retainVisible(sanitizedChunk);
|
|
1289
|
+
}
|
|
1110
1290
|
if (this.#columnDroppedBytes > 0) this.#createFileSink();
|
|
1291
|
+
}
|
|
1292
|
+
|
|
1293
|
+
#retainVisible(visibleChunk: string): void {
|
|
1111
1294
|
const visibleBytes = Buffer.byteLength(visibleChunk, "utf-8");
|
|
1112
1295
|
if (visibleChunk.length > 0) {
|
|
1113
1296
|
this.#processedBytes += visibleBytes;
|
|
@@ -1125,6 +1308,7 @@ export class OutputSink {
|
|
|
1125
1308
|
return;
|
|
1126
1309
|
}
|
|
1127
1310
|
const headSlice = truncateHeadBytes(visibleChunk, room);
|
|
1311
|
+
this.#headRetentionDisabled = true;
|
|
1128
1312
|
if (headSlice.bytes > 0) {
|
|
1129
1313
|
this.#appendHead(headSlice.text, headSlice.bytes);
|
|
1130
1314
|
tailChunk = visibleChunk.substring(headSlice.text.length);
|
|
@@ -1161,34 +1345,106 @@ export class OutputSink {
|
|
|
1161
1345
|
if (segBytes <= remaining) {
|
|
1162
1346
|
parts.push(segment);
|
|
1163
1347
|
this.#currentLineBytes += segBytes;
|
|
1348
|
+
if (this.#plainSecrets) this.#columnLine += segment;
|
|
1164
1349
|
} else {
|
|
1165
1350
|
// First overflow on this line: keep what fits, append ellipsis,
|
|
1166
|
-
// arm the skip-until-newline flag.
|
|
1351
|
+
// arm the skip-until-newline flag. With secrets, do not emit a
|
|
1352
|
+
// kept-head suffix that is still a prefix of a plain secret, and
|
|
1353
|
+
// do not end inside one the whole-string obfuscator must see whole.
|
|
1167
1354
|
const ellipsisBytes = 3; // "…" in UTF-8
|
|
1168
1355
|
const headRoom = Math.max(0, remaining - ellipsisBytes);
|
|
1169
1356
|
let keptBytes = 0;
|
|
1357
|
+
let keptText = "";
|
|
1170
1358
|
if (headRoom > 0) {
|
|
1171
1359
|
const sliced = truncateHeadBytes(segment, headRoom);
|
|
1360
|
+
keptText = sliced.text;
|
|
1172
1361
|
keptBytes = sliced.bytes;
|
|
1173
|
-
parts.push(sliced.text);
|
|
1174
1362
|
}
|
|
1363
|
+
const secrets = this.#plainSecrets;
|
|
1364
|
+
let lineBytesReset = false;
|
|
1365
|
+
if (secrets && segment.startsWith(keptText)) {
|
|
1366
|
+
const prior = this.#columnLine;
|
|
1367
|
+
const prefix = this.#columnLineContext + prior;
|
|
1368
|
+
const text = prefix + segment;
|
|
1369
|
+
const cut = prefix.length + keptText.length;
|
|
1370
|
+
let end = cut;
|
|
1371
|
+
while (end > 0) {
|
|
1372
|
+
const next = Math.min(
|
|
1373
|
+
columnHeadEndOutsideSecrets(text, end, secrets),
|
|
1374
|
+
columnHeadEndBeforeOpenPrefix(text, end, secrets),
|
|
1375
|
+
);
|
|
1376
|
+
if (next >= end) break;
|
|
1377
|
+
end = next;
|
|
1378
|
+
}
|
|
1379
|
+
if (end < cut) {
|
|
1380
|
+
if (end < prefix.length) {
|
|
1381
|
+
this.#retractColumnSuffix(prefix.slice(end));
|
|
1382
|
+
if (end < this.#columnLineContext.length) {
|
|
1383
|
+
this.#columnLineContext = this.#columnLineContext.slice(0, end);
|
|
1384
|
+
this.#columnLine = "";
|
|
1385
|
+
} else {
|
|
1386
|
+
this.#columnLine = prior.slice(0, end - this.#columnLineContext.length);
|
|
1387
|
+
}
|
|
1388
|
+
keptText = "";
|
|
1389
|
+
keptBytes = 0;
|
|
1390
|
+
} else {
|
|
1391
|
+
keptText = segment.slice(0, end - prefix.length);
|
|
1392
|
+
keptBytes = Buffer.byteLength(keptText, "utf-8");
|
|
1393
|
+
this.#columnLine = prior + keptText;
|
|
1394
|
+
}
|
|
1395
|
+
// columnLine already includes the bytes kept from this segment.
|
|
1396
|
+
this.#currentLineBytes = Buffer.byteLength(this.#columnLine, "utf-8");
|
|
1397
|
+
lineBytesReset = true;
|
|
1398
|
+
} else {
|
|
1399
|
+
this.#columnLine = prior + keptText;
|
|
1400
|
+
}
|
|
1401
|
+
}
|
|
1402
|
+
if (keptText.length > 0) parts.push(keptText);
|
|
1175
1403
|
parts.push(ELLIPSIS);
|
|
1176
1404
|
this.#columnDroppedBytes += segBytes - keptBytes;
|
|
1177
1405
|
this.#columnTruncatedLines++;
|
|
1178
|
-
this.#currentLineBytes += keptBytes + ellipsisBytes;
|
|
1406
|
+
this.#currentLineBytes += (lineBytesReset ? 0 : keptBytes) + ellipsisBytes;
|
|
1179
1407
|
this.#columnEllipsisAdded = true;
|
|
1180
1408
|
}
|
|
1181
1409
|
}
|
|
1182
1410
|
}
|
|
1183
1411
|
if (nlIdx === -1) break;
|
|
1184
1412
|
parts.push(NL);
|
|
1413
|
+
if (this.#plainSecretContextChars > 0 && !this.#columnEllipsisAdded) {
|
|
1414
|
+
const context = this.#columnLineContext + this.#columnLine + NL;
|
|
1415
|
+
this.#columnLineContext = context.slice(-this.#plainSecretContextChars);
|
|
1416
|
+
} else {
|
|
1417
|
+
this.#columnLineContext = "";
|
|
1418
|
+
}
|
|
1185
1419
|
this.#currentLineBytes = 0;
|
|
1186
1420
|
this.#columnEllipsisAdded = false;
|
|
1421
|
+
this.#columnLine = "";
|
|
1187
1422
|
cursor = nlIdx + 1;
|
|
1188
1423
|
}
|
|
1189
1424
|
return parts.join("");
|
|
1190
1425
|
}
|
|
1191
1426
|
|
|
1427
|
+
/** Remove a suffix of previously retained output after a secret-aware cut-back. */
|
|
1428
|
+
#retractColumnSuffix(suffix: string): void {
|
|
1429
|
+
if (suffix.length === 0) return;
|
|
1430
|
+
const bytes = Buffer.byteLength(suffix, "utf-8");
|
|
1431
|
+
const startByte = Math.max(0, this.#processedBytes - bytes);
|
|
1432
|
+
const tailStartByte = Math.max(0, this.#processedBytes - this.#bufferBytes);
|
|
1433
|
+
const keptTailBytes = Math.max(0, Math.min(this.#bufferBytes, startByte - tailStartByte));
|
|
1434
|
+
if (keptTailBytes < this.#bufferBytes) {
|
|
1435
|
+
const nextTail = truncateHeadBytes(this.#buffer, keptTailBytes);
|
|
1436
|
+
this.#setTail(nextTail.text, nextTail.bytes);
|
|
1437
|
+
}
|
|
1438
|
+
if (startByte < this.#headBytes) {
|
|
1439
|
+
const nextHead = truncateHeadBytes(this.#head, startByte);
|
|
1440
|
+
this.#head = nextHead.text;
|
|
1441
|
+
this.#headBytes = nextHead.bytes;
|
|
1442
|
+
}
|
|
1443
|
+
this.#processedBytes = Math.max(0, this.#processedBytes - bytes);
|
|
1444
|
+
this.#processedLines = Math.max(0, this.#processedLines - countNewlines(suffix));
|
|
1445
|
+
this.#columnDroppedBytes += bytes;
|
|
1446
|
+
}
|
|
1447
|
+
|
|
1192
1448
|
#retainedSummary(text: string): { text: string; bytes: number; lines: number } {
|
|
1193
1449
|
return { text, bytes: Buffer.byteLength(text, "utf-8"), lines: text.length > 0 ? countNewlines(text) : 0 };
|
|
1194
1450
|
}
|
|
@@ -1458,6 +1714,8 @@ export class OutputSink {
|
|
|
1458
1714
|
*/
|
|
1459
1715
|
replace(text: string): void {
|
|
1460
1716
|
this.#coalesceBuf = "";
|
|
1717
|
+
this.#columnLine = "";
|
|
1718
|
+
this.#columnLineContext = "";
|
|
1461
1719
|
const replacementBytes = Buffer.byteLength(text, "utf-8");
|
|
1462
1720
|
this.#head = "";
|
|
1463
1721
|
this.#headBytes = 0;
|
|
@@ -382,6 +382,16 @@ export function isOwnedAttemptRegistrationIncomplete(lineageIdHash: string, atte
|
|
|
382
382
|
return incompleteOwnedAttempts.has(`${lineageIdHash}\u0000${attemptEpoch}`);
|
|
383
383
|
}
|
|
384
384
|
|
|
385
|
+
/** Move fail-closed saturation authority with all ownership state during endpoint rekey. */
|
|
386
|
+
export function migrateOwnedAttemptSaturationsForEndpoint(
|
|
387
|
+
predecessorEndpointId: string,
|
|
388
|
+
successorEndpointId: string,
|
|
389
|
+
): void {
|
|
390
|
+
for (const [attemptKey, endpointId] of saturatedAttemptEndpoints) {
|
|
391
|
+
if (endpointId === predecessorEndpointId) saturatedAttemptEndpoints.set(attemptKey, successorEndpointId);
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
|
|
385
395
|
/** Exact (jobId, jobGeneration) lookup for completion-origin classification. */
|
|
386
396
|
export function lookupOwnedRegistration(
|
|
387
397
|
jobId: string,
|
|
@@ -841,6 +851,7 @@ export function registerOwnedIfLineaged(
|
|
|
841
851
|
toolCallId: string | undefined,
|
|
842
852
|
jobId: string,
|
|
843
853
|
endpointId?: string,
|
|
854
|
+
ownedRegistrationEndpointId?: string,
|
|
844
855
|
): void {
|
|
845
856
|
try {
|
|
846
857
|
let lineage = resolveToolLineage(toolCallId, endpointId);
|
|
@@ -860,9 +871,10 @@ export function registerOwnedIfLineaged(
|
|
|
860
871
|
const job = manager.getJob?.(jobId);
|
|
861
872
|
const jobGeneration = job?.generation;
|
|
862
873
|
if (!jobGeneration) return;
|
|
874
|
+
const registrationEndpointId = ownedRegistrationEndpointId ?? lineage.endpointId;
|
|
863
875
|
registerOwnedRegistration(
|
|
864
876
|
{
|
|
865
|
-
...(
|
|
877
|
+
...(registrationEndpointId ? { endpointId: registrationEndpointId } : {}),
|
|
866
878
|
endpointGeneration: lineage.endpointGeneration,
|
|
867
879
|
lineageIdHash: lineage.lineageIdHash,
|
|
868
880
|
promptAttemptEpoch: lineage.promptAttemptEpoch,
|
|
@@ -877,7 +889,7 @@ export function registerOwnedIfLineaged(
|
|
|
877
889
|
// job id bg_1) must not be judged terminal using THIS manager's
|
|
878
890
|
// unrelated bg_1 record (review thread P2).
|
|
879
891
|
isJobTerminal: candidate => {
|
|
880
|
-
if ((candidate.endpointId ?? "") !== (
|
|
892
|
+
if ((candidate.endpointId ?? "") !== (registrationEndpointId ?? "")) return undefined;
|
|
881
893
|
const status = manager.getJob?.(candidate.jobId)?.status;
|
|
882
894
|
return status === "completed" || status === "cancelled" || status === "failed" || status === "evicted";
|
|
883
895
|
},
|