@gajae-code/coding-agent 0.13.3 → 0.14.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 +184 -2
- package/README.md +2 -0
- package/bin/gajaessi.js +4 -0
- package/dist/types/async/job-manager.d.ts +55 -2
- package/dist/types/async/support.d.ts +14 -0
- package/dist/types/capability/mcp.d.ts +6 -0
- package/dist/types/capability/types.d.ts +6 -0
- package/dist/types/cli/accounts-cli.d.ts +19 -0
- package/dist/types/cli/args.d.ts +3 -0
- package/dist/types/cli/config-cli.d.ts +6 -0
- package/dist/types/cli/crash-cli.d.ts +10 -0
- package/dist/types/cli/customize-doctor.d.ts +145 -0
- package/dist/types/cli/daemon-cli.d.ts +3 -1
- package/dist/types/cli/mcp-cli.d.ts +11 -0
- package/dist/types/cli/notify-cli.d.ts +1 -1
- package/dist/types/cli/root-flags.d.ts +12 -0
- package/dist/types/cli/setup-cli.d.ts +3 -1
- package/dist/types/cli/skills-cli.d.ts +2 -1
- package/dist/types/cli/update-cli.d.ts +19 -1
- package/dist/types/cli.d.ts +6 -0
- package/dist/types/commands/accounts.d.ts +40 -0
- package/dist/types/commands/crash.d.ts +24 -0
- package/dist/types/commands/customize.d.ts +22 -0
- package/dist/types/commands/daemon.d.ts +1 -1
- package/dist/types/commands/launch.d.ts +6 -0
- package/dist/types/commands/setup.d.ts +6 -0
- package/dist/types/commands/skills.d.ts +6 -1
- package/dist/types/commands/update.d.ts +7 -0
- package/dist/types/config/atomic-yaml-patch-worker.d.ts +25 -0
- package/dist/types/config/atomic-yaml-patch.d.ts +62 -2
- package/dist/types/config/config-schema-version.d.ts +10 -0
- package/dist/types/config/file-lock.d.ts +18 -0
- package/dist/types/config/model-profile-activation.d.ts +3 -1
- package/dist/types/config/model-registry.d.ts +9 -1
- package/dist/types/config/models-config-schema.d.ts +2 -0
- package/dist/types/config/settings-schema.d.ts +70 -6
- package/dist/types/config/settings.d.ts +63 -0
- package/dist/types/config/skill-settings-defaults.d.ts +26 -0
- package/dist/types/config/task-delegation.d.ts +39 -0
- package/dist/types/config/update-channel.d.ts +10 -0
- package/dist/types/coordinator-mcp/server.d.ts +5 -1
- package/dist/types/crash/index-store.d.ts +87 -0
- package/dist/types/crash/nudge.d.ts +50 -0
- package/dist/types/crash/record-loader.d.ts +17 -0
- package/dist/types/crash/report.d.ts +134 -0
- package/dist/types/crash/sanitize.d.ts +25 -0
- package/dist/types/cursor.d.ts +13 -2
- package/dist/types/customization/import.d.ts +37 -0
- package/dist/types/customization/index.d.ts +4 -0
- package/dist/types/customization/inventory.d.ts +25 -0
- package/dist/types/customization/mutations.d.ts +32 -0
- package/dist/types/customization/types.d.ts +115 -0
- package/dist/types/daemon/control-types.d.ts +8 -1
- package/dist/types/daemon/operator-contract.d.ts +4 -4
- package/dist/types/discovery/claude.d.ts +39 -1
- package/dist/types/discovery/codex.d.ts +39 -1
- package/dist/types/discovery/index.d.ts +2 -0
- package/dist/types/discovery/mcp-compat.d.ts +30 -0
- package/dist/types/edit/renderer.d.ts +23 -0
- package/dist/types/extensibility/extensions/runner.d.ts +3 -0
- package/dist/types/extensibility/extensions/types.d.ts +21 -4
- package/dist/types/extensibility/gjc-plugins/observability.d.ts +6 -1
- package/dist/types/extensibility/gjc-plugins/registry.d.ts +8 -1
- package/dist/types/extensibility/hooks/loader.d.ts +19 -5
- package/dist/types/extensibility/runtime-skill-discovery.d.ts +22 -1
- package/dist/types/extensibility/skill-management.d.ts +107 -0
- package/dist/types/gjc-runtime/deep-interview-runtime.d.ts +3 -1
- package/dist/types/gjc-runtime/launch-worktree.d.ts +8 -0
- package/dist/types/gjc-runtime/linux-proc.d.ts +3 -0
- package/dist/types/gjc-runtime/ralplan-runtime.d.ts +10 -6
- package/dist/types/gjc-runtime/session-state-lock.d.ts +104 -0
- package/dist/types/gjc-runtime/session-state-sidecar.d.ts +120 -2
- package/dist/types/gjc-runtime/state-writer.d.ts +10 -1
- package/dist/types/gjc-runtime/ultragoal-guard.d.ts +4 -2
- package/dist/types/gjc-runtime/ultragoal-runtime.d.ts +8 -6
- package/dist/types/gjc-runtime/workflow-settings.d.ts +109 -0
- package/dist/types/hooks/events.d.ts +60 -0
- package/dist/types/hooks/normalize.d.ts +83 -0
- package/dist/types/hooks/skill-state.d.ts +9 -2
- package/dist/types/main.d.ts +1 -1
- package/dist/types/modes/acp/abort-scope.d.ts +13 -0
- package/dist/types/modes/acp/acp-agent.d.ts +7 -4
- package/dist/types/modes/components/customization/customization-dashboard.d.ts +44 -0
- package/dist/types/modes/components/customization/import-wizard.d.ts +21 -0
- package/dist/types/modes/components/customization/index.d.ts +1 -0
- package/dist/types/modes/components/gajae-pet-widget.d.ts +8 -30
- package/dist/types/modes/components/irc-sidebar.d.ts +1 -0
- package/dist/types/modes/components/iterm-pet-transport.d.ts +122 -0
- package/dist/types/modes/components/oauth-selector.d.ts +10 -4
- package/dist/types/modes/components/pet-capability.d.ts +11 -25
- package/dist/types/modes/components/status-line/gh.d.ts +6 -2
- package/dist/types/modes/controllers/extension-ui-controller.d.ts +1 -1
- package/dist/types/modes/controllers/selector-controller.d.ts +7 -8
- package/dist/types/modes/controllers/stt-controller.d.ts +1 -1
- package/dist/types/modes/interactive-mode.d.ts +1 -0
- package/dist/types/modes/types.d.ts +1 -0
- package/dist/types/modes/utils/injected-user-submission.d.ts +10 -0
- package/dist/types/runtime-mcp/config.d.ts +9 -0
- package/dist/types/runtime-mcp/manager.d.ts +26 -1
- package/dist/types/runtime-mcp/oauth-discovery.d.ts +4 -0
- package/dist/types/runtime-mcp/oauth-flow.d.ts +24 -1
- package/dist/types/runtime-mcp/pool-key.d.ts +2 -0
- package/dist/types/runtime-mcp/protocol.d.ts +278 -0
- package/dist/types/runtime-mcp/scope-config.d.ts +29 -0
- package/dist/types/runtime-mcp/tool-bridge.d.ts +15 -13
- package/dist/types/runtime-mcp/transports/http.d.ts +9 -0
- package/dist/types/runtime-mcp/types.d.ts +102 -10
- package/dist/types/sdk/acp/adapter.d.ts +26 -5
- package/dist/types/sdk/broker/broker.d.ts +3 -55
- package/dist/types/sdk/broker/lifecycle.d.ts +18 -12
- package/dist/types/sdk/broker/session-index.d.ts +14 -5
- package/dist/types/sdk/broker/startup-failure.d.ts +16 -1
- package/dist/types/sdk/bus/chat-daemon-control.d.ts +51 -1
- package/dist/types/sdk/bus/chat-daemon-runtime.d.ts +4 -41
- package/dist/types/sdk/bus/config.d.ts +11 -0
- package/dist/types/sdk/bus/conversation-store.d.ts +0 -1
- package/dist/types/sdk/bus/daemon-paths.d.ts +6 -0
- package/dist/types/sdk/bus/discord-conversation.d.ts +2 -0
- package/dist/types/sdk/bus/discord-daemon.d.ts +17 -12
- package/dist/types/sdk/bus/existing-thread-readiness.d.ts +4 -34
- package/dist/types/sdk/bus/inbound-reaction-ordering.d.ts +61 -0
- package/dist/types/sdk/bus/index.d.ts +85 -99
- package/dist/types/sdk/bus/kind-aware-reconciliation.d.ts +29 -7
- package/dist/types/sdk/bus/managed-daemon.d.ts +2 -32
- package/dist/types/sdk/bus/notification-service.d.ts +52 -0
- package/dist/types/sdk/bus/operator-runtime.d.ts +1 -1
- package/dist/types/sdk/bus/prompt-reconciliation.d.ts +16 -5
- package/dist/types/sdk/bus/reconciliation-store.d.ts +113 -5
- package/dist/types/sdk/bus/session-control.d.ts +25 -1
- package/dist/types/sdk/bus/slack-conversation.d.ts +4 -0
- package/dist/types/sdk/bus/slack-daemon.d.ts +9 -17
- package/dist/types/sdk/bus/slack-live-provider.d.ts +3 -1
- package/dist/types/sdk/bus/slack-provider.d.ts +4 -0
- package/dist/types/sdk/bus/slack-thread-binding.d.ts +6 -40
- package/dist/types/sdk/bus/telegram-adoption-intent.d.ts +30 -104
- package/dist/types/sdk/bus/telegram-daemon-cli.d.ts +10 -0
- package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +262 -19
- package/dist/types/sdk/bus/telegram-daemon-orphan-reap.d.ts +125 -0
- package/dist/types/sdk/bus/telegram-daemon-owner-registry.d.ts +22 -0
- package/dist/types/sdk/bus/telegram-daemon.d.ts +119 -310
- package/dist/types/sdk/bus/telegram-reference.d.ts +1 -47
- package/dist/types/sdk/bus/threaded-render.d.ts +1 -0
- package/dist/types/sdk/bus/topic-registry.d.ts +33 -17
- package/dist/types/sdk/cli/rows.d.ts +2 -11
- package/dist/types/sdk/cli/session-cli.d.ts +15 -25
- package/dist/types/sdk/client/client.d.ts +97 -1
- package/dist/types/sdk/host/control/dispatch.d.ts +0 -7
- package/dist/types/sdk/host/control/index.d.ts +1 -1
- package/dist/types/sdk/host/control/operations.d.ts +18 -5
- package/dist/types/sdk/host/control/runtime-gate.d.ts +19 -0
- package/dist/types/sdk/host/host.d.ts +15 -1
- package/dist/types/sdk/host/query/handlers.d.ts +5 -0
- package/dist/types/sdk/host/reverse-leases.d.ts +1 -1
- package/dist/types/sdk/host/session-runtime.d.ts +127 -7
- package/dist/types/sdk/host/types.d.ts +1 -1
- package/dist/types/sdk/host/websocket-transport.d.ts +1 -1
- package/dist/types/sdk/index.d.ts +2 -1
- package/dist/types/sdk/lifecycle/broker-client.d.ts +8 -0
- package/dist/types/sdk/lifecycle/client.d.ts +59 -0
- package/dist/types/sdk/lifecycle/index.d.ts +3 -0
- package/dist/types/sdk/lifecycle/service.d.ts +232 -0
- package/dist/types/sdk/mcp/server.d.ts +9 -6
- package/dist/types/sdk/prompt-deadline-lease.d.ts +43 -0
- package/dist/types/sdk/prompt-deadline-manager.d.ts +28 -0
- package/dist/types/sdk/prompt-status.d.ts +10 -3
- package/dist/types/sdk/protocol/operation-registry.d.ts +5 -0
- package/dist/types/sdk/receipt-state.d.ts +17 -0
- package/dist/types/sdk/reconciliation-extensions.d.ts +2 -0
- package/dist/types/sdk/router/index.d.ts +1 -0
- package/dist/types/sdk/router/session-router.d.ts +152 -0
- package/dist/types/sdk/session-list.d.ts +22 -0
- package/dist/types/sdk/session-reconnect.d.ts +43 -1
- package/dist/types/sdk/session.d.ts +34 -1
- package/dist/types/sdk/transport/serve-cli.d.ts +1 -4
- package/dist/types/session/account-inventory.d.ts +86 -0
- package/dist/types/session/agent-session.d.ts +145 -6
- package/dist/types/session/agent-storage.d.ts +10 -0
- package/dist/types/session/coordinator-tool-label.d.ts +28 -0
- package/dist/types/session/internal/managed-session-storage.d.ts +23 -4
- package/dist/types/session/messages.d.ts +1 -1
- package/dist/types/session/secure-token-file.d.ts +3 -0
- package/dist/types/session/session-manager.d.ts +40 -1
- package/dist/types/session/startup-auth-config.d.ts +44 -0
- package/dist/types/session/terminal-abort.d.ts +378 -0
- package/dist/types/session/yield-queue.d.ts +7 -0
- package/dist/types/session-import/claude.d.ts +26 -0
- package/dist/types/session-import/codex.d.ts +88 -0
- package/dist/types/session-import/command.d.ts +35 -0
- package/dist/types/session-import/detect.d.ts +22 -0
- package/dist/types/session-import/index.d.ts +7 -0
- package/dist/types/session-import/provider-codex.d.ts +22 -0
- package/dist/types/session-import/provider-service.d.ts +38 -0
- package/dist/types/session-import/redact.d.ts +23 -0
- package/dist/types/session-import/service.d.ts +47 -0
- package/dist/types/session-import/types.d.ts +120 -0
- package/dist/types/setup/paseo/check.d.ts +16 -0
- package/dist/types/setup/paseo/install-saga.d.ts +115 -0
- package/dist/types/setup/paseo/json-publisher.d.ts +76 -0
- package/dist/types/setup/paseo/orchestration-preferences.d.ts +24 -0
- package/dist/types/setup/paseo/paseo-ownership.d.ts +87 -0
- package/dist/types/setup/paseo/paseo-setup.d.ts +29 -0
- package/dist/types/setup/paseo/provider-config.d.ts +35 -0
- package/dist/types/setup/paseo/remove.d.ts +14 -0
- package/dist/types/setup/paseo/result-types.d.ts +59 -0
- package/dist/types/setup/paseo/setup-deps.d.ts +73 -0
- package/dist/types/setup/paseo/skills-bridge.d.ts +44 -0
- package/dist/types/skill-state/active-state.d.ts +5 -1
- package/dist/types/slash-commands/acp-builtins.d.ts +5 -1
- package/dist/types/slash-commands/builtin-registry.d.ts +2 -0
- package/dist/types/slash-commands/helpers/credential-switch.d.ts +12 -0
- package/dist/types/slash-commands/helpers/usage-report.d.ts +5 -6
- package/dist/types/slash-commands/types.d.ts +7 -1
- package/dist/types/task/executor.d.ts +10 -0
- package/dist/types/task/index.d.ts +3 -1
- package/dist/types/tools/ask-answer-registry.d.ts +6 -3
- package/dist/types/tools/bash-sleep-advisory.d.ts +14 -0
- package/dist/types/tools/bash.d.ts +2 -6
- package/dist/types/tools/browser/attach.d.ts +11 -0
- package/dist/types/tools/browser/launch.d.ts +20 -0
- package/dist/types/tools/browser/profile-discovery.d.ts +6 -0
- package/dist/types/tools/browser/render.d.ts +1 -0
- package/dist/types/tools/browser.d.ts +3 -1
- package/dist/types/tools/image-gen.d.ts +9 -2
- package/dist/types/tools/index.d.ts +8 -0
- package/dist/types/tools/monitor.d.ts +1 -1
- package/dist/types/tools/skill-discovery.d.ts +8 -1
- package/dist/types/tools/subagent.d.ts +2 -0
- package/dist/types/tools/ultragoal-ask-guard.d.ts +2 -2
- package/dist/types/utils/herdr-pane.d.ts +76 -0
- package/package.json +19 -9
- package/scripts/build-binary.ts +2 -1
- package/scripts/build-sdk-package-smoke.ts +2 -13
- package/scripts/capture-sticky-viewport-showcase.ts +0 -1
- package/scripts/compile-args.ts +2 -0
- package/scripts/generate-sdk-adapter-parity-manifest.ts +31 -16
- package/scripts/generate-sdk-operation-inventory.ts +28 -0
- package/scripts/generate-telegram-baseline-manifest.ts +1 -0
- package/scripts/generate-tool-catalog.ts +6 -4
- package/scripts/verify-gjc-sdk-canonicalization.ts +5 -39
- package/src/async/job-manager.ts +352 -17
- package/src/async/support.ts +25 -0
- package/src/autoresearch/dashboard.ts +28 -13
- package/src/capability/index.ts +3 -1
- package/src/capability/mcp.ts +6 -0
- package/src/capability/types.ts +6 -0
- package/src/cli/accounts-cli.ts +395 -0
- package/src/cli/args.ts +23 -0
- package/src/cli/auth-broker-cli.ts +136 -66
- package/src/cli/auth-gateway-cli.ts +94 -70
- package/src/cli/config-cli.ts +27 -2
- package/src/cli/crash-cli.ts +78 -0
- package/src/cli/customize-doctor.ts +1605 -0
- package/src/cli/daemon-cli.ts +12 -3
- package/src/cli/fast-help.ts +1 -0
- package/src/cli/mcp-cli.ts +128 -60
- package/src/cli/notify-cli.ts +8 -14
- package/src/cli/root-flags.ts +8 -0
- package/src/cli/setup-cli.ts +145 -0
- package/src/cli/skills-cli.ts +38 -2
- package/src/cli/update-cli.ts +167 -12
- package/src/cli.ts +4 -0
- package/src/commands/accounts.ts +71 -0
- package/src/commands/crash.ts +35 -0
- package/src/commands/customize.ts +84 -0
- package/src/commands/daemon.ts +2 -2
- package/src/commands/deep-interview.ts +2 -0
- package/src/commands/launch.ts +1 -0
- package/src/commands/mcp.ts +12 -7
- package/src/commands/ralplan.ts +2 -0
- package/src/commands/sdk.ts +65 -32
- package/src/commands/setup.ts +5 -0
- package/src/commands/skills.ts +14 -4
- package/src/commands/ultragoal.ts +10 -1
- package/src/commands/update.ts +26 -15
- package/src/config/atomic-yaml-patch-worker.ts +57 -0
- package/src/config/atomic-yaml-patch.ts +769 -29
- package/src/config/config-schema-version.ts +10 -0
- package/src/config/file-lock.ts +70 -1
- package/src/config/mcp-schema.json +52 -14
- package/src/config/model-profile-activation.ts +20 -1
- package/src/config/model-profiles.ts +29 -19
- package/src/config/model-registry.ts +132 -21
- package/src/config/models-config-schema.ts +1 -1
- package/src/config/settings-schema.ts +76 -6
- package/src/config/settings.ts +4038 -199
- package/src/config/skill-settings-defaults.ts +41 -3
- package/src/config/task-delegation.ts +87 -0
- package/src/config/update-channel.ts +23 -0
- package/src/coordinator-mcp/server.ts +377 -308
- package/src/crash/index-store.ts +723 -0
- package/src/crash/nudge.ts +89 -0
- package/src/crash/record-loader.ts +149 -0
- package/src/crash/report.ts +551 -0
- package/src/crash/sanitize.ts +129 -0
- package/src/cursor.ts +36 -10
- package/src/customization/import.ts +946 -0
- package/src/customization/index.ts +4 -0
- package/src/customization/inventory.ts +335 -0
- package/src/customization/mutations.ts +141 -0
- package/src/customization/types.ts +223 -0
- package/src/daemon/control-types.ts +8 -1
- package/src/daemon/operator-contract.ts +6 -6
- package/src/defaults/gjc/skills/deep-interview/SKILL.md +11 -22
- package/src/defaults/gjc/skills/ralplan/SKILL.md +36 -23
- package/src/defaults/gjc/skills/ultragoal/SKILL.md +16 -0
- package/src/discovery/claude-hooks.ts +38 -0
- package/src/discovery/claude.ts +142 -209
- package/src/discovery/codex-hooks.ts +38 -0
- package/src/discovery/codex.ts +132 -198
- package/src/discovery/helpers.ts +8 -1
- package/src/discovery/index.ts +2 -0
- package/src/discovery/mcp-compat.ts +234 -0
- package/src/discovery/mcp-json.ts +6 -0
- package/src/edit/renderer.ts +40 -0
- package/src/edit/streaming.ts +4 -2
- package/src/exec/bash-executor.ts +24 -20
- package/src/export/html/index.ts +51 -2
- package/src/extensibility/extensions/loader.ts +9 -1
- package/src/extensibility/extensions/runner.ts +17 -4
- package/src/extensibility/extensions/types.ts +31 -6
- package/src/extensibility/gjc-plugins/constrained-hooks.ts +33 -0
- package/src/extensibility/gjc-plugins/installer.ts +9 -5
- package/src/extensibility/gjc-plugins/observability.ts +8 -2
- package/src/extensibility/gjc-plugins/registry.ts +13 -2
- package/src/extensibility/gjc-plugins/runtime-adapters.ts +3 -1
- package/src/extensibility/gjc-plugins/schema.ts +222 -39
- package/src/extensibility/hooks/loader.ts +154 -10
- package/src/extensibility/runtime-skill-discovery.ts +257 -60
- package/src/extensibility/skill-management.ts +368 -0
- package/src/extensibility/skills.ts +52 -16
- package/src/gjc-runtime/deep-interview-runtime.ts +45 -61
- package/src/gjc-runtime/launch-worktree.ts +56 -2
- package/src/gjc-runtime/linux-proc.ts +7 -1
- package/src/gjc-runtime/ralplan-runtime.ts +100 -164
- package/src/gjc-runtime/session-state-lock.ts +992 -0
- package/src/gjc-runtime/session-state-sidecar.ts +644 -142
- package/src/gjc-runtime/state-writer.ts +39 -14
- package/src/gjc-runtime/tmux-owner-isolation.ts +5 -4
- package/src/gjc-runtime/ultragoal-guard.ts +11 -4
- package/src/gjc-runtime/ultragoal-runtime.ts +44 -41
- package/src/gjc-runtime/workflow-settings.ts +596 -0
- package/src/goals/tools/goal-tool.ts +6 -1
- package/src/hooks/events.ts +291 -0
- package/src/hooks/native-skill-hook.ts +4 -0
- package/src/hooks/normalize.ts +369 -0
- package/src/hooks/skill-state.ts +165 -46
- package/src/internal-urls/docs-index.generated.ts +40 -32
- package/src/main.ts +32 -5
- package/src/migrate/mcp-mapper.ts +25 -2
- package/src/modes/acp/abort-scope.ts +29 -0
- package/src/modes/acp/acp-agent.ts +423 -98
- package/src/modes/components/assistant-message.ts +6 -1
- package/src/modes/components/customization/customization-dashboard.ts +443 -0
- package/src/modes/components/customization/import-wizard.ts +372 -0
- package/src/modes/components/customization/index.ts +1 -0
- package/src/modes/components/gajae-pet-widget.ts +539 -213
- package/src/modes/components/irc-sidebar.ts +11 -0
- package/src/modes/components/iterm-pet-transport.ts +585 -0
- package/src/modes/components/oauth-selector.ts +277 -86
- package/src/modes/components/pet-capability.ts +39 -40
- package/src/modes/components/settings-selector.ts +9 -0
- package/src/modes/components/status-line/gh.ts +45 -1
- package/src/modes/components/status-line/segments.ts +16 -1
- package/src/modes/components/tool-status-header.ts +13 -3
- package/src/modes/controllers/command-controller.ts +20 -8
- package/src/modes/controllers/extension-ui-controller.ts +56 -257
- package/src/modes/controllers/input-controller.ts +10 -0
- package/src/modes/controllers/runtime-mcp-command-controller.ts +16 -3
- package/src/modes/controllers/selector-controller.ts +177 -72
- package/src/modes/controllers/stt-controller.ts +3 -3
- package/src/modes/interactive-mode.ts +88 -16
- package/src/modes/print-mode.ts +35 -6
- package/src/modes/runtime-init.ts +21 -68
- package/src/modes/types.ts +1 -0
- package/src/modes/utils/injected-user-submission.ts +25 -0
- package/src/modes/utils/terminal-bell.ts +15 -1
- package/src/prompts/agent-fragments/ralplan-persistence.md +1 -1
- package/src/prompts/agent-fragments/restricted-bash.md +1 -1
- package/src/prompts/agents/critic.md +1 -1
- package/src/prompts/system/rlm-research.md +1 -1
- package/src/prompts/system/system-prompt.md +0 -2
- package/src/prompts/tools/bash.md +7 -1
- package/src/prompts/tools/browser.md +1 -1
- package/src/prompts/tools/cron.md +6 -6
- package/src/prompts/tools/read.md +0 -2
- package/src/prompts/tools/skill-discovery.md +4 -2
- package/src/runtime-mcp/client.ts +427 -23
- package/src/runtime-mcp/config.ts +37 -5
- package/src/runtime-mcp/manager-pool.test.ts +56 -0
- package/src/runtime-mcp/manager.ts +52 -2
- package/src/runtime-mcp/oauth-discovery.ts +13 -0
- package/src/runtime-mcp/oauth-flow.ts +46 -1
- package/src/runtime-mcp/pool-key.ts +5 -1
- package/src/runtime-mcp/pool.test.ts +9 -0
- package/src/runtime-mcp/protocol.test.ts +353 -0
- package/src/runtime-mcp/protocol.ts +624 -0
- package/src/runtime-mcp/scope-config.ts +78 -0
- package/src/runtime-mcp/tool-bridge.ts +44 -10
- package/src/runtime-mcp/transports/http.ts +84 -13
- package/src/runtime-mcp/types.ts +123 -12
- package/src/sdk/acp/adapter.ts +328 -97
- package/src/sdk/broker/broker.ts +160 -741
- package/src/sdk/broker/ensure.ts +9 -1
- package/src/sdk/broker/lifecycle.ts +510 -116
- package/src/sdk/broker/operation-receipt-ledger.ts +11 -2
- package/src/sdk/broker/process-incarnation.ts +19 -0
- package/src/sdk/broker/session-index.ts +516 -117
- package/src/sdk/broker/startup-failure.ts +28 -5
- package/src/sdk/broker/transport.ts +1 -13
- package/src/sdk/bus/chat-daemon-cli.ts +4 -2
- package/src/sdk/bus/chat-daemon-control.ts +67 -6
- package/src/sdk/bus/chat-daemon-runtime.ts +183 -859
- package/src/sdk/bus/config.ts +13 -0
- package/src/sdk/bus/conversation-store.ts +59 -171
- package/src/sdk/bus/daemon-paths.ts +20 -0
- package/src/sdk/bus/discord-conversation.ts +2 -0
- package/src/sdk/bus/discord-daemon.ts +524 -126
- package/src/sdk/bus/discord-live-provider.ts +206 -79
- package/src/sdk/bus/existing-thread-readiness.ts +4 -97
- package/src/sdk/bus/inbound-reaction-ordering.ts +122 -0
- package/src/sdk/bus/index.ts +2480 -456
- package/src/sdk/bus/kind-aware-reconciliation.ts +204 -34
- package/src/sdk/bus/lifecycle-commands.ts +2 -2
- package/src/sdk/bus/managed-daemon.ts +2 -145
- package/src/sdk/bus/notification-service.ts +196 -0
- package/src/sdk/bus/operator-runtime.ts +1 -1
- package/src/sdk/bus/prompt-reconciliation.ts +24 -15
- package/src/sdk/bus/reconciliation-store.ts +553 -24
- package/src/sdk/bus/session-control.ts +49 -3
- package/src/sdk/bus/slack-conversation.ts +4 -0
- package/src/sdk/bus/slack-daemon.ts +733 -134
- package/src/sdk/bus/slack-live-provider.ts +65 -15
- package/src/sdk/bus/slack-provider.ts +4 -0
- package/src/sdk/bus/slack-thread-binding.ts +9 -104
- package/src/sdk/bus/telegram-adoption-intent.ts +299 -209
- package/src/sdk/bus/telegram-daemon-cli.ts +194 -7
- package/src/sdk/bus/telegram-daemon-contract.ts +265 -19
- package/src/sdk/bus/telegram-daemon-control.ts +3 -13
- package/src/sdk/bus/telegram-daemon-orphan-reap.ts +498 -0
- package/src/sdk/bus/telegram-daemon-owner-registry.ts +176 -0
- package/src/sdk/bus/telegram-daemon.ts +2552 -2280
- package/src/sdk/bus/telegram-reference.ts +1 -226
- package/src/sdk/bus/telegram-setup.ts +2 -2
- package/src/sdk/bus/threaded-render.ts +1 -0
- package/src/sdk/bus/topic-registry.ts +279 -146
- package/src/sdk/cli/rows.ts +2 -35
- package/src/sdk/cli/session-cli.ts +776 -700
- package/src/sdk/client/client.ts +892 -1
- package/src/sdk/host/control/dispatch.ts +93 -58
- package/src/sdk/host/control/index.ts +1 -1
- package/src/sdk/host/control/operations.ts +18 -10
- package/src/sdk/host/control/runtime-gate.ts +40 -0
- package/src/sdk/host/host.ts +63 -12
- package/src/sdk/host/query/handlers.ts +66 -7
- package/src/sdk/host/reverse-leases.ts +2 -2
- package/src/sdk/host/sdk-surface-parity.test.ts +1 -1
- package/src/sdk/host/session-runtime.test.ts +2787 -34
- package/src/sdk/host/session-runtime.ts +1489 -101
- package/src/sdk/host/surface-policy.ts +12 -8
- package/src/sdk/host/types.ts +1 -1
- package/src/sdk/host/websocket-transport.lifecycle.test.ts +31 -59
- package/src/sdk/host/websocket-transport.ts +85 -50
- package/src/sdk/index.ts +2 -1
- package/src/sdk/lifecycle/broker-client.ts +53 -0
- package/src/sdk/lifecycle/client.ts +234 -0
- package/src/sdk/lifecycle/index.ts +3 -0
- package/src/sdk/{bus/recent-activity.ts → lifecycle/recent-sessions.ts} +5 -2
- package/src/sdk/lifecycle/service.ts +720 -0
- package/src/sdk/mcp/server.ts +226 -188
- package/src/sdk/prompt-deadline-lease.ts +55 -0
- package/src/sdk/prompt-deadline-manager.ts +148 -0
- package/src/sdk/prompt-status.ts +11 -3
- package/src/sdk/protocol/operation-inventory.generated.json +272 -129
- package/src/sdk/protocol/operation-registry.ts +71 -8
- package/src/sdk/receipt-state.ts +43 -0
- package/src/sdk/reconciliation-extensions.ts +14 -0
- package/src/sdk/router/index.ts +1 -0
- package/src/sdk/router/session-router.ts +1658 -0
- package/src/sdk/session-list.ts +93 -0
- package/src/sdk/session-reconnect.ts +45 -1
- package/src/sdk/session.ts +716 -120
- package/src/sdk/transport/serve-cli.ts +18 -34
- package/src/session/account-inventory.ts +603 -0
- package/src/session/agent-session.ts +2857 -582
- package/src/session/agent-storage.ts +35 -0
- package/src/session/blob-store.ts +32 -1
- package/src/session/coordinator-tool-label.ts +40 -0
- package/src/session/internal/managed-session-scope.ts +4 -0
- package/src/session/internal/managed-session-storage.ts +173 -12
- package/src/session/messages.ts +1 -1
- package/src/session/secure-token-file.ts +80 -0
- package/src/session/session-manager.ts +730 -58
- package/src/session/session-storage.ts +25 -16
- package/src/session/startup-auth-config.ts +297 -0
- package/src/session/terminal-abort.ts +1253 -0
- package/src/session/yield-queue.ts +57 -15
- package/src/session-import/claude.ts +334 -0
- package/src/session-import/codex.ts +1033 -0
- package/src/session-import/command.ts +77 -0
- package/src/session-import/detect.ts +133 -0
- package/src/session-import/index.ts +7 -0
- package/src/session-import/provider-codex.ts +327 -0
- package/src/session-import/provider-service.ts +457 -0
- package/src/session-import/redact.ts +128 -0
- package/src/session-import/service.ts +1264 -0
- package/src/session-import/types.ts +148 -0
- package/src/setup/paseo/check.ts +213 -0
- package/src/setup/paseo/install-saga.ts +293 -0
- package/src/setup/paseo/json-publisher.ts +263 -0
- package/src/setup/paseo/orchestration-preferences.ts +86 -0
- package/src/setup/paseo/paseo-ownership.ts +224 -0
- package/src/setup/paseo/paseo-setup.ts +253 -0
- package/src/setup/paseo/provider-config.ts +139 -0
- package/src/setup/paseo/remove.ts +183 -0
- package/src/setup/paseo/result-types.ts +79 -0
- package/src/setup/paseo/setup-deps.ts +175 -0
- package/src/setup/paseo/skills-bridge.ts +260 -0
- package/src/skill-state/active-state.ts +21 -11
- package/src/slash-commands/acp-builtins.ts +9 -1
- package/src/slash-commands/builtin-registry.ts +98 -16
- package/src/slash-commands/helpers/credential-switch.ts +143 -0
- package/src/slash-commands/helpers/mcp.ts +9 -3
- package/src/slash-commands/helpers/usage-report.ts +61 -56
- package/src/slash-commands/types.ts +7 -2
- package/src/task/executor.ts +30 -11
- package/src/task/index.ts +66 -7
- package/src/task/render.ts +23 -9
- package/src/tools/ask-answer-registry.ts +23 -7
- package/src/tools/ask.ts +8 -4
- package/src/tools/bash-sleep-advisory.ts +115 -0
- package/src/tools/bash.ts +98 -18
- package/src/tools/browser/attach.ts +71 -3
- package/src/tools/browser/launch.ts +86 -13
- package/src/tools/browser/profile-discovery.ts +57 -7
- package/src/tools/browser/render.ts +8 -1
- package/src/tools/browser.ts +130 -21
- package/src/tools/computer.enforcement.test.ts +1 -1
- package/src/tools/computer.ts +3 -2
- package/src/tools/cron.ts +12 -1
- package/src/tools/image-gen.ts +27 -16
- package/src/tools/index.ts +8 -0
- package/src/tools/job.ts +33 -6
- package/src/tools/monitor.ts +41 -2
- package/src/tools/skill-discovery.ts +21 -29
- package/src/tools/subagent.ts +142 -24
- package/src/tools/tool-catalog.generated.ts +6 -6
- package/src/tools/ultragoal-ask-guard.ts +10 -3
- package/src/utils/herdr-pane.ts +345 -0
- package/src/utils/title-generator.ts +2 -0
- package/src/web/scrapers/index.ts +6 -4
- package/dist/types/cli/malloc-env-guard.test.d.ts +0 -1
- package/dist/types/defaults/gjc-defaults.test.d.ts +0 -1
- package/dist/types/memory-backend/lazy-loading.test.d.ts +0 -1
- package/dist/types/runtime/boot-ordering.test.d.ts +0 -1
- package/dist/types/runtime/lazy-service.test.d.ts +0 -1
- package/dist/types/runtime/network-prewarm-service.test.d.ts +0 -1
- package/dist/types/runtime/workspace-tree-service.test.d.ts +0 -1
- package/dist/types/runtime-mcp/config.test.d.ts +0 -1
- package/dist/types/runtime-mcp/http.test.d.ts +0 -1
- package/dist/types/runtime-mcp/manager-pool.test.d.ts +0 -1
- package/dist/types/runtime-mcp/pool-key.test.d.ts +0 -1
- package/dist/types/runtime-mcp/pool.test.d.ts +0 -1
- package/dist/types/sdk/bus/lifecycle-control-runtime.d.ts +0 -117
- package/dist/types/sdk/bus/lifecycle-orchestrator.d.ts +0 -141
- package/dist/types/sdk/bus/telegram-cli.d.ts +0 -31
- package/dist/types/sdk/elevation/allowlist.d.ts +0 -20
- package/dist/types/sdk/elevation/audit.d.ts +0 -93
- package/dist/types/sdk/elevation/capability.d.ts +0 -3
- package/dist/types/sdk/elevation/digest.d.ts +0 -72
- package/dist/types/sdk/elevation/dispatch-receipt.d.ts +0 -85
- package/dist/types/sdk/elevation/grant-ledger.d.ts +0 -163
- package/dist/types/sdk/elevation/owner.d.ts +0 -48
- package/dist/types/sdk/host/import-graph.test.d.ts +0 -1
- package/dist/types/sdk/host/sdk-surface-parity.test.d.ts +0 -1
- package/dist/types/sdk/host/session-runtime.test.d.ts +0 -1
- package/dist/types/sdk/host/websocket-transport.lifecycle.test.d.ts +0 -1
- package/dist/types/session/auth-broker-config.d.ts +0 -13
- package/dist/types/session/heap-eviction-artifacts.test.d.ts +0 -1
- package/dist/types/skills/skills.test.d.ts +0 -1
- package/dist/types/slash-commands/notify-command.test.d.ts +0 -1
- package/dist/types/task/token-log.test.d.ts +0 -1
- package/dist/types/tools/computer.enforcement.test.d.ts +0 -1
- package/dist/types/tools/descriptors.test.d.ts +0 -1
- package/scripts/g005-daemon-smoke.ts +0 -180
- package/scripts/g011-daemon-path-smoke.ts +0 -153
- package/src/sdk/bus/lifecycle-control-runtime.ts +0 -1733
- package/src/sdk/bus/lifecycle-orchestrator.ts +0 -489
- package/src/sdk/bus/telegram-cli.ts +0 -212
- package/src/sdk/elevation/allowlist.ts +0 -54
- package/src/sdk/elevation/audit.ts +0 -208
- package/src/sdk/elevation/capability.ts +0 -41
- package/src/sdk/elevation/digest.ts +0 -129
- package/src/sdk/elevation/dispatch-receipt.ts +0 -184
- package/src/sdk/elevation/grant-ledger.ts +0 -1142
- package/src/sdk/elevation/owner.ts +0 -109
- package/src/session/auth-broker-config.ts +0 -109
- /package/dist/types/{ai-core-import-gate.test.d.ts → discovery/claude-hooks.d.ts} +0 -0
- /package/dist/types/{cli/fixture-report.test.d.ts → discovery/codex-hooks.d.ts} +0 -0
- /package/dist/types/sdk/{bus/recent-activity.d.ts → lifecycle/recent-sessions.d.ts} +0 -0
|
@@ -0,0 +1,457 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import * as nodeFs from "node:fs";
|
|
3
|
+
import * as fsp from "node:fs/promises";
|
|
4
|
+
import * as path from "node:path";
|
|
5
|
+
import { canContinuePersistedHistory } from "@gajae-code/agent-core";
|
|
6
|
+
import { type SessionDestinationInput, SessionManager } from "../session/session-manager";
|
|
7
|
+
import { parseClaudeCodeTranscript, parseClaudeExport } from "./claude";
|
|
8
|
+
import { detectSessionImportFormat } from "./detect";
|
|
9
|
+
import { parseCodexRollout } from "./provider-codex";
|
|
10
|
+
import { IMPORT_SANITIZER_VERSION, redactImportedText } from "./redact";
|
|
11
|
+
import {
|
|
12
|
+
type ImportedConversation,
|
|
13
|
+
type ImportQuarantineRecord,
|
|
14
|
+
type PreparedSessionImport,
|
|
15
|
+
type SessionImportCompleted,
|
|
16
|
+
type SessionImportCounts,
|
|
17
|
+
SessionImportError,
|
|
18
|
+
type SessionImportProvenance,
|
|
19
|
+
type SessionImportProviderId,
|
|
20
|
+
} from "./types";
|
|
21
|
+
|
|
22
|
+
/** Bumped when normalization/rendering changes; persisted in provenance. */
|
|
23
|
+
export const EXTERNAL_IMPORT_CONVERTER_VERSION = 1;
|
|
24
|
+
/** Persisted custom-entry type carrying import provenance (never enters LLM context). */
|
|
25
|
+
export const EXTERNAL_IMPORT_PROVENANCE_CUSTOM_TYPE = "session-import";
|
|
26
|
+
/** Persisted custom-message type carrying the reconstructed continuation context. */
|
|
27
|
+
export const EXTERNAL_IMPORT_CONTEXT_CUSTOM_TYPE = "session-import";
|
|
28
|
+
|
|
29
|
+
/** Hard source size limit; larger exports fail with `content_too_large`. */
|
|
30
|
+
export const EXTERNAL_IMPORT_SOURCE_MAX_BYTES = 64 * 1024 * 1024;
|
|
31
|
+
const MAX_SOURCE_MESSAGES = 5000;
|
|
32
|
+
const MAX_CONTEXT_CHARS = 120_000;
|
|
33
|
+
const HEAD_CONTEXT_CHARS = 20_000;
|
|
34
|
+
const MAX_MESSAGE_CHARS = 16_000;
|
|
35
|
+
const MAX_TITLE_CHARS = 200;
|
|
36
|
+
const MAX_QUARANTINE_RECORDS = 512;
|
|
37
|
+
|
|
38
|
+
export interface ExternalSessionImportRequest {
|
|
39
|
+
/** Explicit user-selected transcript/export file. */
|
|
40
|
+
sourcePath: string;
|
|
41
|
+
/** Explicit provider; omitted = deterministic auto-detection. */
|
|
42
|
+
provider?: SessionImportProviderId;
|
|
43
|
+
/** Import-owner workspace (recorded as the new session's cwd). */
|
|
44
|
+
cwd: string;
|
|
45
|
+
/** Session destination; defaults to the workspace's managed session store. */
|
|
46
|
+
destination?: SessionDestinationInput;
|
|
47
|
+
/** Test seam for deterministic provenance timestamps. */
|
|
48
|
+
now?: () => Date;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
interface ParsedSource {
|
|
52
|
+
conversation: ImportedConversation;
|
|
53
|
+
quarantine: { present: boolean; truncated: boolean; records: ImportQuarantineRecord[] };
|
|
54
|
+
counts: SessionImportCounts;
|
|
55
|
+
redactionKinds: string[];
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** Read the explicit source file with fixed bounds. The source is never written. */
|
|
59
|
+
async function readImportSource(sourcePath: string): Promise<{ text: string; bytes: number; sha256: string }> {
|
|
60
|
+
const resolved = path.resolve(sourcePath);
|
|
61
|
+
const displayPath = redactImportedText(sourcePath).value;
|
|
62
|
+
let handle: fsp.FileHandle;
|
|
63
|
+
try {
|
|
64
|
+
handle = await fsp.open(resolved, nodeFs.constants.O_RDONLY | nodeFs.constants.O_NOFOLLOW);
|
|
65
|
+
} catch (error) {
|
|
66
|
+
const code = (error as NodeJS.ErrnoException).code;
|
|
67
|
+
if (code === "ENOENT") {
|
|
68
|
+
throw new SessionImportError("source_not_found", "read", `Transcript file does not exist: ${displayPath}`);
|
|
69
|
+
}
|
|
70
|
+
throw new SessionImportError(
|
|
71
|
+
"source_unreadable",
|
|
72
|
+
"read",
|
|
73
|
+
`Transcript file cannot be read (${code ?? "unknown error"}): ${displayPath}`,
|
|
74
|
+
{ retryable: true },
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
try {
|
|
78
|
+
const initial = await handle.stat({ bigint: true });
|
|
79
|
+
if (!initial.isFile()) {
|
|
80
|
+
throw new SessionImportError(
|
|
81
|
+
"invalid_request",
|
|
82
|
+
"read",
|
|
83
|
+
`Import source must be a regular file, not a directory or special file: ${displayPath}`,
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
if (initial.size === 0n) {
|
|
87
|
+
throw new SessionImportError("malformed_input", "parse", `Transcript file is empty: ${displayPath}`);
|
|
88
|
+
}
|
|
89
|
+
if (initial.size > BigInt(EXTERNAL_IMPORT_SOURCE_MAX_BYTES)) {
|
|
90
|
+
throw new SessionImportError(
|
|
91
|
+
"content_too_large",
|
|
92
|
+
"read",
|
|
93
|
+
`Transcript is ${initial.size} bytes, exceeding the ${EXTERNAL_IMPORT_SOURCE_MAX_BYTES}-byte import limit. Export a shorter session or trim the file.`,
|
|
94
|
+
{ limitBytes: EXTERNAL_IMPORT_SOURCE_MAX_BYTES, observedBytes: Number(initial.size) },
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
const bytes = Buffer.alloc(Number(initial.size));
|
|
98
|
+
let offset = 0;
|
|
99
|
+
while (offset < bytes.length) {
|
|
100
|
+
const read = await handle.read(bytes, offset, bytes.length - offset, offset);
|
|
101
|
+
if (read.bytesRead === 0) break;
|
|
102
|
+
offset += read.bytesRead;
|
|
103
|
+
}
|
|
104
|
+
if (offset !== bytes.length) {
|
|
105
|
+
throw new SessionImportError(
|
|
106
|
+
"source_changed",
|
|
107
|
+
"read",
|
|
108
|
+
"The transcript file changed while it was being read.",
|
|
109
|
+
{
|
|
110
|
+
retryable: true,
|
|
111
|
+
},
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
const terminal = await handle.stat({ bigint: true });
|
|
115
|
+
if (
|
|
116
|
+
terminal.dev !== initial.dev ||
|
|
117
|
+
terminal.ino !== initial.ino ||
|
|
118
|
+
terminal.nlink !== initial.nlink ||
|
|
119
|
+
terminal.size !== initial.size ||
|
|
120
|
+
terminal.mtimeNs !== initial.mtimeNs ||
|
|
121
|
+
terminal.ctimeNs !== initial.ctimeNs
|
|
122
|
+
) {
|
|
123
|
+
throw new SessionImportError(
|
|
124
|
+
"source_changed",
|
|
125
|
+
"read",
|
|
126
|
+
"The transcript file changed while it was being read. Re-export or retry with a stable file.",
|
|
127
|
+
{ retryable: true },
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
return {
|
|
131
|
+
text: bytes.toString("utf8"),
|
|
132
|
+
bytes: bytes.length,
|
|
133
|
+
sha256: createHash("sha256").update(bytes).digest("hex"),
|
|
134
|
+
};
|
|
135
|
+
} catch (error) {
|
|
136
|
+
if (error instanceof SessionImportError) throw error;
|
|
137
|
+
const code = (error as NodeJS.ErrnoException).code;
|
|
138
|
+
throw new SessionImportError(
|
|
139
|
+
"source_unreadable",
|
|
140
|
+
"read",
|
|
141
|
+
`Transcript file cannot be read (${code ?? "unknown error"}): ${displayPath}`,
|
|
142
|
+
{ retryable: true },
|
|
143
|
+
);
|
|
144
|
+
} finally {
|
|
145
|
+
await handle.close().catch(() => {});
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function parseDetectedFormat(detection: ReturnType<typeof detectSessionImportFormat>, text: string): ParsedSource {
|
|
150
|
+
switch (detection.format) {
|
|
151
|
+
case "codex-rollout-jsonl":
|
|
152
|
+
return normalizeAdapterResult(parseCodexRollout(text));
|
|
153
|
+
case "claude-code-jsonl":
|
|
154
|
+
return normalizeAdapterResult(parseClaudeCodeTranscript(text));
|
|
155
|
+
case "claude-export-json":
|
|
156
|
+
return normalizeAdapterResult(parseClaudeExport(text));
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function normalizeAdapterResult(result: {
|
|
161
|
+
conversation: ImportedConversation;
|
|
162
|
+
quarantine: ImportQuarantineRecord[];
|
|
163
|
+
counts: SessionImportCounts;
|
|
164
|
+
redactionKinds: string[];
|
|
165
|
+
}): ParsedSource {
|
|
166
|
+
return {
|
|
167
|
+
conversation: result.conversation,
|
|
168
|
+
quarantine: {
|
|
169
|
+
present: result.quarantine.length > 0,
|
|
170
|
+
truncated: result.counts.quarantined > result.quarantine.length,
|
|
171
|
+
records: result.quarantine.slice(0, MAX_QUARANTINE_RECORDS),
|
|
172
|
+
},
|
|
173
|
+
counts: result.counts,
|
|
174
|
+
redactionKinds: result.redactionKinds,
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function assertValidTimestamps(conversation: ImportedConversation): void {
|
|
179
|
+
for (const message of conversation.messages) {
|
|
180
|
+
if (message.timestamp === undefined) continue;
|
|
181
|
+
if (!Number.isFinite(Date.parse(message.timestamp))) {
|
|
182
|
+
throw new SessionImportError(
|
|
183
|
+
"malformed_input",
|
|
184
|
+
"normalize",
|
|
185
|
+
"A message carries an unparseable timestamp; the transcript is not a clean export.",
|
|
186
|
+
);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
function truncateText(text: string, maxChars: number): string {
|
|
192
|
+
if (text.length <= maxChars) return text;
|
|
193
|
+
return `${text.slice(0, maxChars - 1)}…`;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/** Render the bounded continuation context document (head + tail with an elision marker). */
|
|
197
|
+
function renderImportContext(conversation: ImportedConversation): {
|
|
198
|
+
text: string;
|
|
199
|
+
omitted: number;
|
|
200
|
+
truncated: boolean;
|
|
201
|
+
} {
|
|
202
|
+
const titleLine = conversation.title ? ` titled "${truncateText(conversation.title, MAX_TITLE_CHARS)}"` : "";
|
|
203
|
+
const lines: string[] = [
|
|
204
|
+
`<imported-session provider="${conversation.provider}" format="${conversation.format}">`,
|
|
205
|
+
"",
|
|
206
|
+
`The conversation below was imported from an external ${conversation.provider === "codex" ? "Codex" : "Claude"} session transcript${titleLine}.`,
|
|
207
|
+
"It is reconstructed context only: roles and text are preserved, but tool/internal provider state was not cloned, and secrets were redacted.",
|
|
208
|
+
"Continue this conversation naturally as its current participant.",
|
|
209
|
+
"",
|
|
210
|
+
];
|
|
211
|
+
if (conversation.cwd) lines.push(`Original workspace: ${conversation.cwd}`, "");
|
|
212
|
+
|
|
213
|
+
const rendered: string[] = [];
|
|
214
|
+
for (const message of conversation.messages) {
|
|
215
|
+
const speaker = message.role === "user" ? "User" : "Assistant";
|
|
216
|
+
const blocks: string[] = [];
|
|
217
|
+
if (message.text) blocks.push(truncateText(message.text, MAX_MESSAGE_CHARS));
|
|
218
|
+
if (message.toolEvidence && message.toolEvidence.length > 0) {
|
|
219
|
+
blocks.push(["Tool/file evidence:", ...message.toolEvidence.map(line => ` ${line}`)].join("\n"));
|
|
220
|
+
}
|
|
221
|
+
if (blocks.length === 0) continue;
|
|
222
|
+
rendered.push(`### ${speaker}\n\n${blocks.join("\n\n")}`);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
let omitted = 0;
|
|
226
|
+
let truncated = false;
|
|
227
|
+
let body: string[];
|
|
228
|
+
if (rendered.length === 0) {
|
|
229
|
+
body = [];
|
|
230
|
+
} else {
|
|
231
|
+
const chars = rendered.reduce((total, part) => total + part.length + 2, 0);
|
|
232
|
+
if (chars <= MAX_CONTEXT_CHARS) {
|
|
233
|
+
body = rendered;
|
|
234
|
+
} else {
|
|
235
|
+
truncated = true;
|
|
236
|
+
const head: string[] = [];
|
|
237
|
+
let headChars = 0;
|
|
238
|
+
while (head.length < rendered.length && headChars + rendered[head.length]!.length + 2 <= HEAD_CONTEXT_CHARS) {
|
|
239
|
+
headChars += rendered[head.length]!.length + 2;
|
|
240
|
+
head.push(rendered[head.length]!);
|
|
241
|
+
}
|
|
242
|
+
const tail: string[] = [];
|
|
243
|
+
let tailChars = 0;
|
|
244
|
+
for (let index = rendered.length - 1; index >= head.length; index--) {
|
|
245
|
+
const part = rendered[index]!;
|
|
246
|
+
if (tailChars + part.length + 2 > MAX_CONTEXT_CHARS - headChars) break;
|
|
247
|
+
tailChars += part.length + 2;
|
|
248
|
+
tail.unshift(part);
|
|
249
|
+
}
|
|
250
|
+
omitted = rendered.length - head.length - tail.length;
|
|
251
|
+
body = [
|
|
252
|
+
...head,
|
|
253
|
+
`[… ${omitted} earlier message${omitted === 1 ? "" : "s"} from the imported ${conversation.provider} session were elided to bound context; the continuation tail is preserved below. …]`,
|
|
254
|
+
...tail,
|
|
255
|
+
];
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
lines.push(...body);
|
|
260
|
+
lines.push("", "</imported-session>");
|
|
261
|
+
return { text: lines.join("\n"), omitted, truncated };
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* Phase 1: read, detect, parse, normalize, redact, and bound. Performs no
|
|
266
|
+
* session mutation; safe to run before any session transition.
|
|
267
|
+
*/
|
|
268
|
+
export async function prepareExternalSessionImport(
|
|
269
|
+
request: Pick<ExternalSessionImportRequest, "sourcePath" | "provider">,
|
|
270
|
+
): Promise<PreparedSessionImport> {
|
|
271
|
+
if (typeof request.sourcePath !== "string" || request.sourcePath.trim().length === 0) {
|
|
272
|
+
throw new SessionImportError(
|
|
273
|
+
"invalid_request",
|
|
274
|
+
"request",
|
|
275
|
+
"A transcript file path is required: /import-session <file> [--provider codex|claude]",
|
|
276
|
+
);
|
|
277
|
+
}
|
|
278
|
+
if (request.provider !== undefined && request.provider !== "codex" && request.provider !== "claude") {
|
|
279
|
+
throw new SessionImportError(
|
|
280
|
+
"invalid_request",
|
|
281
|
+
"request",
|
|
282
|
+
`Unsupported provider "${String(request.provider)}"; expected codex or claude.`,
|
|
283
|
+
);
|
|
284
|
+
}
|
|
285
|
+
const source = await readImportSource(request.sourcePath);
|
|
286
|
+
const detection = detectSessionImportFormat(source.text, request.provider);
|
|
287
|
+
const parsed = parseDetectedFormat(detection, source.text);
|
|
288
|
+
assertValidTimestamps(parsed.conversation);
|
|
289
|
+
|
|
290
|
+
if (parsed.conversation.messages.length === 0) {
|
|
291
|
+
throw new SessionImportError(
|
|
292
|
+
"malformed_input",
|
|
293
|
+
"parse",
|
|
294
|
+
`No importable user/assistant messages were found in the ${detection.format} transcript.`,
|
|
295
|
+
);
|
|
296
|
+
}
|
|
297
|
+
if (parsed.conversation.messages.length > MAX_SOURCE_MESSAGES) {
|
|
298
|
+
throw new SessionImportError(
|
|
299
|
+
"content_too_large",
|
|
300
|
+
"normalize",
|
|
301
|
+
`The transcript has ${parsed.conversation.messages.length} messages, exceeding the ${MAX_SOURCE_MESSAGES}-message import limit. Export a shorter session.`,
|
|
302
|
+
{ limitBytes: MAX_SOURCE_MESSAGES, observedBytes: parsed.conversation.messages.length },
|
|
303
|
+
);
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
const rendered = renderImportContext(parsed.conversation);
|
|
307
|
+
const counts: SessionImportCounts = { ...parsed.counts, omitted: rendered.omitted };
|
|
308
|
+
const provenance: PreparedSessionImport["provenance"] = {
|
|
309
|
+
schemaVersion: 1,
|
|
310
|
+
customType: EXTERNAL_IMPORT_PROVENANCE_CUSTOM_TYPE,
|
|
311
|
+
provider: parsed.conversation.provider,
|
|
312
|
+
format: parsed.conversation.format,
|
|
313
|
+
sourceFileName: redactImportedText(path.basename(path.resolve(request.sourcePath))).value.slice(0, 255),
|
|
314
|
+
...(parsed.conversation.sourceSessionId ? { sourceSessionId: parsed.conversation.sourceSessionId } : {}),
|
|
315
|
+
...(parsed.conversation.title ? { sourceTitle: truncateText(parsed.conversation.title, MAX_TITLE_CHARS) } : {}),
|
|
316
|
+
sourceSha256: source.sha256,
|
|
317
|
+
sourceBytes: source.bytes,
|
|
318
|
+
converterVersion: EXTERNAL_IMPORT_CONVERTER_VERSION,
|
|
319
|
+
sanitizerVersion: IMPORT_SANITIZER_VERSION,
|
|
320
|
+
counts,
|
|
321
|
+
truncated: rendered.truncated,
|
|
322
|
+
quarantine: parsed.quarantine,
|
|
323
|
+
};
|
|
324
|
+
return {
|
|
325
|
+
conversation: parsed.conversation,
|
|
326
|
+
contextText: rendered.text,
|
|
327
|
+
provenance,
|
|
328
|
+
counts,
|
|
329
|
+
redactionKinds: parsed.redactionKinds,
|
|
330
|
+
sourcePath: path.resolve(request.sourcePath),
|
|
331
|
+
sourceSha256: source.sha256,
|
|
332
|
+
sourceBytes: source.bytes,
|
|
333
|
+
};
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
/**
|
|
337
|
+
* Phase 2: materialize a prepared import as a NEW GJC session file and verify
|
|
338
|
+
* it reopens as a resumable session. The caller's live session is untouched.
|
|
339
|
+
*/
|
|
340
|
+
export async function materializeExternalSessionImport(
|
|
341
|
+
prepared: PreparedSessionImport,
|
|
342
|
+
options: Pick<ExternalSessionImportRequest, "cwd" | "destination" | "now">,
|
|
343
|
+
): Promise<SessionImportCompleted> {
|
|
344
|
+
if (typeof options.cwd !== "string" || options.cwd.length === 0) {
|
|
345
|
+
throw new SessionImportError("invalid_request", "request", "A workspace cwd is required to host the import.");
|
|
346
|
+
}
|
|
347
|
+
const importedAt = (options.now?.() ?? new Date()).toISOString();
|
|
348
|
+
const manager = SessionManager.create(options.cwd, options.destination);
|
|
349
|
+
const destination = manager.getDestinationForFork();
|
|
350
|
+
const sessionFile = manager.getSessionFile();
|
|
351
|
+
if (!sessionFile) {
|
|
352
|
+
await manager.close().catch(() => {});
|
|
353
|
+
throw new SessionImportError(
|
|
354
|
+
"io_failed",
|
|
355
|
+
"persist",
|
|
356
|
+
"Session persistence is unavailable for this workspace; the import has nowhere to live.",
|
|
357
|
+
);
|
|
358
|
+
}
|
|
359
|
+
const targetSessionId = manager.getSessionId();
|
|
360
|
+
try {
|
|
361
|
+
const provenance: SessionImportProvenance = {
|
|
362
|
+
...prepared.provenance,
|
|
363
|
+
targetSessionId,
|
|
364
|
+
importedAt,
|
|
365
|
+
};
|
|
366
|
+
manager.appendCustomEntry(EXTERNAL_IMPORT_PROVENANCE_CUSTOM_TYPE, provenance);
|
|
367
|
+
manager.appendCustomMessageEntry(
|
|
368
|
+
EXTERNAL_IMPORT_CONTEXT_CUSTOM_TYPE,
|
|
369
|
+
prepared.contextText,
|
|
370
|
+
true,
|
|
371
|
+
{
|
|
372
|
+
provider: prepared.conversation.provider,
|
|
373
|
+
format: prepared.conversation.format,
|
|
374
|
+
sourceSha256: prepared.sourceSha256,
|
|
375
|
+
sourceBytes: prepared.sourceBytes,
|
|
376
|
+
counts: prepared.counts,
|
|
377
|
+
truncated: prepared.provenance.truncated,
|
|
378
|
+
importedAt,
|
|
379
|
+
},
|
|
380
|
+
"agent",
|
|
381
|
+
);
|
|
382
|
+
const title =
|
|
383
|
+
prepared.conversation.title ??
|
|
384
|
+
`Imported ${prepared.conversation.provider === "codex" ? "Codex" : "Claude"} session ${(
|
|
385
|
+
prepared.conversation.sourceSessionId ?? prepared.sourceSha256
|
|
386
|
+
).slice(0, 8)}`;
|
|
387
|
+
await manager.setSessionName(truncateText(title, MAX_TITLE_CHARS), "user");
|
|
388
|
+
await manager.ensureOnDisk();
|
|
389
|
+
await manager.flush();
|
|
390
|
+
|
|
391
|
+
// Verify the written file reopens cleanly and is continuable before the
|
|
392
|
+
// caller switches to it. Keep the creating manager open so its captured
|
|
393
|
+
// managed destination authority remains valid throughout verification;
|
|
394
|
+
// closing it afterward releases that authority exactly once.
|
|
395
|
+
const reopened = await SessionManager.open(sessionFile, destination);
|
|
396
|
+
try {
|
|
397
|
+
if (!canContinuePersistedHistory(reopened.buildSessionContext().messages)) {
|
|
398
|
+
throw new SessionImportError(
|
|
399
|
+
"malformed_input",
|
|
400
|
+
"persist",
|
|
401
|
+
"The imported session did not reconstruct a continuable conversation.",
|
|
402
|
+
);
|
|
403
|
+
}
|
|
404
|
+
} finally {
|
|
405
|
+
await reopened.close().catch(() => {});
|
|
406
|
+
}
|
|
407
|
+
await manager.close();
|
|
408
|
+
return { targetSessionId, targetPath: sessionFile, title: truncateText(title, MAX_TITLE_CHARS), prepared };
|
|
409
|
+
} catch (error) {
|
|
410
|
+
await manager.close().catch(() => {});
|
|
411
|
+
if (error instanceof SessionImportError) throw error;
|
|
412
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
413
|
+
throw new SessionImportError("io_failed", "persist", `Failed to persist the imported session: ${message}`, {
|
|
414
|
+
retryable: true,
|
|
415
|
+
});
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
/** Full import: prepare + materialize. Never switches the caller's session. */
|
|
420
|
+
export async function importProviderSessionFile(
|
|
421
|
+
request: ExternalSessionImportRequest,
|
|
422
|
+
): Promise<SessionImportCompleted> {
|
|
423
|
+
const prepared = await prepareExternalSessionImport(request);
|
|
424
|
+
return materializeExternalSessionImport(prepared, request);
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
/** Human-facing summary lines for a completed import. */
|
|
428
|
+
export function formatProviderSessionImportSummary(result: SessionImportCompleted): string {
|
|
429
|
+
const { prepared } = result;
|
|
430
|
+
const providerLabel = prepared.conversation.provider === "codex" ? "Codex" : "Claude";
|
|
431
|
+
const lines = [
|
|
432
|
+
`Imported ${providerLabel} session (${prepared.conversation.format}) into a new GJC session.`,
|
|
433
|
+
`Session: ${result.targetSessionId}`,
|
|
434
|
+
`Title: ${result.title}`,
|
|
435
|
+
`Messages: ${prepared.conversation.messages.length} reconstructed (${prepared.counts.mapped} records mapped, ${prepared.counts.quarantined} quarantined)`,
|
|
436
|
+
`Source: ${prepared.provenance.sourceFileName} (${prepared.sourceBytes} bytes, sha256 ${prepared.sourceSha256.slice(0, 12)}…)`,
|
|
437
|
+
];
|
|
438
|
+
if (prepared.counts.omitted > 0) {
|
|
439
|
+
lines.push(`Bounded: ${prepared.counts.omitted} earlier messages elided between the preserved head and tail.`);
|
|
440
|
+
}
|
|
441
|
+
if (prepared.counts.redacted > 0) {
|
|
442
|
+
lines.push(
|
|
443
|
+
`Redacted: ${prepared.counts.redacted} secret/credential value${prepared.counts.redacted === 1 ? "" : "s"} (${prepared.redactionKinds.join(", ")}).`,
|
|
444
|
+
);
|
|
445
|
+
}
|
|
446
|
+
lines.push("The original transcript file was not modified.");
|
|
447
|
+
return lines.join("\n");
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
/** Human-facing diagnostic for a failed import. */
|
|
451
|
+
export function formatProviderSessionImportError(error: unknown): string {
|
|
452
|
+
if (error instanceof SessionImportError) {
|
|
453
|
+
const retry = error.retryable ? " (retryable)" : "";
|
|
454
|
+
return `Import failed: ${error.code} [${error.phase}]${retry} — ${error.message}`;
|
|
455
|
+
}
|
|
456
|
+
return `Import failed: ${error instanceof Error ? error.message : String(error)}`;
|
|
457
|
+
}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fail-closed secret redaction for imported session content (issue #3709).
|
|
3
|
+
*
|
|
4
|
+
* Every string that crosses the import boundary passes through
|
|
5
|
+
* {@link redactImportedText}. Patterns are conservative: a false positive costs
|
|
6
|
+
* one `[REDACTED]` span in reconstructed context, a false negative leaks a
|
|
7
|
+
* credential into a new session transcript, so the balance favors redaction.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
export const IMPORT_REDACTED_PLACEHOLDER = "[REDACTED]";
|
|
11
|
+
/** Bumped when patterns change; persisted in import provenance. */
|
|
12
|
+
export const IMPORT_SANITIZER_VERSION = 2;
|
|
13
|
+
|
|
14
|
+
const ANSI_ESCAPE = /\x1b(?:\[[0-?]*[ -/]*[@-~]|\][^\x07]*(?:\x07|\x1b\\))/gu;
|
|
15
|
+
const UNSAFE_CONTROL =
|
|
16
|
+
/[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f-\u009f\u200b-\u200f\u202a-\u202e\u2060\u2066-\u2069\ufeff]/gu;
|
|
17
|
+
const HEADER_CREDENTIAL = /(\b(?:authorization|cookie|set-cookie)\b[ \t]*:[ \t]*)([^\r\n]*)/giu;
|
|
18
|
+
const JSON_AUTHORIZATION = /((?:\\?")authorization(?:\\?")\s*:\s*(?:\\?")?)([^",}\r\n]+)/giu;
|
|
19
|
+
|
|
20
|
+
interface RedactionRule {
|
|
21
|
+
readonly id: string;
|
|
22
|
+
readonly pattern: RegExp;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const HEX = "[0-9a-fA-F]";
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Ordered first-match-wins rules. Each rule replaces its full match with the
|
|
29
|
+
* placeholder, so a matched credential never survives partially.
|
|
30
|
+
*/
|
|
31
|
+
const REDACTION_RULES: readonly RedactionRule[] = [
|
|
32
|
+
// PEM private-key blocks, header through footer.
|
|
33
|
+
{
|
|
34
|
+
id: "pem-private-key",
|
|
35
|
+
pattern: /-----BEGIN [A-Z0-9 ]*PRIVATE KEY-----[\s\S]*?-----END [A-Z0-9 ]*PRIVATE KEY-----/g,
|
|
36
|
+
},
|
|
37
|
+
// AWS access key ids.
|
|
38
|
+
{ id: "aws-access-key", pattern: /(?:AKIA|ASIA|ABIA|ACCA)[0-9A-Z]{16}/g },
|
|
39
|
+
// GitHub tokens (PAT, OAuth, server-to-server, refresh, fine-grained).
|
|
40
|
+
{ id: "github-token", pattern: /(?:ghp|gho|ghu|ghs|ghr|github_pat)_[A-Za-z0-9_]{20,}/g },
|
|
41
|
+
// Anthropic API keys.
|
|
42
|
+
{ id: "anthropic-key", pattern: /sk-ant-[A-Za-z0-9_-]{20,}/g },
|
|
43
|
+
// OpenAI-style keys (project keys, service accounts, legacy sk-).
|
|
44
|
+
{ id: "openai-key", pattern: /sk-(?:proj|svcacct|admin)-[A-Za-z0-9_-]{20,}|sk-[A-Za-z0-9]{20,}/g },
|
|
45
|
+
// Slack tokens.
|
|
46
|
+
{ id: "slack-token", pattern: /xox[baprs]-[A-Za-z0-9-]{10,}/g },
|
|
47
|
+
// Google API keys.
|
|
48
|
+
{ id: "google-api-key", pattern: /AIza[0-9A-Za-z_-]{35}/g },
|
|
49
|
+
// Bearer tokens carried in Authorization text.
|
|
50
|
+
{
|
|
51
|
+
id: "bearer-token",
|
|
52
|
+
pattern: /(authorization["'\s]*[:=]["'\s]*bearer\s+|bearer\s+)[A-Za-z0-9._~+/=-]{12,}/gi,
|
|
53
|
+
},
|
|
54
|
+
// JWTs (three base64url segments; middle segment non-trivial).
|
|
55
|
+
{
|
|
56
|
+
id: "jwt",
|
|
57
|
+
pattern: /eyJ[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}/g,
|
|
58
|
+
},
|
|
59
|
+
// Long hex secrets (64+ hex chars: webhook secrets, signing keys).
|
|
60
|
+
{ id: "hex-secret", pattern: new RegExp(`\\b${HEX}{64,}\\b`, "g") },
|
|
61
|
+
// Basic-auth credentials embedded in URLs (https://user:pass@host).
|
|
62
|
+
{ id: "url-credential", pattern: /([a-z][a-z0-9+.-]*:\/\/)[^/\s:@]{1,256}:[^/\s@]{1,256}@/gi },
|
|
63
|
+
// Sensitive env/KEY assignments: OPENAI_API_KEY=..., token: ..., password = ...
|
|
64
|
+
// The name prefix is optional so a bare sensitive name (`password: …`) also matches.
|
|
65
|
+
{
|
|
66
|
+
id: "secret-assignment",
|
|
67
|
+
pattern:
|
|
68
|
+
/\b(?:[A-Za-z_][A-Za-z0-9_]*)?(?:api[_-]?key|secret|token|password|passwd|credential|client[_-]?secret|private[_-]?key|access[_-]?key|refresh[_-]?token|session[_-]?key)[A-Za-z0-9_]*\b(["']?\s*[:=]\s*)("(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*'|[^\s,;}&)]{4,})/gi,
|
|
69
|
+
},
|
|
70
|
+
];
|
|
71
|
+
|
|
72
|
+
export interface ImportRedactionResult {
|
|
73
|
+
value: string;
|
|
74
|
+
redacted: number;
|
|
75
|
+
kinds: string[];
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Redact every known credential shape from imported text. The same placeholder
|
|
80
|
+
* is used for all kinds so a partial pattern overlap cannot leak fragments.
|
|
81
|
+
*/
|
|
82
|
+
export function redactImportedText(input: string): ImportRedactionResult {
|
|
83
|
+
let value = input.replace(ANSI_ESCAPE, "").replace(UNSAFE_CONTROL, "");
|
|
84
|
+
let redacted = 0;
|
|
85
|
+
const kinds = new Set<string>();
|
|
86
|
+
value = value.replace(HEADER_CREDENTIAL, (_match, prefix: string, raw: string) => {
|
|
87
|
+
if (!raw.trim()) return `${prefix}${raw}`;
|
|
88
|
+
redacted++;
|
|
89
|
+
kinds.add("credential-header");
|
|
90
|
+
return `${prefix}${IMPORT_REDACTED_PLACEHOLDER}`;
|
|
91
|
+
});
|
|
92
|
+
value = value.replace(JSON_AUTHORIZATION, (_match, prefix: string, raw: string) => {
|
|
93
|
+
if (!raw.trim()) return `${prefix}${raw}`;
|
|
94
|
+
redacted++;
|
|
95
|
+
kinds.add("authorization-json");
|
|
96
|
+
return `${prefix}${IMPORT_REDACTED_PLACEHOLDER}`;
|
|
97
|
+
});
|
|
98
|
+
for (const rule of REDACTION_RULES) {
|
|
99
|
+
rule.pattern.lastIndex = 0;
|
|
100
|
+
value = value.replace(rule.pattern, match => {
|
|
101
|
+
redacted++;
|
|
102
|
+
kinds.add(rule.id);
|
|
103
|
+
if (rule.id === "url-credential") {
|
|
104
|
+
// Keep the scheme visible; the match ends at "@" so the host survives.
|
|
105
|
+
const schemeEnd = match.indexOf("://") + 3;
|
|
106
|
+
return `${match.slice(0, schemeEnd)}${IMPORT_REDACTED_PLACEHOLDER}@`;
|
|
107
|
+
}
|
|
108
|
+
if (rule.id === "secret-assignment") {
|
|
109
|
+
const sep = /(["']?\s*[:=]\s*)/.exec(match);
|
|
110
|
+
if (sep) {
|
|
111
|
+
const head = match.slice(0, (sep.index ?? 0) + sep[0].length);
|
|
112
|
+
return `${head}${IMPORT_REDACTED_PLACEHOLDER}`;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
if (rule.id === "bearer-token") {
|
|
116
|
+
const bearerAt = /bearer\s+/i.exec(match);
|
|
117
|
+
if (bearerAt) return `${match.slice(0, bearerAt.index + bearerAt[0].length)}${IMPORT_REDACTED_PLACEHOLDER}`;
|
|
118
|
+
}
|
|
119
|
+
return IMPORT_REDACTED_PLACEHOLDER;
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
return { value, redacted, kinds: [...kinds].sort() };
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/** Redact a string, returning only the sanitized value. */
|
|
126
|
+
export function sanitizeImportedString(input: string): string {
|
|
127
|
+
return redactImportedText(input).value;
|
|
128
|
+
}
|