@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
package/src/sdk/session.ts
CHANGED
|
@@ -18,6 +18,7 @@ import {
|
|
|
18
18
|
type Message,
|
|
19
19
|
type Model,
|
|
20
20
|
type ProviderSessionState,
|
|
21
|
+
resolveOAuthStorageProvider,
|
|
21
22
|
type SimpleStreamOptions,
|
|
22
23
|
streamSimple,
|
|
23
24
|
type ToolResultMessage,
|
|
@@ -38,9 +39,16 @@ import {
|
|
|
38
39
|
providerSupportsAppendOnlyAuto,
|
|
39
40
|
resolveAppendOnlyMode,
|
|
40
41
|
} from "../append-only-mode";
|
|
41
|
-
import {
|
|
42
|
+
import {
|
|
43
|
+
type AsyncJob,
|
|
44
|
+
AsyncJobManager,
|
|
45
|
+
asyncJobEndpointId as deriveAsyncJobEndpointId,
|
|
46
|
+
isBackgroundJobSupportEnabled,
|
|
47
|
+
jobElapsedMs,
|
|
48
|
+
} from "../async";
|
|
42
49
|
import { loadCapability } from "../capability";
|
|
43
50
|
import { type Rule, ruleCapability, setActiveRules } from "../capability/rule";
|
|
51
|
+
import type { SourceMeta } from "../capability/types";
|
|
44
52
|
import { resolveModelProfileName } from "../config/model-profile-contract";
|
|
45
53
|
import { resolveProfileBindings } from "../config/model-profiles";
|
|
46
54
|
import { kNoAuth, ModelRegistry } from "../config/model-registry";
|
|
@@ -54,6 +62,7 @@ import {
|
|
|
54
62
|
} from "../config/model-resolver";
|
|
55
63
|
import { loadPromptTemplates as loadPromptTemplatesInternal, type PromptTemplate } from "../config/prompt-templates";
|
|
56
64
|
import { Settings, type SkillsSettings } from "../config/settings";
|
|
65
|
+
import { resolveEagerTaskDelegation } from "../config/task-delegation";
|
|
57
66
|
import { CursorExecHandlers } from "../cursor";
|
|
58
67
|
import type { BashRestrictionProfile } from "../tools/bash-allowed-prefixes";
|
|
59
68
|
import "../discovery";
|
|
@@ -93,9 +102,11 @@ import {
|
|
|
93
102
|
gjcActivationGenerationFor,
|
|
94
103
|
} from "../extensibility/gjc-plugins/runtime-quarantine";
|
|
95
104
|
import { loadActiveSubskillTools } from "../extensibility/gjc-plugins/tools";
|
|
105
|
+
import { discoverAndLoadHookExtensions } from "../extensibility/hooks/loader";
|
|
96
106
|
import { loadSkills, type Skill, type SkillWarning, setActiveSkills } from "../extensibility/skills";
|
|
97
107
|
import type { FileSlashCommand } from "../extensibility/slash-commands";
|
|
98
108
|
import type { HindsightSessionState } from "../hindsight/state";
|
|
109
|
+
import { normalizePluginHook } from "../hooks/normalize";
|
|
99
110
|
import { initializeLocalRoot, LocalProtocolHandler, type LocalProtocolOptions } from "../internal-urls";
|
|
100
111
|
import type { LspStartupServerInfo } from "../lsp";
|
|
101
112
|
import btwUserPrompt from "../prompts/system/btw-user.md" with { type: "text" };
|
|
@@ -106,7 +117,8 @@ import {
|
|
|
106
117
|
createOptionalRuntimeServices,
|
|
107
118
|
type OptionalRuntimeServicesOverrides,
|
|
108
119
|
} from "../runtime/optional-runtime-services";
|
|
109
|
-
import { MCPManager } from "../runtime-mcp";
|
|
120
|
+
import { loadAllMCPConfigs, MCPManager } from "../runtime-mcp";
|
|
121
|
+
import type { MCPServerConfig } from "../runtime-mcp/types";
|
|
110
122
|
import {
|
|
111
123
|
getNotificationConfig,
|
|
112
124
|
isGenericNotificationHostEligible,
|
|
@@ -114,15 +126,29 @@ import {
|
|
|
114
126
|
SPAWN_PROVENANCE_ENV,
|
|
115
127
|
shouldRegisterGenericNotificationsExtension,
|
|
116
128
|
} from "../sdk/bus/config";
|
|
129
|
+
import { createReconciliationStore, type ReconciliationStore } from "../sdk/bus/reconciliation-store";
|
|
117
130
|
import { NotificationSessionController } from "../sdk/bus/session-control";
|
|
118
131
|
import { shouldHostSdk } from "../sdk/host";
|
|
132
|
+
import { createSdkSessionRuntimeExtension, registerSdkOnlyNotificationCommand } from "../sdk/host/session-runtime";
|
|
133
|
+
import { createSdkWebSocketTransport } from "../sdk/host/websocket-transport";
|
|
119
134
|
|
|
120
135
|
import type { SecretObfuscator } from "../secrets";
|
|
121
136
|
import { AgentSession, type ForkContextSeed } from "../session/agent-session";
|
|
122
|
-
import { resolveAuthBrokerConfig } from "../session/auth-broker-config";
|
|
123
137
|
import { AuthBrokerClient, AuthStorage, RemoteAuthCredentialStore } from "../session/auth-storage";
|
|
124
138
|
import { type CustomMessage, convertToLlm } from "../session/messages";
|
|
125
139
|
import { createReadonlySessionManager, SessionManager } from "../session/session-manager";
|
|
140
|
+
import {
|
|
141
|
+
parsePersistedCredentialSelector,
|
|
142
|
+
resolveStartupAuthConfig,
|
|
143
|
+
type StartupAuthConfigSnapshot,
|
|
144
|
+
} from "../session/startup-auth-config";
|
|
145
|
+
import {
|
|
146
|
+
isOwnedCompletionEnvelopeAllowed,
|
|
147
|
+
lookupOwnedRegistration,
|
|
148
|
+
type OwnedCompletionEnvelope,
|
|
149
|
+
retireOwnedRegistrationsForEndpoint,
|
|
150
|
+
unregisterOwnedRegistration,
|
|
151
|
+
} from "../session/terminal-abort";
|
|
126
152
|
import { formatNoModelsAvailableFallback } from "../setup/model-onboarding-guidance";
|
|
127
153
|
import {
|
|
128
154
|
type BuildSystemPromptResult,
|
|
@@ -163,6 +189,8 @@ type AsyncResultEntry = {
|
|
|
163
189
|
result: string;
|
|
164
190
|
job: AsyncJob | undefined;
|
|
165
191
|
durationMs: number | undefined;
|
|
192
|
+
/** Exact owned-completion origin when the job is registered left-running work of a terminal turn. */
|
|
193
|
+
ownedCompletion?: OwnedCompletionEnvelope;
|
|
166
194
|
};
|
|
167
195
|
|
|
168
196
|
type AsyncResultJobDetails = {
|
|
@@ -174,6 +202,8 @@ type AsyncResultJobDetails = {
|
|
|
174
202
|
|
|
175
203
|
type AsyncResultDetails = {
|
|
176
204
|
jobs: AsyncResultJobDetails[];
|
|
205
|
+
/** Private origin envelope(s); absent = ordinary delivery. Never a public field. */
|
|
206
|
+
ownedCompletions?: OwnedCompletionEnvelope[];
|
|
177
207
|
};
|
|
178
208
|
|
|
179
209
|
type McpNotificationEntry = {
|
|
@@ -183,13 +213,53 @@ type McpNotificationEntry = {
|
|
|
183
213
|
|
|
184
214
|
function buildAsyncResultBatchMessage(entries: AsyncResultEntry[]): CustomMessage<AsyncResultDetails> | null {
|
|
185
215
|
if (entries.length === 0) return null;
|
|
186
|
-
|
|
216
|
+
// Partition denied owned-completion entries out ENTIRELY before batch
|
|
217
|
+
// construction (AC 36 zero final calls from stopped work): a denied entry —
|
|
218
|
+
// owned scope, forged tuple, or vanished scope — must never reach
|
|
219
|
+
// followUp/prompt, even inside a mixed batch. Allowed owned-completion and
|
|
220
|
+
// ordinary entries are delivered normally.
|
|
221
|
+
const survivors = entries.filter(
|
|
222
|
+
entry => entry.ownedCompletion === undefined || isOwnedCompletionEnvelopeAllowed(entry.ownedCompletion),
|
|
223
|
+
);
|
|
224
|
+
// Denied entries never reach followUp/prompt (AC 36), so no later
|
|
225
|
+
// settlement boundary sees them — retire their terminal tuples HERE,
|
|
226
|
+
// otherwise an owned_unsettled abort's later completion keeps occupying
|
|
227
|
+
// the global registration and retained-policy capacities (review P2).
|
|
228
|
+
const denied = entries.filter(
|
|
229
|
+
(entry): entry is AsyncResultEntry & { ownedCompletion: OwnedCompletionEnvelope } =>
|
|
230
|
+
entry.ownedCompletion !== undefined && !isOwnedCompletionEnvelopeAllowed(entry.ownedCompletion),
|
|
231
|
+
);
|
|
232
|
+
if (denied.length > 0) {
|
|
233
|
+
// Resolve the manager from the registration's OWN endpoint: the
|
|
234
|
+
// process-global instance is the last-created session, so when B's
|
|
235
|
+
// manager is global with the same local job id still running, the
|
|
236
|
+
// terminality check would observe B's running job and refuse to
|
|
237
|
+
// unregister A's already-terminal tuple (review thread P2).
|
|
238
|
+
const manager =
|
|
239
|
+
AsyncJobManager.forEndpoint(denied[0]?.ownedCompletion.registration.endpointId) ?? AsyncJobManager.instance();
|
|
240
|
+
for (const entry of denied) {
|
|
241
|
+
const registration = entry.ownedCompletion.registration;
|
|
242
|
+
const job = manager?.getJob(registration.jobId);
|
|
243
|
+
const status = job?.generation === registration.jobGeneration ? job?.status : undefined;
|
|
244
|
+
if (job === undefined || status === "completed" || status === "cancelled" || status === "failed") {
|
|
245
|
+
unregisterOwnedRegistration(registration);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
if (survivors.length === 0) return null;
|
|
250
|
+
const jobs = survivors.map(entry => ({
|
|
187
251
|
jobId: entry.jobId,
|
|
188
252
|
result: entry.result,
|
|
189
253
|
type: entry.job?.type,
|
|
190
254
|
label: entry.job?.label,
|
|
191
255
|
durationMs: entry.durationMs,
|
|
192
256
|
}));
|
|
257
|
+
const ownedCompletions = survivors
|
|
258
|
+
.filter(
|
|
259
|
+
(entry): entry is AsyncResultEntry & { ownedCompletion: OwnedCompletionEnvelope } =>
|
|
260
|
+
entry.ownedCompletion !== undefined,
|
|
261
|
+
)
|
|
262
|
+
.map(entry => entry.ownedCompletion);
|
|
193
263
|
const details: AsyncResultDetails = {
|
|
194
264
|
jobs: jobs.map(job => ({
|
|
195
265
|
jobId: job.jobId,
|
|
@@ -197,6 +267,10 @@ function buildAsyncResultBatchMessage(entries: AsyncResultEntry[]): CustomMessag
|
|
|
197
267
|
label: job.label,
|
|
198
268
|
durationMs: job.durationMs,
|
|
199
269
|
})),
|
|
270
|
+
// Private origin envelope for the AgentSession injector; absent for
|
|
271
|
+
// ordinary deliveries. Only ALLOWED owned-completion entries survive
|
|
272
|
+
// partitioning, so the injector never sees a denied envelope here.
|
|
273
|
+
...(ownedCompletions.length > 0 ? { ownedCompletions } : {}),
|
|
200
274
|
};
|
|
201
275
|
return {
|
|
202
276
|
role: "custom",
|
|
@@ -224,6 +298,20 @@ function buildAsyncResultBatchMessage(entries: AsyncResultEntry[]): CustomMessag
|
|
|
224
298
|
* result for any such trailing unpaired tool call so a resumed session always
|
|
225
299
|
* starts from a valid, paired history. No-op for well-formed transcripts.
|
|
226
300
|
*/
|
|
301
|
+
/** Whether the cached SDK-only reconciliation store still matches the current
|
|
302
|
+
* session identity. session_switch/session_branch can move to a DIFFERENT
|
|
303
|
+
* transcript that retains the same copied session id, so the cache is keyed
|
|
304
|
+
* by BOTH the session id and the session-file path (including a null-to-file
|
|
305
|
+
* transition); a mismatch means the store must be recreated or the successor
|
|
306
|
+
* reads/writes the predecessor's reconciliation file (review thread P2). */
|
|
307
|
+
export function sdkOnlyStoreMatches(
|
|
308
|
+
cached: { sessionId: string; sessionFile: string | undefined } | undefined,
|
|
309
|
+
sessionId: string,
|
|
310
|
+
sessionFile: string | undefined,
|
|
311
|
+
): boolean {
|
|
312
|
+
return cached !== undefined && cached.sessionId === sessionId && cached.sessionFile === sessionFile;
|
|
313
|
+
}
|
|
314
|
+
|
|
227
315
|
export function reconcileTrailingToolCalls(messages: AgentMessage[]): AgentMessage[] {
|
|
228
316
|
let lastAssistantIdx = -1;
|
|
229
317
|
for (let i = messages.length - 1; i >= 0; i--) {
|
|
@@ -345,6 +433,10 @@ export interface CreateAgentSessionOptions {
|
|
|
345
433
|
credentialSessionId?: string;
|
|
346
434
|
/** Runtime credential selector for multi-account auth pools. */
|
|
347
435
|
credentialSelector?: { provider?: string; selector: AuthCredentialSelector; raw: string };
|
|
436
|
+
/** Soft runtime credential preference; quota/rate-limit failures may rotate away from it. */
|
|
437
|
+
preferredCredentialSelector?: { provider?: string; selector: AuthCredentialSelector; raw: string };
|
|
438
|
+
/** Durable global pin intent loaded from startup-auth config; session bootstrap resolves it without mutating shared storage. */
|
|
439
|
+
startupAuthConfig?: StartupAuthConfigSnapshot;
|
|
348
440
|
|
|
349
441
|
/** Custom tools to register (in addition to built-in tools). Accepts both CustomTool and ToolDefinition. */
|
|
350
442
|
customTools?: (CustomTool | ToolDefinition)[];
|
|
@@ -354,6 +446,8 @@ export interface CreateAgentSessionOptions {
|
|
|
354
446
|
extensions?: ExtensionFactory[];
|
|
355
447
|
/** Additional extension paths to load (merged with discovery). */
|
|
356
448
|
additionalExtensionPaths?: string[];
|
|
449
|
+
/** Explicit hook module paths to load in addition to native discovery. */
|
|
450
|
+
hookPaths?: string[];
|
|
357
451
|
/** Disable extension discovery (explicit paths still load). */
|
|
358
452
|
disableExtensionDiscovery?: boolean;
|
|
359
453
|
/**
|
|
@@ -386,6 +480,14 @@ export interface CreateAgentSessionOptions {
|
|
|
386
480
|
/** Load MCP tools for a top-level session only from this caller-owned absolute config file path.
|
|
387
481
|
* Mutually exclusive with mcpManager. */
|
|
388
482
|
mcpConfigPath?: string;
|
|
483
|
+
/**
|
|
484
|
+
* Whether conventional MCP autoload is enabled for a top-level standalone
|
|
485
|
+
* session (default: true). When false, native user `~/.gjc/agent/mcp.json`
|
|
486
|
+
* and project `.gjc/mcp.json` registrations are not discovered or connected
|
|
487
|
+
* at startup. Plugin-bundle MCPs and `mcpConfigPath` exact-file sessions
|
|
488
|
+
* are unaffected. CLI: `--no-mcp`.
|
|
489
|
+
*/
|
|
490
|
+
enableMcpAutoload?: boolean;
|
|
389
491
|
/**
|
|
390
492
|
* Defer connecting an exact MCP config until the interactive UI is ready.
|
|
391
493
|
* @internal CLI-only startup optimization; SDK callers retain synchronous loading by default.
|
|
@@ -439,6 +541,8 @@ export interface CreateAgentSessionOptions {
|
|
|
439
541
|
agentRegistry?: AgentRegistry;
|
|
440
542
|
/** Parent task ID prefix for nested artifact naming (e.g., "6-Extensions") */
|
|
441
543
|
parentTaskPrefix?: string;
|
|
544
|
+
/** Parent manager borrowed by a child session; never disposed by the child. */
|
|
545
|
+
inheritedAsyncJobManager?: AsyncJobManager;
|
|
442
546
|
/**
|
|
443
547
|
* W6b: the parent's scope-held MCP facade, handed to a canonical sub-session so
|
|
444
548
|
* it can inherit always-on MCP tools without owning the manager. Replaces the
|
|
@@ -554,9 +658,13 @@ function getDefaultAgentDir(): string {
|
|
|
554
658
|
* back into the broker through the {@link AuthStorageOptions.refreshOAuthCredential}
|
|
555
659
|
* override to re-mint access tokens when needed.
|
|
556
660
|
*/
|
|
557
|
-
export async function discoverAuthStorage(
|
|
558
|
-
|
|
559
|
-
|
|
661
|
+
export async function discoverAuthStorage(
|
|
662
|
+
agentDir: string = getDefaultAgentDir(),
|
|
663
|
+
startupAuth?: StartupAuthConfigSnapshot,
|
|
664
|
+
): Promise<AuthStorage> {
|
|
665
|
+
const resolvedStartupAuth = startupAuth ?? (await resolveStartupAuthConfig(agentDir));
|
|
666
|
+
const brokerConfig = resolvedStartupAuth.broker;
|
|
667
|
+
const credentialRankingMode = resolvedStartupAuth.credentialRankingMode;
|
|
560
668
|
if (brokerConfig) {
|
|
561
669
|
const client = new AuthBrokerClient({ url: brokerConfig.url, token: brokerConfig.token });
|
|
562
670
|
const initialResult = await client.fetchSnapshot();
|
|
@@ -600,18 +708,7 @@ export async function discoverAuthStorage(agentDir: string = getDefaultAgentDir(
|
|
|
600
708
|
return storage;
|
|
601
709
|
}
|
|
602
710
|
|
|
603
|
-
/**
|
|
604
|
-
* Opt-in multi-account credential ranking mode, read from the
|
|
605
|
-
* `GJC_CREDENTIAL_RANKING_MODE` env var. Unset/unknown → `undefined`, leaving
|
|
606
|
-
* {@link AuthStorage}'s default (`balanced`) untouched. `earliest-reset`
|
|
607
|
-
* switches to earliest-expiry-first selection so soon-to-reset tumbling-window
|
|
608
|
-
* quota is drained before it is lost.
|
|
609
|
-
*/
|
|
610
|
-
function resolveCredentialRankingMode(): "balanced" | "earliest-reset" | undefined {
|
|
611
|
-
const raw = process.env.GJC_CREDENTIAL_RANKING_MODE?.trim();
|
|
612
|
-
if (raw === "balanced" || raw === "earliest-reset") return raw;
|
|
613
|
-
return undefined;
|
|
614
|
-
}
|
|
711
|
+
/** Ranking is resolved as part of the typed startup-auth snapshot. */
|
|
615
712
|
|
|
616
713
|
/**
|
|
617
714
|
* Discover extensions from cwd.
|
|
@@ -847,12 +944,26 @@ function createCustomToolsExtension(tools: CustomTool[]): ExtensionFactory {
|
|
|
847
944
|
|
|
848
945
|
export function createPluginHooksExtension(hooks: ConstrainedPluginHook[]): ExtensionFactory {
|
|
849
946
|
return api => {
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
947
|
+
const normalizedHooks = hooks.map(hook => ({
|
|
948
|
+
hook,
|
|
949
|
+
result: normalizePluginHook({
|
|
950
|
+
declaredEvent: hook.event,
|
|
951
|
+
target: hook.target,
|
|
952
|
+
phase: hook.phase,
|
|
953
|
+
plugin: hook.plugin,
|
|
954
|
+
source: `plugin:${hook.plugin}`,
|
|
955
|
+
}),
|
|
956
|
+
}));
|
|
957
|
+
const diagnostics = normalizedHooks.flatMap(entry => entry.result.diagnostics);
|
|
958
|
+
if (normalizedHooks.some(entry => !entry.result.hook)) {
|
|
959
|
+
throw new Error(diagnostics.map(diagnostic => `${diagnostic.code}: ${diagnostic.message}`).join("; "));
|
|
960
|
+
}
|
|
961
|
+
|
|
962
|
+
for (const { hook, result } of normalizedHooks) {
|
|
963
|
+
const normalized = result.hook;
|
|
964
|
+
if (!normalized) throw new Error("Hook normalization invariant violated");
|
|
965
|
+
const registrationEvent = normalized.runtimeEvent;
|
|
966
|
+
const target = normalized.toolName === "*" ? undefined : normalized.toolName;
|
|
856
967
|
const handler = target
|
|
857
968
|
? (event: { toolName?: string; tool?: { name?: string }; name?: string }, ...rest: unknown[]) => {
|
|
858
969
|
const toolName = event?.toolName ?? event?.tool?.name ?? event?.name;
|
|
@@ -949,10 +1060,11 @@ function buildMCPPromptCommands(manager: MCPManager): LoadedCustomCommand[] {
|
|
|
949
1060
|
|
|
950
1061
|
function withEmbeddedDefaultGjcSkills(skills: Skill[]): Skill[] {
|
|
951
1062
|
const byName = new Map(skills.map(skill => [skill.name, skill]));
|
|
1063
|
+
// The four public GJC workflow skills are a product invariant: even if a
|
|
1064
|
+
// caller-supplied or filesystem skill shares a name, the bundled definition
|
|
1065
|
+
// wins so workflow routing can never be silently hijacked.
|
|
952
1066
|
for (const defaultSkill of getEmbeddedDefaultGjcSkills()) {
|
|
953
|
-
|
|
954
|
-
byName.set(defaultSkill.name, defaultSkill);
|
|
955
|
-
}
|
|
1067
|
+
byName.set(defaultSkill.name, defaultSkill);
|
|
956
1068
|
}
|
|
957
1069
|
return [...byName.values()];
|
|
958
1070
|
}
|
|
@@ -983,6 +1095,7 @@ const MAX_EXACT_MCP_TOOL_COLLISION_NAMES = 10;
|
|
|
983
1095
|
const DEFERRED_MCP_CONFIG_STARTUP_ERROR = "MCP tools could not be loaded.";
|
|
984
1096
|
const MAX_EXACT_MCP_TOOL_NAME_LENGTH = 100;
|
|
985
1097
|
const pluginMcpManagerServers = new WeakMap<MCPManager, ReadonlySet<string>>();
|
|
1098
|
+
const conventionalMcpManagerServers = new WeakMap<MCPManager, ReadonlySet<string>>();
|
|
986
1099
|
|
|
987
1100
|
class ExactMcpToolNameCollisionError extends Error {
|
|
988
1101
|
constructor(toolNames: Iterable<string>) {
|
|
@@ -1075,6 +1188,11 @@ function safeErrorForLog(value: unknown): unknown {
|
|
|
1075
1188
|
return safeErrorDescription(value);
|
|
1076
1189
|
}
|
|
1077
1190
|
|
|
1191
|
+
function sanitizeProviderForLog(provider: string): string {
|
|
1192
|
+
const sanitized = provider.replace(/[^\x20-\x7e]/g, "?").slice(0, 128);
|
|
1193
|
+
return sanitized.length > 0 ? sanitized : "<empty>";
|
|
1194
|
+
}
|
|
1195
|
+
|
|
1078
1196
|
function attachMcpCleanupDiagnostic(primary: unknown, cleanup: unknown): unknown {
|
|
1079
1197
|
const diagnostic = { code: "MCP_MANAGER_CLEANUP_FAILED" as const, cause: cleanup };
|
|
1080
1198
|
if (primary && (typeof primary === "object" || typeof primary === "function")) {
|
|
@@ -1158,14 +1276,23 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
1158
1276
|
const explicitMcpConfigPath = !isCanonicalSubSession && !options.mcpManager ? options.mcpConfigPath : undefined;
|
|
1159
1277
|
const agentDir = options.agentDir ?? getDefaultAgentDir();
|
|
1160
1278
|
const eventBus = options.eventBus ?? new EventBus();
|
|
1279
|
+
const hasInjectedAuth = options.authStorage !== undefined || options.modelRegistry !== undefined;
|
|
1161
1280
|
|
|
1162
1281
|
// Pin authStorage to modelRegistry.authStorage: ModelRegistry.getApiKey() routes refresh
|
|
1163
1282
|
// failures through that instance, so any divergent storage handed to the bridge / mcpManager
|
|
1164
1283
|
// / session would silently miss credential_disabled events.
|
|
1284
|
+
// Injected auth is already the caller's authority; do not discover global
|
|
1285
|
+
// startup auth or apply persistent pins while constructing this session.
|
|
1286
|
+
const startupAuthConfig = hasInjectedAuth
|
|
1287
|
+
? undefined
|
|
1288
|
+
: (options.startupAuthConfig ?? (await resolveStartupAuthConfig(agentDir)));
|
|
1165
1289
|
const ownsAuthStorage = options.modelRegistry === undefined && options.authStorage === undefined;
|
|
1166
1290
|
const modelRegistry =
|
|
1167
1291
|
options.modelRegistry ??
|
|
1168
|
-
new ModelRegistry(
|
|
1292
|
+
new ModelRegistry(
|
|
1293
|
+
options.authStorage ??
|
|
1294
|
+
(await logger.time("discoverModels", () => discoverAuthStorage(agentDir, startupAuthConfig))),
|
|
1295
|
+
);
|
|
1169
1296
|
const authStorage = modelRegistry.authStorage;
|
|
1170
1297
|
if (options.authStorage && options.authStorage !== authStorage) {
|
|
1171
1298
|
throw new Error(
|
|
@@ -1177,6 +1304,9 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
1177
1304
|
let session!: AgentSession;
|
|
1178
1305
|
let hasSession = false;
|
|
1179
1306
|
let hasRegistered = false;
|
|
1307
|
+
let asyncJobManager: AsyncJobManager | undefined;
|
|
1308
|
+
let asyncJobManagerAdmitted = false;
|
|
1309
|
+
let priorAsyncJobManager: AsyncJobManager | undefined;
|
|
1180
1310
|
let cleanupOwnedMcpManager: (() => Promise<void>) | undefined;
|
|
1181
1311
|
const agentRegistry = options.agentRegistry ?? AgentRegistry.global();
|
|
1182
1312
|
const resolvedAgentId = options.agentId ?? options.parentTaskPrefix ?? MAIN_AGENT_ID;
|
|
@@ -1196,7 +1326,13 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
1196
1326
|
};
|
|
1197
1327
|
const enableLsp = options.enableLsp ?? true;
|
|
1198
1328
|
let authStorageClosed = false;
|
|
1329
|
+
let credentialScopeId: string | undefined;
|
|
1330
|
+
let credentialScopeLeased = false;
|
|
1199
1331
|
const closeOwnedAuthStorage = (): void => {
|
|
1332
|
+
if (!hasSession && credentialScopeLeased && credentialScopeId) {
|
|
1333
|
+
authStorage.releaseCredentialScope(credentialScopeId);
|
|
1334
|
+
credentialScopeLeased = false;
|
|
1335
|
+
}
|
|
1200
1336
|
if (!ownsAuthStorage || authStorageClosed) return;
|
|
1201
1337
|
authStorageClosed = true;
|
|
1202
1338
|
authStorage.close();
|
|
@@ -1223,23 +1359,14 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
1223
1359
|
startupCredentialDisabledEvents.push(event);
|
|
1224
1360
|
}
|
|
1225
1361
|
});
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
if (!options.credentialSelector || runtimeCredentialSelectorInstalled) return;
|
|
1229
|
-
authStorage.setRuntimeCredentialSelector(provider, options.credentialSelector.selector);
|
|
1230
|
-
runtimeCredentialSelectorInstalled = true;
|
|
1362
|
+
const applyCredentialSelector = (scopeId: string, provider: string, selector: AuthCredentialSelector): void => {
|
|
1363
|
+
authStorage.setSessionCredentialSelector(scopeId, provider, selector);
|
|
1231
1364
|
};
|
|
1232
|
-
const earlyCredentialSelectorProvider = options.credentialSelector?.provider ?? options.model?.provider;
|
|
1233
|
-
if (earlyCredentialSelectorProvider) {
|
|
1234
|
-
installRuntimeCredentialSelector(earlyCredentialSelectorProvider);
|
|
1235
|
-
}
|
|
1236
1365
|
const settings = options.settings ?? (await logger.time("settings", Settings.init, { cwd, agentDir }));
|
|
1237
1366
|
const runtimeServices = createOptionalRuntimeServices(settings, options.runtimeServices, { cwd });
|
|
1238
1367
|
modelRegistry.applyConfiguredModelBindings(settings);
|
|
1239
1368
|
logger.time("initializeWithSettings", initializeWithSettings, settings);
|
|
1240
|
-
|
|
1241
|
-
!options.credentialSelector || runtimeCredentialSelectorInstalled || options.modelRegistry !== undefined;
|
|
1242
|
-
if (!options.modelRegistry && canRefreshModelsBeforeCredentialSelector) {
|
|
1369
|
+
if (!options.modelRegistry) {
|
|
1243
1370
|
modelRegistry.refreshInBackground();
|
|
1244
1371
|
}
|
|
1245
1372
|
// Resolve the workspace tree through its runtime service. The compatibility
|
|
@@ -1304,7 +1431,46 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
1304
1431
|
// session_id upstream, where session-owning transports reject the extra
|
|
1305
1432
|
// downstreams (owner_busy) and degrade those turns to uncached HTTP.
|
|
1306
1433
|
const providerSessionId = options.providerSessionId ?? logicalSessionId;
|
|
1434
|
+
// AsyncJobManager ownership is distinct from both the persisted logical
|
|
1435
|
+
// header and provider cache affinity. Managed child transcripts may share a
|
|
1436
|
+
// logical header, while unrelated top-level sessions may intentionally share
|
|
1437
|
+
// providerSessionId. Bind explicit provider scopes to the independently
|
|
1438
|
+
// persisted transcript path so ownership is collision-free and stable across
|
|
1439
|
+
// detached resume; ordinary sessions keep the transition-aware logical id.
|
|
1440
|
+
const sessionFile = sessionManager.getSessionFile();
|
|
1441
|
+
const asyncJobEndpointId = deriveAsyncJobEndpointId(
|
|
1442
|
+
options.providerSessionId === undefined ? undefined : providerSessionId,
|
|
1443
|
+
logicalSessionId,
|
|
1444
|
+
sessionFile,
|
|
1445
|
+
);
|
|
1307
1446
|
const credentialSessionId = options.credentialSessionId ?? providerSessionId;
|
|
1447
|
+
credentialScopeId = credentialSessionId;
|
|
1448
|
+
|
|
1449
|
+
const scopeAlreadyLeased = authStorage.hasCredentialScopeLease(credentialSessionId);
|
|
1450
|
+
authStorage.acquireCredentialScope(credentialSessionId);
|
|
1451
|
+
credentialScopeLeased = true;
|
|
1452
|
+
const configuredPins = hasInjectedAuth ? {} : (startupAuthConfig?.credentialPins ?? {});
|
|
1453
|
+
const persistedPinStoreMatches =
|
|
1454
|
+
startupAuthConfig?.credentialPinStoreIdentity === startupAuthConfig?.credentialStoreIdentity;
|
|
1455
|
+
// AuthStorage has no scope-level "pinned but unavailable" marker. Keep the
|
|
1456
|
+
// discarded durable pin identity local to this credential scope so startup
|
|
1457
|
+
// config cannot immediately replace it with a different account; later
|
|
1458
|
+
// request-time resolution for this provider remains ordinary AUTO ranking.
|
|
1459
|
+
const staleDurableCredentialPins = new Set<string>();
|
|
1460
|
+
if (options.credentialSelector) {
|
|
1461
|
+
const provider = options.credentialSelector.provider ?? options.model?.provider;
|
|
1462
|
+
if (provider) applyCredentialSelector(credentialSessionId, provider, options.credentialSelector.selector);
|
|
1463
|
+
}
|
|
1464
|
+
const preferredCredentialProvider = options.preferredCredentialSelector
|
|
1465
|
+
? (options.preferredCredentialSelector.provider ??
|
|
1466
|
+
authStorage.resolveRuntimePreferredCredentialSelectorProvider(options.preferredCredentialSelector.selector))
|
|
1467
|
+
: undefined;
|
|
1468
|
+
if (options.preferredCredentialSelector && preferredCredentialProvider) {
|
|
1469
|
+
authStorage.setRuntimePreferredCredentialSelector(
|
|
1470
|
+
preferredCredentialProvider,
|
|
1471
|
+
options.preferredCredentialSelector.selector,
|
|
1472
|
+
);
|
|
1473
|
+
}
|
|
1308
1474
|
const modelApiKeyAvailability = new Map<string, boolean>();
|
|
1309
1475
|
const getModelAvailabilityKey = (candidate: Model): string =>
|
|
1310
1476
|
`${candidate.provider}\u0000${candidate.baseUrl ?? ""}`;
|
|
@@ -1315,17 +1481,22 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
1315
1481
|
return cached;
|
|
1316
1482
|
}
|
|
1317
1483
|
|
|
1318
|
-
const credentialSelector =
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
options.credentialSelector.provider === candidate.provider
|
|
1322
|
-
? options.credentialSelector.selector
|
|
1323
|
-
: undefined
|
|
1324
|
-
: undefined;
|
|
1484
|
+
const credentialSelector = options.credentialSelector?.provider
|
|
1485
|
+
? undefined
|
|
1486
|
+
: options.credentialSelector?.selector;
|
|
1325
1487
|
if (options.credentialSelector?.provider && options.credentialSelector.provider !== candidate.provider) {
|
|
1326
1488
|
modelApiKeyAvailability.set(availabilityKey, false);
|
|
1327
1489
|
return false;
|
|
1328
1490
|
}
|
|
1491
|
+
// A preferred (soft) credential is always installed synchronously above, so
|
|
1492
|
+
// availability just needs to exclude candidates from a different provider —
|
|
1493
|
+
// `--prefer-credential` names one provider's account, and letting a
|
|
1494
|
+
// different-provider default model win here would silently strand the
|
|
1495
|
+
// preference.
|
|
1496
|
+
if (preferredCredentialProvider && preferredCredentialProvider !== candidate.provider) {
|
|
1497
|
+
modelApiKeyAvailability.set(availabilityKey, false);
|
|
1498
|
+
return false;
|
|
1499
|
+
}
|
|
1329
1500
|
const key = await modelRegistry
|
|
1330
1501
|
.getApiKey(candidate, credentialSessionId, { credentialSelector })
|
|
1331
1502
|
.catch(error => {
|
|
@@ -1373,6 +1544,83 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
1373
1544
|
const hasThinkingEntry = existingBranch.some(entry => entry.type === "thinking_level_change");
|
|
1374
1545
|
const hasServiceTierEntry = existingBranch.some(entry => entry.type === "service_tier_change");
|
|
1375
1546
|
|
|
1547
|
+
for (const entry of existingBranch) {
|
|
1548
|
+
if (entry.type !== "custom" || entry.customType !== "auth-credential-pin") continue;
|
|
1549
|
+
const data = entry.data;
|
|
1550
|
+
if (!data || typeof data !== "object") continue;
|
|
1551
|
+
const record = data as {
|
|
1552
|
+
v?: unknown;
|
|
1553
|
+
scopeId?: unknown;
|
|
1554
|
+
provider?: unknown;
|
|
1555
|
+
pin?: unknown;
|
|
1556
|
+
credentialStoreIdentity?: unknown;
|
|
1557
|
+
};
|
|
1558
|
+
if (record.v !== 1 || record.scopeId !== credentialSessionId || typeof record.provider !== "string") continue;
|
|
1559
|
+
const pin = record.pin as { auto?: unknown; kind?: unknown; value?: unknown } | undefined;
|
|
1560
|
+
// Durable replay must never abort session startup: a pinned account may
|
|
1561
|
+
// have been removed, disabled, or deduplicated since the pin was written.
|
|
1562
|
+
// Pin identity is preserved by leaving the provider unpinned on failure —
|
|
1563
|
+
// never silently retarget to another account; the user re-pins or picks AUTO.
|
|
1564
|
+
try {
|
|
1565
|
+
if (pin?.auto === true) {
|
|
1566
|
+
authStorage.setSessionCredentialAuto(record.provider, credentialSessionId);
|
|
1567
|
+
staleDurableCredentialPins.delete(resolveOAuthStorageProvider(record.provider));
|
|
1568
|
+
} else if (
|
|
1569
|
+
pin &&
|
|
1570
|
+
(pin.kind === "id" || pin.kind === "email" || pin.kind === "account" || pin.kind === "project") &&
|
|
1571
|
+
typeof pin.value === "string"
|
|
1572
|
+
) {
|
|
1573
|
+
if (
|
|
1574
|
+
pin.kind === "id" &&
|
|
1575
|
+
(!startupAuthConfig?.credentialStoreIdentity ||
|
|
1576
|
+
record.credentialStoreIdentity !== startupAuthConfig.credentialStoreIdentity)
|
|
1577
|
+
) {
|
|
1578
|
+
throw new Error("Durable numeric credential pin authority changed");
|
|
1579
|
+
}
|
|
1580
|
+
authStorage.setSessionCredentialSelector(credentialSessionId, record.provider, {
|
|
1581
|
+
kind: pin.kind,
|
|
1582
|
+
value: pin.value,
|
|
1583
|
+
});
|
|
1584
|
+
staleDurableCredentialPins.delete(resolveOAuthStorageProvider(record.provider));
|
|
1585
|
+
}
|
|
1586
|
+
} catch {
|
|
1587
|
+
// A newer stale pin must not leave an earlier replayed selector active.
|
|
1588
|
+
authStorage.clearSessionCredentialSelector(record.provider, credentialSessionId);
|
|
1589
|
+
staleDurableCredentialPins.add(resolveOAuthStorageProvider(record.provider));
|
|
1590
|
+
}
|
|
1591
|
+
}
|
|
1592
|
+
if (staleDurableCredentialPins.size > 0) {
|
|
1593
|
+
logger.warn("Stale durable credential pin discarded; re-pin or select AUTO explicitly", {
|
|
1594
|
+
providers: [...staleDurableCredentialPins].map(sanitizeProviderForLog),
|
|
1595
|
+
});
|
|
1596
|
+
}
|
|
1597
|
+
if (!scopeAlreadyLeased) {
|
|
1598
|
+
for (const [provider, rawSelector] of Object.entries(configuredPins)) {
|
|
1599
|
+
if (staleDurableCredentialPins.has(resolveOAuthStorageProvider(provider))) continue;
|
|
1600
|
+
if (
|
|
1601
|
+
authStorage.hasSessionCredentialSelector(provider, credentialSessionId) ||
|
|
1602
|
+
authStorage.hasSessionCredentialAuto(provider, credentialSessionId)
|
|
1603
|
+
)
|
|
1604
|
+
continue;
|
|
1605
|
+
const selector = parsePersistedCredentialSelector(rawSelector);
|
|
1606
|
+
if (!selector) continue;
|
|
1607
|
+
if (selector.kind === "id" && !persistedPinStoreMatches) {
|
|
1608
|
+
logger.warn("Numeric persistent credential pin discarded after credential-store authority changed", {
|
|
1609
|
+
provider: sanitizeProviderForLog(provider),
|
|
1610
|
+
});
|
|
1611
|
+
continue;
|
|
1612
|
+
}
|
|
1613
|
+
applyCredentialSelector(credentialSessionId, provider, selector);
|
|
1614
|
+
}
|
|
1615
|
+
}
|
|
1616
|
+
if (options.credentialSelector?.provider) {
|
|
1617
|
+
applyCredentialSelector(
|
|
1618
|
+
credentialSessionId,
|
|
1619
|
+
options.credentialSelector.provider,
|
|
1620
|
+
options.credentialSelector.selector,
|
|
1621
|
+
);
|
|
1622
|
+
}
|
|
1623
|
+
|
|
1376
1624
|
const hasExplicitModel = options.model !== undefined || options.modelPattern !== undefined;
|
|
1377
1625
|
const modelMatchPreferences = {
|
|
1378
1626
|
usageOrder: settings.getStorage()?.getModelUsageOrder(),
|
|
@@ -1450,13 +1698,25 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
1450
1698
|
},
|
|
1451
1699
|
);
|
|
1452
1700
|
model = restoredDefaultResolution.model;
|
|
1701
|
+
// A restored session model from a different provider than an active
|
|
1702
|
+
// `--prefer-credential` preference is discarded rather than kept: the
|
|
1703
|
+
// preference names one provider's account, and silently resuming on
|
|
1704
|
+
// another provider would strand it without any error.
|
|
1705
|
+
if (model && preferredCredentialProvider && model.provider !== preferredCredentialProvider) {
|
|
1706
|
+
model = undefined;
|
|
1707
|
+
}
|
|
1453
1708
|
if (!model) modelFallbackMessage = `Could not restore model ${defaultModelEntries.join(" -> ")}`;
|
|
1454
1709
|
});
|
|
1455
1710
|
}
|
|
1456
1711
|
|
|
1457
1712
|
// If still no model, try settings default.
|
|
1458
1713
|
// Skip settings fallback when an explicit model was requested.
|
|
1459
|
-
if (
|
|
1714
|
+
if (
|
|
1715
|
+
!hasExplicitModel &&
|
|
1716
|
+
!model &&
|
|
1717
|
+
defaultRoleSpec.model &&
|
|
1718
|
+
(!preferredCredentialProvider || defaultRoleSpec.model.provider === preferredCredentialProvider)
|
|
1719
|
+
) {
|
|
1460
1720
|
const settingsDefaultModel = defaultRoleSpec.model;
|
|
1461
1721
|
logger.time("resolveSettingsDefaultModel", () => {
|
|
1462
1722
|
// defaultRoleSpec.model already comes from modelRegistry.getAvailable(),
|
|
@@ -1536,8 +1796,9 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
1536
1796
|
skillWarnings = skillsResult.warnings;
|
|
1537
1797
|
} else {
|
|
1538
1798
|
// GJC's four public workflow skills are bundled into the binary so the
|
|
1539
|
-
// default workflow surface survives accidental .gjc deletion.
|
|
1540
|
-
//
|
|
1799
|
+
// default workflow surface survives accidental .gjc deletion. Filesystem
|
|
1800
|
+
// skill discovery is enabled by default (`skills.enabled`), so this
|
|
1801
|
+
// branch only runs when the user explicitly disabled it.
|
|
1541
1802
|
skills = getEmbeddedDefaultGjcSkills();
|
|
1542
1803
|
skillWarnings = [];
|
|
1543
1804
|
}
|
|
@@ -1612,12 +1873,17 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
1612
1873
|
const asyncMaxJobs = Math.min(100, Math.max(1, settings.get("async.maxJobs") ?? 100));
|
|
1613
1874
|
const ASYNC_INLINE_RESULT_MAX_CHARS = 12_000;
|
|
1614
1875
|
const ASYNC_PREVIEW_MAX_CHARS = 4_000;
|
|
1615
|
-
const formatAsyncResultForFollowUp = async (result: string): Promise<string> => {
|
|
1876
|
+
const formatAsyncResultForFollowUp = async (result: string, allowArtifact = true): Promise<string> => {
|
|
1616
1877
|
if (result.length <= ASYNC_INLINE_RESULT_MAX_CHARS) {
|
|
1617
1878
|
return result;
|
|
1618
1879
|
}
|
|
1619
1880
|
|
|
1620
1881
|
const preview = `${result.slice(0, ASYNC_PREVIEW_MAX_CHARS)}\n\n[Output truncated. Showing first ${ASYNC_PREVIEW_MAX_CHARS.toLocaleString()} characters.]`;
|
|
1882
|
+
// A delivery already denied by a scope:"owned" gate never reaches the
|
|
1883
|
+
// model: allocating an artifact for it would leave the stopped job's
|
|
1884
|
+
// output in an unreferenced artifact after the flush drops it (review
|
|
1885
|
+
// thread P2). Only the inline preview is produced.
|
|
1886
|
+
if (!allowArtifact) return preview;
|
|
1621
1887
|
try {
|
|
1622
1888
|
const { path: artifactPath, id: artifactId } = await sessionManager.allocateArtifactPath("async");
|
|
1623
1889
|
if (artifactPath && artifactId) {
|
|
@@ -1636,14 +1902,49 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
1636
1902
|
// parent's manager via `AsyncJobManager.instance()` (set below), so creating
|
|
1637
1903
|
// a second instance here just to leave it orphaned wastes a constructor and
|
|
1638
1904
|
// risks accidental disposal of the parent's manager on subagent teardown.
|
|
1639
|
-
|
|
1905
|
+
asyncJobManager =
|
|
1640
1906
|
backgroundJobsEnabled && !options.parentTaskPrefix
|
|
1641
1907
|
? new AsyncJobManager({
|
|
1642
1908
|
maxRunningJobs: asyncMaxJobs,
|
|
1643
1909
|
onJobComplete: async (jobId, result, job) => {
|
|
1644
1910
|
if (!session) return;
|
|
1645
|
-
|
|
1911
|
+
// Mandated boundary comment (corrected turn semantics):
|
|
1912
|
+
// turn-scope abort blocks only deliveries whose origin is a
|
|
1913
|
+
// continuation of the aborted turn. Owned-completion deliveries
|
|
1914
|
+
// from work deliberately left running are intentionally allowed
|
|
1915
|
+
// to resume the agent through the normal followUp/prompt path
|
|
1916
|
+
// and receive a fresh turn attempt. Recover the immutable origin
|
|
1917
|
+
// BEFORE formatting or artifact allocation; missing metadata
|
|
1918
|
+
// fails closed to an ordinary delivery.
|
|
1919
|
+
// Preserve ownership on the queued entry REGARDLESS of whether a
|
|
1920
|
+
// terminal scope exists yet: the registration is determined at
|
|
1921
|
+
// job-registration time, and the scope is determined at abort
|
|
1922
|
+
// time (or later, at flush). A completion finished before the
|
|
1923
|
+
// abort must not become an ordinary entry that owned cleanup
|
|
1924
|
+
// cannot identify/purge (review thread P1).
|
|
1925
|
+
// Owned tuples are keyed by the LOGICAL endpoint, not a
|
|
1926
|
+
// provider-facing session id. providerSessionId may differ
|
|
1927
|
+
// from sessionManager.getSessionId(), so session.sessionId
|
|
1928
|
+
// would miss the tuple and turn a left-running completion
|
|
1929
|
+
// into an ordinary follow-up (review thread P1).
|
|
1930
|
+
const endpointId = AsyncJobManager.endpointIdOf(asyncJobManager) ?? asyncJobEndpointId;
|
|
1931
|
+
const registration = job ? lookupOwnedRegistration(jobId, job.generation, endpointId) : undefined;
|
|
1932
|
+
const ownedCompletion = registration
|
|
1933
|
+
? {
|
|
1934
|
+
lineageIdHash: registration.lineageIdHash,
|
|
1935
|
+
promptAttemptEpoch: registration.promptAttemptEpoch,
|
|
1936
|
+
registration,
|
|
1937
|
+
}
|
|
1938
|
+
: undefined;
|
|
1939
|
+
// Check suppression and classify the captured envelope BEFORE
|
|
1940
|
+
// formatting/artifact allocation: a delivery already suppressed
|
|
1941
|
+
// by the manager (owned-stop cancel/acknowledge) or already
|
|
1942
|
+
// denied by a scope:"owned" gate must not allocate an
|
|
1943
|
+
// unreferenced artifact (review thread P2).
|
|
1646
1944
|
if (asyncJobManager!.isDeliverySuppressed(jobId, job?.generation)) return;
|
|
1945
|
+
const deniedOwnedDelivery =
|
|
1946
|
+
ownedCompletion !== undefined && !isOwnedCompletionEnvelopeAllowed(ownedCompletion);
|
|
1947
|
+
const formattedResult = await formatAsyncResultForFollowUp(result, !deniedOwnedDelivery);
|
|
1647
1948
|
|
|
1648
1949
|
const durationMs = job ? jobElapsedMs(job) : undefined;
|
|
1649
1950
|
session.yieldQueue.enqueue<AsyncResultEntry>("async-result", {
|
|
@@ -1652,10 +1953,19 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
1652
1953
|
result: formattedResult,
|
|
1653
1954
|
job,
|
|
1654
1955
|
durationMs,
|
|
1956
|
+
...(ownedCompletion
|
|
1957
|
+
? {
|
|
1958
|
+
ownedCompletion: {
|
|
1959
|
+
lineageIdHash: ownedCompletion.lineageIdHash,
|
|
1960
|
+
promptAttemptEpoch: ownedCompletion.promptAttemptEpoch,
|
|
1961
|
+
registration: ownedCompletion.registration,
|
|
1962
|
+
},
|
|
1963
|
+
}
|
|
1964
|
+
: {}),
|
|
1655
1965
|
});
|
|
1656
1966
|
},
|
|
1657
1967
|
})
|
|
1658
|
-
:
|
|
1968
|
+
: options.inheritedAsyncJobManager;
|
|
1659
1969
|
|
|
1660
1970
|
let promptMetadataModel: Model | undefined;
|
|
1661
1971
|
const getActiveModelString = (): string | undefined => {
|
|
@@ -1691,7 +2001,14 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
1691
2001
|
assertEvalExecutionAllowed: () => session?.assertEvalExecutionAllowed(),
|
|
1692
2002
|
trackEvalExecution: (execution, abortController) =>
|
|
1693
2003
|
session ? session.trackEvalExecution(execution, abortController) : execution,
|
|
1694
|
-
|
|
2004
|
+
getAsyncJobManager: () => asyncJobManager,
|
|
2005
|
+
// Subagents inherit the parent's manager; its registered endpoint is
|
|
2006
|
+
// authoritative for owned-registration keying and endpoint-first
|
|
2007
|
+
// manager lookup (the child's own id is never registered), so tools
|
|
2008
|
+
// pass the same endpoint the manager's completion callback resolves
|
|
2009
|
+
// (review thread P1). For a top-level session this equals the
|
|
2010
|
+
// session id.
|
|
2011
|
+
getSessionId: () => AsyncJobManager.endpointIdOf(asyncJobManager) ?? asyncJobEndpointId,
|
|
1695
2012
|
getCredentialSessionId: () => session?.credentialSessionId ?? credentialSessionId,
|
|
1696
2013
|
getMcpManager: () => mcpManager ?? options.inheritedMcpManager,
|
|
1697
2014
|
isManagedSessionDestination: () => sessionManager.isManagedDestination(),
|
|
@@ -1799,7 +2116,26 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
1799
2116
|
if (!options.parentTaskPrefix) {
|
|
1800
2117
|
setActiveSkills(skills);
|
|
1801
2118
|
setActiveRules([...rulebookRules, ...alwaysApplyRules]);
|
|
1802
|
-
if (asyncJobManager)
|
|
2119
|
+
if (asyncJobManager) {
|
|
2120
|
+
// Register under the session endpoint so concurrent sessions'
|
|
2121
|
+
// owned work settles in the correct manager (review thread P1).
|
|
2122
|
+
// `session` is not yet constructed here; the provider identity is
|
|
2123
|
+
// the endpoint identity. ADMIT THE ENDPOINT FIRST: a second
|
|
2124
|
+
// top-level session constructed or resumed under an endpoint id
|
|
2125
|
+
// already held by another LIVE manager must fail construction
|
|
2126
|
+
// BEFORE the global instance is replaced — otherwise the
|
|
2127
|
+
// rejected construction leaves this orphan manager as the
|
|
2128
|
+
// process-global instance, redirecting global-manager
|
|
2129
|
+
// consumers away from the live session (review thread P1).
|
|
2130
|
+
if (!AsyncJobManager.registerForEndpoint(asyncJobEndpointId, asyncJobManager)) {
|
|
2131
|
+
throw new Error(
|
|
2132
|
+
`Cannot construct session "${asyncJobEndpointId}": the endpoint id is already held by another live async job manager`,
|
|
2133
|
+
);
|
|
2134
|
+
}
|
|
2135
|
+
asyncJobManagerAdmitted = true;
|
|
2136
|
+
priorAsyncJobManager = AsyncJobManager.instance();
|
|
2137
|
+
AsyncJobManager.setInstance(asyncJobManager);
|
|
2138
|
+
}
|
|
1803
2139
|
}
|
|
1804
2140
|
await initializeLocalRoot(localProtocolOptions);
|
|
1805
2141
|
if (options.localProtocolOptions) {
|
|
@@ -1823,16 +2159,18 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
1823
2159
|
// Create built-in tools (already wrapped with meta notice formatting)
|
|
1824
2160
|
const builtinTools = await logger.time("createAllTools", createTools, toolSession, options.toolNames);
|
|
1825
2161
|
|
|
1826
|
-
//
|
|
1827
|
-
//
|
|
1828
|
-
//
|
|
1829
|
-
//
|
|
1830
|
-
// below after `customTools` is populated.
|
|
2162
|
+
// A top-level session loads MCP tools from three bounded surfaces: a
|
|
2163
|
+
// caller-supplied exact config (`--mcp-config`), conventional native
|
|
2164
|
+
// user/project registrations (`~/.gjc/agent/mcp.json` and `.gjc/mcp.json`
|
|
2165
|
+
// written by `gjc mcp add`; disabled by `--no-mcp`), and plugin-bundle
|
|
2166
|
+
// MCP servers (created below after `customTools` is populated). Existing
|
|
2167
|
+
// caller-supplied managers remain available for legacy in-process callers.
|
|
1831
2168
|
let mcpManager: MCPManager | undefined = options.mcpManager;
|
|
1832
2169
|
let ownsMcpManager = false;
|
|
1833
2170
|
const customTools: CustomTool[] = [];
|
|
1834
2171
|
const exactMcpToolNames: string[] = [];
|
|
1835
2172
|
const pluginMcpToolNames: string[] = [];
|
|
2173
|
+
const conventionalMcpToolNames: string[] = [];
|
|
1836
2174
|
let deferredExactMcpConfig: { manager: MCPManager; configPath: string } | undefined;
|
|
1837
2175
|
|
|
1838
2176
|
// Add image tools when an image role model is configured.
|
|
@@ -1953,28 +2291,77 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
1953
2291
|
}
|
|
1954
2292
|
}
|
|
1955
2293
|
} else if (!mcpManager && !isCanonicalSubSession) {
|
|
1956
|
-
//
|
|
1957
|
-
//
|
|
2294
|
+
// Conventional MCP autoload: top-level standalone sessions consume
|
|
2295
|
+
// enabled registrations from GJC's own native configs — project
|
|
2296
|
+
// `.gjc/mcp.json` and user `~/.gjc/agent/mcp.json` (written by
|
|
2297
|
+
// `gjc mcp add`). Native project scope wins over native user scope on
|
|
2298
|
+
// name collisions (capability priority), and plugin-bundle MCPs below
|
|
2299
|
+
// override conventional entries with the same name. Claude Code/Codex
|
|
2300
|
+
// MCP files are explicit import sources into `.gjc` (#4291), never
|
|
2301
|
+
// implicit runtime authorities here. `--no-mcp` opts a session out;
|
|
2302
|
+
// `--mcp-config` exact-file sessions never reach here. The owned manager's
|
|
2303
|
+
// tools are surfaced as always-on custom tools (like plugin MCPs), so an
|
|
2304
|
+
// ordinary session exposes them without needing MCP discovery mode.
|
|
2305
|
+
let conventionalConfigs: Record<string, MCPServerConfig> = {};
|
|
2306
|
+
let conventionalSources: Record<string, SourceMeta> = {};
|
|
2307
|
+
if (options.enableMcpAutoload !== false) {
|
|
2308
|
+
try {
|
|
2309
|
+
const loaded = await loadAllMCPConfigs(cwd, {
|
|
2310
|
+
// Project-scope native config loads by default; only an
|
|
2311
|
+
// explicitly configured `mcp.enableProjectConfig: false`
|
|
2312
|
+
// disables it (the legacy schema default stays false for
|
|
2313
|
+
// foreign-format discovery consumers of the capability system).
|
|
2314
|
+
enableProjectConfig: settings.has("mcp.enableProjectConfig")
|
|
2315
|
+
? settings.get("mcp.enableProjectConfig")
|
|
2316
|
+
: true,
|
|
2317
|
+
autoloadOnly: true,
|
|
2318
|
+
// Runtime authority is GJC's native `.gjc` config in both scopes;
|
|
2319
|
+
// Claude Code/Codex MCP files are explicit import sources into
|
|
2320
|
+
// `.gjc`, not implicit competing runtime authorities.
|
|
2321
|
+
nativeOnly: true,
|
|
2322
|
+
});
|
|
2323
|
+
conventionalConfigs = loaded.configs;
|
|
2324
|
+
conventionalSources = loaded.sources;
|
|
2325
|
+
for (const warning of loaded.warnings) {
|
|
2326
|
+
logger.warn("MCP conventional discovery warning", { warning });
|
|
2327
|
+
}
|
|
2328
|
+
if (loaded.configurationWarning) {
|
|
2329
|
+
logger.warn("MCP configuration unavailable.");
|
|
2330
|
+
}
|
|
2331
|
+
} catch (error) {
|
|
2332
|
+
logger.warn("Failed to discover conventional MCP servers", { error: safeErrorForLog(error) });
|
|
2333
|
+
}
|
|
2334
|
+
}
|
|
2335
|
+
// Always-on GJC plugin-bundle MCP servers, merged over conventional
|
|
2336
|
+
// servers on name collisions. Top-level sessions own a manager and
|
|
2337
|
+
// connect the validated servers; subagents inherit the parent's manager
|
|
1958
2338
|
// via options.mcpManager and never spawn their own (prevents duplicate
|
|
1959
|
-
// processes and leaks). Per the plugin product contract, connected MCP
|
|
1960
|
-
// are surfaced as always-on tools rather than gated behind MCP
|
|
2339
|
+
// processes and leaks). Per the plugin product contract, connected MCP
|
|
2340
|
+
// tools are surfaced as always-on tools rather than gated behind MCP
|
|
2341
|
+
// selection.
|
|
1961
2342
|
try {
|
|
1962
2343
|
const { configs, quarantine } = await buildPluginMcpConfigs({ cwd });
|
|
1963
2344
|
for (const q of quarantine) {
|
|
1964
2345
|
gjcFindings.add({ identity: q.identity, surfaceId: q.surfaceId, code: q.code, message: q.message });
|
|
1965
2346
|
logger.warn("Quarantined GJC plugin MCP", { plugin: q.plugin, surface: q.surfaceId, code: q.code });
|
|
1966
2347
|
}
|
|
1967
|
-
|
|
2348
|
+
const pluginNames = new Set(Object.keys(configs));
|
|
2349
|
+
const mergedConfigs = { ...conventionalConfigs, ...configs };
|
|
2350
|
+
const mergedSources = {
|
|
2351
|
+
...conventionalSources,
|
|
2352
|
+
...Object.fromEntries(
|
|
2353
|
+
Object.keys(configs).map(name => [
|
|
2354
|
+
name,
|
|
2355
|
+
{ provider: "gjc-plugins", providerName: "GJC plugin bundle", level: "project" as const },
|
|
2356
|
+
]),
|
|
2357
|
+
),
|
|
2358
|
+
};
|
|
2359
|
+
if (Object.keys(mergedConfigs).length > 0) {
|
|
1968
2360
|
const owned = new MCPManager(cwd, null, { sharedPoolIdleMs: settings.get("mcp.sharedPoolIdleMs") });
|
|
2361
|
+
owned.setAuthStorage(authStorage);
|
|
1969
2362
|
cleanupOwnedMcpManager = () => owned.disconnectAll();
|
|
1970
2363
|
try {
|
|
1971
|
-
const
|
|
1972
|
-
Object.keys(configs).map(name => [
|
|
1973
|
-
name,
|
|
1974
|
-
{ provider: "gjc-plugins", providerName: "GJC plugin bundle", level: "project" as const },
|
|
1975
|
-
]),
|
|
1976
|
-
);
|
|
1977
|
-
const result = await owned.connectServers(configs, sources as never);
|
|
2364
|
+
const result = await owned.connectServers(mergedConfigs, mergedSources as never);
|
|
1978
2365
|
for (const [server, err] of result.errors) {
|
|
1979
2366
|
// A server that failed to connect leaves this generation
|
|
1980
2367
|
// incomplete: its surfaces produced no evidence, so publishing
|
|
@@ -1989,9 +2376,22 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
1989
2376
|
mcpManager = owned;
|
|
1990
2377
|
ownsMcpManager = true;
|
|
1991
2378
|
customTools.push(...(result.tools as CustomTool[]));
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
2379
|
+
const connectedPluginNames = new Set(
|
|
2380
|
+
result.connectedServers.filter(name => pluginNames.has(name)),
|
|
2381
|
+
);
|
|
2382
|
+
pluginMcpManagerServers.set(owned, connectedPluginNames);
|
|
2383
|
+
conventionalMcpManagerServers.set(owned, new Set(result.connectedServers));
|
|
2384
|
+
for (const tool of result.tools) {
|
|
2385
|
+
const serverName = tool.mcpServerName;
|
|
2386
|
+
if (serverName === undefined) continue;
|
|
2387
|
+
if (connectedPluginNames.has(serverName)) pluginMcpToolNames.push(tool.name);
|
|
2388
|
+
else conventionalMcpToolNames.push(tool.name);
|
|
2389
|
+
}
|
|
2390
|
+
// Plugin-bundle connections are fixed for the session lifetime
|
|
2391
|
+
// (existing plugin contract). Sessions without plugin MCPs keep
|
|
2392
|
+
// a mutable connection set so `/mcp reload` can re-discover
|
|
2393
|
+
// conventional registrations.
|
|
2394
|
+
if (connectedPluginNames.size > 0) owned.sealConnectionSet();
|
|
1995
2395
|
} else {
|
|
1996
2396
|
try {
|
|
1997
2397
|
await owned.disconnectAll();
|
|
@@ -2026,22 +2426,22 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
2026
2426
|
});
|
|
2027
2427
|
}
|
|
2028
2428
|
} else if (isCanonicalSubSession) {
|
|
2029
|
-
// Subagents inherit the parent's always-on plugin
|
|
2030
|
-
// owning the manager (no connect, no callbacks, no
|
|
2031
|
-
// is carried explicitly by the parent session
|
|
2032
|
-
// is never consulted for MCP routing.
|
|
2429
|
+
// Subagents inherit the parent's always-on plugin and conventional MCP
|
|
2430
|
+
// tools WITHOUT owning the manager (no connect, no callbacks, no
|
|
2431
|
+
// disposal). The facade is carried explicitly by the parent session
|
|
2432
|
+
// scope; process-global state is never consulted for MCP routing.
|
|
2033
2433
|
const inherited = mcpManager ?? options.inheritedMcpManager;
|
|
2034
2434
|
if (inherited) {
|
|
2035
2435
|
try {
|
|
2036
2436
|
const inheritedTools = inherited.getTools();
|
|
2037
2437
|
if (inheritedTools.length > 0) customTools.push(...(inheritedTools as CustomTool[]));
|
|
2038
2438
|
const pluginServers = pluginMcpManagerServers.get(inherited);
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
);
|
|
2439
|
+
const conventionalServers = conventionalMcpManagerServers.get(inherited);
|
|
2440
|
+
for (const tool of inheritedTools) {
|
|
2441
|
+
const serverName = tool.mcpServerName;
|
|
2442
|
+
if (serverName === undefined) continue;
|
|
2443
|
+
if (pluginServers?.has(serverName)) pluginMcpToolNames.push(tool.name);
|
|
2444
|
+
else if (conventionalServers?.has(serverName)) conventionalMcpToolNames.push(tool.name);
|
|
2045
2445
|
}
|
|
2046
2446
|
} catch (error) {
|
|
2047
2447
|
logger.warn("Failed to inherit MCP tools in subagent", { error: safeErrorForLog(error) });
|
|
@@ -2050,13 +2450,25 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
2050
2450
|
}
|
|
2051
2451
|
// MCP routing is scope-held; no process-global manager registration.
|
|
2052
2452
|
|
|
2053
|
-
//
|
|
2054
|
-
//
|
|
2055
|
-
//
|
|
2453
|
+
// General extension discovery is quarantined from the public SDK surface.
|
|
2454
|
+
// Recognized hook conventions are the bounded exception: their descriptors
|
|
2455
|
+
// normalize before import and then adapt into the authoritative ExtensionRunner.
|
|
2056
2456
|
const inlineExtensions: ExtensionFactory[] = [...(options.extensions ?? [])];
|
|
2457
|
+
const discoveredHookExtensions: Array<{ factory: ExtensionFactory; name: string }> = [];
|
|
2057
2458
|
if (customTools.length > 0) {
|
|
2058
2459
|
inlineExtensions.push(createCustomToolsExtension(customTools));
|
|
2059
2460
|
}
|
|
2461
|
+
if (!options.disableExtensionDiscovery) {
|
|
2462
|
+
try {
|
|
2463
|
+
const hookExtensions = await discoverAndLoadHookExtensions(options.hookPaths ?? [], cwd);
|
|
2464
|
+
discoveredHookExtensions.push(...hookExtensions.factories);
|
|
2465
|
+
for (const error of hookExtensions.errors) {
|
|
2466
|
+
logger.warn("Rejected discovered hook", { path: error.path, error: error.error });
|
|
2467
|
+
}
|
|
2468
|
+
} catch (error) {
|
|
2469
|
+
logger.warn("Failed to discover hook extensions", { error: safeErrorForLog(error) });
|
|
2470
|
+
}
|
|
2471
|
+
}
|
|
2060
2472
|
|
|
2061
2473
|
// Always-on constrained plugin hooks (validated registry surfaces). Additive
|
|
2062
2474
|
// and a no-op without installed plugins; the loader denies all dangerous APIs.
|
|
@@ -2130,13 +2542,41 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
2130
2542
|
if (lifecycleStartupCapability) attachLifecycleStartupCapability(api, lifecycleStartupCapability);
|
|
2131
2543
|
if (lifecycleStartupCapability && process.env.GJC_SDK_TEST_FACTORY_FAILURE === cwd)
|
|
2132
2544
|
throw new Error(process.env.GJC_SDK_TEST_FACTORY_SECRET ?? "Lifecycle factory test failure.");
|
|
2133
|
-
if (notificationsExtensionEligible
|
|
2545
|
+
if (notificationsExtensionEligible) {
|
|
2134
2546
|
const createNotificationsExtension = await notificationAdapterService.get("session-extension");
|
|
2135
2547
|
createNotificationsExtension(api, {
|
|
2136
2548
|
settings,
|
|
2137
2549
|
controller: notificationSessionController,
|
|
2138
2550
|
spawnedByGjc,
|
|
2139
2551
|
sdkHostModeSupported: options.sdkHostModeSupported,
|
|
2552
|
+
// INTERNAL terminal-abort seams, threaded directly from the
|
|
2553
|
+
// owning session (NOT on the public extension context).
|
|
2554
|
+
terminalAbortSeams: {
|
|
2555
|
+
getTerminalTurnEpoch: () => {
|
|
2556
|
+
if (!session) throw new Error("Terminal abort session is not initialized.");
|
|
2557
|
+
return session.getTerminalTurnEpoch();
|
|
2558
|
+
},
|
|
2559
|
+
cancelPendingPreflightForTerminalAbort: () => {
|
|
2560
|
+
if (!session) throw new Error("Terminal abort session is not initialized.");
|
|
2561
|
+
session.cancelPendingPreflightForTerminalAbort();
|
|
2562
|
+
},
|
|
2563
|
+
captureTerminalAbortSteeringSnapshot: () => {
|
|
2564
|
+
if (!session) throw new Error("Terminal abort session is not initialized.");
|
|
2565
|
+
return session.captureTerminalAbortSteeringSnapshot();
|
|
2566
|
+
},
|
|
2567
|
+
discardTerminalAbortSteeringSnapshot: (token: number) => {
|
|
2568
|
+
if (!session) throw new Error("Terminal abort session is not initialized.");
|
|
2569
|
+
session.discardTerminalAbortSteeringSnapshot(token);
|
|
2570
|
+
},
|
|
2571
|
+
rebindTerminalAbortSteeringSnapshot: (token: number) => {
|
|
2572
|
+
if (!session) throw new Error("Terminal abort session is not initialized.");
|
|
2573
|
+
session.rebindTerminalAbortSteeringSnapshot(token);
|
|
2574
|
+
},
|
|
2575
|
+
abortPromptAndWaitWithTerminal: (handle, seamOptions) => {
|
|
2576
|
+
if (!session) throw new Error("Terminal abort session is not initialized.");
|
|
2577
|
+
return session.abortPromptAndWait(handle, seamOptions);
|
|
2578
|
+
},
|
|
2579
|
+
},
|
|
2140
2580
|
ensureProviderDaemon: options.ensureNotificationProviderDaemon,
|
|
2141
2581
|
runBtwTurn: async (question, signal) => {
|
|
2142
2582
|
if (!session) throw new Error("Ephemeral turns are unavailable.");
|
|
@@ -2148,6 +2588,75 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
2148
2588
|
return { replyText };
|
|
2149
2589
|
},
|
|
2150
2590
|
});
|
|
2591
|
+
} else if (sdkHostEligible) {
|
|
2592
|
+
registerSdkOnlyNotificationCommand(api);
|
|
2593
|
+
let sdkOnlyReconciliationStore:
|
|
2594
|
+
| { sessionId: string; sessionFile: string | undefined; store: ReconciliationStore }
|
|
2595
|
+
| undefined;
|
|
2596
|
+
createSdkSessionRuntimeExtension(api, {
|
|
2597
|
+
agentDir,
|
|
2598
|
+
brokerRegistrationRequired: lifecycleStartupCapability !== undefined,
|
|
2599
|
+
createTransport: input => createSdkWebSocketTransport(input),
|
|
2600
|
+
settings,
|
|
2601
|
+
configOverrides: new Map(),
|
|
2602
|
+
// INTERNAL terminal-abort seams, threaded directly from the
|
|
2603
|
+
// owning session (NOT on the public extension context).
|
|
2604
|
+
terminalAbortSeams: {
|
|
2605
|
+
getReconciliationStore: () => {
|
|
2606
|
+
const sessionId = sessionManager.getSessionId();
|
|
2607
|
+
const sessionFile = sessionManager.getSessionFile();
|
|
2608
|
+
// session_switch/session_branch may move to a DIFFERENT transcript
|
|
2609
|
+
// that retains the same copied session id: the store must be
|
|
2610
|
+
// recreated (keyed by session id AND file path, including a
|
|
2611
|
+
// null-to-file transition) or the successor reads/writes the
|
|
2612
|
+
// predecessor's reconciliation file, spuriously replaying or
|
|
2613
|
+
// conflicting with its keys and losing its own replay authority
|
|
2614
|
+
// after restart (review thread P2).
|
|
2615
|
+
if (
|
|
2616
|
+
!sdkOnlyReconciliationStore ||
|
|
2617
|
+
!sdkOnlyStoreMatches(sdkOnlyReconciliationStore, sessionId, sessionFile)
|
|
2618
|
+
) {
|
|
2619
|
+
sdkOnlyReconciliationStore = {
|
|
2620
|
+
sessionId,
|
|
2621
|
+
sessionFile,
|
|
2622
|
+
store: createReconciliationStore({
|
|
2623
|
+
sessionFile,
|
|
2624
|
+
sessionId,
|
|
2625
|
+
}),
|
|
2626
|
+
};
|
|
2627
|
+
}
|
|
2628
|
+
return sdkOnlyReconciliationStore.store;
|
|
2629
|
+
},
|
|
2630
|
+
getTerminalTurnEpoch: () => {
|
|
2631
|
+
if (!session) throw new Error("Terminal abort session is not initialized.");
|
|
2632
|
+
return session.getTerminalTurnEpoch();
|
|
2633
|
+
},
|
|
2634
|
+
getActivePromptHandle: () => {
|
|
2635
|
+
if (!session) throw new Error("Terminal abort session is not initialized.");
|
|
2636
|
+
return session.activePromptHandle;
|
|
2637
|
+
},
|
|
2638
|
+
cancelPendingPreflightForTerminalAbort: () => {
|
|
2639
|
+
if (!session) throw new Error("Terminal abort session is not initialized.");
|
|
2640
|
+
session.cancelPendingPreflightForTerminalAbort();
|
|
2641
|
+
},
|
|
2642
|
+
captureTerminalAbortSteeringSnapshot: () => {
|
|
2643
|
+
if (!session) throw new Error("Terminal abort session is not initialized.");
|
|
2644
|
+
return session.captureTerminalAbortSteeringSnapshot();
|
|
2645
|
+
},
|
|
2646
|
+
discardTerminalAbortSteeringSnapshot: (token: number) => {
|
|
2647
|
+
if (!session) throw new Error("Terminal abort session is not initialized.");
|
|
2648
|
+
session.discardTerminalAbortSteeringSnapshot(token);
|
|
2649
|
+
},
|
|
2650
|
+
rebindTerminalAbortSteeringSnapshot: (token: number) => {
|
|
2651
|
+
if (!session) throw new Error("Terminal abort session is not initialized.");
|
|
2652
|
+
session.rebindTerminalAbortSteeringSnapshot(token);
|
|
2653
|
+
},
|
|
2654
|
+
abortPromptAndWaitWithTerminal: (handle, seamOptions) => {
|
|
2655
|
+
if (!session) throw new Error("Terminal abort session is not initialized.");
|
|
2656
|
+
return session.abortPromptAndWait(handle, seamOptions);
|
|
2657
|
+
},
|
|
2658
|
+
},
|
|
2659
|
+
});
|
|
2151
2660
|
}
|
|
2152
2661
|
} catch (error) {
|
|
2153
2662
|
lifecycleStartupCapability?.settleFailure(
|
|
@@ -2193,6 +2702,16 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
2193
2702
|
extensionsResult.extensions.push(loaded);
|
|
2194
2703
|
}
|
|
2195
2704
|
}
|
|
2705
|
+
for (const entry of discoveredHookExtensions) {
|
|
2706
|
+
const loaded = await loadExtensionFromFactory(
|
|
2707
|
+
entry.factory,
|
|
2708
|
+
cwd,
|
|
2709
|
+
eventBus,
|
|
2710
|
+
extensionsResult.runtime,
|
|
2711
|
+
entry.name,
|
|
2712
|
+
);
|
|
2713
|
+
extensionsResult.extensions.push(loaded);
|
|
2714
|
+
}
|
|
2196
2715
|
|
|
2197
2716
|
// Process provider registrations queued during extension loading.
|
|
2198
2717
|
// This must happen before the runner is created so that models registered by
|
|
@@ -2258,18 +2777,25 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
2258
2777
|
: formatNoModelsAvailableFallback();
|
|
2259
2778
|
}
|
|
2260
2779
|
}
|
|
2780
|
+
if (options.credentialSelector && !options.credentialSelector.provider && model) {
|
|
2781
|
+
applyCredentialSelector(credentialSessionId, model.provider, options.credentialSelector.selector);
|
|
2782
|
+
}
|
|
2261
2783
|
|
|
2262
|
-
if (options.credentialSelector && !
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2784
|
+
if (options.credentialSelector && !options.credentialSelector.provider && !model?.provider) {
|
|
2785
|
+
throw new Error(
|
|
2786
|
+
`--credential ${options.credentialSelector.raw} requires a resolved model or an explicit provider prefix`,
|
|
2787
|
+
);
|
|
2788
|
+
}
|
|
2789
|
+
// Safety net: every resolution branch above already filters candidates by
|
|
2790
|
+
// `preferredCredentialProvider` (session restore, settings default, fallback
|
|
2791
|
+
// scan via `hasModelApiKey`), but an explicit `--model`/`--models` request for
|
|
2792
|
+
// a different provider takes priority earlier in resolution and would
|
|
2793
|
+
// otherwise silently ignore the preference. Fail closed with a clear error
|
|
2794
|
+
// instead.
|
|
2795
|
+
if (model && preferredCredentialProvider && model.provider !== preferredCredentialProvider) {
|
|
2796
|
+
throw new Error(
|
|
2797
|
+
`--prefer-credential ${options.preferredCredentialSelector?.raw ?? ""} matches ${preferredCredentialProvider}, but the resolved model uses ${model.provider}`,
|
|
2798
|
+
);
|
|
2273
2799
|
}
|
|
2274
2800
|
const customCommandsResult: CustomCommandsLoadResult = { commands: [], errors: [] };
|
|
2275
2801
|
|
|
@@ -2354,9 +2880,20 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
2354
2880
|
|
|
2355
2881
|
// All built-in tools are active (conditional tools like git/ask return null from factory if disabled)
|
|
2356
2882
|
const toolRegistry = new Map<string, Tool>();
|
|
2883
|
+
/**
|
|
2884
|
+
* Identity — not name — provenance for the coordinator-visible tool label.
|
|
2885
|
+
*
|
|
2886
|
+
* Every tool object this builder constructs from a built-in descriptor is recorded
|
|
2887
|
+
* here as it is created, BEFORE extension, MCP, and custom tools overwrite registry
|
|
2888
|
+
* names below. An extension or custom tool that registers itself as `bash` replaces
|
|
2889
|
+
* the registry entry with an object that was never recorded, so the label resolves to
|
|
2890
|
+
* `custom` instead of impersonating the built-in.
|
|
2891
|
+
*/
|
|
2892
|
+
const builtinToolIdentities = new Set<object>();
|
|
2357
2893
|
let builtinCandidateTools = [...builtinTools];
|
|
2358
2894
|
for (const tool of builtinTools) {
|
|
2359
2895
|
toolRegistry.set(tool.name, tool);
|
|
2896
|
+
builtinToolIdentities.add(tool);
|
|
2360
2897
|
}
|
|
2361
2898
|
const goalStateToolNames = ["goal"] as const;
|
|
2362
2899
|
if (settings.get("goal.enabled")) {
|
|
@@ -2371,6 +2908,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
2371
2908
|
const wrappedGoalStateTool = wrapToolWithMetaNotice(goalStateTool);
|
|
2372
2909
|
builtinCandidateTools.push(wrappedGoalStateTool);
|
|
2373
2910
|
toolRegistry.set(wrappedGoalStateTool.name, wrappedGoalStateTool);
|
|
2911
|
+
builtinToolIdentities.add(wrappedGoalStateTool);
|
|
2374
2912
|
}
|
|
2375
2913
|
}
|
|
2376
2914
|
}
|
|
@@ -2379,7 +2917,11 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
2379
2917
|
}
|
|
2380
2918
|
if (extensionRunner) {
|
|
2381
2919
|
for (const tool of toolRegistry.values()) {
|
|
2382
|
-
|
|
2920
|
+
const wrapped = new ExtensionToolWrapper(tool, extensionRunner);
|
|
2921
|
+
// A wrapper built from a proven built-in inherits that provenance; one built
|
|
2922
|
+
// from an extension/custom tool does not.
|
|
2923
|
+
if (builtinToolIdentities.has(tool)) builtinToolIdentities.add(wrapped);
|
|
2924
|
+
toolRegistry.set(tool.name, wrapped);
|
|
2383
2925
|
}
|
|
2384
2926
|
}
|
|
2385
2927
|
if (model?.provider === "cursor") {
|
|
@@ -2396,6 +2938,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
2396
2938
|
const wrappedResolveTool = wrapToolWithMetaNotice(resolveTool);
|
|
2397
2939
|
builtinCandidateTools.push(wrappedResolveTool);
|
|
2398
2940
|
toolRegistry.set(wrappedResolveTool.name, wrappedResolveTool);
|
|
2941
|
+
builtinToolIdentities.add(wrappedResolveTool);
|
|
2399
2942
|
}
|
|
2400
2943
|
}
|
|
2401
2944
|
// Exact-config MCP tools cannot claim a name already represented by the final candidate catalog.
|
|
@@ -2434,7 +2977,18 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
2434
2977
|
});
|
|
2435
2978
|
|
|
2436
2979
|
const repeatToolDescriptions = settings.get("repeatToolDescriptions");
|
|
2437
|
-
|
|
2980
|
+
// Re-resolved per prompt build so a profile activated after session start is
|
|
2981
|
+
// reflected on the next refresh. Vendor-separated worker roles imply eager
|
|
2982
|
+
// delegation unless `task.eager` is configured explicitly.
|
|
2983
|
+
const resolveEagerTasks = (): boolean => {
|
|
2984
|
+
const profileName = startupActiveModelProfile ?? settings.get("modelProfile.default");
|
|
2985
|
+
const resolvedProfileName = profileName ? resolveModelProfileName(profileName, persistedProfiles) : undefined;
|
|
2986
|
+
return resolveEagerTaskDelegation({
|
|
2987
|
+
settings,
|
|
2988
|
+
profile: resolvedProfileName ? persistedProfiles.get(resolvedProfileName) : undefined,
|
|
2989
|
+
}).eagerTasks;
|
|
2990
|
+
};
|
|
2991
|
+
const eagerTasks = resolveEagerTasks();
|
|
2438
2992
|
const intentTracingEnabled = resolveIntentTracingEnabled(settings.get("tools.intentTracing"), {
|
|
2439
2993
|
subSession: isCanonicalSubSession,
|
|
2440
2994
|
});
|
|
@@ -2519,7 +3073,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
2519
3073
|
repeatToolDescriptions,
|
|
2520
3074
|
intentField,
|
|
2521
3075
|
toolDiscoveryActive: effectiveDiscoveryMode === "all" || mcpDiscoveryEnabled,
|
|
2522
|
-
eagerTasks,
|
|
3076
|
+
eagerTasks: resolveEagerTasks(),
|
|
2523
3077
|
secretsEnabled,
|
|
2524
3078
|
workspaceTree: workspaceTreePromise,
|
|
2525
3079
|
subagent: options.parentTaskPrefix !== undefined,
|
|
@@ -2612,6 +3166,9 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
2612
3166
|
...new Set([
|
|
2613
3167
|
...discoveryDefaultServerToolNames,
|
|
2614
3168
|
...(explicitMcpConfigPath !== undefined ? exactMcpToolNames : []),
|
|
3169
|
+
// Conventional autoload servers are selected by default so their
|
|
3170
|
+
// tools are exposed in sessions that enable MCP discovery mode.
|
|
3171
|
+
...conventionalMcpToolNames,
|
|
2615
3172
|
]),
|
|
2616
3173
|
];
|
|
2617
3174
|
hasExplicitMCPToolSelection =
|
|
@@ -2631,11 +3188,13 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
2631
3188
|
|
|
2632
3189
|
// Custom, extension-registered, and plugin-bundle MCP tools are always
|
|
2633
3190
|
// included regardless of the caller's built-in tool filter. Plugin MCPs
|
|
2634
|
-
// remain always-on even when generic MCP
|
|
3191
|
+
// and conventional autoload MCPs remain always-on even when generic MCP
|
|
3192
|
+
// discovery is disabled.
|
|
2635
3193
|
const alwaysInclude: string[] = [
|
|
2636
3194
|
...(options.customTools?.map(t => (isCustomTool(t) ? t.name : t.name)) ?? []),
|
|
2637
3195
|
...registeredTools.filter(t => !t.definition.defaultInactive).map(t => t.definition.name),
|
|
2638
3196
|
...pluginMcpToolNames,
|
|
3197
|
+
...conventionalMcpToolNames,
|
|
2639
3198
|
];
|
|
2640
3199
|
const pluginMcpToolNameSet = new Set(pluginMcpToolNames);
|
|
2641
3200
|
for (const name of alwaysInclude) {
|
|
@@ -2794,6 +3353,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
2794
3353
|
activeSkillState: session?.getActiveSkillState(),
|
|
2795
3354
|
sessionId: sessionManager.getSessionId?.() ?? null,
|
|
2796
3355
|
}),
|
|
3356
|
+
() => session?.getSessionAgentDir(),
|
|
2797
3357
|
),
|
|
2798
3358
|
);
|
|
2799
3359
|
|
|
@@ -2872,20 +3432,13 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
2872
3432
|
getApiKey: async provider => {
|
|
2873
3433
|
// Read agent.sessionId at call time so credential selection stays aligned
|
|
2874
3434
|
// with metadataResolver after /new, fork, resume, or branch switches.
|
|
2875
|
-
const key = await modelRegistry.getApiKeyForProvider(
|
|
2876
|
-
provider,
|
|
2877
|
-
options.credentialSessionId ?? agent.providerSessionId ?? agent.sessionId,
|
|
2878
|
-
);
|
|
3435
|
+
const key = await modelRegistry.getApiKeyForProvider(provider, credentialSessionId);
|
|
2879
3436
|
if (!key) {
|
|
2880
3437
|
throw new Error(`No API key found for provider "${provider}"`);
|
|
2881
3438
|
}
|
|
2882
3439
|
return key;
|
|
2883
3440
|
},
|
|
2884
|
-
getAuthCredentialType: provider =>
|
|
2885
|
-
modelRegistry.getSessionCredentialType(
|
|
2886
|
-
provider,
|
|
2887
|
-
options.credentialSessionId ?? agent.providerSessionId ?? agent.sessionId,
|
|
2888
|
-
),
|
|
3441
|
+
getAuthCredentialType: provider => modelRegistry.getSessionCredentialType(provider, credentialSessionId),
|
|
2889
3442
|
streamFn: async (streamModel, context, streamOptions) => {
|
|
2890
3443
|
const requestStartedAt = performance.now();
|
|
2891
3444
|
let stream: Awaited<ReturnType<typeof streamSimple>>;
|
|
@@ -2893,8 +3446,6 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
2893
3446
|
stream = await streamSimple(streamModel, context, {
|
|
2894
3447
|
...streamOptions,
|
|
2895
3448
|
onAuthError: async (provider, oldKey, error) => {
|
|
2896
|
-
const credentialSessionId =
|
|
2897
|
-
options.credentialSessionId ?? agent.providerSessionId ?? agent.sessionId;
|
|
2898
3449
|
await modelRegistry.authStorage.invalidateCredentialMatching(provider, oldKey, {
|
|
2899
3450
|
signal: streamOptions?.signal,
|
|
2900
3451
|
sessionId: credentialSessionId,
|
|
@@ -3002,6 +3553,12 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
3002
3553
|
thinkingLevel,
|
|
3003
3554
|
sessionManager,
|
|
3004
3555
|
settings,
|
|
3556
|
+
// The session's REQUESTED agent directory when the caller explicitly
|
|
3557
|
+
// supplied it, independent of the reused global Settings singleton
|
|
3558
|
+
// (which may belong to an earlier session). When the option is
|
|
3559
|
+
// absent, the injected settings instance's own profile wins via the
|
|
3560
|
+
// AgentSession fallback - never the process default.
|
|
3561
|
+
agentDir: options.agentDir,
|
|
3005
3562
|
memoryBackend: runtimeServices.memoryBackend,
|
|
3006
3563
|
notificationSessionController,
|
|
3007
3564
|
evalKernelOwnerId,
|
|
@@ -3010,6 +3567,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
3010
3567
|
// AsyncJobManager on teardown; subagents inherit the parent's and
|
|
3011
3568
|
// **MUST NOT** tear it down.
|
|
3012
3569
|
ownedAsyncJobManager: asyncJobManager,
|
|
3570
|
+
disposeAsyncJobManager: !options.parentTaskPrefix,
|
|
3013
3571
|
// Only an MCP manager owned by this session is torn down on dispose;
|
|
3014
3572
|
// subagents and callers that merely observe a manager must not (see
|
|
3015
3573
|
// AgentSession.dispose).
|
|
@@ -3027,6 +3585,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
3027
3585
|
taskDepth,
|
|
3028
3586
|
workflowGatePublication: isCanonicalSubSession ? "local" : "endpoint",
|
|
3029
3587
|
toolRegistry,
|
|
3588
|
+
builtinToolIdentities,
|
|
3030
3589
|
workflowGateToolSession: toolSession,
|
|
3031
3590
|
transformContext,
|
|
3032
3591
|
onPayload,
|
|
@@ -3064,8 +3623,10 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
3064
3623
|
obfuscator,
|
|
3065
3624
|
agentId: resolvedAgentId,
|
|
3066
3625
|
agentRegistry,
|
|
3626
|
+
asyncJobProviderSessionId: options.providerSessionId,
|
|
3067
3627
|
providerSessionId: options.providerSessionId,
|
|
3068
|
-
credentialSessionId
|
|
3628
|
+
credentialSessionId,
|
|
3629
|
+
credentialStoreIdentity: startupAuthConfig?.credentialStoreIdentity,
|
|
3069
3630
|
providerCacheSessionId: providerSessionId,
|
|
3070
3631
|
forkContextSeed: options.forkContextSeed,
|
|
3071
3632
|
providerSessionState: options.providerSessionState,
|
|
@@ -3073,9 +3634,17 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
3073
3634
|
session.setActiveModelProfile(startupActiveModelProfile);
|
|
3074
3635
|
session.configWarnings.push(...contextFileWarnings);
|
|
3075
3636
|
hasSession = true;
|
|
3076
|
-
|
|
3637
|
+
const sessionAsyncJobManager = asyncJobManager;
|
|
3638
|
+
if (sessionAsyncJobManager) {
|
|
3077
3639
|
session.yieldQueue.register<AsyncResultEntry>("async-result", {
|
|
3078
|
-
isStale: entry =>
|
|
3640
|
+
isStale: entry => sessionAsyncJobManager.isDeliverySuppressed(entry.jobId, entry.generation),
|
|
3641
|
+
// Build one message per ownership origin so an owned-scope drop of
|
|
3642
|
+
// one turn's message never suppresses other turns'/ordinary
|
|
3643
|
+
// completions batched in the same flush (review thread P2).
|
|
3644
|
+
groupKey: entry =>
|
|
3645
|
+
entry.ownedCompletion
|
|
3646
|
+
? `${entry.ownedCompletion.lineageIdHash}\u0000${entry.ownedCompletion.promptAttemptEpoch}`
|
|
3647
|
+
: "ordinary",
|
|
3079
3648
|
build: buildAsyncResultBatchMessage,
|
|
3080
3649
|
});
|
|
3081
3650
|
}
|
|
@@ -3098,6 +3667,20 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
3098
3667
|
releaseCredentialDisabledSubscription();
|
|
3099
3668
|
releaseLocalProtocolOverride();
|
|
3100
3669
|
} finally {
|
|
3670
|
+
// The endpoint is gone: its owned registrations can never
|
|
3671
|
+
// reach a delivery settlement boundary, and foreign-endpoint
|
|
3672
|
+
// tuples are deliberately never classified terminal by other
|
|
3673
|
+
// managers — retire them before unregistering the manager so
|
|
3674
|
+
// repeated session churn cannot saturate the registry
|
|
3675
|
+
// (review thread P2). The endpoint is the manager's live
|
|
3676
|
+
// registration key, which survives newSession/switchSession
|
|
3677
|
+
// rekeying and may differ from the persisted logical session id.
|
|
3678
|
+
if (!options.parentTaskPrefix) {
|
|
3679
|
+
retireOwnedRegistrationsForEndpoint(
|
|
3680
|
+
AsyncJobManager.endpointIdOf(asyncJobManager) ?? asyncJobEndpointId,
|
|
3681
|
+
);
|
|
3682
|
+
AsyncJobManager.unregisterManager(asyncJobManager);
|
|
3683
|
+
}
|
|
3101
3684
|
closeOwnedAuthStorage();
|
|
3102
3685
|
}
|
|
3103
3686
|
}
|
|
@@ -3316,6 +3899,19 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
3316
3899
|
await session.dispose();
|
|
3317
3900
|
} else {
|
|
3318
3901
|
if (hasRegistered) agentRegistry.unregister(resolvedAgentId);
|
|
3902
|
+
// Admission happens before session construction. Any later startup
|
|
3903
|
+
// failure must remove THIS manager's endpoint mapping and restore
|
|
3904
|
+
// the prior global only when this manager is still global: otherwise
|
|
3905
|
+
// a retry under the same endpoint is falsely rejected and an orphan
|
|
3906
|
+
// redirects global-manager consumers away from the live session
|
|
3907
|
+
// (review thread P1).
|
|
3908
|
+
if (asyncJobManagerAdmitted && asyncJobManager) {
|
|
3909
|
+
AsyncJobManager.unregisterManager(asyncJobManager);
|
|
3910
|
+
if (AsyncJobManager.instance() === asyncJobManager) {
|
|
3911
|
+
AsyncJobManager.setInstance(priorAsyncJobManager);
|
|
3912
|
+
}
|
|
3913
|
+
await asyncJobManager.dispose({ timeoutMs: 100 });
|
|
3914
|
+
}
|
|
3319
3915
|
await cleanupOwnedMcpManager?.();
|
|
3320
3916
|
const [{ disposeKernelSessionsByOwner }, { disposeVmContextsByOwner }] = await Promise.all([
|
|
3321
3917
|
import("../eval/py/executor"),
|