@gajae-code/coding-agent 0.14.2 → 0.15.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 +120 -1
- package/dist/types/async/job-manager.d.ts +4 -1
- package/dist/types/autoresearch/dashboard.d.ts +29 -4
- package/dist/types/autoresearch/data-context.d.ts +25 -0
- package/dist/types/autoresearch/git.d.ts +65 -7
- package/dist/types/autoresearch/harness.d.ts +58 -0
- package/dist/types/autoresearch/prompts.d.ts +51 -0
- package/dist/types/autoresearch/runs.d.ts +173 -0
- package/dist/types/autoresearch/types.d.ts +9 -139
- package/dist/types/capability/types.d.ts +20 -0
- package/dist/types/cli/accounts-cli.d.ts +1 -0
- package/dist/types/commands/{rlm.d.ts → autoresearch.d.ts} +2 -4
- package/dist/types/config/autorouting-contract.d.ts +146 -0
- package/dist/types/config/autorouting-generator.d.ts +40 -0
- package/dist/types/config/autorouting-tier-map.d.ts +16844 -0
- package/dist/types/config/autorouting.d.ts +35 -0
- package/dist/types/config/model-discovery-manager.d.ts +8 -1
- package/dist/types/config/model-registry.d.ts +30 -1
- package/dist/types/config/provider-selection-policy.d.ts +23 -0
- package/dist/types/config/settings-schema.d.ts +61 -3
- package/dist/types/config/settings.d.ts +3 -0
- package/dist/types/coordinator/contract.d.ts +1 -1
- package/dist/types/coordinator-mcp/durability.d.ts +29 -0
- package/dist/types/coordinator-mcp/event-webhook.d.ts +50 -0
- package/dist/types/coordinator-mcp/question-state.d.ts +1 -1
- package/dist/types/coordinator-mcp/server.d.ts +14 -0
- package/dist/types/crash/index-store.d.ts +3 -0
- package/dist/types/crash/record-loader.d.ts +2 -0
- package/dist/types/crash/upstream/relay.d.ts +5 -3
- package/dist/types/cursor.d.ts +12 -0
- package/dist/types/defaults/gjc-defaults.d.ts +35 -1
- package/dist/types/defaults/gjc-skills.generated.d.ts +1 -1
- package/dist/types/discovery/helpers.d.ts +1 -1
- package/dist/types/edit/index.d.ts +2 -1
- package/dist/types/eval/py/executor.d.ts +2 -0
- package/dist/types/extensibility/extensions/loader.d.ts +32 -0
- package/dist/types/extensibility/gjc-plugins/types.d.ts +1 -1
- package/dist/types/extensibility/skills.d.ts +2 -0
- package/dist/types/gjc-runtime/autoresearch-runtime.d.ts +194 -0
- package/dist/types/gjc-runtime/gc-runtime.d.ts +1 -1
- package/dist/types/gjc-runtime/psmux-detect.d.ts +1 -1
- package/dist/types/gjc-runtime/python-transcript.d.ts +19 -0
- package/dist/types/gjc-runtime/ralplan-runtime.d.ts +2 -8
- package/dist/types/gjc-runtime/session-layout.d.ts +6 -3
- package/dist/types/gjc-runtime/state-renderer.d.ts +0 -22
- package/dist/types/gjc-runtime/state-schema.d.ts +7 -7
- package/dist/types/gjc-runtime/tmux-common.d.ts +2 -2
- package/dist/types/gjc-runtime/ultragoal-change-set.d.ts +38 -1
- package/dist/types/gjc-runtime/ultragoal-runtime.d.ts +2 -24
- package/dist/types/gjc-runtime/ultragoal-validation-policy.d.ts +73 -0
- package/dist/types/gjc-runtime/workflow-recovery-projection.d.ts +100 -0
- package/dist/types/hooks/skill-keywords.d.ts +1 -1
- package/dist/types/main.d.ts +0 -10
- package/dist/types/modes/acp/acp-agent.d.ts +13 -6
- package/dist/types/modes/components/model-selector.d.ts +14 -0
- package/dist/types/modes/components/skill-hud/render.d.ts +6 -1
- package/dist/types/modes/components/smart-routing-panel.d.ts +82 -0
- package/dist/types/modes/controllers/selector-controller.d.ts +14 -0
- package/dist/types/modes/interactive-mode.d.ts +3 -0
- package/dist/types/modes/types.d.ts +9 -0
- package/dist/types/rlm/artifacts.d.ts +4 -4
- package/dist/types/runtime/optional-runtime-services.d.ts +2 -1
- package/dist/types/runtime/workspace-tree-service.d.ts +5 -1
- package/dist/types/runtime-mcp/config.d.ts +6 -0
- package/dist/types/runtime-mcp/manager.d.ts +10 -0
- package/dist/types/sdk/acp/adapter.d.ts +0 -4
- package/dist/types/sdk/broker/broker.d.ts +1 -1
- package/dist/types/sdk/broker/lifecycle.d.ts +57 -2
- package/dist/types/sdk/broker/session-index.d.ts +15 -0
- package/dist/types/sdk/bus/chat-daemon-control.d.ts +6 -0
- package/dist/types/sdk/bus/chat-daemon-runtime.d.ts +4 -0
- package/dist/types/sdk/bus/index.d.ts +8 -0
- package/dist/types/sdk/bus/kind-aware-reconciliation.d.ts +2 -0
- package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +12 -2
- package/dist/types/sdk/client/client.d.ts +39 -4
- package/dist/types/sdk/host/host.d.ts +1 -0
- package/dist/types/sdk/host/internal-autorouting-state.d.ts +3 -0
- package/dist/types/sdk/host/model-pin.d.ts +29 -0
- package/dist/types/sdk/host/session-runtime.d.ts +2 -1
- package/dist/types/sdk/lifecycle-session.d.ts +13 -0
- package/dist/types/sdk/router/session-router.d.ts +27 -1
- package/dist/types/sdk/session.d.ts +11 -1
- package/dist/types/session/agent-session.d.ts +36 -6
- package/dist/types/session/artifacts.d.ts +16 -1
- package/dist/types/session/internal/managed-session-storage.d.ts +7 -0
- package/dist/types/session/session-manager.d.ts +84 -2
- package/dist/types/session/session-staging-paths.d.ts +3 -0
- package/dist/types/skill-state/canonical-skills.d.ts +1 -1
- package/dist/types/skill-state/workflow-hud.d.ts +12 -21
- package/dist/types/skill-state/workflow-mutation-guard.d.ts +1 -0
- package/dist/types/slash-commands/helpers/autorouting-status.d.ts +8 -0
- package/dist/types/system-prompt.d.ts +23 -0
- package/dist/types/task/executor.d.ts +35 -4
- package/dist/types/task/index.d.ts +17 -3
- package/dist/types/task/receipt.d.ts +3 -1
- package/dist/types/task/types.d.ts +144 -0
- package/dist/types/tools/atomic-file-write.d.ts +72 -0
- package/dist/types/tools/descriptors.d.ts +1 -0
- package/dist/types/tools/index.d.ts +36 -1
- package/dist/types/tools/move-session.d.ts +40 -0
- package/dist/types/tools/python.d.ts +12 -0
- package/dist/types/tools/resource-gc.d.ts +0 -3
- package/dist/types/tools/search.d.ts +8 -1
- package/dist/types/tools/skill.d.ts +9 -4
- package/dist/types/tools/subagent.d.ts +33 -3
- package/dist/types/utils/xml-escape.d.ts +2 -0
- package/dist/types/workflow/workflow-intent-diff.d.ts +2 -2
- package/dist/types/workflow/workflow-intent-report-consumer.d.ts +1 -1
- package/package.json +11 -12
- package/scripts/build-sdk-package-smoke.ts +7 -5
- package/scripts/check-autorouting-tier-map.ts +183 -0
- package/scripts/generate-sdk-operation-inventory.ts +12 -0
- package/scripts/generate-tool-catalog.ts +40 -5
- package/src/async/job-manager.ts +5 -2
- package/src/autoresearch/dashboard.ts +134 -314
- package/src/autoresearch/data-context.ts +35 -0
- package/src/autoresearch/git.ts +186 -26
- package/src/autoresearch/{helpers.ts → harness.ts} +65 -40
- package/src/autoresearch/{prompt-setup.md → prompts/prompt-setup.md} +7 -7
- package/src/autoresearch/prompts/prompt.md +103 -0
- package/src/autoresearch/prompts.ts +126 -0
- package/src/autoresearch/runs.ts +708 -0
- package/src/autoresearch/types.ts +9 -150
- package/src/capability/index.ts +4 -4
- package/src/capability/types.ts +20 -0
- package/src/cli/accounts-cli.ts +21 -5
- package/src/cli/config-cli.ts +25 -1
- package/src/cli/fast-help.ts +1 -2
- package/src/cli/list-models.ts +5 -8
- package/src/cli/setup-cli.ts +8 -0
- package/src/cli.ts +2 -21
- package/src/commands/autoresearch.ts +33 -0
- package/src/commands/coordinator.ts +15 -0
- package/src/commands/launch.ts +2 -4
- package/src/commands/quick-lane.ts +1 -1
- package/src/commands/sdk.ts +24 -0
- package/src/commands/state.ts +1 -1
- package/src/config/autorouting-contract.ts +474 -0
- package/src/config/autorouting-generator.ts +238 -0
- package/src/config/autorouting-tier-map.ts +6108 -0
- package/src/config/autorouting.ts +120 -0
- package/src/config/model-discovery-manager.ts +42 -11
- package/src/config/model-registry.ts +348 -68
- package/src/config/provider-selection-policy.ts +60 -9
- package/src/config/settings-schema.ts +195 -6
- package/src/config/settings.ts +62 -1
- package/src/config.ts +2 -3
- package/src/coordinator/contract.ts +0 -1
- package/src/coordinator-mcp/codex-handoff.ts +68 -42
- package/src/coordinator-mcp/durability.ts +200 -0
- package/src/coordinator-mcp/event-webhook.ts +278 -0
- package/src/coordinator-mcp/question-state.ts +109 -46
- package/src/coordinator-mcp/server.ts +932 -504
- package/src/crash/index-store.ts +126 -22
- package/src/crash/record-loader.ts +7 -0
- package/src/crash/upstream/relay.ts +82 -34
- package/src/cursor.ts +84 -1
- package/src/debug/index.ts +26 -0
- package/src/defaults/gjc/skills/autoresearch/SKILL.md +116 -0
- package/src/defaults/gjc/skills/autoresearch/auto-critic.md +46 -0
- package/src/defaults/gjc/skills/autoresearch/auto-iterate.md +42 -0
- package/src/defaults/gjc/skills/deep-interview/SKILL.md +17 -37
- package/src/defaults/gjc/skills/ralplan/SKILL.md +21 -18
- package/src/defaults/gjc/skills/ultragoal/SKILL.md +6 -17
- package/src/defaults/gjc/skills/ultragoal/ai-slop-cleaner.md +2 -2
- package/src/defaults/gjc-defaults.ts +89 -2
- package/src/defaults/gjc-skills.generated.ts +24 -17
- package/src/discovery/builtin.ts +18 -5
- package/src/discovery/helpers.ts +6 -6
- package/src/edit/index.ts +5 -2
- package/src/eval/py/executor.ts +8 -0
- package/src/extensibility/extensions/loader.ts +135 -13
- package/src/extensibility/extensions/wrapper.ts +1 -2
- package/src/extensibility/gjc-plugins/schema.ts +1 -1
- package/src/extensibility/runtime-skill-discovery.ts +2 -2
- package/src/extensibility/skill-management.ts +2 -3
- package/src/extensibility/skills.ts +16 -8
- package/src/gjc-runtime/autoresearch-runtime.ts +1509 -0
- package/src/gjc-runtime/gc-render.ts +0 -1
- package/src/gjc-runtime/gc-runtime.ts +1 -11
- package/src/gjc-runtime/launch-tmux.ts +3 -3
- package/src/gjc-runtime/psmux-detect.ts +2 -2
- package/src/gjc-runtime/python-transcript.ts +58 -0
- package/src/gjc-runtime/ralplan-runtime.ts +6 -18
- package/src/gjc-runtime/session-layout.ts +18 -7
- package/src/gjc-runtime/session-state-sidecar.ts +58 -9
- package/src/gjc-runtime/state-renderer.ts +0 -38
- package/src/gjc-runtime/state-runtime.ts +43 -22
- package/src/gjc-runtime/state-schema.ts +1 -1
- package/src/gjc-runtime/tmux-common.ts +7 -7
- package/src/gjc-runtime/ultragoal-change-set.ts +232 -11
- package/src/gjc-runtime/ultragoal-runtime.ts +309 -114
- package/src/gjc-runtime/ultragoal-validation-policy.ts +247 -0
- package/src/gjc-runtime/workflow-command-ref.ts +15 -11
- package/src/gjc-runtime/workflow-manifest.generated.json +413 -532
- package/src/gjc-runtime/workflow-manifest.ts +43 -83
- package/src/gjc-runtime/workflow-recovery-projection.ts +616 -0
- package/src/hooks/skill-keywords.ts +3 -3
- package/src/hooks/skill-state.ts +2 -2
- package/src/internal-urls/docs-index.generated.ts +26 -24
- package/src/lsp/index.ts +25 -5
- package/src/main.ts +2 -27
- package/src/modes/acp/acp-agent.ts +200 -75
- package/src/modes/components/model-selector.ts +216 -13
- package/src/modes/components/oauth-selector.ts +5 -5
- package/src/modes/components/skill-hud/render.ts +173 -35
- package/src/modes/components/smart-routing-panel.ts +531 -0
- package/src/modes/components/tool-status-header.ts +6 -3
- package/src/modes/controllers/command-controller.ts +17 -7
- package/src/modes/controllers/input-controller.ts +44 -9
- package/src/modes/controllers/runtime-mcp-command-controller.ts +4 -0
- package/src/modes/controllers/selector-controller.ts +298 -65
- package/src/modes/interactive-mode.ts +8 -1
- package/src/modes/shared/agent-wire/workflow-gate-types.ts +1 -1
- package/src/modes/types.ts +9 -1
- package/src/prompts/agents/planner.md +1 -1
- package/src/prompts/system/system-prompt.md +1 -1
- package/src/prompts/system/volatile-project-context.md +2 -1
- package/src/prompts/tools/move-session.md +15 -0
- package/src/prompts/tools/python.md +12 -0
- package/src/prompts/tools/skill-discovery.md +1 -1
- package/src/prompts/tools/skill.md +2 -2
- package/src/prompts/tools/task-summary.md +1 -0
- package/src/prompts/tools/task.md +6 -0
- package/src/rlm/artifacts.ts +12 -10
- package/src/runtime/optional-runtime-services.ts +3 -2
- package/src/runtime/workspace-tree-service.ts +11 -3
- package/src/runtime-mcp/config.ts +8 -1
- package/src/runtime-mcp/manager.ts +33 -0
- package/src/sdk/acp/adapter.ts +48 -1
- package/src/sdk/broker/broker.ts +30 -2
- package/src/sdk/broker/discovery.ts +217 -2
- package/src/sdk/broker/lifecycle.ts +385 -59
- package/src/sdk/broker/session-index.ts +243 -48
- package/src/sdk/broker/transport.ts +1 -0
- package/src/sdk/bus/chat-daemon-control.ts +8 -2
- package/src/sdk/bus/chat-daemon-runtime.ts +5 -0
- package/src/sdk/bus/index.ts +208 -11
- package/src/sdk/bus/kind-aware-reconciliation.ts +16 -0
- package/src/sdk/bus/reconciliation-store.ts +27 -5
- package/src/sdk/bus/telegram-daemon-contract.ts +12 -2
- package/src/sdk/client/client.ts +204 -22
- package/src/sdk/host/host.ts +16 -0
- package/src/sdk/host/internal-autorouting-state.ts +13 -0
- package/src/sdk/host/model-pin.ts +101 -0
- package/src/sdk/host/reverse-leases.ts +2 -1
- package/src/sdk/host/session-runtime.test.ts +224 -1
- package/src/sdk/host/session-runtime.ts +125 -37
- package/src/sdk/lifecycle/service.ts +3 -1
- package/src/sdk/lifecycle-session.ts +42 -1
- package/src/sdk/protocol/operation-inventory.generated.json +66 -0
- package/src/sdk/router/session-router.ts +375 -36
- package/src/sdk/session.ts +547 -47
- package/src/session/account-inventory.ts +1 -1
- package/src/session/agent-session.ts +765 -189
- package/src/session/artifacts.ts +234 -4
- package/src/session/internal/managed-session-storage.ts +91 -4
- package/src/session/provider-safety-stop-hint.ts +3 -2
- package/src/session/session-manager.ts +775 -16
- package/src/session/session-staging-paths.ts +8 -0
- package/src/setup/hermes/templates/operator-instructions.v1.md +0 -1
- package/src/setup/host-plugin-setup.ts +2 -2
- package/src/skill-state/active-state.ts +2 -2
- package/src/skill-state/canonical-skills.ts +1 -1
- package/src/skill-state/initial-phase.ts +1 -1
- package/src/skill-state/workflow-hud.ts +41 -41
- package/src/skill-state/workflow-mutation-guard.ts +26 -7
- package/src/slash-commands/builtin-registry.ts +77 -0
- package/src/slash-commands/helpers/autorouting-status.ts +55 -0
- package/src/system-prompt.ts +102 -5
- package/src/task/discovery.ts +2 -3
- package/src/task/executor.ts +617 -43
- package/src/task/index.ts +262 -15
- package/src/task/receipt.ts +30 -0
- package/src/task/render.ts +11 -1
- package/src/task/types.ts +316 -0
- package/src/tools/atomic-file-write.ts +594 -0
- package/src/tools/bisect.ts +3 -2
- package/src/tools/descriptors.test.ts +3 -0
- package/src/tools/descriptors.ts +24 -0
- package/src/tools/image-gen.ts +5 -1
- package/src/tools/index.ts +62 -5
- package/src/tools/move-session.ts +107 -0
- package/src/tools/python.ts +205 -0
- package/src/tools/read.ts +88 -15
- package/src/tools/renderers.ts +2 -0
- package/src/tools/resource-gc.ts +8 -177
- package/src/tools/search.ts +30 -6
- package/src/tools/skill-discovery.ts +1 -0
- package/src/tools/skill.ts +37 -7
- package/src/tools/subagent-render.ts +119 -13
- package/src/tools/subagent.ts +91 -74
- package/src/tools/tool-catalog.generated.ts +64 -2
- package/src/tools/write.ts +16 -12
- package/src/utils/xml-escape.ts +14 -0
- package/src/web/search/providers/codex.ts +2 -2
- package/src/workflow/workflow-intent-diff.ts +13 -12
- package/src/workflow/workflow-intent-report-consumer.ts +6 -6
- package/dist/types/autoresearch/helpers.d.ts +0 -24
- package/dist/types/autoresearch/index.d.ts +0 -2
- package/dist/types/autoresearch/state.d.ts +0 -17
- package/dist/types/autoresearch/storage.d.ts +0 -142
- package/dist/types/autoresearch/tools/init-experiment.d.ts +0 -31
- package/dist/types/autoresearch/tools/log-experiment.d.ts +0 -23
- package/dist/types/autoresearch/tools/run-experiment.d.ts +0 -8
- package/dist/types/autoresearch/tools/update-notes.d.ts +0 -12
- package/dist/types/commands/team.d.ts +0 -24
- package/dist/types/gjc-runtime/team-gc.d.ts +0 -7
- package/dist/types/gjc-runtime/team-launch.d.ts +0 -61
- package/dist/types/gjc-runtime/team-notify.d.ts +0 -45
- package/dist/types/gjc-runtime/team-runtime.d.ts +0 -336
- package/dist/types/gjc-runtime/team-store.d.ts +0 -168
- package/dist/types/gjc-runtime/team-worker-heartbeat.d.ts +0 -43
- package/dist/types/gjc-runtime/team-worker-memory-guard.d.ts +0 -92
- package/dist/types/gjc-runtime/team-workers.d.ts +0 -83
- package/dist/types/rlm/index.d.ts +0 -47
- package/dist/types/rlm/preset.d.ts +0 -23
- package/dist/types/rlm/python-tool.d.ts +0 -16
- package/src/autoresearch/command-resume.md +0 -14
- package/src/autoresearch/index.ts +0 -536
- package/src/autoresearch/prompt.md +0 -103
- package/src/autoresearch/resume-message.md +0 -10
- package/src/autoresearch/state.ts +0 -273
- package/src/autoresearch/storage.ts +0 -699
- package/src/autoresearch/tools/init-experiment.ts +0 -272
- package/src/autoresearch/tools/log-experiment.ts +0 -524
- package/src/autoresearch/tools/run-experiment.ts +0 -475
- package/src/autoresearch/tools/update-notes.ts +0 -109
- package/src/commands/rlm.ts +0 -19
- package/src/commands/team.ts +0 -238
- package/src/defaults/gjc/skills/deep-interview/auto-research-greenfield.md +0 -42
- package/src/defaults/gjc/skills/team/SKILL.md +0 -467
- package/src/gjc-runtime/team-gc.ts +0 -49
- package/src/gjc-runtime/team-launch.ts +0 -404
- package/src/gjc-runtime/team-notify.ts +0 -326
- package/src/gjc-runtime/team-runtime.ts +0 -5923
- package/src/gjc-runtime/team-store.ts +0 -878
- package/src/gjc-runtime/team-worker-heartbeat.ts +0 -149
- package/src/gjc-runtime/team-worker-memory-guard.ts +0 -363
- package/src/gjc-runtime/team-workers.ts +0 -1123
- package/src/prompts/system/rlm-report-command.md +0 -1
- package/src/prompts/system/rlm-research.md +0 -23
- package/src/rlm/index.ts +0 -365
- package/src/rlm/preset.ts +0 -76
- package/src/rlm/python-tool.ts +0 -68
package/src/sdk/session.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import * as nodeFs from "node:fs";
|
|
2
|
+
import * as fs from "node:fs/promises";
|
|
1
3
|
import * as path from "node:path";
|
|
2
4
|
import {
|
|
3
5
|
Agent,
|
|
@@ -33,6 +35,7 @@ import {
|
|
|
33
35
|
postmortem,
|
|
34
36
|
prompt,
|
|
35
37
|
Snowflake,
|
|
38
|
+
setProjectDir,
|
|
36
39
|
} from "@gajae-code/utils";
|
|
37
40
|
import {
|
|
38
41
|
createAppendOnlyContextManager,
|
|
@@ -46,9 +49,10 @@ import {
|
|
|
46
49
|
isBackgroundJobSupportEnabled,
|
|
47
50
|
jobElapsedMs,
|
|
48
51
|
} from "../async";
|
|
49
|
-
import { loadCapability } from "../capability";
|
|
52
|
+
import { loadCapability, reset as resetCapabilities } from "../capability";
|
|
50
53
|
import { type Rule, ruleCapability, setActiveRules } from "../capability/rule";
|
|
51
54
|
import type { SourceMeta } from "../capability/types";
|
|
55
|
+
import { AUTOROUTING_INACTIVE_WARNING } from "../config/autorouting-contract";
|
|
52
56
|
import { resolveModelProfileName } from "../config/model-profile-contract";
|
|
53
57
|
import { resolveProfileBindings } from "../config/model-profiles";
|
|
54
58
|
import { kNoAuth, ModelRegistry } from "../config/model-registry";
|
|
@@ -64,12 +68,15 @@ import { loadPromptTemplates as loadPromptTemplatesInternal, type PromptTemplate
|
|
|
64
68
|
import { Settings, type SkillsSettings } from "../config/settings";
|
|
65
69
|
import { resolveEagerTaskDelegation } from "../config/task-delegation";
|
|
66
70
|
import { CursorExecHandlers } from "../cursor";
|
|
71
|
+
import { EditTool } from "../edit";
|
|
67
72
|
import type { BashRestrictionProfile } from "../tools/bash-allowed-prefixes";
|
|
73
|
+
import { SearchTool } from "../tools/search";
|
|
68
74
|
import "../discovery";
|
|
69
75
|
import { resolveConfigValue } from "../config/resolve-config-value";
|
|
70
76
|
import { getEmbeddedDefaultGjcSkills } from "../defaults/gjc-defaults";
|
|
71
77
|
import { BUNDLED_GROK_BUILD_EXTENSION_ID, getBundledGrokBuildExtensionFactory } from "../defaults/gjc-grok-cli";
|
|
72
78
|
import { initializeWithSettings } from "../discovery";
|
|
79
|
+
import { clearPluginRootsAndCaches, resolveActiveProjectRegistryPath } from "../discovery/helpers";
|
|
73
80
|
import { TtsrManager } from "../export/ttsr";
|
|
74
81
|
import type { CustomCommandsLoadResult, LoadedCustomCommand } from "../extensibility/custom-commands";
|
|
75
82
|
import type { CustomTool, CustomToolContext, CustomToolSessionEvent } from "../extensibility/custom-tools/types";
|
|
@@ -109,6 +116,7 @@ import type { HindsightSessionState } from "../hindsight/state";
|
|
|
109
116
|
import { normalizePluginHook } from "../hooks/normalize";
|
|
110
117
|
import { initializeLocalRoot, LocalProtocolHandler, type LocalProtocolOptions } from "../internal-urls";
|
|
111
118
|
import type { LspStartupServerInfo } from "../lsp";
|
|
119
|
+
import { shutdownAll as shutdownAllLspClients } from "../lsp/client";
|
|
112
120
|
import btwUserPrompt from "../prompts/system/btw-user.md" with { type: "text" };
|
|
113
121
|
import asyncResultTemplate from "../prompts/tools/async-result.md" with { type: "text" };
|
|
114
122
|
import { AgentRegistry, MAIN_AGENT_ID } from "../registry/agent-registry";
|
|
@@ -129,9 +137,9 @@ import {
|
|
|
129
137
|
import { createReconciliationStore, type ReconciliationStore } from "../sdk/bus/reconciliation-store";
|
|
130
138
|
import { NotificationSessionController } from "../sdk/bus/session-control";
|
|
131
139
|
import { shouldHostSdk } from "../sdk/host";
|
|
140
|
+
import { markAutoroutingInactive } from "../sdk/host/internal-autorouting-state";
|
|
132
141
|
import { createSdkSessionRuntimeExtension, registerSdkOnlyNotificationCommand } from "../sdk/host/session-runtime";
|
|
133
142
|
import { createSdkWebSocketTransport } from "../sdk/host/websocket-transport";
|
|
134
|
-
|
|
135
143
|
import type { SecretObfuscator } from "../secrets";
|
|
136
144
|
import { AgentSession, type ForkContextSeed } from "../session/agent-session";
|
|
137
145
|
import { AuthBrokerClient, AuthStorage, RemoteAuthCredentialStore } from "../session/auth-storage";
|
|
@@ -161,6 +169,8 @@ import { AgentOutputManager } from "../task/output-manager";
|
|
|
161
169
|
import { parseThinkingLevel, resolveThinkingLevelForModel, toReasoningEffort } from "../thinking";
|
|
162
170
|
import { isMCPBridgeTool, selectRestorableDiscoveredBuiltinToolNames } from "../tool-discovery/tool-index";
|
|
163
171
|
import {
|
|
172
|
+
type AutomationToolName,
|
|
173
|
+
type AutomationTools,
|
|
164
174
|
BUILTIN_TOOL_DESCRIPTORS,
|
|
165
175
|
BUILTIN_TOOLS,
|
|
166
176
|
computeEssentialBuiltinNames,
|
|
@@ -183,6 +193,8 @@ import {
|
|
|
183
193
|
type SdkStartupCapability,
|
|
184
194
|
} from "./startup-capability";
|
|
185
195
|
|
|
196
|
+
export type { AutomationToolName, AutomationTools } from "../tools";
|
|
197
|
+
|
|
186
198
|
type AsyncResultEntry = {
|
|
187
199
|
jobId: string;
|
|
188
200
|
generation: string;
|
|
@@ -211,6 +223,14 @@ type McpNotificationEntry = {
|
|
|
211
223
|
uri: string;
|
|
212
224
|
};
|
|
213
225
|
|
|
226
|
+
/** Capture the cursor edit grant before the model-facing edit entry is removed. */
|
|
227
|
+
export function captureCursorEditTool<T>(
|
|
228
|
+
toolRegistry: ReadonlyMap<string, unknown>,
|
|
229
|
+
createReplaceTool: () => T,
|
|
230
|
+
): T | undefined {
|
|
231
|
+
return toolRegistry.has("edit") ? createReplaceTool() : undefined;
|
|
232
|
+
}
|
|
233
|
+
|
|
214
234
|
function buildAsyncResultBatchMessage(entries: AsyncResultEntry[]): CustomMessage<AsyncResultDetails> | null {
|
|
215
235
|
if (entries.length === 0) return null;
|
|
216
236
|
// Partition denied owned-completion entries out ENTIRELY before batch
|
|
@@ -440,6 +460,13 @@ export interface CreateAgentSessionOptions {
|
|
|
440
460
|
|
|
441
461
|
/** Custom tools to register (in addition to built-in tools). Accepts both CustomTool and ToolDefinition. */
|
|
442
462
|
customTools?: (CustomTool | ToolDefinition)[];
|
|
463
|
+
/**
|
|
464
|
+
* Host-owned implementations for the built-in `browser` and `computer`
|
|
465
|
+
* surfaces. These are materialized as built-ins, retain built-in provenance
|
|
466
|
+
* and activation behavior, and receive cancellation through AgentTool's
|
|
467
|
+
* AbortSignal. A matching custom or extension tool is rejected.
|
|
468
|
+
*/
|
|
469
|
+
automationTools?: AutomationTools;
|
|
443
470
|
/** Explicit parent/phase used to load active GJC sub-skill tools for this session. */
|
|
444
471
|
gjcSubskillToolContext?: { parent: string; phase: string; sessionId?: string; cwd?: string };
|
|
445
472
|
/** Inline extensions (merged with discovery). */
|
|
@@ -1246,7 +1273,31 @@ function findDeferredExactMcpToolNameCollisions(
|
|
|
1246
1273
|
return [...collisions];
|
|
1247
1274
|
}
|
|
1248
1275
|
|
|
1276
|
+
const AUTOMATION_TOOL_NAMES = new Set<AutomationToolName>(["browser", "computer"]);
|
|
1277
|
+
|
|
1278
|
+
function validateAutomationTools(options: CreateAgentSessionOptions): AutomationTools {
|
|
1279
|
+
const automationTools = options.automationTools ?? {};
|
|
1280
|
+
const entries = Object.entries(automationTools);
|
|
1281
|
+
for (const [name, tool] of entries) {
|
|
1282
|
+
if (!AUTOMATION_TOOL_NAMES.has(name as AutomationToolName)) {
|
|
1283
|
+
throw new Error(`Unsupported SDK automation tool name: ${name}`);
|
|
1284
|
+
}
|
|
1285
|
+
if (!tool || tool.name !== name) {
|
|
1286
|
+
throw new Error(`SDK automation tool ${name} must expose the built-in name ${JSON.stringify(name)}`);
|
|
1287
|
+
}
|
|
1288
|
+
}
|
|
1289
|
+
const externalNames = new Set(entries.map(([name]) => name));
|
|
1290
|
+
const collisions = [
|
|
1291
|
+
...new Set((options.customTools ?? []).map(tool => tool.name).filter(name => externalNames.has(name))),
|
|
1292
|
+
];
|
|
1293
|
+
if (collisions.length > 0) {
|
|
1294
|
+
throw new Error(`SDK automation tools cannot collide with custom tools: ${collisions.join(", ")}`);
|
|
1295
|
+
}
|
|
1296
|
+
return automationTools;
|
|
1297
|
+
}
|
|
1298
|
+
|
|
1249
1299
|
export async function createAgentSession(options: CreateAgentSessionOptions = {}): Promise<CreateAgentSessionResult> {
|
|
1300
|
+
const automationTools = validateAutomationTools(options);
|
|
1250
1301
|
const lifecycleStartupCapability = (
|
|
1251
1302
|
options as CreateAgentSessionOptions & { [lifecycleStartupCapabilityOption]?: SdkStartupCapability }
|
|
1252
1303
|
)[lifecycleStartupCapabilityOption];
|
|
@@ -1302,7 +1353,9 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
1302
1353
|
|
|
1303
1354
|
let agent: Agent;
|
|
1304
1355
|
let session!: AgentSession;
|
|
1356
|
+
let sessionManager!: SessionManager;
|
|
1305
1357
|
let hasSession = false;
|
|
1358
|
+
let processCwdClaimed = false;
|
|
1306
1359
|
let hasRegistered = false;
|
|
1307
1360
|
let asyncJobManager: AsyncJobManager | undefined;
|
|
1308
1361
|
let asyncJobManagerAdmitted = false;
|
|
@@ -1363,7 +1416,15 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
1363
1416
|
authStorage.setSessionCredentialSelector(scopeId, provider, selector);
|
|
1364
1417
|
};
|
|
1365
1418
|
const settings = options.settings ?? (await logger.time("settings", Settings.init, { cwd, agentDir }));
|
|
1366
|
-
const
|
|
1419
|
+
const autoroutingInactive =
|
|
1420
|
+
settings.get("task.autorouting.enabled") === true && !settings.getEffectiveAutorouting().active;
|
|
1421
|
+
// Cwd-derived runtime state must follow a rescope (`move_session`, `/move`),
|
|
1422
|
+
// so services resolve the LIVE session cwd per activation instead of
|
|
1423
|
+
// capturing the launch root. Before the manager exists the launch cwd is the
|
|
1424
|
+
// only truth available, and it is also the manager's initial cwd.
|
|
1425
|
+
let liveSessionManager: SessionManager | undefined;
|
|
1426
|
+
const getLiveCwd = (): string => liveSessionManager?.getCwd() ?? cwd;
|
|
1427
|
+
const runtimeServices = createOptionalRuntimeServices(settings, options.runtimeServices, { cwd: getLiveCwd });
|
|
1367
1428
|
modelRegistry.applyConfiguredModelBindings(settings);
|
|
1368
1429
|
logger.time("initializeWithSettings", initializeWithSettings, settings);
|
|
1369
1430
|
if (!options.modelRegistry) {
|
|
@@ -1420,11 +1481,12 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
1420
1481
|
}
|
|
1421
1482
|
applyConfiguredSearchTimeout(settings);
|
|
1422
1483
|
|
|
1423
|
-
|
|
1484
|
+
sessionManager =
|
|
1424
1485
|
options.sessionManager ??
|
|
1425
1486
|
(await logger.time("sessionManager", async () => {
|
|
1426
1487
|
return SessionManager.create(cwd, SessionManager.managedDestination(cwd, agentDir));
|
|
1427
1488
|
}));
|
|
1489
|
+
liveSessionManager = sessionManager;
|
|
1428
1490
|
const logicalSessionId = sessionManager.getSessionId();
|
|
1429
1491
|
// Fork-context seeds carry conversation content only, never provider identity:
|
|
1430
1492
|
// a shared continuity id would make concurrent subagents present the same
|
|
@@ -1866,7 +1928,10 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
1866
1928
|
contextFilesResultPromise,
|
|
1867
1929
|
raceWithDeadline("buildWorkspaceTree", workspaceTreePromise),
|
|
1868
1930
|
]);
|
|
1869
|
-
|
|
1931
|
+
// Mutable: a rescope re-discovers cwd-derived project instructions so the
|
|
1932
|
+
// model is never shown the launcher root's AGENTS.md alongside the new cwd.
|
|
1933
|
+
let contextFiles = contextFilesResult.contextFiles;
|
|
1934
|
+
let liveWorkspaceTree: WorkspaceTree | undefined = resolvedWorkspaceTree;
|
|
1870
1935
|
const discoveredContextFileWarnings = contextFilesResult.warnings;
|
|
1871
1936
|
|
|
1872
1937
|
const backgroundJobsEnabled = isBackgroundJobSupportEnabled(settings);
|
|
@@ -1974,6 +2039,168 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
1974
2039
|
if (model) return formatModelString(model);
|
|
1975
2040
|
return undefined;
|
|
1976
2041
|
};
|
|
2042
|
+
let mcpManager: MCPManager | undefined = options.mcpManager;
|
|
2043
|
+
let ownsMcpManager = false;
|
|
2044
|
+
const cwdCapturingToolNames: string[] = [];
|
|
2045
|
+
const notificationDebounceTimers = new Map<string, Timer>();
|
|
2046
|
+
const wireMcpManagerCallbacks = (manager: MCPManager): void => {
|
|
2047
|
+
manager.setOnPromptsChanged(serverName => {
|
|
2048
|
+
const promptCommands = buildMCPPromptCommands(manager);
|
|
2049
|
+
session.setMCPPromptCommands(promptCommands);
|
|
2050
|
+
logger.debug("MCP prompt commands refreshed", { path: `mcp:${serverName}` });
|
|
2051
|
+
});
|
|
2052
|
+
manager.setOnResourcesChanged((serverName, uri) => {
|
|
2053
|
+
logger.debug("MCP resources changed", { path: `mcp:${serverName}`, uri });
|
|
2054
|
+
if (!settings.get("mcp.notifications")) return;
|
|
2055
|
+
const debounceMs = settings.get("mcp.notificationDebounceMs");
|
|
2056
|
+
const key = `${serverName}:${uri}`;
|
|
2057
|
+
const existing = notificationDebounceTimers.get(key);
|
|
2058
|
+
if (existing) clearTimeout(existing);
|
|
2059
|
+
notificationDebounceTimers.set(
|
|
2060
|
+
key,
|
|
2061
|
+
setTimeout(() => {
|
|
2062
|
+
notificationDebounceTimers.delete(key);
|
|
2063
|
+
if (!settings.get("mcp.notifications")) return;
|
|
2064
|
+
session.yieldQueue.enqueue<McpNotificationEntry>("mcp-notification", { serverName, uri });
|
|
2065
|
+
}, debounceMs),
|
|
2066
|
+
);
|
|
2067
|
+
});
|
|
2068
|
+
};
|
|
2069
|
+
const rebindCwdCapturingAuthority = async (to: string): Promise<void> => {
|
|
2070
|
+
if (!session) return;
|
|
2071
|
+
if (options.mcpManager && !ownsMcpManager) {
|
|
2072
|
+
throw new Error(
|
|
2073
|
+
"Cannot rescope a session with caller-owned MCP authority; recreate the session at the target cwd.",
|
|
2074
|
+
);
|
|
2075
|
+
}
|
|
2076
|
+
await session.refreshGjcSubskillTools();
|
|
2077
|
+
const previousCwdCapturing = [...cwdCapturingToolNames];
|
|
2078
|
+
const nextCwdCapturing: string[] = [];
|
|
2079
|
+
const nextCustomTools: CustomTool[] = [];
|
|
2080
|
+
try {
|
|
2081
|
+
const declarations = await getGjcPluginToolDeclarations(to);
|
|
2082
|
+
const pluginToolResult = await loadAlwaysOnPluginTools({
|
|
2083
|
+
cwd: to,
|
|
2084
|
+
reservedToolNames: session.getAllToolNames().filter(name => !previousCwdCapturing.includes(name)),
|
|
2085
|
+
declarations,
|
|
2086
|
+
});
|
|
2087
|
+
nextCustomTools.push(...pluginToolResult.tools);
|
|
2088
|
+
nextCwdCapturing.push(...pluginToolResult.tools.map(tool => tool.name));
|
|
2089
|
+
} catch (error) {
|
|
2090
|
+
logger.warn("Failed to reload always-on plugin tools after session rescope", {
|
|
2091
|
+
error: error instanceof Error ? error.message : String(error),
|
|
2092
|
+
});
|
|
2093
|
+
}
|
|
2094
|
+
if (!options.mcpManager && explicitMcpConfigPath === undefined) {
|
|
2095
|
+
const previousManager = mcpManager;
|
|
2096
|
+
if (previousManager) await previousManager.disconnectAll().catch(() => {});
|
|
2097
|
+
pluginMcpToolNames.length = 0;
|
|
2098
|
+
conventionalMcpToolNames.length = 0;
|
|
2099
|
+
let nextManager: MCPManager | undefined;
|
|
2100
|
+
try {
|
|
2101
|
+
const loaded = await loadAllMCPConfigs(to, {
|
|
2102
|
+
agentDir,
|
|
2103
|
+
enableProjectConfig: settings.has("mcp.enableProjectConfig")
|
|
2104
|
+
? settings.get("mcp.enableProjectConfig")
|
|
2105
|
+
: true,
|
|
2106
|
+
autoloadOnly: true,
|
|
2107
|
+
nativeOnly: true,
|
|
2108
|
+
});
|
|
2109
|
+
const { configs: pluginConfigs } = await buildPluginMcpConfigs({ cwd: to });
|
|
2110
|
+
const pluginNames = new Set(Object.keys(pluginConfigs));
|
|
2111
|
+
const mergedConfigs = { ...loaded.configs, ...pluginConfigs };
|
|
2112
|
+
const mergedSources = {
|
|
2113
|
+
...loaded.sources,
|
|
2114
|
+
...Object.fromEntries(
|
|
2115
|
+
Object.keys(pluginConfigs).map(name => [
|
|
2116
|
+
name,
|
|
2117
|
+
{ provider: "gjc-plugins", providerName: "GJC plugin bundle", level: "project" as const },
|
|
2118
|
+
]),
|
|
2119
|
+
),
|
|
2120
|
+
};
|
|
2121
|
+
if (Object.keys(mergedConfigs).length > 0) {
|
|
2122
|
+
nextManager = new MCPManager(to, null, { sharedPoolIdleMs: settings.get("mcp.sharedPoolIdleMs") });
|
|
2123
|
+
nextManager.setAuthStorage(authStorage);
|
|
2124
|
+
wireMcpManagerCallbacks(nextManager);
|
|
2125
|
+
const result = await nextManager.connectServers(mergedConfigs, mergedSources as never);
|
|
2126
|
+
nextCustomTools.push(...(result.tools as CustomTool[]));
|
|
2127
|
+
nextCwdCapturing.push(...result.tools.map(tool => tool.name));
|
|
2128
|
+
for (const tool of result.tools) {
|
|
2129
|
+
const serverName = tool.mcpServerName;
|
|
2130
|
+
if (serverName === undefined) continue;
|
|
2131
|
+
if (pluginNames.has(serverName)) pluginMcpToolNames.push(tool.name);
|
|
2132
|
+
else conventionalMcpToolNames.push(tool.name);
|
|
2133
|
+
}
|
|
2134
|
+
}
|
|
2135
|
+
} catch (error) {
|
|
2136
|
+
logger.warn("Failed to recreate MCP authority after session rescope", {
|
|
2137
|
+
error: error instanceof Error ? error.message : String(error),
|
|
2138
|
+
});
|
|
2139
|
+
}
|
|
2140
|
+
mcpManager = nextManager;
|
|
2141
|
+
ownsMcpManager = Boolean(nextManager);
|
|
2142
|
+
await session.replaceOwnedMcpManager(nextManager);
|
|
2143
|
+
await session.refreshMCPTools((nextManager?.getTools() ?? []) as CustomTool[]);
|
|
2144
|
+
}
|
|
2145
|
+
cwdCapturingToolNames.length = 0;
|
|
2146
|
+
cwdCapturingToolNames.push(...nextCwdCapturing);
|
|
2147
|
+
await session.replaceNamedCustomTools(
|
|
2148
|
+
previousCwdCapturing.filter(name => !nextCustomTools.some(tool => tool.name === name)),
|
|
2149
|
+
nextCustomTools,
|
|
2150
|
+
);
|
|
2151
|
+
};
|
|
2152
|
+
|
|
2153
|
+
/**
|
|
2154
|
+
* Re-discover the cwd-derived read-only state the model is shown after a
|
|
2155
|
+
* committed rescope: project instructions, skills, and the workspace tree.
|
|
2156
|
+
* Without this the volatile message pairs the NEW cwd with the launch
|
|
2157
|
+
* root's AGENTS.md and tree, and subagents inherit the same mismatch.
|
|
2158
|
+
*/
|
|
2159
|
+
const applyRescopedReadState = async (to: string): Promise<void> => {
|
|
2160
|
+
try {
|
|
2161
|
+
const rediscovered = await loadContextFilesResultInternal({ cwd: to });
|
|
2162
|
+
contextFiles = rediscovered.contextFiles;
|
|
2163
|
+
} catch (error) {
|
|
2164
|
+
logger.warn("Failed to re-discover context files after session rescope", {
|
|
2165
|
+
error: safeErrorForLog(error),
|
|
2166
|
+
});
|
|
2167
|
+
}
|
|
2168
|
+
if (options.skills === undefined && settings.get("skills.enabled")) {
|
|
2169
|
+
try {
|
|
2170
|
+
const reloaded = await loadSkills({
|
|
2171
|
+
...settings.getGroup("skills"),
|
|
2172
|
+
cwd: to,
|
|
2173
|
+
disabledExtensions: settings.get("disabledExtensions"),
|
|
2174
|
+
});
|
|
2175
|
+
skills = withEmbeddedDefaultGjcSkills(reloaded.skills);
|
|
2176
|
+
if (!options.parentTaskPrefix) setActiveSkills(skills);
|
|
2177
|
+
await session?.replaceSkills(skills);
|
|
2178
|
+
} catch (error) {
|
|
2179
|
+
logger.warn("Failed to reload skills after session rescope", { error: safeErrorForLog(error) });
|
|
2180
|
+
}
|
|
2181
|
+
}
|
|
2182
|
+
// The launch-bound tree is retired immediately: a stale root-scoped tree is
|
|
2183
|
+
// worse than none, and the next turn re-scans at the new cwd.
|
|
2184
|
+
liveWorkspaceTree = undefined;
|
|
2185
|
+
workspaceTreePromise = Promise.resolve({
|
|
2186
|
+
rootPath: to,
|
|
2187
|
+
rendered: "",
|
|
2188
|
+
truncated: false,
|
|
2189
|
+
totalLines: 0,
|
|
2190
|
+
agentsMdFiles: [],
|
|
2191
|
+
});
|
|
2192
|
+
workspaceTreePromise.catch(() => {});
|
|
2193
|
+
try {
|
|
2194
|
+
session?.retireWorkspaceTreeForRescope();
|
|
2195
|
+
await session?.refreshBaseSystemPrompt();
|
|
2196
|
+
} catch (error) {
|
|
2197
|
+
logger.warn("Committed session rescope could not refresh the post-move prompt", {
|
|
2198
|
+
error: safeErrorForLog(error),
|
|
2199
|
+
cwd: to,
|
|
2200
|
+
});
|
|
2201
|
+
}
|
|
2202
|
+
};
|
|
2203
|
+
|
|
1977
2204
|
const toolSession: ToolSession = {
|
|
1978
2205
|
get cwd() {
|
|
1979
2206
|
return sessionManager.getCwd();
|
|
@@ -1988,14 +2215,248 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
1988
2215
|
return !requestedToolNames || requestedToolNames.includes("edit");
|
|
1989
2216
|
},
|
|
1990
2217
|
skipPythonPreflight: options.skipPythonPreflight,
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
2218
|
+
// Getters, not snapshots: subagents launched after a rescope inherit the
|
|
2219
|
+
// CURRENT cwd's context files, skills, and tree rather than the launch
|
|
2220
|
+
// root's, which would otherwise pair the new cwd with retired instructions.
|
|
2221
|
+
get contextFiles() {
|
|
2222
|
+
return contextFiles;
|
|
2223
|
+
},
|
|
2224
|
+
get workspaceTree() {
|
|
2225
|
+
return liveWorkspaceTree;
|
|
2226
|
+
},
|
|
2227
|
+
get skills() {
|
|
2228
|
+
return skills;
|
|
2229
|
+
},
|
|
1994
2230
|
eventBus,
|
|
1995
2231
|
outputSchema: options.outputSchema,
|
|
1996
2232
|
requireYieldTool: options.requireYieldTool,
|
|
1997
2233
|
taskDepth: options.taskDepth ?? 0,
|
|
1998
2234
|
currentAgentType: options.currentAgentType,
|
|
2235
|
+
// Agent-invokable session rescope (#4629). Provided only where
|
|
2236
|
+
// relocation is safe: canonical top-level sessions (the same
|
|
2237
|
+
// isCanonicalSubSession predicate that gates SDK hosting — taskDepth 0,
|
|
2238
|
+
// no parentTaskPrefix, no currentAgentType) without a restricted bash
|
|
2239
|
+
// surface. Runs the same sequence as the text/ACP `/move` handler so
|
|
2240
|
+
// tool path resolution, the bash default cwd, and plugin caches follow
|
|
2241
|
+
// the move. Unlike the user-driven `/move`, the model-invoked accessor
|
|
2242
|
+
// is bound to at most one successful move per session, rejects
|
|
2243
|
+
// re-entrant calls, refuses to move while a workflow skill is active
|
|
2244
|
+
// (its cwd-local state and guards stay pinned to the launch root), and
|
|
2245
|
+
// only narrows: the canonical target must be a strict descendant of
|
|
2246
|
+
// the canonical current cwd, so an injected or speculative call cannot
|
|
2247
|
+
// widen the session's tool/write scope to a parent, a sibling project,
|
|
2248
|
+
// or an arbitrary absolute path.
|
|
2249
|
+
...(!isCanonicalSubSession &&
|
|
2250
|
+
!options.bashRestrictionProfile &&
|
|
2251
|
+
(options.bashAllowedPrefixes ?? []).length === 0 &&
|
|
2252
|
+
!options.mcpManager &&
|
|
2253
|
+
explicitMcpConfigPath === undefined &&
|
|
2254
|
+
options.workspaceTree === undefined
|
|
2255
|
+
? {
|
|
2256
|
+
rescopeSessionCwd: (() => {
|
|
2257
|
+
let moveConsumed = false;
|
|
2258
|
+
return async (target: string): Promise<{ from: string; to: string }> => {
|
|
2259
|
+
if (moveConsumed) {
|
|
2260
|
+
throw new Error(
|
|
2261
|
+
"This session has already been rescoped; only one agent-invoked move is allowed per session.",
|
|
2262
|
+
);
|
|
2263
|
+
}
|
|
2264
|
+
if (session?.getEffectiveActiveWorkflowSkillState()) {
|
|
2265
|
+
throw new Error(
|
|
2266
|
+
"A workflow skill is active in this session; finish or exit it before rescoping.",
|
|
2267
|
+
);
|
|
2268
|
+
}
|
|
2269
|
+
if (options.mcpManager && !ownsMcpManager) {
|
|
2270
|
+
throw new Error(
|
|
2271
|
+
"Cannot rescope a session with caller-owned MCP authority; recreate the session at the target cwd.",
|
|
2272
|
+
);
|
|
2273
|
+
}
|
|
2274
|
+
return sessionManager.runExclusiveCwdTransition(async () => {
|
|
2275
|
+
if (moveConsumed) {
|
|
2276
|
+
throw new Error(
|
|
2277
|
+
"This session has already been rescoped; only one agent-invoked move is allowed per session.",
|
|
2278
|
+
);
|
|
2279
|
+
}
|
|
2280
|
+
if (session?.getEffectiveActiveWorkflowSkillState()) {
|
|
2281
|
+
throw new Error(
|
|
2282
|
+
"A workflow skill became active while waiting for the cwd transition; finish or exit it before rescoping.",
|
|
2283
|
+
);
|
|
2284
|
+
}
|
|
2285
|
+
const from = sessionManager.getCwd();
|
|
2286
|
+
const resolvedPath = path.resolve(from, target);
|
|
2287
|
+
let canonicalFrom: string;
|
|
2288
|
+
let canonicalTarget: string;
|
|
2289
|
+
try {
|
|
2290
|
+
canonicalFrom = await fs.realpath(from);
|
|
2291
|
+
canonicalTarget = await fs.realpath(resolvedPath);
|
|
2292
|
+
} catch {
|
|
2293
|
+
throw new Error(`Directory does not exist or is not a directory: ${resolvedPath}`);
|
|
2294
|
+
}
|
|
2295
|
+
if (!(await fs.stat(canonicalTarget)).isDirectory()) {
|
|
2296
|
+
throw new Error(`Directory does not exist or is not a directory: ${resolvedPath}`);
|
|
2297
|
+
}
|
|
2298
|
+
const relative = path.relative(canonicalFrom, canonicalTarget);
|
|
2299
|
+
if (relative === "") {
|
|
2300
|
+
throw new Error(
|
|
2301
|
+
`Target ${canonicalTarget} is the current session directory; nothing to move.`,
|
|
2302
|
+
);
|
|
2303
|
+
}
|
|
2304
|
+
if (relative === ".." || relative.startsWith(`..${path.sep}`) || path.isAbsolute(relative)) {
|
|
2305
|
+
throw new Error(
|
|
2306
|
+
`Refusing to rescope outside the current session directory: ${canonicalTarget} is not within ${canonicalFrom}. move_session only narrows the session scope; ask the user to restart or /move for a broader relocation.`,
|
|
2307
|
+
);
|
|
2308
|
+
}
|
|
2309
|
+
let targetHandle: nodeFs.promises.FileHandle | undefined;
|
|
2310
|
+
let expectedIdentity: { dev: bigint; ino: bigint };
|
|
2311
|
+
try {
|
|
2312
|
+
targetHandle = await SessionManager.openNoFollowDirectory(canonicalTarget);
|
|
2313
|
+
const opened = await targetHandle.stat({ bigint: true });
|
|
2314
|
+
if (!opened.isDirectory()) {
|
|
2315
|
+
throw new Error(`Directory does not exist or is not a directory: ${resolvedPath}`);
|
|
2316
|
+
}
|
|
2317
|
+
expectedIdentity = { dev: opened.dev, ino: opened.ino };
|
|
2318
|
+
await fs.access(canonicalTarget, nodeFs.constants.R_OK | nodeFs.constants.X_OK);
|
|
2319
|
+
} catch (error) {
|
|
2320
|
+
await targetHandle?.close().catch(() => {});
|
|
2321
|
+
if (error instanceof Error && error.message.startsWith("Directory does not exist")) {
|
|
2322
|
+
throw error;
|
|
2323
|
+
}
|
|
2324
|
+
throw new Error(
|
|
2325
|
+
`Directory identity or access unavailable: ${canonicalTarget}${
|
|
2326
|
+
error instanceof Error ? ` (${error.message})` : ""
|
|
2327
|
+
}`,
|
|
2328
|
+
);
|
|
2329
|
+
}
|
|
2330
|
+
// Process-cwd authority is an explicit claim, never inferred from
|
|
2331
|
+
// `process.cwd() === from`: sibling sessions launched at the same
|
|
2332
|
+
// root both satisfy that, so acting on it would chdir the process
|
|
2333
|
+
// and clear process-global caches underneath the sibling.
|
|
2334
|
+
const ownsProcessCwd = SessionManager.isProcessCwdOwner(sessionManager);
|
|
2335
|
+
const restoreLaunchRoot = async (failure: unknown): Promise<never> => {
|
|
2336
|
+
const restoreErrors: Error[] = [];
|
|
2337
|
+
if (ownsProcessCwd) {
|
|
2338
|
+
try {
|
|
2339
|
+
setProjectDir(canonicalFrom);
|
|
2340
|
+
if (path.resolve(process.cwd()) !== path.resolve(canonicalFrom)) {
|
|
2341
|
+
throw new Error("Process cwd did not restore to the launch root.");
|
|
2342
|
+
}
|
|
2343
|
+
} catch (error) {
|
|
2344
|
+
restoreErrors.push(error instanceof Error ? error : new Error(String(error)));
|
|
2345
|
+
}
|
|
2346
|
+
try {
|
|
2347
|
+
resetCapabilities();
|
|
2348
|
+
const restoreRegistry = await resolveActiveProjectRegistryPath(canonicalFrom).catch(
|
|
2349
|
+
() => undefined,
|
|
2350
|
+
);
|
|
2351
|
+
clearPluginRootsAndCaches(restoreRegistry ? [restoreRegistry] : undefined);
|
|
2352
|
+
} catch (error) {
|
|
2353
|
+
restoreErrors.push(error instanceof Error ? error : new Error(String(error)));
|
|
2354
|
+
}
|
|
2355
|
+
}
|
|
2356
|
+
try {
|
|
2357
|
+
await rebindCwdCapturingAuthority(canonicalFrom);
|
|
2358
|
+
} catch (error) {
|
|
2359
|
+
restoreErrors.push(error instanceof Error ? error : new Error(String(error)));
|
|
2360
|
+
}
|
|
2361
|
+
if (restoreErrors.length > 0) {
|
|
2362
|
+
throw new AggregateError(
|
|
2363
|
+
restoreErrors,
|
|
2364
|
+
"Failed to restore launch-root rescope authority.",
|
|
2365
|
+
{
|
|
2366
|
+
cause: failure,
|
|
2367
|
+
},
|
|
2368
|
+
);
|
|
2369
|
+
}
|
|
2370
|
+
throw failure;
|
|
2371
|
+
};
|
|
2372
|
+
try {
|
|
2373
|
+
// Every fallible step that the moved session depends on runs
|
|
2374
|
+
// BEFORE the session-file commit, so a failure here leaves the
|
|
2375
|
+
// session exactly where it was and the tool call is a clean
|
|
2376
|
+
// rejection rather than a half-moved session.
|
|
2377
|
+
if (ownsProcessCwd) {
|
|
2378
|
+
setProjectDir(canonicalTarget);
|
|
2379
|
+
try {
|
|
2380
|
+
// `setProjectDir` chdirs a NAME. Confirm the process actually
|
|
2381
|
+
// landed on the pinned directory, so a path replaced after
|
|
2382
|
+
// the name checks cannot escape the validated descendant.
|
|
2383
|
+
await SessionManager.assertProcessCwdIdentity(expectedIdentity);
|
|
2384
|
+
} catch (error) {
|
|
2385
|
+
setProjectDir(canonicalFrom);
|
|
2386
|
+
throw error;
|
|
2387
|
+
}
|
|
2388
|
+
}
|
|
2389
|
+
let rescopeFailure: unknown;
|
|
2390
|
+
try {
|
|
2391
|
+
if (ownsProcessCwd) {
|
|
2392
|
+
resetCapabilities();
|
|
2393
|
+
const projectRegistry = await resolveActiveProjectRegistryPath(canonicalTarget);
|
|
2394
|
+
clearPluginRootsAndCaches(projectRegistry ? [projectRegistry] : undefined);
|
|
2395
|
+
}
|
|
2396
|
+
// Plugin/MCP/Python authority must be rebound successfully
|
|
2397
|
+
// before committing; swallowing a failure here is what leaves
|
|
2398
|
+
// a moved session holding launch-root tool authority.
|
|
2399
|
+
await rebindCwdCapturingAuthority(canonicalTarget);
|
|
2400
|
+
} catch (error) {
|
|
2401
|
+
rescopeFailure = error;
|
|
2402
|
+
}
|
|
2403
|
+
if (rescopeFailure !== undefined) {
|
|
2404
|
+
await restoreLaunchRoot(rescopeFailure);
|
|
2405
|
+
}
|
|
2406
|
+
try {
|
|
2407
|
+
await sessionManager.flush();
|
|
2408
|
+
// Commit last: `moveTo` re-validates the pinned identity through
|
|
2409
|
+
// the still-open handle at the state-changing boundary.
|
|
2410
|
+
await sessionManager.moveTo(canonicalTarget, {
|
|
2411
|
+
expectedIdentity,
|
|
2412
|
+
targetHandle,
|
|
2413
|
+
});
|
|
2414
|
+
} catch (error) {
|
|
2415
|
+
const committedCwd = sessionManager.getCwd();
|
|
2416
|
+
const stayedAtLaunchRoot = path.resolve(committedCwd) === path.resolve(from);
|
|
2417
|
+
if (stayedAtLaunchRoot) await restoreLaunchRoot(error);
|
|
2418
|
+
// SessionManager can publish the durable move before a later metadata
|
|
2419
|
+
// write fails. Treat that state as committed rather than reporting a
|
|
2420
|
+
// rejection after the session has moved.
|
|
2421
|
+
moveConsumed = true;
|
|
2422
|
+
logger.warn("Session rescope committed before finalization failed", {
|
|
2423
|
+
error: safeErrorForLog(error),
|
|
2424
|
+
cwd: committedCwd,
|
|
2425
|
+
});
|
|
2426
|
+
}
|
|
2427
|
+
moveConsumed = true;
|
|
2428
|
+
if (ownsProcessCwd) {
|
|
2429
|
+
try {
|
|
2430
|
+
await shutdownAllLspClients();
|
|
2431
|
+
} catch (error) {
|
|
2432
|
+
logger.warn("Failed to reset launch-root LSP clients after session rescope", {
|
|
2433
|
+
error: safeErrorForLog(error),
|
|
2434
|
+
});
|
|
2435
|
+
}
|
|
2436
|
+
}
|
|
2437
|
+
// Cwd-derived read-only state the prompt and subagents consume.
|
|
2438
|
+
// Best-effort by design: the move is committed, and a failed
|
|
2439
|
+
// re-discovery must not present a committed move as a failure.
|
|
2440
|
+
await applyRescopedReadState(sessionManager.getCwd());
|
|
2441
|
+
try {
|
|
2442
|
+
await session?.refreshSshTool({ activateIfAvailable: true });
|
|
2443
|
+
} catch (error) {
|
|
2444
|
+
// Non-fatal: the session has moved; the SSH tool refreshes
|
|
2445
|
+
// on its next activation attempt.
|
|
2446
|
+
logger.warn("Committed session rescope could not refresh the SSH tool", {
|
|
2447
|
+
error: safeErrorForLog(error),
|
|
2448
|
+
cwd: sessionManager.getCwd(),
|
|
2449
|
+
});
|
|
2450
|
+
}
|
|
2451
|
+
return { from, to: sessionManager.getCwd() };
|
|
2452
|
+
} finally {
|
|
2453
|
+
await targetHandle.close().catch(() => {});
|
|
2454
|
+
}
|
|
2455
|
+
});
|
|
2456
|
+
};
|
|
2457
|
+
})(),
|
|
2458
|
+
}
|
|
2459
|
+
: {}),
|
|
1999
2460
|
getSessionFile: () => sessionManager.getSessionFile() ?? null,
|
|
2000
2461
|
getEvalKernelOwnerId: () => evalKernelOwnerId,
|
|
2001
2462
|
assertEvalExecutionAllowed: () => session?.assertEvalExecutionAllowed(),
|
|
@@ -2123,6 +2584,13 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
2123
2584
|
if (!options.parentTaskPrefix) {
|
|
2124
2585
|
setActiveSkills(skills);
|
|
2125
2586
|
setActiveRules([...rulebookRules, ...alwaysApplyRules]);
|
|
2587
|
+
// Claim process-cwd authority for the FIRST top-level session launched at
|
|
2588
|
+
// the process cwd. A later sibling launched at the same root does not get
|
|
2589
|
+
// the claim, so its rescope leaves process-global state (chdir,
|
|
2590
|
+
// capabilities, plugin caches, browser tab cwd) untouched.
|
|
2591
|
+
if (!isCanonicalSubSession && path.resolve(process.cwd()) === path.resolve(sessionManager.getCwd())) {
|
|
2592
|
+
processCwdClaimed = SessionManager.claimProcessCwdOwnership(sessionManager);
|
|
2593
|
+
}
|
|
2126
2594
|
if (asyncJobManager) {
|
|
2127
2595
|
// Register under the session endpoint so concurrent sessions'
|
|
2128
2596
|
// owned work settles in the correct manager (review thread P1).
|
|
@@ -2164,7 +2632,13 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
2164
2632
|
);
|
|
2165
2633
|
|
|
2166
2634
|
// Create built-in tools (already wrapped with meta notice formatting)
|
|
2167
|
-
const builtinTools = await logger.time(
|
|
2635
|
+
const builtinTools = await logger.time(
|
|
2636
|
+
"createAllTools",
|
|
2637
|
+
createTools,
|
|
2638
|
+
toolSession,
|
|
2639
|
+
options.toolNames,
|
|
2640
|
+
automationTools,
|
|
2641
|
+
);
|
|
2168
2642
|
|
|
2169
2643
|
// A top-level session loads MCP tools from three bounded surfaces: a
|
|
2170
2644
|
// caller-supplied exact config (`--mcp-config`), conventional native
|
|
@@ -2172,8 +2646,6 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
2172
2646
|
// written by `gjc mcp add`; disabled by `--no-mcp`), and plugin-bundle
|
|
2173
2647
|
// MCP servers (created below after `customTools` is populated). Existing
|
|
2174
2648
|
// caller-supplied managers remain available for legacy in-process callers.
|
|
2175
|
-
let mcpManager: MCPManager | undefined = options.mcpManager;
|
|
2176
|
-
let ownsMcpManager = false;
|
|
2177
2649
|
const customTools: CustomTool[] = [];
|
|
2178
2650
|
const exactMcpToolNames: string[] = [];
|
|
2179
2651
|
const pluginMcpToolNames: string[] = [];
|
|
@@ -2263,7 +2735,10 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
2263
2735
|
reservedToolNames: [...getReservedSubskillToolNames(), ...customTools.map(tool => tool.name)],
|
|
2264
2736
|
declarations: gjcToolDeclarations,
|
|
2265
2737
|
});
|
|
2266
|
-
if (pluginToolResult.tools.length > 0)
|
|
2738
|
+
if (pluginToolResult.tools.length > 0) {
|
|
2739
|
+
customTools.push(...pluginToolResult.tools);
|
|
2740
|
+
cwdCapturingToolNames.push(...pluginToolResult.tools.map(tool => tool.name));
|
|
2741
|
+
}
|
|
2267
2742
|
for (const q of pluginToolResult.quarantine) {
|
|
2268
2743
|
gjcFindings.add({ identity: q.identity, surfaceId: q.surfaceId, code: q.code, message: q.message });
|
|
2269
2744
|
logger.warn("Quarantined GJC plugin surface", { plugin: q.plugin, surface: q.surfaceId, code: q.code });
|
|
@@ -2293,6 +2768,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
2293
2768
|
const resultTools = result.tools as CustomTool[];
|
|
2294
2769
|
exactMcpToolNames.push(...resultTools.map(tool => tool.name));
|
|
2295
2770
|
customTools.push(...resultTools);
|
|
2771
|
+
cwdCapturingToolNames.push(...resultTools.map(tool => tool.name));
|
|
2296
2772
|
if (result.errors.size > 0 || result.tools.length === 0) {
|
|
2297
2773
|
logger.warn("MCP tools could not be loaded.");
|
|
2298
2774
|
}
|
|
@@ -2314,6 +2790,10 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
2314
2790
|
if (options.enableMcpAutoload !== false) {
|
|
2315
2791
|
try {
|
|
2316
2792
|
const loaded = await loadAllMCPConfigs(cwd, {
|
|
2793
|
+
// User scope is this session's agent directory, the same file
|
|
2794
|
+
// `gjc mcp add` (user scope) writes; an SDK embedder that runs on
|
|
2795
|
+
// its own agent directory autoloads its own registrations.
|
|
2796
|
+
agentDir,
|
|
2317
2797
|
// Project-scope native config loads by default; only an
|
|
2318
2798
|
// explicitly configured `mcp.enableProjectConfig: false`
|
|
2319
2799
|
// disables it (the legacy schema default stays false for
|
|
@@ -2383,6 +2863,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
2383
2863
|
mcpManager = owned;
|
|
2384
2864
|
ownsMcpManager = true;
|
|
2385
2865
|
customTools.push(...(result.tools as CustomTool[]));
|
|
2866
|
+
cwdCapturingToolNames.push(...result.tools.map(tool => tool.name));
|
|
2386
2867
|
const connectedPluginNames = new Set(
|
|
2387
2868
|
result.connectedServers.filter(name => pluginNames.has(name)),
|
|
2388
2869
|
);
|
|
@@ -2546,6 +3027,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
2546
3027
|
if (notificationsExtensionEligible || sdkHostEligible) {
|
|
2547
3028
|
inlineExtensions.push(async api => {
|
|
2548
3029
|
try {
|
|
3030
|
+
if (autoroutingInactive) markAutoroutingInactive(api);
|
|
2549
3031
|
if (lifecycleStartupCapability) attachLifecycleStartupCapability(api, lifecycleStartupCapability);
|
|
2550
3032
|
if (lifecycleStartupCapability && process.env.GJC_SDK_TEST_FACTORY_FAILURE === cwd)
|
|
2551
3033
|
throw new Error(process.env.GJC_SDK_TEST_FACTORY_SECRET ?? "Lifecycle factory test failure.");
|
|
@@ -2553,6 +3035,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
2553
3035
|
const createNotificationsExtension = await notificationAdapterService.get("session-extension");
|
|
2554
3036
|
createNotificationsExtension(api, {
|
|
2555
3037
|
settings,
|
|
3038
|
+
|
|
2556
3039
|
controller: notificationSessionController,
|
|
2557
3040
|
spawnedByGjc,
|
|
2558
3041
|
sdkHostModeSupported: options.sdkHostModeSupported,
|
|
@@ -2741,14 +3224,23 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
2741
3224
|
const matchPreferences = {
|
|
2742
3225
|
usageOrder: settings.getStorage()?.getModelUsageOrder(),
|
|
2743
3226
|
};
|
|
2744
|
-
const { model: resolved } = parseModelPattern(
|
|
2745
|
-
|
|
2746
|
-
|
|
2747
|
-
|
|
2748
|
-
|
|
3227
|
+
const { model: resolved, thinkingLevel: resolvedThinkingLevel } = parseModelPattern(
|
|
3228
|
+
options.modelPattern,
|
|
3229
|
+
availableModels,
|
|
3230
|
+
matchPreferences,
|
|
3231
|
+
{
|
|
3232
|
+
modelRegistry,
|
|
3233
|
+
sessionId: logicalSessionId,
|
|
3234
|
+
credentialSessionId,
|
|
3235
|
+
},
|
|
3236
|
+
);
|
|
2749
3237
|
if (resolved) {
|
|
2750
3238
|
model = resolved;
|
|
2751
3239
|
modelFallbackMessage = undefined;
|
|
3240
|
+
if (resolvedThinkingLevel !== undefined) {
|
|
3241
|
+
thinkingLevel = resolvedThinkingLevel;
|
|
3242
|
+
thinkingLevelFromSchemaDefault = false;
|
|
3243
|
+
}
|
|
2752
3244
|
if (thinkingLevelFromSchemaDefault && resolved.thinking?.defaultLevel !== undefined) {
|
|
2753
3245
|
thinkingLevel = resolved.thinking.defaultLevel;
|
|
2754
3246
|
thinkingLevelFromSchemaDefault = false;
|
|
@@ -2845,9 +3337,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
2845
3337
|
model: agent.state.model,
|
|
2846
3338
|
isIdle: () => !session.isStreaming,
|
|
2847
3339
|
hasQueuedMessages: () => session.queuedMessageCount > 0,
|
|
2848
|
-
abort: () =>
|
|
2849
|
-
session.abort();
|
|
2850
|
-
},
|
|
3340
|
+
abort: () => session.abort(),
|
|
2851
3341
|
settings,
|
|
2852
3342
|
});
|
|
2853
3343
|
const toolContextStore = new ToolContextStore(getSessionContext);
|
|
@@ -2884,6 +3374,15 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
2884
3374
|
.filter(isCustomTool)
|
|
2885
3375
|
.map(tool => CustomToolAdapter.wrap(tool, customToolContext));
|
|
2886
3376
|
}
|
|
3377
|
+
const automationToolNames = new Set(Object.keys(automationTools));
|
|
3378
|
+
const automationToolCollisions = [
|
|
3379
|
+
...new Set(wrappedExtensionTools.map(tool => tool.name).filter(name => automationToolNames.has(name))),
|
|
3380
|
+
];
|
|
3381
|
+
if (automationToolCollisions.length > 0) {
|
|
3382
|
+
throw new Error(
|
|
3383
|
+
`SDK automation tools cannot collide with extension or MCP tools: ${automationToolCollisions.join(", ")}`,
|
|
3384
|
+
);
|
|
3385
|
+
}
|
|
2887
3386
|
|
|
2888
3387
|
// All built-in tools are active (conditional tools like git/ask return null from factory if disabled)
|
|
2889
3388
|
const toolRegistry = new Map<string, Tool>();
|
|
@@ -2931,6 +3430,12 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
2931
3430
|
toolRegistry.set(tool.name, wrapped);
|
|
2932
3431
|
}
|
|
2933
3432
|
}
|
|
3433
|
+
const rawCursorReplaceEditTool = captureCursorEditTool(toolRegistry, () => new EditTool(toolSession, "replace"));
|
|
3434
|
+
const cursorReplaceEditTool: AgentTool | undefined = rawCursorReplaceEditTool
|
|
3435
|
+
? extensionRunner
|
|
3436
|
+
? (new ExtensionToolWrapper(rawCursorReplaceEditTool, extensionRunner) as AgentTool)
|
|
3437
|
+
: (rawCursorReplaceEditTool as AgentTool)
|
|
3438
|
+
: undefined;
|
|
2934
3439
|
if (model?.provider === "cursor") {
|
|
2935
3440
|
toolRegistry.delete("edit");
|
|
2936
3441
|
builtinCandidateTools = builtinCandidateTools.filter(tool => tool.name !== "edit");
|
|
@@ -2978,6 +3483,14 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
2978
3483
|
const cursorExecHandlers = new CursorExecHandlers({
|
|
2979
3484
|
cwd,
|
|
2980
3485
|
tools: toolRegistry,
|
|
3486
|
+
getEditReplaceTool: () => cursorReplaceEditTool,
|
|
3487
|
+
createSearchTool: options => {
|
|
3488
|
+
if (!toolRegistry.has("search")) return undefined;
|
|
3489
|
+
const search = new SearchTool(toolSession, options);
|
|
3490
|
+
return extensionRunner
|
|
3491
|
+
? (new ExtensionToolWrapper(search as unknown as AgentTool, extensionRunner) as AgentTool)
|
|
3492
|
+
: (search as unknown as AgentTool);
|
|
3493
|
+
},
|
|
2981
3494
|
getToolContext: () => toolContextStore.getContext(),
|
|
2982
3495
|
emitEvent: event => cursorEventEmitter?.(event),
|
|
2983
3496
|
createEventEmitter: () => agent.createExternalEventEmitterForCurrentRun(),
|
|
@@ -3053,7 +3566,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
3053
3566
|
const appendPrompt: string | undefined = memoryInstructions ?? undefined;
|
|
3054
3567
|
let pluginSystemAppendices = "";
|
|
3055
3568
|
try {
|
|
3056
|
-
pluginSystemAppendices = await renderAlwaysOnSystemAppendices({ cwd });
|
|
3569
|
+
pluginSystemAppendices = await renderAlwaysOnSystemAppendices({ cwd: getLiveCwd() });
|
|
3057
3570
|
} catch (error) {
|
|
3058
3571
|
gjcProducersComplete = false;
|
|
3059
3572
|
logger.warn("Failed to render GJC plugin system appendices", { error: safeErrorForLog(error) });
|
|
@@ -3067,7 +3580,9 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
3067
3580
|
// `unavailable` rather than a stale generation.
|
|
3068
3581
|
if (gjcProducersComplete) gjcRuntimeStore.publish(gjcFindings.snapshot(), gjcPassEpoch);
|
|
3069
3582
|
const defaultPrompt = await buildSystemPromptInternal({
|
|
3070
|
-
cwd,
|
|
3583
|
+
// Live cwd: the prompt is rebuilt after a rescope, and describing the
|
|
3584
|
+
// retired launcher root there is what makes the model pick wrong paths.
|
|
3585
|
+
cwd: getLiveCwd(),
|
|
3071
3586
|
skills,
|
|
3072
3587
|
contextFiles,
|
|
3073
3588
|
tools: promptTools,
|
|
@@ -3600,7 +4115,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
3600
4115
|
convertToLlm: convertToLlmFinal,
|
|
3601
4116
|
rebuildSystemPrompt,
|
|
3602
4117
|
getMcpServerInstructions:
|
|
3603
|
-
explicitMcpConfigPath === undefined
|
|
4118
|
+
explicitMcpConfigPath === undefined ? () => mcpManager?.getServerInstructions() : undefined,
|
|
3604
4119
|
workspaceTree: options.workspaceTree ?? (workspaceTreeMode === "eager" ? resolvedWorkspaceTree : undefined),
|
|
3605
4120
|
workspaceTreeService: options.workspaceTree ? undefined : runtimeServices.workspaceTree,
|
|
3606
4121
|
networkPrewarmService: runtimeServices.networkPrewarm,
|
|
@@ -3640,6 +4155,10 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
3640
4155
|
});
|
|
3641
4156
|
session.setActiveModelProfile(startupActiveModelProfile);
|
|
3642
4157
|
session.configWarnings.push(...contextFileWarnings);
|
|
4158
|
+
// Determined once, here, where settings are already available. Keep the
|
|
4159
|
+
// durable warning for interactive and print consumers; ACP delivery is
|
|
4160
|
+
// carried by the host replay ring through the internal runtime seam above.
|
|
4161
|
+
if (autoroutingInactive) session.configWarnings.push(AUTOROUTING_INACTIVE_WARNING);
|
|
3643
4162
|
hasSession = true;
|
|
3644
4163
|
const sessionAsyncJobManager = asyncJobManager;
|
|
3645
4164
|
if (sessionAsyncJobManager) {
|
|
@@ -3792,35 +4311,12 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
3792
4311
|
void session.refreshMCPTools(tools);
|
|
3793
4312
|
});
|
|
3794
4313
|
}
|
|
3795
|
-
// Wire prompt refresh → rebuild MCP prompt slash commands
|
|
3796
|
-
mcpManager.setOnPromptsChanged(serverName => {
|
|
3797
|
-
const promptCommands = buildMCPPromptCommands(mcpManager);
|
|
3798
|
-
session.setMCPPromptCommands(promptCommands);
|
|
3799
|
-
logger.debug("MCP prompt commands refreshed", { path: `mcp:${serverName}` });
|
|
3800
|
-
});
|
|
3801
|
-
const notificationDebounceTimers = new Map<string, Timer>();
|
|
3802
4314
|
const clearDebounceTimers = () => {
|
|
3803
4315
|
for (const timer of notificationDebounceTimers.values()) clearTimeout(timer);
|
|
3804
4316
|
notificationDebounceTimers.clear();
|
|
3805
4317
|
};
|
|
3806
4318
|
postmortem.register("mcp-notification-cleanup", clearDebounceTimers);
|
|
3807
|
-
mcpManager
|
|
3808
|
-
logger.debug("MCP resources changed", { path: `mcp:${serverName}`, uri });
|
|
3809
|
-
if (!settings.get("mcp.notifications")) return;
|
|
3810
|
-
const debounceMs = settings.get("mcp.notificationDebounceMs");
|
|
3811
|
-
const key = `${serverName}:${uri}`;
|
|
3812
|
-
const existing = notificationDebounceTimers.get(key);
|
|
3813
|
-
if (existing) clearTimeout(existing);
|
|
3814
|
-
notificationDebounceTimers.set(
|
|
3815
|
-
key,
|
|
3816
|
-
setTimeout(() => {
|
|
3817
|
-
notificationDebounceTimers.delete(key);
|
|
3818
|
-
// Re-check: user may have disabled notifications during the debounce window
|
|
3819
|
-
if (!settings.get("mcp.notifications")) return;
|
|
3820
|
-
session.yieldQueue.enqueue<McpNotificationEntry>("mcp-notification", { serverName, uri });
|
|
3821
|
-
}, debounceMs),
|
|
3822
|
-
);
|
|
3823
|
-
});
|
|
4319
|
+
wireMcpManagerCallbacks(mcpManager);
|
|
3824
4320
|
}
|
|
3825
4321
|
|
|
3826
4322
|
// Constructor-time workflow-gate tool restoration is deferred by one
|
|
@@ -3934,6 +4430,10 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
3934
4430
|
cleanupDiagnostic: safeCleanupDiagnosticForLog(cleanupDiagnostic),
|
|
3935
4431
|
});
|
|
3936
4432
|
} finally {
|
|
4433
|
+
if (processCwdClaimed) {
|
|
4434
|
+
SessionManager.releaseProcessCwdOwnership(sessionManager);
|
|
4435
|
+
processCwdClaimed = false;
|
|
4436
|
+
}
|
|
3937
4437
|
releaseLocalProtocolOverride();
|
|
3938
4438
|
try {
|
|
3939
4439
|
closeOwnedAuthStorage();
|