| author | |
| committer | |
| log | 784b5625f9e228cca8dd67e6e24e402325672544 |
| tree | ab854947597d458ebed305e249b0b4111713ab7d |
| parent | 68ab16f3084236871685bed5032cb05c7c436dbd |
| signature | Signed by PGP key B5690EEEBB952194 |
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>48 files changed, 1467 insertions(+), 653 deletions(-)
apps/server/src/mcp/OrchestratorMcpToolkit.integration.test.ts+4-1| ... | @@ -59,6 +59,7 @@ import * as Orchestrator from "../orchestration-v2/Orchestrator.ts"; | ... | @@ -59,6 +59,7 @@ import * as Orchestrator from "../orchestration-v2/Orchestrator.ts"; |
| 59 | import * as ThreadManagementService from "../orchestration-v2/ThreadManagementService.ts"; | 59 | import * as ThreadManagementService from "../orchestration-v2/ThreadManagementService.ts"; |
| 60 | import * as ProviderAdapterRegistry from "../orchestration-v2/ProviderAdapterRegistry.ts"; | 60 | import * as ProviderAdapterRegistry from "../orchestration-v2/ProviderAdapterRegistry.ts"; |
| 61 | import * as ProviderContinuationRequests from "@t3tools/provider-core/server/ProviderContinuationRequests"; | 61 | import * as ProviderContinuationRequests from "@t3tools/provider-core/server/ProviderContinuationRequests"; |
| 62 | import * as McpProviderSessions from "@t3tools/provider-core/server/McpProviderSessions"; | ||
| 62 | import { checkpointWorkspace } from "@t3tools/provider-testing/replayWorkspace"; | 63 | import { checkpointWorkspace } from "@t3tools/provider-testing/replayWorkspace"; |
| 63 | import * as ProviderReplayHarness from "../orchestration-v2/testkit/ProviderReplayHarness.ts"; | 64 | import * as ProviderReplayHarness from "../orchestration-v2/testkit/ProviderReplayHarness.ts"; |
| 64 | import { | 65 | import { |
| ... | @@ -3810,7 +3811,9 @@ describe("orchestrator MCP toolkit", () => { | ... | @@ -3810,7 +3811,9 @@ describe("orchestrator MCP toolkit", () => { |
| 3810 | Layer.provideMerge(McpServer.McpServer.layer), | 3811 | Layer.provideMerge(McpServer.McpServer.layer), |
| 3811 | Layer.provideMerge(layerOrchestration), | 3812 | Layer.provideMerge(layerOrchestration), |
| 3812 | Layer.provide( | 3813 | Layer.provide( |
| 3813 | CodexOrchestratorReplayHarness.makeProviderAdapterRegistryLayer(transcript), | 3814 | CodexOrchestratorReplayHarness.makeProviderAdapterRegistryLayer(transcript).pipe( |
| 3815 | Layer.provide(McpProviderSessions.layer), | ||
| 3816 | ), | ||
| 3814 | ), | 3817 | ), |
| 3815 | Layer.provide(layerProviderRegistry), | 3818 | Layer.provide(layerProviderRegistry), |
| 3816 | Layer.provide(layerUnusedScheduledTaskStub), | 3819 | Layer.provide(layerUnusedScheduledTaskStub), |
apps/server/src/orchestration-v2/AcpRegistryOrchestratorV2.live.test.ts+2| ... | @@ -30,6 +30,7 @@ import * as ServerEnvironment from "../environment/ServerEnvironment.ts"; | ... | @@ -30,6 +30,7 @@ import * as ServerEnvironment from "../environment/ServerEnvironment.ts"; |
| 30 | import * as ModelManifest from "../provider/ModelManifest.ts"; | 30 | import * as ModelManifest from "../provider/ModelManifest.ts"; |
| 31 | import * as ProviderInstanceRegistryHydration from "../provider/ProviderInstanceRegistryHydration.ts"; | 31 | import * as ProviderInstanceRegistryHydration from "../provider/ProviderInstanceRegistryHydration.ts"; |
| 32 | import * as ProviderLatestVersions from "@t3tools/provider-core/server/ProviderLatestVersions"; | 32 | import * as ProviderLatestVersions from "@t3tools/provider-core/server/ProviderLatestVersions"; |
| 33 | import * as McpProviderSessions from "@t3tools/provider-core/server/McpProviderSessions"; | ||
| 33 | import * as ProviderEventLoggers from "@t3tools/provider-core/server/ProviderEventLoggers"; | 34 | import * as ProviderEventLoggers from "@t3tools/provider-core/server/ProviderEventLoggers"; |
| 34 | import * as OpenCodeRuntime from "@t3tools/provider-opencode/server/OpenCodeRuntime"; | 35 | import * as OpenCodeRuntime from "@t3tools/provider-opencode/server/OpenCodeRuntime"; |
| 35 | import * as OpenCodeServerLedger from "@t3tools/provider-opencode/server/OpenCodeServerLedger"; | 36 | import * as OpenCodeServerLedger from "@t3tools/provider-opencode/server/OpenCodeServerLedger"; |
| ... | @@ -144,6 +145,7 @@ const layerLive = RuntimeLayer.layer.pipe( | ... | @@ -144,6 +145,7 @@ const layerLive = RuntimeLayer.layer.pipe( |
| 144 | Layer.provide(layerBackgroundPolicy), | 145 | Layer.provide(layerBackgroundPolicy), |
| 145 | Layer.provide(ProviderTurnStartServiceTestkit.layer), | 146 | Layer.provide(ProviderTurnStartServiceTestkit.layer), |
| 146 | Layer.provide(layerPlatformTest), | 147 | Layer.provide(layerPlatformTest), |
| 148 | Layer.provide(McpProviderSessions.layer), | ||
| 147 | ); | 149 | ); |
| 148 | 150 | ||
| 149 | const waitForIdle = Effect.fn("AcpRegistryOrchestratorV2Live.waitForIdle")(function* ( | 151 | const waitForIdle = Effect.fn("AcpRegistryOrchestratorV2Live.waitForIdle")(function* ( |
apps/server/src/orchestration-v2/Adapters/AcpAdapterV2.test.ts+12-24| ... | @@ -54,7 +54,7 @@ import type * as EffectAcpProtocol from "effect-acp/protocol"; | ... | @@ -54,7 +54,7 @@ import type * as EffectAcpProtocol from "effect-acp/protocol"; |
| 54 | import type * as EffectAcpSchema from "effect-acp/compat"; | 54 | import type * as EffectAcpSchema from "effect-acp/compat"; |
| 55 | 55 | ||
| 56 | import { layerTestProviderHost } from "@t3tools/provider-testing/host"; | 56 | import { layerTestProviderHost } from "@t3tools/provider-testing/host"; |
| 57 | import * as McpProviderSession from "@t3tools/provider-core/server/mcpSession"; | 57 | import * as McpProviderSessions from "@t3tools/provider-core/server/McpProviderSessions"; |
| 58 | import * as AcpSessionRuntime from "@t3tools/provider-acp/server/AcpSessionRuntime"; | 58 | import * as AcpSessionRuntime from "@t3tools/provider-acp/server/AcpSessionRuntime"; |
| 59 | import { | 59 | import { |
| 60 | extractXAiAcpSubagentEndNotice, | 60 | extractXAiAcpSubagentEndNotice, |
| ... | @@ -96,7 +96,12 @@ const DEFAULT_GROK_SETTINGS = Schema.decodeSync(GrokSettings)({}); | ... | @@ -96,7 +96,12 @@ const DEFAULT_GROK_SETTINGS = Schema.decodeSync(GrokSettings)({}); |
| 96 | 96 | ||
| 97 | const layerHost = layerTestProviderHost().pipe(Layer.provide(NodeServices.layer)); | 97 | const layerHost = layerTestProviderHost().pipe(Layer.provide(NodeServices.layer)); |
| 98 | 98 | ||
| 99 | const layerTest = Layer.mergeAll(NodeServices.layer, IdAllocator.layer, layerHost); | 99 | const layerTest = Layer.mergeAll( |
| 100 | NodeServices.layer, | ||
| 101 | IdAllocator.layer, | ||
| 102 | McpProviderSessions.layer, | ||
| 103 | layerHost, | ||
| 104 | ); | ||
| 100 | const ACP_TEST_DRIVER = ProviderDriverKind.make("acp-test"); | 105 | const ACP_TEST_DRIVER = ProviderDriverKind.make("acp-test"); |
| 101 | const decodeUnknownJson = Schema.decodeUnknownOption(Schema.fromJsonString(Schema.Unknown)); | 106 | const decodeUnknownJson = Schema.decodeUnknownOption(Schema.fromJsonString(Schema.Unknown)); |
| 102 | 107 | ||
| ... | @@ -708,7 +713,7 @@ describe("AcpAdapterV2", () => { | ... | @@ -708,7 +713,7 @@ describe("AcpAdapterV2", () => { |
| 708 | 713 | ||
| 709 | const instanceId = ProviderInstanceId.make("acp-test-self-contained-mcp-bridge"); | 714 | const instanceId = ProviderInstanceId.make("acp-test-self-contained-mcp-bridge"); |
| 710 | const threadId = ThreadId.make("thread-acp-self-contained-mcp-bridge"); | 715 | const threadId = ThreadId.make("thread-acp-self-contained-mcp-bridge"); |
| 711 | McpProviderSession.setMcpProviderSession({ | 716 | yield* (yield* McpProviderSessions.McpProviderSessions).set({ |
| 712 | environmentId: EnvironmentId.make("environment-acp-self-contained-mcp-bridge"), | 717 | environmentId: EnvironmentId.make("environment-acp-self-contained-mcp-bridge"), |
| 713 | threadId, | 718 | threadId, |
| 714 | providerSessionId: "mcp-session-acp-self-contained-mcp-bridge", | 719 | providerSessionId: "mcp-session-acp-self-contained-mcp-bridge", |
| ... | @@ -717,11 +722,6 @@ describe("AcpAdapterV2", () => { | ... | @@ -717,11 +722,6 @@ describe("AcpAdapterV2", () => { |
| 717 | authorizationHeader: "Bearer self-contained-mcp-bridge-token", | 722 | authorizationHeader: "Bearer self-contained-mcp-bridge-token", |
| 718 | browserToolsAvailable: false, | 723 | browserToolsAvailable: false, |
| 719 | }); | 724 | }); |
| 720 | yield* Effect.addFinalizer(() => | ||
| 721 | Effect.sync(() => { | ||
| 722 | McpProviderSession.clearMcpProviderSession(threadId); | ||
| 723 | }), | ||
| 724 | ); | ||
| 725 | 725 | ||
| 726 | let runtimeInput: AcpAdapterV2RuntimeInput | undefined; | 726 | let runtimeInput: AcpAdapterV2RuntimeInput | undefined; |
| 727 | const makeRuntime = makeMockRuntime({ childProcessSpawner, mockAgentPath }); | 727 | const makeRuntime = makeMockRuntime({ childProcessSpawner, mockAgentPath }); |
| ... | @@ -772,7 +772,7 @@ describe("AcpAdapterV2", () => { | ... | @@ -772,7 +772,7 @@ describe("AcpAdapterV2", () => { |
| 772 | const protocolEvents = yield* Queue.unbounded<EffectAcpProtocol.AcpProtocolLogEvent>(); | 772 | const protocolEvents = yield* Queue.unbounded<EffectAcpProtocol.AcpProtocolLogEvent>(); |
| 773 | const instanceId = ProviderInstanceId.make("acp-test-instruction-transitions"); | 773 | const instanceId = ProviderInstanceId.make("acp-test-instruction-transitions"); |
| 774 | const threadId = ThreadId.make("thread-acp-instruction-transitions"); | 774 | const threadId = ThreadId.make("thread-acp-instruction-transitions"); |
| 775 | McpProviderSession.setMcpProviderSession({ | 775 | yield* (yield* McpProviderSessions.McpProviderSessions).set({ |
| 776 | environmentId: EnvironmentId.make("environment-acp-instruction-transitions"), | 776 | environmentId: EnvironmentId.make("environment-acp-instruction-transitions"), |
| 777 | threadId, | 777 | threadId, |
| 778 | providerSessionId: "mcp-session-acp-instruction-transitions", | 778 | providerSessionId: "mcp-session-acp-instruction-transitions", |
| ... | @@ -781,9 +781,6 @@ describe("AcpAdapterV2", () => { | ... | @@ -781,9 +781,6 @@ describe("AcpAdapterV2", () => { |
| 781 | authorizationHeader: "Bearer instruction-transition-token", | 781 | authorizationHeader: "Bearer instruction-transition-token", |
| 782 | browserToolsAvailable: false, | 782 | browserToolsAvailable: false, |
| 783 | }); | 783 | }); |
| 784 | yield* Effect.addFinalizer(() => | ||
| 785 | Effect.sync(() => McpProviderSession.clearMcpProviderSession(threadId)), | ||
| 786 | ); | ||
| 787 | const adapter = yield* makeAcpAdapterV2({ | 784 | const adapter = yield* makeAcpAdapterV2({ |
| 788 | instanceId, | 785 | instanceId, |
| 789 | flavor: { | 786 | flavor: { |
| ... | @@ -2197,7 +2194,7 @@ describe("AcpAdapterV2", () => { | ... | @@ -2197,7 +2194,7 @@ describe("AcpAdapterV2", () => { |
| 2197 | }); | 2194 | }); |
| 2198 | const sourceThreadId = ThreadId.make("thread-acp-native-fork-source"); | 2195 | const sourceThreadId = ThreadId.make("thread-acp-native-fork-source"); |
| 2199 | const targetThreadId = ThreadId.make("thread-acp-native-fork-target"); | 2196 | const targetThreadId = ThreadId.make("thread-acp-native-fork-target"); |
| 2200 | McpProviderSession.setMcpProviderSession({ | 2197 | yield* (yield* McpProviderSessions.McpProviderSessions).set({ |
| 2201 | environmentId: EnvironmentId.make("environment-acp-native-fork-source"), | 2198 | environmentId: EnvironmentId.make("environment-acp-native-fork-source"), |
| 2202 | threadId: sourceThreadId, | 2199 | threadId: sourceThreadId, |
| 2203 | providerSessionId: "mcp-session-acp-native-fork-source", | 2200 | providerSessionId: "mcp-session-acp-native-fork-source", |
| ... | @@ -2206,7 +2203,7 @@ describe("AcpAdapterV2", () => { | ... | @@ -2206,7 +2203,7 @@ describe("AcpAdapterV2", () => { |
| 2206 | authorizationHeader: "Bearer source-thread-token", | 2203 | authorizationHeader: "Bearer source-thread-token", |
| 2207 | browserToolsAvailable: true, | 2204 | browserToolsAvailable: true, |
| 2208 | }); | 2205 | }); |
| 2209 | McpProviderSession.setMcpProviderSession({ | 2206 | yield* (yield* McpProviderSessions.McpProviderSessions).set({ |
| 2210 | environmentId: EnvironmentId.make("environment-acp-native-fork"), | 2207 | environmentId: EnvironmentId.make("environment-acp-native-fork"), |
| 2211 | threadId: targetThreadId, | 2208 | threadId: targetThreadId, |
| 2212 | providerSessionId: "mcp-session-acp-native-fork", | 2209 | providerSessionId: "mcp-session-acp-native-fork", |
| ... | @@ -2215,12 +2212,6 @@ describe("AcpAdapterV2", () => { | ... | @@ -2215,12 +2212,6 @@ describe("AcpAdapterV2", () => { |
| 2215 | authorizationHeader: "Bearer target-thread-token", | 2212 | authorizationHeader: "Bearer target-thread-token", |
| 2216 | browserToolsAvailable: true, | 2213 | browserToolsAvailable: true, |
| 2217 | }); | 2214 | }); |
| 2218 | yield* Effect.addFinalizer(() => | ||
| 2219 | Effect.sync(() => { | ||
| 2220 | McpProviderSession.clearMcpProviderSession(sourceThreadId); | ||
| 2221 | McpProviderSession.clearMcpProviderSession(targetThreadId); | ||
| 2222 | }), | ||
| 2223 | ); | ||
| 2224 | const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({ | 2215 | const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({ |
| 2225 | runtimeMode: "full-access", | 2216 | runtimeMode: "full-access", |
| 2226 | interactionMode: "default", | 2217 | interactionMode: "default", |
| ... | @@ -2640,7 +2631,7 @@ describe("AcpAdapterV2", () => { | ... | @@ -2640,7 +2631,7 @@ describe("AcpAdapterV2", () => { |
| 2640 | ); | 2631 | ); |
| 2641 | const instanceId = ProviderInstanceId.make("acp-test-rollback-session"); | 2632 | const instanceId = ProviderInstanceId.make("acp-test-rollback-session"); |
| 2642 | const rollbackThreadId = ThreadId.make("thread-acp-rollback-session-target"); | 2633 | const rollbackThreadId = ThreadId.make("thread-acp-rollback-session-target"); |
| 2643 | McpProviderSession.setMcpProviderSession({ | 2634 | yield* (yield* McpProviderSessions.McpProviderSessions).set({ |
| 2644 | environmentId: EnvironmentId.make("environment-acp-rollback-session-target"), | 2635 | environmentId: EnvironmentId.make("environment-acp-rollback-session-target"), |
| 2645 | threadId: rollbackThreadId, | 2636 | threadId: rollbackThreadId, |
| 2646 | providerSessionId: "mcp-session-acp-rollback-session-target", | 2637 | providerSessionId: "mcp-session-acp-rollback-session-target", |
| ... | @@ -2649,9 +2640,6 @@ describe("AcpAdapterV2", () => { | ... | @@ -2649,9 +2640,6 @@ describe("AcpAdapterV2", () => { |
| 2649 | authorizationHeader: "Bearer rollback-target-token", | 2640 | authorizationHeader: "Bearer rollback-target-token", |
| 2650 | browserToolsAvailable: false, | 2641 | browserToolsAvailable: false, |
| 2651 | }); | 2642 | }); |
| 2652 | yield* Effect.addFinalizer(() => | ||
| 2653 | Effect.sync(() => McpProviderSession.clearMcpProviderSession(rollbackThreadId)), | ||
| 2654 | ); | ||
| 2655 | const runtimeInputs: Array<AcpAdapterV2RuntimeInput> = []; | 2643 | const runtimeInputs: Array<AcpAdapterV2RuntimeInput> = []; |
| 2656 | const makeRuntime = makeMockRuntime({ | 2644 | const makeRuntime = makeMockRuntime({ |
| 2657 | childProcessSpawner, | 2645 | childProcessSpawner, |
apps/server/src/orchestration-v2/Adapters/AcpRegistryAdapterV2.test.ts+2| ... | @@ -14,6 +14,7 @@ import { ChildProcessSpawner } from "effect/process"; | ... | @@ -14,6 +14,7 @@ import { ChildProcessSpawner } from "effect/process"; |
| 14 | import * as EffectAcpErrors from "effect-acp/errors"; | 14 | import * as EffectAcpErrors from "effect-acp/errors"; |
| 15 | 15 | ||
| 16 | import * as ProviderHost from "@t3tools/provider-core/server/ProviderHost"; | 16 | import * as ProviderHost from "@t3tools/provider-core/server/ProviderHost"; |
| 17 | import * as McpProviderSessions from "@t3tools/provider-core/server/McpProviderSessions"; | ||
| 17 | import { layerTestProviderHost } from "@t3tools/provider-testing/host"; | 18 | import { layerTestProviderHost } from "@t3tools/provider-testing/host"; |
| 18 | import * as ServerSettings from "../../serverSettings.ts"; | 19 | import * as ServerSettings from "../../serverSettings.ts"; |
| 19 | import type { | 20 | import type { |
| ... | @@ -84,6 +85,7 @@ const layerRegistry = Layer.succeed( | ... | @@ -84,6 +85,7 @@ const layerRegistry = Layer.succeed( |
| 84 | const layerTest = Layer.mergeAll( | 85 | const layerTest = Layer.mergeAll( |
| 85 | NodeServices.layer, | 86 | NodeServices.layer, |
| 86 | IdAllocator.layer, | 87 | IdAllocator.layer, |
| 88 | McpProviderSessions.layer, | ||
| 87 | layerHost, | 89 | layerHost, |
| 88 | layerRegistry, | 90 | layerRegistry, |
| 89 | ServerSettings.layerTest(), | 91 | ServerSettings.layerTest(), |
apps/server/src/orchestration-v2/Adapters/AntigravityAdapterV2.test.ts+2| ... | @@ -23,6 +23,7 @@ import { ChildProcessSpawner } from "effect/process"; | ... | @@ -23,6 +23,7 @@ import { ChildProcessSpawner } from "effect/process"; |
| 23 | import type * as EffectAcpSchema from "effect-acp/compat"; | 23 | import type * as EffectAcpSchema from "effect-acp/compat"; |
| 24 | 24 | ||
| 25 | import * as ProviderHost from "@t3tools/provider-core/server/ProviderHost"; | 25 | import * as ProviderHost from "@t3tools/provider-core/server/ProviderHost"; |
| 26 | import * as McpProviderSessions from "@t3tools/provider-core/server/McpProviderSessions"; | ||
| 26 | import { layerTestProviderHost } from "@t3tools/provider-testing/host"; | 27 | import { layerTestProviderHost } from "@t3tools/provider-testing/host"; |
| 27 | import type * as AcpSessionRuntime from "@t3tools/provider-acp/server/AcpSessionRuntime"; | 28 | import type * as AcpSessionRuntime from "@t3tools/provider-acp/server/AcpSessionRuntime"; |
| 28 | import { makeAntigravityAcpRuntime } from "../../provider/acp/AntigravityAcpSupport.ts"; | 29 | import { makeAntigravityAcpRuntime } from "../../provider/acp/AntigravityAcpSupport.ts"; |
| ... | @@ -129,6 +130,7 @@ describe("AntigravityAdapterV2 flavor", () => { | ... | @@ -129,6 +130,7 @@ describe("AntigravityAdapterV2 flavor", () => { |
| 129 | const layerSession = Layer.mergeAll( | 130 | const layerSession = Layer.mergeAll( |
| 130 | NodeServices.layer, | 131 | NodeServices.layer, |
| 131 | IdAllocator.layer, | 132 | IdAllocator.layer, |
| 133 | McpProviderSessions.layer, | ||
| 132 | layerTestProviderHost().pipe(Layer.provide(NodeServices.layer)), | 134 | layerTestProviderHost().pipe(Layer.provide(NodeServices.layer)), |
| 133 | ); | 135 | ); |
| 134 | 136 |
apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.test.ts+587-267| ... | @@ -53,6 +53,7 @@ import { SpawnExecutableResolution } from "@t3tools/shared/shell"; | ... | @@ -53,6 +53,7 @@ import { SpawnExecutableResolution } from "@t3tools/shared/shell"; |
| 53 | import { attachmentRelativePath } from "../../attachmentStore.ts"; | 53 | import { attachmentRelativePath } from "../../attachmentStore.ts"; |
| 54 | import * as ServerConfig from "../../config.ts"; | 54 | import * as ServerConfig from "../../config.ts"; |
| 55 | import * as McpProviderSession from "@t3tools/provider-core/server/mcpSession"; | 55 | import * as McpProviderSession from "@t3tools/provider-core/server/mcpSession"; |
| 56 | import * as McpProviderSessions from "@t3tools/provider-core/server/McpProviderSessions"; | ||
| 56 | import { PreviewControlsToolkit } from "../../mcp/toolkits/previewControls/tools.ts"; | 57 | import { PreviewControlsToolkit } from "../../mcp/toolkits/previewControls/tools.ts"; |
| 57 | import { HtmlToolkit } from "../../mcp/toolkits/html/tools.ts"; | 58 | import { HtmlToolkit } from "../../mcp/toolkits/html/tools.ts"; |
| 58 | import { EnvironmentToolkit } from "../../mcp/toolkits/environment/tools.ts"; | 59 | import { EnvironmentToolkit } from "../../mcp/toolkits/environment/tools.ts"; |
| ... | @@ -483,26 +484,22 @@ describe("ClaudeAdapterV2 MCP query overrides", () => { | ... | @@ -483,26 +484,22 @@ describe("ClaudeAdapterV2 MCP query overrides", () => { |
| 483 | T3_CODE_MCP_AUTHORIZATION: "Bearer secret-claude-token", | 484 | T3_CODE_MCP_AUTHORIZATION: "Bearer secret-claude-token", |
| 484 | } as const; | 485 | } as const; |
| 485 | 486 | ||
| 486 | const withMcpSession = (threadId: ThreadId, run: () => void) => { | 487 | const mcpSessionFor = ( |
| 487 | McpProviderSession.setMcpProviderSession({ | 488 | threadId: ThreadId, |
| 488 | environmentId: EnvironmentId.make(`environment-${threadId}`), | 489 | authorizationHeader = "Bearer secret-claude-token", |
| 489 | threadId, | 490 | ): McpProviderSession.McpProviderSessionConfig => ({ |
| 490 | providerSessionId: `mcp-session-${threadId}`, | 491 | environmentId: EnvironmentId.make(`environment-${threadId}`), |
| 491 | providerInstanceId: ProviderInstanceId.make("claudeAgent"), | 492 | threadId, |
| 492 | endpoint: "http://127.0.0.1:43123/mcp", | 493 | providerSessionId: `mcp-session-${threadId}`, |
| 493 | authorizationHeader: "Bearer secret-claude-token", | 494 | providerInstanceId: ProviderInstanceId.make("claudeAgent"), |
| 494 | browserToolsAvailable: true, | 495 | endpoint: "http://127.0.0.1:43123/mcp", |
| 495 | }); | 496 | authorizationHeader, |
| 496 | try { | 497 | browserToolsAvailable: true, |
| 497 | run(); | 498 | }); |
| 498 | } finally { | ||
| 499 | McpProviderSession.clearMcpProviderSession(threadId); | ||
| 500 | } | ||
| 501 | }; | ||
| 502 | 499 | ||
| 503 | it("leaves an absent allowlist absent when no MCP session exists", () => { | 500 | it("leaves an absent allowlist absent when no MCP session exists", () => { |
| 504 | const overrides = ClaudeAdapterV2.claudeMcpQueryOverrides({ | 501 | const overrides = ClaudeAdapterV2.claudeMcpQueryOverrides({ |
| 505 | threadId: ThreadId.make("thread-claude-no-mcp-no-allowlist"), | 502 | mcpSession: undefined, |
| 506 | readOnlySandbox: false, | 503 | readOnlySandbox: false, |
| 507 | }); | 504 | }); |
| 508 | 505 | ||
| ... | @@ -511,7 +508,7 @@ describe("ClaudeAdapterV2 MCP query overrides", () => { | ... | @@ -511,7 +508,7 @@ describe("ClaudeAdapterV2 MCP query overrides", () => { |
| 511 | 508 | ||
| 512 | it("preserves an explicit allowlist when no MCP session exists", () => { | 509 | it("preserves an explicit allowlist when no MCP session exists", () => { |
| 513 | const overrides = ClaudeAdapterV2.claudeMcpQueryOverrides({ | 510 | const overrides = ClaudeAdapterV2.claudeMcpQueryOverrides({ |
| 514 | threadId: ThreadId.make("thread-claude-no-mcp-with-allowlist"), | 511 | mcpSession: undefined, |
| 515 | readOnlySandbox: false, | 512 | readOnlySandbox: false, |
| 516 | allowedTools: ["Read"], | 513 | allowedTools: ["Read"], |
| 517 | }); | 514 | }); |
| ... | @@ -521,135 +518,122 @@ describe("ClaudeAdapterV2 MCP query overrides", () => { | ... | @@ -521,135 +518,122 @@ describe("ClaudeAdapterV2 MCP query overrides", () => { |
| 521 | 518 | ||
| 522 | it("pre-approves all t3-code tools when attaching an MCP session without an allowlist", () => { | 519 | it("pre-approves all t3-code tools when attaching an MCP session without an allowlist", () => { |
| 523 | const threadId = ThreadId.make("thread-claude-mcp-no-allowlist"); | 520 | const threadId = ThreadId.make("thread-claude-mcp-no-allowlist"); |
| 524 | withMcpSession(threadId, () => { | 521 | const mcpSession = mcpSessionFor(threadId); |
| 525 | const overrides = ClaudeAdapterV2.claudeMcpQueryOverrides({ | 522 | const overrides = ClaudeAdapterV2.claudeMcpQueryOverrides({ |
| 526 | threadId, | 523 | mcpSession, |
| 527 | readOnlySandbox: false, | 524 | readOnlySandbox: false, |
| 528 | }); | 525 | }); |
| 529 | 526 | ||
| 530 | assert.deepEqual(overrides, { | 527 | assert.deepEqual(overrides, { |
| 531 | allowedTools: [ClaudeAdapterV2.CLAUDE_T3_MCP_TOOL_WILDCARD], | 528 | allowedTools: [ClaudeAdapterV2.CLAUDE_T3_MCP_TOOL_WILDCARD], |
| 532 | mcpServers: T3_MCP_SERVERS, | 529 | mcpServers: T3_MCP_SERVERS, |
| 533 | mcpEnvironment: T3_MCP_ENVIRONMENT, | 530 | mcpEnvironment: T3_MCP_ENVIRONMENT, |
| 534 | }); | ||
| 535 | }); | 531 | }); |
| 536 | }); | 532 | }); |
| 537 | 533 | ||
| 538 | it("extends an explicit allowlist with the t3-code wildcard", () => { | 534 | it("extends an explicit allowlist with the t3-code wildcard", () => { |
| 539 | const threadId = ThreadId.make("thread-claude-mcp-with-allowlist"); | 535 | const threadId = ThreadId.make("thread-claude-mcp-with-allowlist"); |
| 540 | withMcpSession(threadId, () => { | 536 | const mcpSession = mcpSessionFor(threadId); |
| 541 | const overrides = ClaudeAdapterV2.claudeMcpQueryOverrides({ | 537 | const overrides = ClaudeAdapterV2.claudeMcpQueryOverrides({ |
| 542 | threadId, | 538 | mcpSession, |
| 543 | readOnlySandbox: false, | 539 | readOnlySandbox: false, |
| 544 | allowedTools: ["Read", "mcp__t3-code__*"], | 540 | allowedTools: ["Read", "mcp__t3-code__*"], |
| 545 | }); | 541 | }); |
| 546 | 542 | ||
| 547 | assert.deepEqual(overrides, { | 543 | assert.deepEqual(overrides, { |
| 548 | allowedTools: ["Read", "mcp__t3-code__*"], | 544 | allowedTools: ["Read", "mcp__t3-code__*"], |
| 549 | mcpServers: T3_MCP_SERVERS, | 545 | mcpServers: T3_MCP_SERVERS, |
| 550 | mcpEnvironment: T3_MCP_ENVIRONMENT, | 546 | mcpEnvironment: T3_MCP_ENVIRONMENT, |
| 551 | }); | ||
| 552 | }); | 547 | }); |
| 553 | }); | 548 | }); |
| 554 | 549 | ||
| 555 | it("pre-approves only read-only t3-code tools in a read-only sandbox", () => { | 550 | it("pre-approves only read-only t3-code tools in a read-only sandbox", () => { |
| 556 | const threadId = ThreadId.make("thread-claude-mcp-read-only"); | 551 | const threadId = ThreadId.make("thread-claude-mcp-read-only"); |
| 557 | withMcpSession(threadId, () => { | 552 | const mcpSession = mcpSessionFor(threadId); |
| 558 | const overrides = ClaudeAdapterV2.claudeMcpQueryOverrides({ | 553 | const overrides = ClaudeAdapterV2.claudeMcpQueryOverrides({ |
| 559 | threadId, | 554 | mcpSession, |
| 560 | readOnlySandbox: true, | 555 | readOnlySandbox: true, |
| 561 | allowedTools: [...ClaudeAdapterV2.CLAUDE_READ_ONLY_ALLOWED_TOOLS], | 556 | allowedTools: [...ClaudeAdapterV2.CLAUDE_READ_ONLY_ALLOWED_TOOLS], |
| 562 | }); | 557 | }); |
| 563 | 558 | ||
| 564 | assert.deepEqual(overrides, { | 559 | assert.deepEqual(overrides, { |
| 565 | allowedTools: [ | 560 | allowedTools: [ |
| 566 | ...ClaudeAdapterV2.CLAUDE_READ_ONLY_ALLOWED_TOOLS, | 561 | ...ClaudeAdapterV2.CLAUDE_READ_ONLY_ALLOWED_TOOLS, |
| 567 | ...ClaudeAdapterV2.CLAUDE_READ_ONLY_T3_MCP_ALLOWED_TOOLS, | 562 | ...ClaudeAdapterV2.CLAUDE_READ_ONLY_T3_MCP_ALLOWED_TOOLS, |
| 568 | ], | 563 | ], |
| 569 | mcpServers: T3_MCP_SERVERS, | 564 | mcpServers: T3_MCP_SERVERS, |
| 570 | mcpEnvironment: T3_MCP_ENVIRONMENT, | 565 | mcpEnvironment: T3_MCP_ENVIRONMENT, |
| 571 | }); | ||
| 572 | assert.isFalse(overrides.allowedTools?.includes(ClaudeAdapterV2.CLAUDE_T3_MCP_TOOL_WILDCARD)); | ||
| 573 | }); | 566 | }); |
| 567 | assert.isFalse(overrides.allowedTools?.includes(ClaudeAdapterV2.CLAUDE_T3_MCP_TOOL_WILDCARD)); | ||
| 574 | }); | 568 | }); |
| 575 | 569 | ||
| 576 | it("pre-approves only read-only t3-code tools in a read-only sandbox without an allowlist", () => { | 570 | it("pre-approves only read-only t3-code tools in a read-only sandbox without an allowlist", () => { |
| 577 | const threadId = ThreadId.make("thread-claude-mcp-read-only-no-allowlist"); | 571 | const threadId = ThreadId.make("thread-claude-mcp-read-only-no-allowlist"); |
| 578 | withMcpSession(threadId, () => { | 572 | const mcpSession = mcpSessionFor(threadId); |
| 579 | const overrides = ClaudeAdapterV2.claudeMcpQueryOverrides({ | 573 | const overrides = ClaudeAdapterV2.claudeMcpQueryOverrides({ |
| 580 | threadId, | 574 | mcpSession, |
| 581 | readOnlySandbox: true, | 575 | readOnlySandbox: true, |
| 582 | }); | ||
| 583 | |||
| 584 | assert.deepEqual(overrides.allowedTools, [ | ||
| 585 | ...ClaudeAdapterV2.CLAUDE_READ_ONLY_T3_MCP_ALLOWED_TOOLS, | ||
| 586 | ]); | ||
| 587 | }); | 576 | }); |
| 577 | |||
| 578 | assert.deepEqual(overrides.allowedTools, [ | ||
| 579 | ...ClaudeAdapterV2.CLAUDE_READ_ONLY_T3_MCP_ALLOWED_TOOLS, | ||
| 580 | ]); | ||
| 588 | }); | 581 | }); |
| 589 | 582 | ||
| 590 | it("keys live-query reuse on the MCP-derived pre-approvals", () => { | 583 | it("keys live-query reuse on the MCP-derived pre-approvals", () => { |
| 591 | const threadId = ThreadId.make("thread-claude-mcp-query-key"); | 584 | const threadId = ThreadId.make("thread-claude-mcp-query-key"); |
| 592 | withMcpSession(threadId, () => { | 585 | const mcpSession = mcpSessionFor(threadId); |
| 593 | const queryPolicy = ClaudeAdapterV2.claudeRuntimeQueryPolicyForRuntimePolicy( | 586 | const queryPolicy = ClaudeAdapterV2.claudeRuntimeQueryPolicyForRuntimePolicy( |
| 594 | ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({ | 587 | ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({ |
| 595 | runtimeMode: "full-access", | 588 | runtimeMode: "full-access", |
| 596 | interactionMode: "default", | 589 | interactionMode: "default", |
| 597 | cwd: "/workspace", | 590 | cwd: "/workspace", |
| 598 | approvalPolicy: "on-request", | 591 | approvalPolicy: "on-request", |
| 599 | sandboxPolicy: { | 592 | sandboxPolicy: { |
| 600 | type: "readOnly", | 593 | type: "readOnly", |
| 601 | access: { type: "fullAccess" }, | 594 | access: { type: "fullAccess" }, |
| 602 | networkAccess: false, | 595 | networkAccess: false, |
| 603 | }, | 596 | }, |
| 604 | }), | 597 | }), |
| 605 | ); | 598 | ); |
| 606 | 599 | ||
| 607 | const readOnlyKey = ClaudeAdapterV2.claudeEffectiveQueryPolicyKey( | 600 | const readOnlyKey = ClaudeAdapterV2.claudeEffectiveQueryPolicyKey( |
| 608 | queryPolicy, | 601 | queryPolicy, |
| 609 | ClaudeAdapterV2.claudeMcpQueryOverrides({ threadId, readOnlySandbox: true }), | 602 | ClaudeAdapterV2.claudeMcpQueryOverrides({ mcpSession, readOnlySandbox: true }), |
| 610 | ); | 603 | ); |
| 611 | const fullAccessKey = ClaudeAdapterV2.claudeEffectiveQueryPolicyKey( | 604 | const fullAccessKey = ClaudeAdapterV2.claudeEffectiveQueryPolicyKey( |
| 612 | queryPolicy, | 605 | queryPolicy, |
| 613 | ClaudeAdapterV2.claudeMcpQueryOverrides({ threadId, readOnlySandbox: false }), | 606 | ClaudeAdapterV2.claudeMcpQueryOverrides({ mcpSession, readOnlySandbox: false }), |
| 614 | ); | 607 | ); |
| 615 | const detachedKey = ClaudeAdapterV2.claudeEffectiveQueryPolicyKey(queryPolicy, {}); | 608 | const detachedKey = ClaudeAdapterV2.claudeEffectiveQueryPolicyKey(queryPolicy, {}); |
| 616 | 609 | ||
| 617 | assert.notEqual(readOnlyKey, fullAccessKey); | 610 | assert.notEqual(readOnlyKey, fullAccessKey); |
| 618 | assert.notEqual(fullAccessKey, detachedKey); | 611 | assert.notEqual(fullAccessKey, detachedKey); |
| 619 | }); | ||
| 620 | }); | 612 | }); |
| 621 | 613 | ||
| 622 | it("invalidates live-query reuse when MCP credentials rotate", () => { | 614 | it("invalidates live-query reuse when MCP credentials rotate", () => { |
| 623 | const threadId = ThreadId.make("thread-claude-mcp-credential-rotation"); | 615 | const threadId = ThreadId.make("thread-claude-mcp-credential-rotation"); |
| 624 | withMcpSession(threadId, () => { | 616 | const mcpSession = mcpSessionFor(threadId); |
| 625 | const queryPolicy = ClaudeAdapterV2.claudeRuntimeQueryPolicyForRuntimePolicy( | 617 | const queryPolicy = ClaudeAdapterV2.claudeRuntimeQueryPolicyForRuntimePolicy( |
| 626 | ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({ | 618 | ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({ |
| 627 | runtimeMode: "approval-required", | 619 | runtimeMode: "approval-required", |
| 628 | interactionMode: "default", | 620 | interactionMode: "default", |
| 629 | cwd: "/workspace", | 621 | cwd: "/workspace", |
| 630 | }), | 622 | }), |
| 631 | ); | 623 | ); |
| 632 | const initialKey = ClaudeAdapterV2.claudeEffectiveQueryPolicyKey( | 624 | const initialKey = ClaudeAdapterV2.claudeEffectiveQueryPolicyKey( |
| 633 | queryPolicy, | 625 | queryPolicy, |
| 634 | ClaudeAdapterV2.claudeMcpQueryOverrides({ threadId, readOnlySandbox: false }), | 626 | ClaudeAdapterV2.claudeMcpQueryOverrides({ mcpSession, readOnlySandbox: false }), |
| 635 | ); | 627 | ); |
| 636 | |||
| 637 | McpProviderSession.setMcpProviderSession({ | ||
| 638 | environmentId: EnvironmentId.make(`environment-${threadId}`), | ||
| 639 | threadId, | ||
| 640 | providerSessionId: `mcp-session-${threadId}`, | ||
| 641 | providerInstanceId: ProviderInstanceId.make("claudeAgent"), | ||
| 642 | endpoint: "http://127.0.0.1:43123/mcp", | ||
| 643 | authorizationHeader: "Bearer rotated-claude-token", | ||
| 644 | browserToolsAvailable: true, | ||
| 645 | }); | ||
| 646 | 628 | ||
| 647 | const rotatedKey = ClaudeAdapterV2.claudeEffectiveQueryPolicyKey( | 629 | const rotatedKey = ClaudeAdapterV2.claudeEffectiveQueryPolicyKey( |
| 648 | queryPolicy, | 630 | queryPolicy, |
| 649 | ClaudeAdapterV2.claudeMcpQueryOverrides({ threadId, readOnlySandbox: false }), | 631 | ClaudeAdapterV2.claudeMcpQueryOverrides({ |
| 650 | ); | 632 | mcpSession: mcpSessionFor(threadId, "Bearer rotated-claude-token"), |
| 651 | assert.notEqual(rotatedKey, initialKey); | 633 | readOnlySandbox: false, |
| 652 | }); | 634 | }), |
| 635 | ); | ||
| 636 | assert.notEqual(rotatedKey, initialKey); | ||
| 653 | }); | 637 | }); |
| 654 | 638 | ||
| 655 | it("matches the read-only allowlist to the orchestrator toolkit annotations", () => { | 639 | it("matches the read-only allowlist to the orchestrator toolkit annotations", () => { |
| ... | @@ -676,7 +660,7 @@ describe("ClaudeAdapterV2 MCP query overrides", () => { | ... | @@ -676,7 +660,7 @@ describe("ClaudeAdapterV2 MCP query overrides", () => { |
| 676 | describe("ClaudeAdapterV2 native protocol logging", () => { | 660 | describe("ClaudeAdapterV2 native protocol logging", () => { |
| 677 | it("injects thread-scoped MCP configuration without logging the credential", () => { | 661 | it("injects thread-scoped MCP configuration without logging the credential", () => { |
| 678 | const threadId = ThreadId.make("thread-claude-mcp"); | 662 | const threadId = ThreadId.make("thread-claude-mcp"); |
| 679 | McpProviderSession.setMcpProviderSession({ | 663 | const mcpSession = { |
| 680 | environmentId: EnvironmentId.make("environment-claude-mcp"), | 664 | environmentId: EnvironmentId.make("environment-claude-mcp"), |
| 681 | threadId, | 665 | threadId, |
| 682 | providerSessionId: "mcp-session-claude", | 666 | providerSessionId: "mcp-session-claude", |
| ... | @@ -684,60 +668,56 @@ describe("ClaudeAdapterV2 native protocol logging", () => { | ... | @@ -684,60 +668,56 @@ describe("ClaudeAdapterV2 native protocol logging", () => { |
| 684 | endpoint: "http://127.0.0.1:43123/mcp", | 668 | endpoint: "http://127.0.0.1:43123/mcp", |
| 685 | authorizationHeader: "Bearer secret-claude-token", | 669 | authorizationHeader: "Bearer secret-claude-token", |
| 686 | browserToolsAvailable: true, | 670 | browserToolsAvailable: true, |
| 687 | }); | 671 | }; |
| 688 | 672 | ||
| 689 | try { | 673 | const overrides = ClaudeAdapterV2.claudeMcpQueryOverrides({ |
| 690 | const overrides = ClaudeAdapterV2.claudeMcpQueryOverrides({ | 674 | mcpSession, |
| 691 | threadId, | 675 | readOnlySandbox: false, |
| 692 | readOnlySandbox: false, | 676 | allowedTools: ["Read"], |
| 693 | allowedTools: ["Read"], | 677 | }); |
| 694 | }); | 678 | assert.deepEqual(overrides, { |
| 695 | assert.deepEqual(overrides, { | 679 | allowedTools: ["Read", "mcp__t3-code__*"], |
| 696 | allowedTools: ["Read", "mcp__t3-code__*"], | 680 | mcpServers: { |
| 697 | mcpServers: { | 681 | "t3-code": { |
| 698 | "t3-code": { | 682 | type: "http", |
| 699 | type: "http", | 683 | url: "http://127.0.0.1:43123/mcp", |
| 700 | url: "http://127.0.0.1:43123/mcp", | 684 | headers: { |
| 701 | headers: { | 685 | Authorization: "${T3_CODE_MCP_AUTHORIZATION}", |
| 702 | Authorization: "${T3_CODE_MCP_AUTHORIZATION}", | ||
| 703 | }, | ||
| 704 | timeout: ClaudeAdapterV2.CLAUDE_T3_MCP_TOOL_TIMEOUT_MS, | ||
| 705 | }, | 686 | }, |
| 687 | timeout: ClaudeAdapterV2.CLAUDE_T3_MCP_TOOL_TIMEOUT_MS, | ||
| 706 | }, | 688 | }, |
| 707 | mcpEnvironment: { T3_CODE_MCP_AUTHORIZATION: "Bearer secret-claude-token" }, | 689 | }, |
| 708 | }); | 690 | mcpEnvironment: { T3_CODE_MCP_AUTHORIZATION: "Bearer secret-claude-token" }, |
| 691 | }); | ||
| 709 | 692 | ||
| 710 | const options = ClaudeAdapterV2.makeClaudeQueryOptions({ | 693 | const options = ClaudeAdapterV2.makeClaudeQueryOptions({ |
| 711 | modelSelection: { | 694 | modelSelection: { |
| 712 | instanceId: ProviderInstanceId.make("claudeAgent"), | 695 | instanceId: ProviderInstanceId.make("claudeAgent"), |
| 713 | model: "claude-sonnet-4-6", | 696 | model: "claude-sonnet-4-6", |
| 714 | }, | 697 | }, |
| 715 | nativeThreadId: "native-thread-claude-mcp", | 698 | nativeThreadId: "native-thread-claude-mcp", |
| 716 | resume: false, | 699 | resume: false, |
| 717 | cwd: "/workspace", | 700 | cwd: "/workspace", |
| 718 | allowedTools: overrides.allowedTools ?? [], | 701 | allowedTools: overrides.allowedTools ?? [], |
| 719 | mcpServers: overrides.mcpServers ?? {}, | 702 | mcpServers: overrides.mcpServers ?? {}, |
| 720 | environment: { ...overrides.mcpEnvironment }, | 703 | environment: { ...overrides.mcpEnvironment }, |
| 721 | }); | 704 | }); |
| 722 | // mcpServers becomes a CLI argument, readable by every local user; the | 705 | // mcpServers becomes a CLI argument, readable by every local user; the |
| 723 | // credential may only travel in the child's environment. | 706 | // credential may only travel in the child's environment. |
| 724 | assert.notInclude(JSON.stringify(options.mcpServers), "secret-claude-token"); | 707 | assert.notInclude(JSON.stringify(options.mcpServers), "secret-claude-token"); |
| 725 | assert.equal(options.env?.T3_CODE_MCP_AUTHORIZATION, "Bearer secret-claude-token"); | 708 | assert.equal(options.env?.T3_CODE_MCP_AUTHORIZATION, "Bearer secret-claude-token"); |
| 726 | assert.isObject(options.systemPrompt); | 709 | assert.isObject(options.systemPrompt); |
| 727 | const systemPrompt = options.systemPrompt as { | 710 | const systemPrompt = options.systemPrompt as { |
| 728 | readonly type: string; | 711 | readonly type: string; |
| 729 | readonly preset: string; | 712 | readonly preset: string; |
| 730 | readonly append?: string; | 713 | readonly append?: string; |
| 731 | }; | 714 | }; |
| 732 | assert.equal(systemPrompt.type, "preset"); | 715 | assert.equal(systemPrompt.type, "preset"); |
| 733 | assert.equal(systemPrompt.preset, "claude_code"); | 716 | assert.equal(systemPrompt.preset, "claude_code"); |
| 734 | assert.include(systemPrompt.append ?? "", "Use `delegate_task`"); | 717 | assert.include(systemPrompt.append ?? "", "Use `delegate_task`"); |
| 735 | const logged = ClaudeAdapterV2.loggedClaudeQueryOptions(options); | 718 | const logged = ClaudeAdapterV2.loggedClaudeQueryOptions(options); |
| 736 | assert.equal(logged.hasMcpServers, true); | 719 | assert.equal(logged.hasMcpServers, true); |
| 737 | assert.notInclude(JSON.stringify(logged), "secret-claude-token"); | 720 | assert.notInclude(JSON.stringify(logged), "secret-claude-token"); |
| 738 | } finally { | ||
| 739 | McpProviderSession.clearMcpProviderSession(threadId); | ||
| 740 | } | ||
| 741 | }); | 721 | }); |
| 742 | 722 | ||
| 743 | it.effect("writes Claude Agent SDK protocol frames to the native provider log", () => | 723 | it.effect("writes Claude Agent SDK protocol frames to the native provider log", () => |
| ... | @@ -961,7 +941,7 @@ describe("ClaudeAdapterV2 Auto-accept edits", () => { | ... | @@ -961,7 +941,7 @@ describe("ClaudeAdapterV2 Auto-accept edits", () => { |
| 961 | prefix: "t3-claude-accept-edits-", | 941 | prefix: "t3-claude-accept-edits-", |
| 962 | }); | 942 | }); |
| 963 | let openedOptions: ClaudeAdapterV2.ClaudeAgentSdkQueryOptions | undefined; | 943 | let openedOptions: ClaudeAdapterV2.ClaudeAgentSdkQueryOptions | undefined; |
| 964 | const adapter = ClaudeAdapterV2.makeClaudeAdapterV2({ | 944 | const adapter = yield* ClaudeAdapterV2.makeClaudeAdapterV2({ |
| 965 | instanceId: ClaudeAdapterV2.CLAUDE_DEFAULT_INSTANCE_ID, | 945 | instanceId: ClaudeAdapterV2.CLAUDE_DEFAULT_INSTANCE_ID, |
| 966 | settings: DEFAULT_CLAUDE_SETTINGS, | 946 | settings: DEFAULT_CLAUDE_SETTINGS, |
| 967 | environment: {}, | 947 | environment: {}, |
| ... | @@ -1056,7 +1036,11 @@ describe("ClaudeAdapterV2 Auto-accept edits", () => { | ... | @@ -1056,7 +1036,11 @@ describe("ClaudeAdapterV2 Auto-accept edits", () => { |
| 1056 | decision: "accept", | 1036 | decision: "accept", |
| 1057 | }); | 1037 | }); |
| 1058 | assert.equal((yield* Fiber.join(decision))?.behavior, "allow"); | 1038 | assert.equal((yield* Fiber.join(decision))?.behavior, "allow"); |
| 1059 | }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 1039 | }).pipe( |
| 1040 | Effect.provide( | ||
| 1041 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 1042 | ), | ||
| 1043 | ), | ||
| 1060 | ), | 1044 | ), |
| 1061 | ); | 1045 | ); |
| 1062 | }); | 1046 | }); |
| ... | @@ -1174,7 +1158,11 @@ describe("ClaudeAdapterV2 executable path", () => { | ... | @@ -1174,7 +1158,11 @@ describe("ClaudeAdapterV2 executable path", () => { |
| 1174 | 1158 | ||
| 1175 | assert.deepEqual(executablePaths, [path.join(NodeOS.homedir(), "bin", "claude")]); | 1159 | assert.deepEqual(executablePaths, [path.join(NodeOS.homedir(), "bin", "claude")]); |
| 1176 | }), | 1160 | }), |
| 1177 | ).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 1161 | ).pipe( |
| 1162 | Effect.provide( | ||
| 1163 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 1164 | ), | ||
| 1165 | ), | ||
| 1178 | ); | 1166 | ); |
| 1179 | 1167 | ||
| 1180 | it.effect("follows a bare claude on Windows to the npm package executable", () => | 1168 | it.effect("follows a bare claude on Windows to the npm package executable", () => |
| ... | @@ -1190,7 +1178,11 @@ describe("ClaudeAdapterV2 executable path", () => { | ... | @@ -1190,7 +1178,11 @@ describe("ClaudeAdapterV2 executable path", () => { |
| 1190 | 1178 | ||
| 1191 | assert.deepEqual(executablePaths, [packageExe]); | 1179 | assert.deepEqual(executablePaths, [packageExe]); |
| 1192 | }), | 1180 | }), |
| 1193 | ).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 1181 | ).pipe( |
| 1182 | Effect.provide( | ||
| 1183 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 1184 | ), | ||
| 1185 | ), | ||
| 1194 | ); | 1186 | ); |
| 1195 | }); | 1187 | }); |
| 1196 | 1188 | ||
| ... | @@ -1204,7 +1196,7 @@ describe("ClaudeAdapterV2 resume compaction", () => { | ... | @@ -1204,7 +1196,7 @@ describe("ClaudeAdapterV2 resume compaction", () => { |
| 1204 | prefix: "t3-claude-resume-", | 1196 | prefix: "t3-claude-resume-", |
| 1205 | }); | 1197 | }); |
| 1206 | let openedOptions: ClaudeAdapterV2.ClaudeAgentSdkQueryOptions | undefined; | 1198 | let openedOptions: ClaudeAdapterV2.ClaudeAgentSdkQueryOptions | undefined; |
| 1207 | const adapter = ClaudeAdapterV2.makeClaudeAdapterV2({ | 1199 | const adapter = yield* ClaudeAdapterV2.makeClaudeAdapterV2({ |
| 1208 | instanceId: ClaudeAdapterV2.CLAUDE_DEFAULT_INSTANCE_ID, | 1200 | instanceId: ClaudeAdapterV2.CLAUDE_DEFAULT_INSTANCE_ID, |
| 1209 | settings: DEFAULT_CLAUDE_SETTINGS, | 1201 | settings: DEFAULT_CLAUDE_SETTINGS, |
| 1210 | environment: {}, | 1202 | environment: {}, |
| ... | @@ -1408,7 +1400,11 @@ describe("ClaudeAdapterV2 resume compaction", () => { | ... | @@ -1408,7 +1400,11 @@ describe("ClaudeAdapterV2 resume compaction", () => { |
| 1408 | ), | 1400 | ), |
| 1409 | { behavior: "cancelled" }, | 1401 | { behavior: "cancelled" }, |
| 1410 | ); | 1402 | ); |
| 1411 | }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 1403 | }).pipe( |
| 1404 | Effect.provide( | ||
| 1405 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 1406 | ), | ||
| 1407 | ), | ||
| 1412 | ), | 1408 | ), |
| 1413 | ); | 1409 | ); |
| 1414 | }); | 1410 | }); |
| ... | @@ -1424,7 +1420,7 @@ describe("ClaudeAdapterV2 attachments", () => { | ... | @@ -1424,7 +1420,7 @@ describe("ClaudeAdapterV2 attachments", () => { |
| 1424 | prefix: "t3-claude-v2-attachments-", | 1420 | prefix: "t3-claude-v2-attachments-", |
| 1425 | }); | 1421 | }); |
| 1426 | const offeredMessages: Array<SDKUserMessage> = []; | 1422 | const offeredMessages: Array<SDKUserMessage> = []; |
| 1427 | const adapter = ClaudeAdapterV2.makeClaudeAdapterV2({ | 1423 | const adapter = yield* ClaudeAdapterV2.makeClaudeAdapterV2({ |
| 1428 | instanceId: ClaudeAdapterV2.CLAUDE_DEFAULT_INSTANCE_ID, | 1424 | instanceId: ClaudeAdapterV2.CLAUDE_DEFAULT_INSTANCE_ID, |
| 1429 | settings: DEFAULT_CLAUDE_SETTINGS, | 1425 | settings: DEFAULT_CLAUDE_SETTINGS, |
| 1430 | environment: {}, | 1426 | environment: {}, |
| ... | @@ -1552,7 +1548,11 @@ describe("ClaudeAdapterV2 attachments", () => { | ... | @@ -1552,7 +1548,11 @@ describe("ClaudeAdapterV2 attachments", () => { |
| 1552 | text: `Ultrathink:\nFocus on the diagram labels.\n\n[Attached image "diagram.png" is saved at: ${expectedAttachmentPath}]\n\n[Attached file "requirements.pdf" is saved at: ${expectedDocumentPath}]`, | 1548 | text: `Ultrathink:\nFocus on the diagram labels.\n\n[Attached image "diagram.png" is saved at: ${expectedAttachmentPath}]\n\n[Attached file "requirements.pdf" is saved at: ${expectedDocumentPath}]`, |
| 1553 | }, | 1549 | }, |
| 1554 | ]); | 1550 | ]); |
| 1555 | }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 1551 | }).pipe( |
| 1552 | Effect.provide( | ||
| 1553 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 1554 | ), | ||
| 1555 | ), | ||
| 1556 | ), | 1556 | ), |
| 1557 | ); | 1557 | ); |
| 1558 | 1558 | ||
| ... | @@ -1565,7 +1565,7 @@ describe("ClaudeAdapterV2 attachments", () => { | ... | @@ -1565,7 +1565,7 @@ describe("ClaudeAdapterV2 attachments", () => { |
| 1565 | prefix: "t3-claude-v2-unsupported-attachment-", | 1565 | prefix: "t3-claude-v2-unsupported-attachment-", |
| 1566 | }); | 1566 | }); |
| 1567 | let openCount = 0; | 1567 | let openCount = 0; |
| 1568 | const adapter = ClaudeAdapterV2.makeClaudeAdapterV2({ | 1568 | const adapter = yield* ClaudeAdapterV2.makeClaudeAdapterV2({ |
| 1569 | instanceId: ClaudeAdapterV2.CLAUDE_DEFAULT_INSTANCE_ID, | 1569 | instanceId: ClaudeAdapterV2.CLAUDE_DEFAULT_INSTANCE_ID, |
| 1570 | settings: DEFAULT_CLAUDE_SETTINGS, | 1570 | settings: DEFAULT_CLAUDE_SETTINGS, |
| 1571 | environment: {}, | 1571 | environment: {}, |
| ... | @@ -1634,7 +1634,11 @@ describe("ClaudeAdapterV2 attachments", () => { | ... | @@ -1634,7 +1634,11 @@ describe("ClaudeAdapterV2 attachments", () => { |
| 1634 | assert.equal(error._tag, "ProviderAdapterTurnStartError"); | 1634 | assert.equal(error._tag, "ProviderAdapterTurnStartError"); |
| 1635 | assert.include(String(error.cause), "Unsupported Claude image attachment type"); | 1635 | assert.include(String(error.cause), "Unsupported Claude image attachment type"); |
| 1636 | assert.equal(openCount, 0); | 1636 | assert.equal(openCount, 0); |
| 1637 | }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 1637 | }).pipe( |
| 1638 | Effect.provide( | ||
| 1639 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 1640 | ), | ||
| 1641 | ), | ||
| 1638 | ), | 1642 | ), |
| 1639 | ); | 1643 | ); |
| 1640 | }); | 1644 | }); |
| ... | @@ -1655,7 +1659,7 @@ describe("ClaudeAdapterV2 native fork", () => { | ... | @@ -1655,7 +1659,7 @@ describe("ClaudeAdapterV2 native fork", () => { |
| 1655 | readonly threadId: ThreadId; | 1659 | readonly threadId: ThreadId; |
| 1656 | readonly providerSessionId: ProviderSessionId; | 1660 | readonly providerSessionId: ProviderSessionId; |
| 1657 | }> = []; | 1661 | }> = []; |
| 1658 | const adapter = ClaudeAdapterV2.makeClaudeAdapterV2({ | 1662 | const adapter = yield* ClaudeAdapterV2.makeClaudeAdapterV2({ |
| 1659 | instanceId: ClaudeAdapterV2.CLAUDE_DEFAULT_INSTANCE_ID, | 1663 | instanceId: ClaudeAdapterV2.CLAUDE_DEFAULT_INSTANCE_ID, |
| 1660 | settings: DEFAULT_CLAUDE_SETTINGS, | 1664 | settings: DEFAULT_CLAUDE_SETTINGS, |
| 1661 | environment: {}, | 1665 | environment: {}, |
| ... | @@ -1813,7 +1817,11 @@ describe("ClaudeAdapterV2 native fork", () => { | ... | @@ -1813,7 +1817,11 @@ describe("ClaudeAdapterV2 native fork", () => { |
| 1813 | 1817 | ||
| 1814 | assert.equal(openedQueries[0]?.options.resume, "forked-native-session"); | 1818 | assert.equal(openedQueries[0]?.options.resume, "forked-native-session"); |
| 1815 | assert.equal(openedQueries[0]?.options.sessionId, undefined); | 1819 | assert.equal(openedQueries[0]?.options.sessionId, undefined); |
| 1816 | }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 1820 | }).pipe( |
| 1821 | Effect.provide( | ||
| 1822 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 1823 | ), | ||
| 1824 | ), | ||
| 1817 | ), | 1825 | ), |
| 1818 | ); | 1826 | ); |
| 1819 | }); | 1827 | }); |
| ... | @@ -1828,7 +1836,7 @@ describe("ClaudeAdapterV2 native session identity", () => { | ... | @@ -1828,7 +1836,7 @@ describe("ClaudeAdapterV2 native session identity", () => { |
| 1828 | prefix: "t3-claude-v2-session-identity-", | 1836 | prefix: "t3-claude-v2-session-identity-", |
| 1829 | }); | 1837 | }); |
| 1830 | const openedQueries: Array<ClaudeAdapterV2.ClaudeAgentSdkQueryOpenInput> = []; | 1838 | const openedQueries: Array<ClaudeAdapterV2.ClaudeAgentSdkQueryOpenInput> = []; |
| 1831 | const adapter = ClaudeAdapterV2.makeClaudeAdapterV2({ | 1839 | const adapter = yield* ClaudeAdapterV2.makeClaudeAdapterV2({ |
| 1832 | instanceId: ClaudeAdapterV2.CLAUDE_DEFAULT_INSTANCE_ID, | 1840 | instanceId: ClaudeAdapterV2.CLAUDE_DEFAULT_INSTANCE_ID, |
| 1833 | settings: DEFAULT_CLAUDE_SETTINGS, | 1841 | settings: DEFAULT_CLAUDE_SETTINGS, |
| 1834 | environment: {}, | 1842 | environment: {}, |
| ... | @@ -1883,7 +1891,11 @@ describe("ClaudeAdapterV2 native session identity", () => { | ... | @@ -1883,7 +1891,11 @@ describe("ClaudeAdapterV2 native session identity", () => { |
| 1883 | }), | 1891 | }), |
| 1884 | ); | 1892 | ); |
| 1885 | return openedQueries; | 1893 | return openedQueries; |
| 1886 | }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 1894 | }).pipe( |
| 1895 | Effect.provide( | ||
| 1896 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 1897 | ), | ||
| 1898 | ), | ||
| 1887 | ); | 1899 | ); |
| 1888 | 1900 | ||
| 1889 | it.effect("creates the native session on the first provider turn", () => | 1901 | it.effect("creates the native session on the first provider turn", () => |
| ... | @@ -2127,7 +2139,7 @@ describe("ClaudeAdapterV2 background wake turns", () => { | ... | @@ -2127,7 +2139,7 @@ describe("ClaudeAdapterV2 background wake turns", () => { |
| 2127 | Extract<ProviderAdapter.ProviderAdapterV2Event, { type: "turn_item.updated" }> | 2139 | Extract<ProviderAdapter.ProviderAdapterV2Event, { type: "turn_item.updated" }> |
| 2128 | >(); | 2140 | >(); |
| 2129 | let openedOptions: ClaudeAdapterV2.ClaudeAgentSdkQueryOptions | undefined; | 2141 | let openedOptions: ClaudeAdapterV2.ClaudeAgentSdkQueryOptions | undefined; |
| 2130 | const adapter = ClaudeAdapterV2.makeClaudeAdapterV2({ | 2142 | const adapter = yield* ClaudeAdapterV2.makeClaudeAdapterV2({ |
| 2131 | instanceId: ClaudeAdapterV2.CLAUDE_DEFAULT_INSTANCE_ID, | 2143 | instanceId: ClaudeAdapterV2.CLAUDE_DEFAULT_INSTANCE_ID, |
| 2132 | settings: DEFAULT_CLAUDE_SETTINGS, | 2144 | settings: DEFAULT_CLAUDE_SETTINGS, |
| 2133 | environment: options?.environment ?? {}, | 2145 | environment: options?.environment ?? {}, |
| ... | @@ -2365,7 +2377,12 @@ describe("ClaudeAdapterV2 background wake turns", () => { | ... | @@ -2365,7 +2377,12 @@ describe("ClaudeAdapterV2 background wake turns", () => { |
| 2365 | assert.deepEqual(item.input, { city: "Berlin" }); | 2377 | assert.deepEqual(item.input, { city: "Berlin" }); |
| 2366 | } | 2378 | } |
| 2367 | assert.equal(new Set(items.map((item) => item.id)).size, 1); | 2379 | assert.equal(new Set(items.map((item) => item.id)).size, 1); |
| 2368 | }).pipe(Effect.scoped, Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 2380 | }).pipe( |
| 2381 | Effect.scoped, | ||
| 2382 | Effect.provide( | ||
| 2383 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 2384 | ), | ||
| 2385 | ), | ||
| 2369 | ); | 2386 | ); |
| 2370 | 2387 | ||
| 2371 | it.effect( | 2388 | it.effect( |
| ... | @@ -2426,7 +2443,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { | ... | @@ -2426,7 +2443,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { |
| 2426 | assert.strictEqual(harness.getOpenedOptions(), originalOptions); | 2443 | assert.strictEqual(harness.getOpenedOptions(), originalOptions); |
| 2427 | assert.lengthOf(harness.offeredMessages, 2); | 2444 | assert.lengthOf(harness.offeredMessages, 2); |
| 2428 | assert.isTrue(yield* harness.hasPendingBackgroundWork); | 2445 | assert.isTrue(yield* harness.hasPendingBackgroundWork); |
| 2429 | }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 2446 | }).pipe( |
| 2447 | Effect.provide( | ||
| 2448 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 2449 | ), | ||
| 2450 | ), | ||
| 2430 | ), | 2451 | ), |
| 2431 | ); | 2452 | ); |
| 2432 | 2453 | ||
| ... | @@ -2545,7 +2566,12 @@ describe("ClaudeAdapterV2 background wake turns", () => { | ... | @@ -2545,7 +2566,12 @@ describe("ClaudeAdapterV2 background wake turns", () => { |
| 2545 | ), | 2566 | ), |
| 2546 | ); | 2567 | ); |
| 2547 | for (const item of latest.values()) assert.notInclude(item.text, "secret-signature"); | 2568 | for (const item of latest.values()) assert.notInclude(item.text, "secret-signature"); |
| 2548 | }).pipe(Effect.scoped, Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 2569 | }).pipe( |
| 2570 | Effect.scoped, | ||
| 2571 | Effect.provide( | ||
| 2572 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 2573 | ), | ||
| 2574 | ), | ||
| 2549 | ); | 2575 | ); |
| 2550 | 2576 | ||
| 2551 | it.effect.each(["cancelled", "denied", "permission_denied", undefined])( | 2577 | it.effect.each(["cancelled", "denied", "permission_denied", undefined])( |
| ... | @@ -2615,7 +2641,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { | ... | @@ -2615,7 +2641,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { |
| 2615 | event.type === "node.updated" && event.node.nativeItemRef?.nativeId === "tool-error", | 2641 | event.type === "node.updated" && event.node.nativeItemRef?.nativeId === "tool-error", |
| 2616 | ); | 2642 | ); |
| 2617 | assert.equal(node?.type === "node.updated" ? node.node.status : undefined, failed.status); | 2643 | assert.equal(node?.type === "node.updated" ? node.node.status : undefined, failed.status); |
| 2618 | }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 2644 | }).pipe( |
| 2645 | Effect.provide( | ||
| 2646 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 2647 | ), | ||
| 2648 | ), | ||
| 2619 | ), | 2649 | ), |
| 2620 | ); | 2650 | ); |
| 2621 | 2651 | ||
| ... | @@ -2688,7 +2718,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { | ... | @@ -2688,7 +2718,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { |
| 2688 | ); | 2718 | ); |
| 2689 | } | 2719 | } |
| 2690 | assert.lengthOf(harness.terminalEvents(), 1); | 2720 | assert.lengthOf(harness.terminalEvents(), 1); |
| 2691 | }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 2721 | }).pipe( |
| 2722 | Effect.provide( | ||
| 2723 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 2724 | ), | ||
| 2725 | ), | ||
| 2692 | ), | 2726 | ), |
| 2693 | ); | 2727 | ); |
| 2694 | 2728 | ||
| ... | @@ -2754,7 +2788,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { | ... | @@ -2754,7 +2788,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { |
| 2754 | yield* Queue.take(harness.terminalReceipts); | 2788 | yield* Queue.take(harness.terminalReceipts); |
| 2755 | assert.lengthOf(notices(), 3); | 2789 | assert.lengthOf(notices(), 3); |
| 2756 | assert.notEqual(notices()[0]?.id, notices()[2]?.id); | 2790 | assert.notEqual(notices()[0]?.id, notices()[2]?.id); |
| 2757 | }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 2791 | }).pipe( |
| 2792 | Effect.provide( | ||
| 2793 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 2794 | ), | ||
| 2795 | ), | ||
| 2758 | ); | 2796 | ); |
| 2759 | 2797 | ||
| 2760 | it.effect("keeps usage warnings and provisioned overage silent", () => | 2798 | it.effect("keeps usage warnings and provisioned overage silent", () => |
| ... | @@ -2801,7 +2839,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { | ... | @@ -2801,7 +2839,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { |
| 2801 | (event) => event.type === "turn_item.updated" && event.turnItem.type === "system_notice", | 2839 | (event) => event.type === "turn_item.updated" && event.turnItem.type === "system_notice", |
| 2802 | ), | 2840 | ), |
| 2803 | ); | 2841 | ); |
| 2804 | }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 2842 | }).pipe( |
| 2843 | Effect.provide( | ||
| 2844 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 2845 | ), | ||
| 2846 | ), | ||
| 2805 | ); | 2847 | ); |
| 2806 | 2848 | ||
| 2807 | it.effect("names an expired Claude login instead of the terminal API error", () => | 2849 | it.effect("names an expired Claude login instead of the terminal API error", () => |
| ... | @@ -2846,7 +2888,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { | ... | @@ -2846,7 +2888,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { |
| 2846 | assert.include(terminal.failure.message, configDir); | 2888 | assert.include(terminal.failure.message, configDir); |
| 2847 | assert.include(terminal.failure.message, cwd); | 2889 | assert.include(terminal.failure.message, cwd); |
| 2848 | assert.notInclude(terminal.failure.message, "repeated API errors"); | 2890 | assert.notInclude(terminal.failure.message, "repeated API errors"); |
| 2849 | }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 2891 | }).pipe( |
| 2892 | Effect.provide( | ||
| 2893 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 2894 | ), | ||
| 2895 | ), | ||
| 2850 | ); | 2896 | ); |
| 2851 | 2897 | ||
| 2852 | it.effect.each([ | 2898 | it.effect.each([ |
| ... | @@ -2900,7 +2946,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { | ... | @@ -2900,7 +2946,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { |
| 2900 | if (terminal.status !== "failed") return; | 2946 | if (terminal.status !== "failed") return; |
| 2901 | assert.include(terminal.failure.message, expected); | 2947 | assert.include(terminal.failure.message, expected); |
| 2902 | assert.equal(terminal.failure.class, recovered ? "provider_error" : "usage_limit"); | 2948 | assert.equal(terminal.failure.class, recovered ? "provider_error" : "usage_limit"); |
| 2903 | }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 2949 | }).pipe( |
| 2950 | Effect.provide( | ||
| 2951 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 2952 | ), | ||
| 2953 | ), | ||
| 2904 | ); | 2954 | ); |
| 2905 | 2955 | ||
| 2906 | it.effect.each([ | 2956 | it.effect.each([ |
| ... | @@ -2960,7 +3010,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { | ... | @@ -2960,7 +3010,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { |
| 2960 | ? "Claude usage limit reached. Send the message again once the limit resets." | 3010 | ? "Claude usage limit reached. Send the message again once the limit resets." |
| 2961 | : "Claude gave up after repeated API errors.", | 3011 | : "Claude gave up after repeated API errors.", |
| 2962 | ); | 3012 | ); |
| 2963 | }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 3013 | }).pipe( |
| 3014 | Effect.provide( | ||
| 3015 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 3016 | ), | ||
| 3017 | ), | ||
| 2964 | ); | 3018 | ); |
| 2965 | 3019 | ||
| 2966 | it.effect.each([429, 401, 529])( | 3020 | it.effect.each([429, 401, 529])( |
| ... | @@ -3004,7 +3058,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { | ... | @@ -3004,7 +3058,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { |
| 3004 | ); | 3058 | ); |
| 3005 | if (apiErrorStatus !== 429) | 3059 | if (apiErrorStatus !== 429) |
| 3006 | assert.notInclude(terminal.failure.message.toLowerCase(), "usage limit"); | 3060 | assert.notInclude(terminal.failure.message.toLowerCase(), "usage limit"); |
| 3007 | }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 3061 | }).pipe( |
| 3062 | Effect.provide( | ||
| 3063 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 3064 | ), | ||
| 3065 | ), | ||
| 3008 | ); | 3066 | ); |
| 3009 | 3067 | ||
| 3010 | it.effect("surfaces a Claude safety model fallback without failing the turn", () => | 3068 | it.effect("surfaces a Claude safety model fallback without failing the turn", () => |
| ... | @@ -3063,7 +3121,12 @@ describe("ClaudeAdapterV2 background wake turns", () => { | ... | @@ -3063,7 +3121,12 @@ describe("ClaudeAdapterV2 background wake turns", () => { |
| 3063 | (event) => event.type === "turn_item.updated" && event.turnItem.type === "error", | 3121 | (event) => event.type === "turn_item.updated" && event.turnItem.type === "error", |
| 3064 | ), | 3122 | ), |
| 3065 | ); | 3123 | ); |
| 3066 | }).pipe(Effect.scoped, Effect.provide(Layer.mergeAll(NodeServices.layer, IdAllocator.layer))), | 3124 | }).pipe( |
| 3125 | Effect.scoped, | ||
| 3126 | Effect.provide( | ||
| 3127 | Layer.mergeAll(NodeServices.layer, IdAllocator.layer, McpProviderSessions.layer), | ||
| 3128 | ), | ||
| 3129 | ), | ||
| 3067 | ); | 3130 | ); |
| 3068 | 3131 | ||
| 3069 | it.effect( | 3132 | it.effect( |
| ... | @@ -3135,7 +3198,12 @@ describe("ClaudeAdapterV2 background wake turns", () => { | ... | @@ -3135,7 +3198,12 @@ describe("ClaudeAdapterV2 background wake turns", () => { |
| 3135 | cacheCreationTokens: 0, | 3198 | cacheCreationTokens: 0, |
| 3136 | }); | 3199 | }); |
| 3137 | } | 3200 | } |
| 3138 | }).pipe(Effect.scoped, Effect.provide(Layer.mergeAll(NodeServices.layer, IdAllocator.layer))), | 3201 | }).pipe( |
| 3202 | Effect.scoped, | ||
| 3203 | Effect.provide( | ||
| 3204 | Layer.mergeAll(NodeServices.layer, IdAllocator.layer, McpProviderSessions.layer), | ||
| 3205 | ), | ||
| 3206 | ), | ||
| 3139 | ); | 3207 | ); |
| 3140 | 3208 | ||
| 3141 | it.effect("titles Claude reads, searches, and skills on tool completion", () => | 3209 | it.effect("titles Claude reads, searches, and skills on tool completion", () => |
| ... | @@ -3233,7 +3301,12 @@ describe("ClaudeAdapterV2 background wake turns", () => { | ... | @@ -3233,7 +3301,12 @@ describe("ClaudeAdapterV2 background wake turns", () => { |
| 3233 | ); | 3301 | ); |
| 3234 | for (const item of items.filter((item) => item.nativeItemRef?.nativeId !== "image")) | 3302 | for (const item of items.filter((item) => item.nativeItemRef?.nativeId !== "image")) |
| 3235 | assert.notProperty(item, "viewedImagePath"); | 3303 | assert.notProperty(item, "viewedImagePath"); |
| 3236 | }).pipe(Effect.scoped, Effect.provide(Layer.mergeAll(NodeServices.layer, IdAllocator.layer))), | 3304 | }).pipe( |
| 3305 | Effect.scoped, | ||
| 3306 | Effect.provide( | ||
| 3307 | Layer.mergeAll(NodeServices.layer, IdAllocator.layer, McpProviderSessions.layer), | ||
| 3308 | ), | ||
| 3309 | ), | ||
| 3237 | ); | 3310 | ); |
| 3238 | 3311 | ||
| 3239 | it.effect("preserves typed Claude plans and todos through generic tool completion", () => | 3312 | it.effect("preserves typed Claude plans and todos through generic tool completion", () => |
| ... | @@ -3442,7 +3515,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { | ... | @@ -3442,7 +3515,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { |
| 3442 | // The second prompt reuses the live process, which is still in the | 3515 | // The second prompt reuses the live process, which is still in the |
| 3443 | // plan mode Claude entered, so it is put back in the thread's mode. | 3516 | // plan mode Claude entered, so it is put back in the thread's mode. |
| 3444 | assert.deepEqual(harness.permissionModeChanges, ["bypassPermissions"]); | 3517 | assert.deepEqual(harness.permissionModeChanges, ["bypassPermissions"]); |
| 3445 | }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 3518 | }).pipe( |
| 3519 | Effect.provide( | ||
| 3520 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 3521 | ), | ||
| 3522 | ), | ||
| 3446 | ), | 3523 | ), |
| 3447 | ); | 3524 | ); |
| 3448 | 3525 | ||
| ... | @@ -3516,7 +3593,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { | ... | @@ -3516,7 +3593,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { |
| 3516 | }), | 3593 | }), |
| 3517 | ); | 3594 | ); |
| 3518 | yield* awaitUntil(() => harness.terminalEvents().length === 1, "recovered Claude turn"); | 3595 | yield* awaitUntil(() => harness.terminalEvents().length === 1, "recovered Claude turn"); |
| 3519 | }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 3596 | }).pipe( |
| 3597 | Effect.provide( | ||
| 3598 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 3599 | ), | ||
| 3600 | ), | ||
| 3520 | ), | 3601 | ), |
| 3521 | ); | 3602 | ); |
| 3522 | 3603 | ||
| ... | @@ -3570,7 +3651,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { | ... | @@ -3570,7 +3651,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { |
| 3570 | }); | 3651 | }); |
| 3571 | assert.isDefined(terminal.retryStartedAt); | 3652 | assert.isDefined(terminal.retryStartedAt); |
| 3572 | assert.equal(terminal.failure.code, "api_error_529"); | 3653 | assert.equal(terminal.failure.code, "api_error_529"); |
| 3573 | }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 3654 | }).pipe( |
| 3655 | Effect.provide( | ||
| 3656 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 3657 | ), | ||
| 3658 | ), | ||
| 3574 | ), | 3659 | ), |
| 3575 | ); | 3660 | ); |
| 3576 | 3661 | ||
| ... | @@ -3632,7 +3717,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { | ... | @@ -3632,7 +3717,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { |
| 3632 | terminal.failure.code, | 3717 | terminal.failure.code, |
| 3633 | terminalReason === "overloaded_status" ? "api_error_529" : terminalReason, | 3718 | terminalReason === "overloaded_status" ? "api_error_529" : terminalReason, |
| 3634 | ); | 3719 | ); |
| 3635 | }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 3720 | }).pipe( |
| 3721 | Effect.provide( | ||
| 3722 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 3723 | ), | ||
| 3724 | ), | ||
| 3636 | ), | 3725 | ), |
| 3637 | ); | 3726 | ); |
| 3638 | 3727 | ||
| ... | @@ -3697,7 +3786,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { | ... | @@ -3697,7 +3786,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { |
| 3697 | "empty roster clear", | 3786 | "empty roster clear", |
| 3698 | ); | 3787 | ); |
| 3699 | assert.isFalse(yield* harness.hasPendingBackgroundWork); | 3788 | assert.isFalse(yield* harness.hasPendingBackgroundWork); |
| 3700 | }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 3789 | }).pipe( |
| 3790 | Effect.provide( | ||
| 3791 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 3792 | ), | ||
| 3793 | ), | ||
| 3701 | ), | 3794 | ), |
| 3702 | ); | 3795 | ); |
| 3703 | 3796 | ||
| ... | @@ -3832,7 +3925,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { | ... | @@ -3832,7 +3925,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { |
| 3832 | assert.deepEqual(roster, [ | 3925 | assert.deepEqual(roster, [ |
| 3833 | { taskId: longRunning.taskId, kind: "monitor", description: "Monitor 0" }, | 3926 | { taskId: longRunning.taskId, kind: "monitor", description: "Monitor 0" }, |
| 3834 | ]); | 3927 | ]); |
| 3835 | }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 3928 | }).pipe( |
| 3929 | Effect.provide( | ||
| 3930 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 3931 | ), | ||
| 3932 | ), | ||
| 3836 | ), | 3933 | ), |
| 3837 | ); | 3934 | ); |
| 3838 | 3935 | ||
| ... | @@ -3882,7 +3979,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { | ... | @@ -3882,7 +3979,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { |
| 3882 | ); | 3979 | ); |
| 3883 | assert.isFalse(yield* harness.hasPendingBackgroundWork); | 3980 | assert.isFalse(yield* harness.hasPendingBackgroundWork); |
| 3884 | assert.lengthOf(harness.continuationRequests, 0); | 3981 | assert.lengthOf(harness.continuationRequests, 0); |
| 3885 | }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 3982 | }).pipe( |
| 3983 | Effect.provide( | ||
| 3984 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 3985 | ), | ||
| 3986 | ), | ||
| 3886 | ), | 3987 | ), |
| 3887 | ); | 3988 | ); |
| 3888 | 3989 | ||
| ... | @@ -3927,7 +4028,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { | ... | @@ -3927,7 +4028,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { |
| 3927 | ); | 4028 | ); |
| 3928 | assert.isFalse(yield* harness.hasPendingBackgroundWork); | 4029 | assert.isFalse(yield* harness.hasPendingBackgroundWork); |
| 3929 | assert.lengthOf(harness.continuationRequests, 0); | 4030 | assert.lengthOf(harness.continuationRequests, 0); |
| 3930 | }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 4031 | }).pipe( |
| 4032 | Effect.provide( | ||
| 4033 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 4034 | ), | ||
| 4035 | ), | ||
| 3931 | ), | 4036 | ), |
| 3932 | ); | 4037 | ); |
| 3933 | 4038 | ||
| ... | @@ -3944,7 +4049,7 @@ describe("ClaudeAdapterV2 background wake turns", () => { | ... | @@ -3944,7 +4049,7 @@ describe("ClaudeAdapterV2 background wake turns", () => { |
| 3944 | const events: Array<ProviderAdapter.ProviderAdapterV2Event> = []; | 4049 | const events: Array<ProviderAdapter.ProviderAdapterV2Event> = []; |
| 3945 | const continuationRequests: Array<ProviderContinuationRequests.ProviderContinuationRequest> = | 4050 | const continuationRequests: Array<ProviderContinuationRequests.ProviderContinuationRequest> = |
| 3946 | []; | 4051 | []; |
| 3947 | const adapter = ClaudeAdapterV2.makeClaudeAdapterV2({ | 4052 | const adapter = yield* ClaudeAdapterV2.makeClaudeAdapterV2({ |
| 3948 | instanceId: ClaudeAdapterV2.CLAUDE_DEFAULT_INSTANCE_ID, | 4053 | instanceId: ClaudeAdapterV2.CLAUDE_DEFAULT_INSTANCE_ID, |
| 3949 | settings: DEFAULT_CLAUDE_SETTINGS, | 4054 | settings: DEFAULT_CLAUDE_SETTINGS, |
| 3950 | environment: {}, | 4055 | environment: {}, |
| ... | @@ -4109,7 +4214,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { | ... | @@ -4109,7 +4214,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { |
| 4109 | }), | 4214 | }), |
| 4110 | ); | 4215 | ); |
| 4111 | yield* awaitUntil(() => continuationRequests.length === 1, "replacement wake"); | 4216 | yield* awaitUntil(() => continuationRequests.length === 1, "replacement wake"); |
| 4112 | }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 4217 | }).pipe( |
| 4218 | Effect.provide( | ||
| 4219 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 4220 | ), | ||
| 4221 | ), | ||
| 4113 | ), | 4222 | ), |
| 4114 | ); | 4223 | ); |
| 4115 | 4224 | ||
| ... | @@ -4167,7 +4276,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { | ... | @@ -4167,7 +4276,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { |
| 4167 | const afterFailure = providerThreadRosterEvents(harness.events).at(-1); | 4276 | const afterFailure = providerThreadRosterEvents(harness.events).at(-1); |
| 4168 | assert.deepEqual(afterFailure?.providerThread.pendingBackgroundTasks ?? [], []); | 4277 | assert.deepEqual(afterFailure?.providerThread.pendingBackgroundTasks ?? [], []); |
| 4169 | assert.isFalse(yield* harness.hasPendingBackgroundWork); | 4278 | assert.isFalse(yield* harness.hasPendingBackgroundWork); |
| 4170 | }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 4279 | }).pipe( |
| 4280 | Effect.provide( | ||
| 4281 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 4282 | ), | ||
| 4283 | ), | ||
| 4171 | ), | 4284 | ), |
| 4172 | ); | 4285 | ); |
| 4173 | 4286 | ||
| ... | @@ -4190,7 +4303,7 @@ describe("ClaudeAdapterV2 background wake turns", () => { | ... | @@ -4190,7 +4303,7 @@ describe("ClaudeAdapterV2 background wake turns", () => { |
| 4190 | readonly queue: Queue.Queue<SDKMessage>; | 4303 | readonly queue: Queue.Queue<SDKMessage>; |
| 4191 | }> = []; | 4304 | }> = []; |
| 4192 | const events: Array<ProviderAdapter.ProviderAdapterV2Event> = []; | 4305 | const events: Array<ProviderAdapter.ProviderAdapterV2Event> = []; |
| 4193 | const adapter = ClaudeAdapterV2.makeClaudeAdapterV2({ | 4306 | const adapter = yield* ClaudeAdapterV2.makeClaudeAdapterV2({ |
| 4194 | instanceId: ClaudeAdapterV2.CLAUDE_DEFAULT_INSTANCE_ID, | 4307 | instanceId: ClaudeAdapterV2.CLAUDE_DEFAULT_INSTANCE_ID, |
| 4195 | settings: DEFAULT_CLAUDE_SETTINGS, | 4308 | settings: DEFAULT_CLAUDE_SETTINGS, |
| 4196 | environment: {}, | 4309 | environment: {}, |
| ... | @@ -4429,7 +4542,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { | ... | @@ -4429,7 +4542,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { |
| 4429 | assert.isFalse(yield* hasPendingBackgroundWorkForThread(providerThreadA)); | 4542 | assert.isFalse(yield* hasPendingBackgroundWorkForThread(providerThreadA)); |
| 4430 | assert.isFalse(yield* hasPendingBackgroundWorkForThread(providerThreadB)); | 4543 | assert.isFalse(yield* hasPendingBackgroundWorkForThread(providerThreadB)); |
| 4431 | assert.isFalse(yield* hasPendingBackgroundWork); | 4544 | assert.isFalse(yield* hasPendingBackgroundWork); |
| 4432 | }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 4545 | }).pipe( |
| 4546 | Effect.provide( | ||
| 4547 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 4548 | ), | ||
| 4549 | ), | ||
| 4433 | ), | 4550 | ), |
| 4434 | ); | 4551 | ); |
| 4435 | 4552 | ||
| ... | @@ -4649,7 +4766,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { | ... | @@ -4649,7 +4766,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { |
| 4649 | userRunItems.map((item) => item.type), | 4766 | userRunItems.map((item) => item.type), |
| 4650 | ["assistant_message"], | 4767 | ["assistant_message"], |
| 4651 | ); | 4768 | ); |
| 4652 | }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 4769 | }).pipe( |
| 4770 | Effect.provide( | ||
| 4771 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 4772 | ), | ||
| 4773 | ), | ||
| 4653 | ), | 4774 | ), |
| 4654 | ); | 4775 | ); |
| 4655 | 4776 | ||
| ... | @@ -4732,7 +4853,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { | ... | @@ -4732,7 +4853,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { |
| 4732 | : undefined, | 4853 | : undefined, |
| 4733 | "On branch main\nwarning: dirty", | 4854 | "On branch main\nwarning: dirty", |
| 4734 | ); | 4855 | ); |
| 4735 | }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 4856 | }).pipe( |
| 4857 | Effect.provide( | ||
| 4858 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 4859 | ), | ||
| 4860 | ), | ||
| 4736 | ), | 4861 | ), |
| 4737 | ); | 4862 | ); |
| 4738 | 4863 | ||
| ... | @@ -4861,7 +4986,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { | ... | @@ -4861,7 +4986,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { |
| 4861 | }); | 4986 | }); |
| 4862 | const result = yield* Fiber.join(permission); | 4987 | const result = yield* Fiber.join(permission); |
| 4863 | assert.equal(result?.behavior, "allow"); | 4988 | assert.equal(result?.behavior, "allow"); |
| 4864 | }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 4989 | }).pipe( |
| 4990 | Effect.provide( | ||
| 4991 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 4992 | ), | ||
| 4993 | ), | ||
| 4865 | ), | 4994 | ), |
| 4866 | ); | 4995 | ); |
| 4867 | 4996 | ||
| ... | @@ -4957,7 +5086,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { | ... | @@ -4957,7 +5086,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { |
| 4957 | event.message.text === STALE_TASK_NOTIFICATION_RESULT_TEXT, | 5086 | event.message.text === STALE_TASK_NOTIFICATION_RESULT_TEXT, |
| 4958 | ), | 5087 | ), |
| 4959 | ); | 5088 | ); |
| 4960 | }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 5089 | }).pipe( |
| 5090 | Effect.provide( | ||
| 5091 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 5092 | ), | ||
| 5093 | ), | ||
| 4961 | ), | 5094 | ), |
| 4962 | ); | 5095 | ); |
| 4963 | 5096 | ||
| ... | @@ -5035,7 +5168,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { | ... | @@ -5035,7 +5168,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { |
| 5035 | yield* awaitUntil(() => harness.terminalEvents().length === 2, "second turn terminal"); | 5168 | yield* awaitUntil(() => harness.terminalEvents().length === 2, "second turn terminal"); |
| 5036 | assert.deepEqual(assistantTexts(), ["One.", "Two."]); | 5169 | assert.deepEqual(assistantTexts(), ["One.", "Two."]); |
| 5037 | assert.equal(harness.terminalEvents()[1]?.status, "failed"); | 5170 | assert.equal(harness.terminalEvents()[1]?.status, "failed"); |
| 5038 | }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 5171 | }).pipe( |
| 5172 | Effect.provide( | ||
| 5173 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 5174 | ), | ||
| 5175 | ), | ||
| 5039 | ), | 5176 | ), |
| 5040 | ); | 5177 | ); |
| 5041 | 5178 | ||
| ... | @@ -5061,7 +5198,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { | ... | @@ -5061,7 +5198,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { |
| 5061 | ); | 5198 | ); |
| 5062 | } | 5199 | } |
| 5063 | assert.notEqual(original.promptUuid(0), copy.promptUuid(0)); | 5200 | assert.notEqual(original.promptUuid(0), copy.promptUuid(0)); |
| 5064 | }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 5201 | }).pipe( |
| 5202 | Effect.provide( | ||
| 5203 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 5204 | ), | ||
| 5205 | ), | ||
| 5065 | ), | 5206 | ), |
| 5066 | ); | 5207 | ); |
| 5067 | 5208 | ||
| ... | @@ -5098,7 +5239,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { | ... | @@ -5098,7 +5239,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { |
| 5098 | if (terminal?.status !== "failed") return; | 5239 | if (terminal?.status !== "failed") return; |
| 5099 | assert.equal(terminal.threadDisposition, "reusable"); | 5240 | assert.equal(terminal.threadDisposition, "reusable"); |
| 5100 | assert.include(terminal.failure.message, "never started a turn"); | 5241 | assert.include(terminal.failure.message, "never started a turn"); |
| 5101 | }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 5242 | }).pipe( |
| 5243 | Effect.provide( | ||
| 5244 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 5245 | ), | ||
| 5246 | ), | ||
| 5102 | ), | 5247 | ), |
| 5103 | ); | 5248 | ); |
| 5104 | 5249 | ||
| ... | @@ -5147,7 +5292,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { | ... | @@ -5147,7 +5292,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { |
| 5147 | ); | 5292 | ); |
| 5148 | yield* awaitUntil(() => harness.terminalEvents().length === 1, "turn terminal"); | 5293 | yield* awaitUntil(() => harness.terminalEvents().length === 1, "turn terminal"); |
| 5149 | assert.equal(harness.terminalEvents()[0]?.status, "completed"); | 5294 | assert.equal(harness.terminalEvents()[0]?.status, "completed"); |
| 5150 | }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 5295 | }).pipe( |
| 5296 | Effect.provide( | ||
| 5297 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 5298 | ), | ||
| 5299 | ), | ||
| 5151 | ), | 5300 | ), |
| 5152 | ); | 5301 | ); |
| 5153 | 5302 | ||
| ... | @@ -5193,7 +5342,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { | ... | @@ -5193,7 +5342,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { |
| 5193 | assert.lengthOf(harness.continuationRequests, 1); | 5342 | assert.lengthOf(harness.continuationRequests, 1); |
| 5194 | assert.lengthOf(harness.terminalEvents(), 1); | 5343 | assert.lengthOf(harness.terminalEvents(), 1); |
| 5195 | assert.isTrue(yield* harness.hasPendingBackgroundWork); | 5344 | assert.isTrue(yield* harness.hasPendingBackgroundWork); |
| 5196 | }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 5345 | }).pipe( |
| 5346 | Effect.provide( | ||
| 5347 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 5348 | ), | ||
| 5349 | ), | ||
| 5197 | ), | 5350 | ), |
| 5198 | ); | 5351 | ); |
| 5199 | 5352 | ||
| ... | @@ -5250,7 +5403,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { | ... | @@ -5250,7 +5403,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { |
| 5250 | (event) => event.type === "message.updated" && event.message.text === WAKE_RESULT_TEXT, | 5403 | (event) => event.type === "message.updated" && event.message.text === WAKE_RESULT_TEXT, |
| 5251 | ), | 5404 | ), |
| 5252 | ); | 5405 | ); |
| 5253 | }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 5406 | }).pipe( |
| 5407 | Effect.provide( | ||
| 5408 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 5409 | ), | ||
| 5410 | ), | ||
| 5254 | ), | 5411 | ), |
| 5255 | ); | 5412 | ); |
| 5256 | 5413 | ||
| ... | @@ -5301,7 +5458,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { | ... | @@ -5301,7 +5458,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { |
| 5301 | ); | 5458 | ); |
| 5302 | assert.equal(harness.terminalEvents()[1]?.status, "completed"); | 5459 | assert.equal(harness.terminalEvents()[1]?.status, "completed"); |
| 5303 | assert.lengthOf(harness.offeredMessages, 1); | 5460 | assert.lengthOf(harness.offeredMessages, 1); |
| 5304 | }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 5461 | }).pipe( |
| 5462 | Effect.provide( | ||
| 5463 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 5464 | ), | ||
| 5465 | ), | ||
| 5305 | ), | 5466 | ), |
| 5306 | ); | 5467 | ); |
| 5307 | 5468 | ||
| ... | @@ -5389,7 +5550,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { | ... | @@ -5389,7 +5550,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { |
| 5389 | assert.equal(pause.turnItem.type, "system_notice"); | 5550 | assert.equal(pause.turnItem.type, "system_notice"); |
| 5390 | if (pause.turnItem.type !== "system_notice") return; | 5551 | if (pause.turnItem.type !== "system_notice") return; |
| 5391 | assert.include(pause.turnItem.message, "This turn is paused until the 5-hour limit"); | 5552 | assert.include(pause.turnItem.message, "This turn is paused until the 5-hour limit"); |
| 5392 | }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 5553 | }).pipe( |
| 5554 | Effect.provide( | ||
| 5555 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 5556 | ), | ||
| 5557 | ), | ||
| 5393 | ), | 5558 | ), |
| 5394 | ); | 5559 | ); |
| 5395 | 5560 | ||
| ... | @@ -5480,7 +5645,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { | ... | @@ -5480,7 +5645,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { |
| 5480 | ), | 5645 | ), |
| 5481 | ); | 5646 | ); |
| 5482 | assert.isFalse(yield* harness.hasPendingBackgroundWork); | 5647 | assert.isFalse(yield* harness.hasPendingBackgroundWork); |
| 5483 | }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 5648 | }).pipe( |
| 5649 | Effect.provide( | ||
| 5650 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 5651 | ), | ||
| 5652 | ), | ||
| 5484 | ), | 5653 | ), |
| 5485 | ); | 5654 | ); |
| 5486 | 5655 | ||
| ... | @@ -5542,7 +5711,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { | ... | @@ -5542,7 +5711,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { |
| 5542 | ); | 5711 | ); |
| 5543 | yield* awaitUntil(() => harness.terminalEvents().length === 2, "queued turn terminal"); | 5712 | yield* awaitUntil(() => harness.terminalEvents().length === 2, "queued turn terminal"); |
| 5544 | assert.equal(harness.terminalEvents()[1]?.status, "completed"); | 5713 | assert.equal(harness.terminalEvents()[1]?.status, "completed"); |
| 5545 | }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 5714 | }).pipe( |
| 5715 | Effect.provide( | ||
| 5716 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 5717 | ), | ||
| 5718 | ), | ||
| 5546 | ), | 5719 | ), |
| 5547 | ); | 5720 | ); |
| 5548 | 5721 | ||
| ... | @@ -5580,7 +5753,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { | ... | @@ -5580,7 +5753,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { |
| 5580 | (event) => event.type === "message.updated" && event.message.text === fallbackText, | 5753 | (event) => event.type === "message.updated" && event.message.text === fallbackText, |
| 5581 | ), | 5754 | ), |
| 5582 | ); | 5755 | ); |
| 5583 | }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 5756 | }).pipe( |
| 5757 | Effect.provide( | ||
| 5758 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 5759 | ), | ||
| 5760 | ), | ||
| 5584 | ), | 5761 | ), |
| 5585 | ); | 5762 | ); |
| 5586 | 5763 | ||
| ... | @@ -5663,7 +5840,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { | ... | @@ -5663,7 +5840,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { |
| 5663 | assert.equal(harness.terminalEvents()[0]?.status, "completed"); | 5840 | assert.equal(harness.terminalEvents()[0]?.status, "completed"); |
| 5664 | assert.isTrue(hasMessageText(recoveryAssistantText)); | 5841 | assert.isTrue(hasMessageText(recoveryAssistantText)); |
| 5665 | assert.isFalse(hasMessageText(staleResultText)); | 5842 | assert.isFalse(hasMessageText(staleResultText)); |
| 5666 | }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 5843 | }).pipe( |
| 5844 | Effect.provide( | ||
| 5845 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 5846 | ), | ||
| 5847 | ), | ||
| 5667 | ), | 5848 | ), |
| 5668 | ); | 5849 | ); |
| 5669 | 5850 | ||
| ... | @@ -5713,7 +5894,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { | ... | @@ -5713,7 +5894,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { |
| 5713 | ); | 5894 | ); |
| 5714 | yield* awaitUntil(() => harness.terminalEvents().length === 2, "continuation terminal"); | 5895 | yield* awaitUntil(() => harness.terminalEvents().length === 2, "continuation terminal"); |
| 5715 | assert.equal(harness.terminalEvents()[1]?.status, "completed"); | 5896 | assert.equal(harness.terminalEvents()[1]?.status, "completed"); |
| 5716 | }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 5897 | }).pipe( |
| 5898 | Effect.provide( | ||
| 5899 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 5900 | ), | ||
| 5901 | ), | ||
| 5717 | ), | 5902 | ), |
| 5718 | ); | 5903 | ); |
| 5719 | 5904 | ||
| ... | @@ -5772,7 +5957,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { | ... | @@ -5772,7 +5957,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { |
| 5772 | assert.isTrue(terminalized); | 5957 | assert.isTrue(terminalized); |
| 5773 | assert.equal(harness.terminalEvents()[0]?.status, "interrupted"); | 5958 | assert.equal(harness.terminalEvents()[0]?.status, "interrupted"); |
| 5774 | assert.lengthOf(harness.terminalEvents(), 1); | 5959 | assert.lengthOf(harness.terminalEvents(), 1); |
| 5775 | }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 5960 | }).pipe( |
| 5961 | Effect.provide( | ||
| 5962 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 5963 | ), | ||
| 5964 | ), | ||
| 5776 | ), | 5965 | ), |
| 5777 | ); | 5966 | ); |
| 5778 | 5967 | ||
| ... | @@ -5831,7 +6020,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { | ... | @@ -5831,7 +6020,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { |
| 5831 | (event) => event.type === "message.updated" && event.message.text === staleText, | 6020 | (event) => event.type === "message.updated" && event.message.text === staleText, |
| 5832 | ), | 6021 | ), |
| 5833 | ); | 6022 | ); |
| 5834 | }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 6023 | }).pipe( |
| 6024 | Effect.provide( | ||
| 6025 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 6026 | ), | ||
| 6027 | ), | ||
| 5835 | ), | 6028 | ), |
| 5836 | ); | 6029 | ); |
| 5837 | 6030 | ||
| ... | @@ -5865,7 +6058,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { | ... | @@ -5865,7 +6058,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { |
| 5865 | ); | 6058 | ); |
| 5866 | yield* awaitUntil(() => harness.terminalEvents().length === 1, "failed terminal"); | 6059 | yield* awaitUntil(() => harness.terminalEvents().length === 1, "failed terminal"); |
| 5867 | assert.equal(harness.terminalEvents()[0]?.status, "failed"); | 6060 | assert.equal(harness.terminalEvents()[0]?.status, "failed"); |
| 5868 | }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 6061 | }).pipe( |
| 6062 | Effect.provide( | ||
| 6063 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 6064 | ), | ||
| 6065 | ), | ||
| 5869 | ), | 6066 | ), |
| 5870 | ); | 6067 | ); |
| 5871 | 6068 | ||
| ... | @@ -5891,7 +6088,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { | ... | @@ -5891,7 +6088,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { |
| 5891 | yield* awaitUntil(() => harness.terminalEvents().length === 1, "spurious terminal"); | 6088 | yield* awaitUntil(() => harness.terminalEvents().length === 1, "spurious terminal"); |
| 5892 | assert.equal(harness.terminalEvents()[0]?.status, "completed"); | 6089 | assert.equal(harness.terminalEvents()[0]?.status, "completed"); |
| 5893 | assert.lengthOf(harness.offeredMessages, 0); | 6090 | assert.lengthOf(harness.offeredMessages, 0); |
| 5894 | }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 6091 | }).pipe( |
| 6092 | Effect.provide( | ||
| 6093 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 6094 | ), | ||
| 6095 | ), | ||
| 5895 | ), | 6096 | ), |
| 5896 | ); | 6097 | ); |
| 5897 | 6098 | ||
| ... | @@ -6088,7 +6289,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { | ... | @@ -6088,7 +6289,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { |
| 6088 | harness.continuationRequests[1]?.notification?.summary, | 6289 | harness.continuationRequests[1]?.notification?.summary, |
| 6089 | `Command "${WAKE_TASK_DESCRIPTION}" finished`, | 6290 | `Command "${WAKE_TASK_DESCRIPTION}" finished`, |
| 6090 | ); | 6291 | ); |
| 6091 | }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 6292 | }).pipe( |
| 6293 | Effect.provide( | ||
| 6294 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 6295 | ), | ||
| 6296 | ), | ||
| 6092 | ), | 6297 | ), |
| 6093 | ); | 6298 | ); |
| 6094 | 6299 | ||
| ... | @@ -6149,7 +6354,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { | ... | @@ -6149,7 +6354,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { |
| 6149 | harness.continuationRequests[0]?.notification?.summary, | 6354 | harness.continuationRequests[0]?.notification?.summary, |
| 6150 | `Command "${WAKE_TASK_DESCRIPTION}" finished`, | 6355 | `Command "${WAKE_TASK_DESCRIPTION}" finished`, |
| 6151 | ); | 6356 | ); |
| 6152 | }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 6357 | }).pipe( |
| 6358 | Effect.provide( | ||
| 6359 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 6360 | ), | ||
| 6361 | ), | ||
| 6153 | ), | 6362 | ), |
| 6154 | ); | 6363 | ); |
| 6155 | 6364 | ||
| ... | @@ -6305,7 +6514,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { | ... | @@ -6305,7 +6514,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { |
| 6305 | : [], | 6514 | : [], |
| 6306 | ); | 6515 | ); |
| 6307 | assert.deepEqual([...new Set(userRunItems)], ["assistant_message"]); | 6516 | assert.deepEqual([...new Set(userRunItems)], ["assistant_message"]); |
| 6308 | }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 6517 | }).pipe( |
| 6518 | Effect.provide( | ||
| 6519 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 6520 | ), | ||
| 6521 | ), | ||
| 6309 | ), | 6522 | ), |
| 6310 | ); | 6523 | ); |
| 6311 | 6524 | ||
| ... | @@ -6411,7 +6624,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { | ... | @@ -6411,7 +6624,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { |
| 6411 | finalSubagent?.type === "subagent.updated" && finalSubagent.subagent.status, | 6624 | finalSubagent?.type === "subagent.updated" && finalSubagent.subagent.status, |
| 6412 | "completed", | 6625 | "completed", |
| 6413 | ); | 6626 | ); |
| 6414 | }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 6627 | }).pipe( |
| 6628 | Effect.provide( | ||
| 6629 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 6630 | ), | ||
| 6631 | ), | ||
| 6415 | ), | 6632 | ), |
| 6416 | ); | 6633 | ); |
| 6417 | 6634 | ||
| ... | @@ -6572,7 +6789,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { | ... | @@ -6572,7 +6789,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { |
| 6572 | finalSubagent?.type === "subagent.updated" && finalSubagent.subagent.status, | 6789 | finalSubagent?.type === "subagent.updated" && finalSubagent.subagent.status, |
| 6573 | "completed", | 6790 | "completed", |
| 6574 | ); | 6791 | ); |
| 6575 | }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 6792 | }).pipe( |
| 6793 | Effect.provide( | ||
| 6794 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 6795 | ), | ||
| 6796 | ), | ||
| 6576 | ), | 6797 | ), |
| 6577 | ); | 6798 | ); |
| 6578 | 6799 | ||
| ... | @@ -6760,7 +6981,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { | ... | @@ -6760,7 +6981,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { |
| 6760 | subagentRouting(harness.events, []).assistantTexts(outer?.childThreadId ?? undefined), | 6981 | subagentRouting(harness.events, []).assistantTexts(outer?.childThreadId ?? undefined), |
| 6761 | ["OUTER_WORKING", "OUTER_DONE"], | 6982 | ["OUTER_WORKING", "OUTER_DONE"], |
| 6762 | ); | 6983 | ); |
| 6763 | }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 6984 | }).pipe( |
| 6985 | Effect.provide( | ||
| 6986 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 6987 | ), | ||
| 6988 | ), | ||
| 6764 | ), | 6989 | ), |
| 6765 | ); | 6990 | ); |
| 6766 | 6991 | ||
| ... | @@ -6890,7 +7115,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { | ... | @@ -6890,7 +7115,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { |
| 6890 | ); | 7115 | ); |
| 6891 | assert.equal(nested?.type === "subagent.updated" && nested.subagent.status, "completed"); | 7116 | assert.equal(nested?.type === "subagent.updated" && nested.subagent.status, "completed"); |
| 6892 | assert.lengthOf(harness.continuationRequests, 0); | 7117 | assert.lengthOf(harness.continuationRequests, 0); |
| 6893 | }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 7118 | }).pipe( |
| 7119 | Effect.provide( | ||
| 7120 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 7121 | ), | ||
| 7122 | ), | ||
| 6894 | ), | 7123 | ), |
| 6895 | ); | 7124 | ); |
| 6896 | 7125 | ||
| ... | @@ -6942,7 +7171,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { | ... | @@ -6942,7 +7171,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { |
| 6942 | yield* Queue.shutdown(harness.sdkMessages); | 7171 | yield* Queue.shutdown(harness.sdkMessages); |
| 6943 | yield* awaitUntil(() => bashStatuses().length === 2, "call ended"); | 7172 | yield* awaitUntil(() => bashStatuses().length === 2, "call ended"); |
| 6944 | assert.deepEqual(bashStatuses(), ["command_execution:running", "command_execution:failed"]); | 7173 | assert.deepEqual(bashStatuses(), ["command_execution:running", "command_execution:failed"]); |
| 6945 | }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 7174 | }).pipe( |
| 7175 | Effect.provide( | ||
| 7176 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 7177 | ), | ||
| 7178 | ), | ||
| 6946 | ), | 7179 | ), |
| 6947 | ); | 7180 | ); |
| 6948 | 7181 | ||
| ... | @@ -7004,7 +7237,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { | ... | @@ -7004,7 +7237,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { |
| 7004 | "command_execution:running", | 7237 | "command_execution:running", |
| 7005 | "command_execution:interrupted", | 7238 | "command_execution:interrupted", |
| 7006 | ]); | 7239 | ]); |
| 7007 | }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 7240 | }).pipe( |
| 7241 | Effect.provide( | ||
| 7242 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 7243 | ), | ||
| 7244 | ), | ||
| 7008 | ), | 7245 | ), |
| 7009 | ); | 7246 | ); |
| 7010 | 7247 | ||
| ... | @@ -7170,7 +7407,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { | ... | @@ -7170,7 +7407,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { |
| 7170 | `command_execution:running:${input}`, | 7407 | `command_execution:running:${input}`, |
| 7171 | `command_execution:completed:${input}`, | 7408 | `command_execution:completed:${input}`, |
| 7172 | ]); | 7409 | ]); |
| 7173 | }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 7410 | }).pipe( |
| 7411 | Effect.provide( | ||
| 7412 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 7413 | ), | ||
| 7414 | ), | ||
| 7174 | ), | 7415 | ), |
| 7175 | ); | 7416 | ); |
| 7176 | 7417 | ||
| ... | @@ -7326,7 +7567,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { | ... | @@ -7326,7 +7567,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { |
| 7326 | `dynamic_tool:running:${input}`, | 7567 | `dynamic_tool:running:${input}`, |
| 7327 | `dynamic_tool:completed:${input}`, | 7568 | `dynamic_tool:completed:${input}`, |
| 7328 | ]); | 7569 | ]); |
| 7329 | }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 7570 | }).pipe( |
| 7571 | Effect.provide( | ||
| 7572 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 7573 | ), | ||
| 7574 | ), | ||
| 7330 | ), | 7575 | ), |
| 7331 | ); | 7576 | ); |
| 7332 | 7577 | ||
| ... | @@ -7403,7 +7648,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { | ... | @@ -7403,7 +7648,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { |
| 7403 | "Part two.", | 7648 | "Part two.", |
| 7404 | ]); | 7649 | ]); |
| 7405 | assert.deepEqual(routing.assistantTexts(harness.threadId), ["The auditor finished."]); | 7650 | assert.deepEqual(routing.assistantTexts(harness.threadId), ["The auditor finished."]); |
| 7406 | }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 7651 | }).pipe( |
| 7652 | Effect.provide( | ||
| 7653 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 7654 | ), | ||
| 7655 | ), | ||
| 7407 | ), | 7656 | ), |
| 7408 | ); | 7657 | ); |
| 7409 | 7658 | ||
| ... | @@ -7578,7 +7827,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { | ... | @@ -7578,7 +7827,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { |
| 7578 | ), | 7827 | ), |
| 7579 | ); | 7828 | ); |
| 7580 | assert.lengthOf(harness.continuationRequests, 1); | 7829 | assert.lengthOf(harness.continuationRequests, 1); |
| 7581 | }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 7830 | }).pipe( |
| 7831 | Effect.provide( | ||
| 7832 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 7833 | ), | ||
| 7834 | ), | ||
| 7582 | ), | 7835 | ), |
| 7583 | ); | 7836 | ); |
| 7584 | 7837 | ||
| ... | @@ -7639,7 +7892,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { | ... | @@ -7639,7 +7892,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { |
| 7639 | : [], | 7892 | : [], |
| 7640 | ); | 7893 | ); |
| 7641 | assert.deepEqual(retryStatuses, ["running"]); | 7894 | assert.deepEqual(retryStatuses, ["running"]); |
| 7642 | }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 7895 | }).pipe( |
| 7896 | Effect.provide( | ||
| 7897 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 7898 | ), | ||
| 7899 | ), | ||
| 7643 | ), | 7900 | ), |
| 7644 | ); | 7901 | ); |
| 7645 | 7902 | ||
| ... | @@ -7728,7 +7985,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { | ... | @@ -7728,7 +7985,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { |
| 7728 | FINAL_REPORT, | 7985 | FINAL_REPORT, |
| 7729 | ]); | 7986 | ]); |
| 7730 | assert.deepEqual(routing.assistantTexts(harness.threadId), ["The auditor finished."]); | 7987 | assert.deepEqual(routing.assistantTexts(harness.threadId), ["The auditor finished."]); |
| 7731 | }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 7988 | }).pipe( |
| 7989 | Effect.provide( | ||
| 7990 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 7991 | ), | ||
| 7992 | ), | ||
| 7732 | ), | 7993 | ), |
| 7733 | ); | 7994 | ); |
| 7734 | 7995 | ||
| ... | @@ -7835,7 +8096,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { | ... | @@ -7835,7 +8096,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { |
| 7835 | ); | 8096 | ); |
| 7836 | const child = harness.events.find((event) => event.type === "app_thread.created"); | 8097 | const child = harness.events.find((event) => event.type === "app_thread.created"); |
| 7837 | assert.equal(child?.appThread.modelSelection?.model, initialModel ?? parentModel); | 8098 | assert.equal(child?.appThread.modelSelection?.model, initialModel ?? parentModel); |
| 7838 | }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 8099 | }).pipe( |
| 8100 | Effect.provide( | ||
| 8101 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 8102 | ), | ||
| 8103 | ), | ||
| 7839 | ), | 8104 | ), |
| 7840 | ); | 8105 | ); |
| 7841 | 8106 | ||
| ... | @@ -7920,7 +8185,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { | ... | @@ -7920,7 +8185,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { |
| 7920 | }), | 8185 | }), |
| 7921 | ); | 8186 | ); |
| 7922 | yield* awaitUntil(() => harness.terminalEvents().length === 1, "turn terminal"); | 8187 | yield* awaitUntil(() => harness.terminalEvents().length === 1, "turn terminal"); |
| 7923 | }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 8188 | }).pipe( |
| 8189 | Effect.provide( | ||
| 8190 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 8191 | ), | ||
| 8192 | ), | ||
| 7924 | ), | 8193 | ), |
| 7925 | ); | 8194 | ); |
| 7926 | 8195 | ||
| ... | @@ -8013,7 +8282,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { | ... | @@ -8013,7 +8282,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { |
| 8013 | 8282 | ||
| 8014 | assert.equal(subagentEvents().at(-1)?.subagent.status, "cancelled"); | 8283 | assert.equal(subagentEvents().at(-1)?.subagent.status, "cancelled"); |
| 8015 | assert.isFalse(yield* harness.hasPendingBackgroundWork); | 8284 | assert.isFalse(yield* harness.hasPendingBackgroundWork); |
| 8016 | }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 8285 | }).pipe( |
| 8286 | Effect.provide( | ||
| 8287 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 8288 | ), | ||
| 8289 | ), | ||
| 8017 | ), | 8290 | ), |
| 8018 | ); | 8291 | ); |
| 8019 | 8292 | ||
| ... | @@ -8299,7 +8572,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { | ... | @@ -8299,7 +8572,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { |
| 8299 | assert.lengthOf(resumedWorkIndexes, 1); | 8572 | assert.lengthOf(resumedWorkIndexes, 1); |
| 8300 | assert.isAbove(resumedWorkIndexes[0] ?? -1, reopenIndex); | 8573 | assert.isAbove(resumedWorkIndexes[0] ?? -1, reopenIndex); |
| 8301 | assert.isFalse(yield* harness.hasPendingBackgroundWork); | 8574 | assert.isFalse(yield* harness.hasPendingBackgroundWork); |
| 8302 | }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 8575 | }).pipe( |
| 8576 | Effect.provide( | ||
| 8577 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 8578 | ), | ||
| 8579 | ), | ||
| 8303 | ), | 8580 | ), |
| 8304 | ); | 8581 | ); |
| 8305 | 8582 | ||
| ... | @@ -8315,7 +8592,7 @@ describe("ClaudeAdapterV2 background wake turns", () => { | ... | @@ -8315,7 +8592,7 @@ describe("ClaudeAdapterV2 background wake turns", () => { |
| 8315 | const events: Array<ProviderAdapter.ProviderAdapterV2Event> = []; | 8592 | const events: Array<ProviderAdapter.ProviderAdapterV2Event> = []; |
| 8316 | const continuationRequests: Array<ProviderContinuationRequests.ProviderContinuationRequest> = | 8593 | const continuationRequests: Array<ProviderContinuationRequests.ProviderContinuationRequest> = |
| 8317 | []; | 8594 | []; |
| 8318 | const adapter = ClaudeAdapterV2.makeClaudeAdapterV2({ | 8595 | const adapter = yield* ClaudeAdapterV2.makeClaudeAdapterV2({ |
| 8319 | instanceId: ClaudeAdapterV2.CLAUDE_DEFAULT_INSTANCE_ID, | 8596 | instanceId: ClaudeAdapterV2.CLAUDE_DEFAULT_INSTANCE_ID, |
| 8320 | settings: DEFAULT_CLAUDE_SETTINGS, | 8597 | settings: DEFAULT_CLAUDE_SETTINGS, |
| 8321 | environment: {}, | 8598 | environment: {}, |
| ... | @@ -8482,7 +8759,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { | ... | @@ -8482,7 +8759,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { |
| 8482 | () => events.filter((event) => event.type === "turn.terminal").length === 2, | 8759 | () => events.filter((event) => event.type === "turn.terminal").length === 2, |
| 8483 | "second turn terminal", | 8760 | "second turn terminal", |
| 8484 | ); | 8761 | ); |
| 8485 | }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 8762 | }).pipe( |
| 8763 | Effect.provide( | ||
| 8764 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 8765 | ), | ||
| 8766 | ), | ||
| 8486 | ), | 8767 | ), |
| 8487 | ); | 8768 | ); |
| 8488 | 8769 | ||
| ... | @@ -8500,7 +8781,7 @@ describe("ClaudeAdapterV2 background wake turns", () => { | ... | @@ -8500,7 +8781,7 @@ describe("ClaudeAdapterV2 background wake turns", () => { |
| 8500 | const events: Array<ProviderAdapter.ProviderAdapterV2Event> = []; | 8781 | const events: Array<ProviderAdapter.ProviderAdapterV2Event> = []; |
| 8501 | const continuationRequests: Array<ProviderContinuationRequests.ProviderContinuationRequest> = | 8782 | const continuationRequests: Array<ProviderContinuationRequests.ProviderContinuationRequest> = |
| 8502 | []; | 8783 | []; |
| 8503 | const adapter = ClaudeAdapterV2.makeClaudeAdapterV2({ | 8784 | const adapter = yield* ClaudeAdapterV2.makeClaudeAdapterV2({ |
| 8504 | instanceId: ClaudeAdapterV2.CLAUDE_DEFAULT_INSTANCE_ID, | 8785 | instanceId: ClaudeAdapterV2.CLAUDE_DEFAULT_INSTANCE_ID, |
| 8505 | settings: DEFAULT_CLAUDE_SETTINGS, | 8786 | settings: DEFAULT_CLAUDE_SETTINGS, |
| 8506 | environment: {}, | 8787 | environment: {}, |
| ... | @@ -8670,7 +8951,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { | ... | @@ -8670,7 +8951,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { |
| 8670 | ), | 8951 | ), |
| 8671 | ); | 8952 | ); |
| 8672 | assert.isFalse(yield* hasPendingBackgroundWork); | 8953 | assert.isFalse(yield* hasPendingBackgroundWork); |
| 8673 | }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 8954 | }).pipe( |
| 8955 | Effect.provide( | ||
| 8956 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 8957 | ), | ||
| 8958 | ), | ||
| 8674 | ), | 8959 | ), |
| 8675 | ); | 8960 | ); |
| 8676 | 8961 | ||
| ... | @@ -8737,7 +9022,7 @@ describe("ClaudeAdapterV2 background wake turns", () => { | ... | @@ -8737,7 +9022,7 @@ describe("ClaudeAdapterV2 background wake turns", () => { |
| 8737 | const events: Array<ProviderAdapter.ProviderAdapterV2Event> = []; | 9022 | const events: Array<ProviderAdapter.ProviderAdapterV2Event> = []; |
| 8738 | const continuationRequests: Array<ProviderContinuationRequests.ProviderContinuationRequest> = | 9023 | const continuationRequests: Array<ProviderContinuationRequests.ProviderContinuationRequest> = |
| 8739 | []; | 9024 | []; |
| 8740 | const adapter = ClaudeAdapterV2.makeClaudeAdapterV2({ | 9025 | const adapter = yield* ClaudeAdapterV2.makeClaudeAdapterV2({ |
| 8741 | instanceId: ClaudeAdapterV2.CLAUDE_DEFAULT_INSTANCE_ID, | 9026 | instanceId: ClaudeAdapterV2.CLAUDE_DEFAULT_INSTANCE_ID, |
| 8742 | settings: DEFAULT_CLAUDE_SETTINGS, | 9027 | settings: DEFAULT_CLAUDE_SETTINGS, |
| 8743 | environment: {}, | 9028 | environment: {}, |
| ... | @@ -8920,7 +9205,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { | ... | @@ -8920,7 +9205,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { |
| 8920 | ); | 9205 | ); |
| 8921 | assert.equal(subagentNodeEvents.at(-1)?.node.status, "completed"); | 9206 | assert.equal(subagentNodeEvents.at(-1)?.node.status, "completed"); |
| 8922 | assert.isFalse(yield* hasPendingBackgroundWork); | 9207 | assert.isFalse(yield* hasPendingBackgroundWork); |
| 8923 | }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 9208 | }).pipe( |
| 9209 | Effect.provide( | ||
| 9210 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 9211 | ), | ||
| 9212 | ), | ||
| 8924 | ), | 9213 | ), |
| 8925 | ); | 9214 | ); |
| 8926 | 9215 | ||
| ... | @@ -8936,7 +9225,7 @@ describe("ClaudeAdapterV2 background wake turns", () => { | ... | @@ -8936,7 +9225,7 @@ describe("ClaudeAdapterV2 background wake turns", () => { |
| 8936 | }); | 9225 | }); |
| 8937 | const processQueues: Array<Queue.Queue<SDKMessage>> = []; | 9226 | const processQueues: Array<Queue.Queue<SDKMessage>> = []; |
| 8938 | const events: Array<ProviderAdapter.ProviderAdapterV2Event> = []; | 9227 | const events: Array<ProviderAdapter.ProviderAdapterV2Event> = []; |
| 8939 | const adapter = ClaudeAdapterV2.makeClaudeAdapterV2({ | 9228 | const adapter = yield* ClaudeAdapterV2.makeClaudeAdapterV2({ |
| 8940 | instanceId: ClaudeAdapterV2.CLAUDE_DEFAULT_INSTANCE_ID, | 9229 | instanceId: ClaudeAdapterV2.CLAUDE_DEFAULT_INSTANCE_ID, |
| 8941 | settings: DEFAULT_CLAUDE_SETTINGS, | 9230 | settings: DEFAULT_CLAUDE_SETTINGS, |
| 8942 | environment: {}, | 9231 | environment: {}, |
| ... | @@ -9086,7 +9375,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { | ... | @@ -9086,7 +9375,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { |
| 9086 | }), | 9375 | }), |
| 9087 | ); | 9376 | ); |
| 9088 | assert.lengthOf(processQueues, 3); | 9377 | assert.lengthOf(processQueues, 3); |
| 9089 | }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 9378 | }).pipe( |
| 9379 | Effect.provide( | ||
| 9380 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 9381 | ), | ||
| 9382 | ), | ||
| 9090 | ), | 9383 | ), |
| 9091 | ); | 9384 | ); |
| 9092 | 9385 | ||
| ... | @@ -9103,7 +9396,7 @@ describe("ClaudeAdapterV2 background wake turns", () => { | ... | @@ -9103,7 +9396,7 @@ describe("ClaudeAdapterV2 background wake turns", () => { |
| 9103 | let openCount = 0; | 9396 | let openCount = 0; |
| 9104 | const processQueues: Array<Queue.Queue<SDKMessage>> = []; | 9397 | const processQueues: Array<Queue.Queue<SDKMessage>> = []; |
| 9105 | const events: Array<ProviderAdapter.ProviderAdapterV2Event> = []; | 9398 | const events: Array<ProviderAdapter.ProviderAdapterV2Event> = []; |
| 9106 | const adapter = ClaudeAdapterV2.makeClaudeAdapterV2({ | 9399 | const adapter = yield* ClaudeAdapterV2.makeClaudeAdapterV2({ |
| 9107 | instanceId: ClaudeAdapterV2.CLAUDE_DEFAULT_INSTANCE_ID, | 9400 | instanceId: ClaudeAdapterV2.CLAUDE_DEFAULT_INSTANCE_ID, |
| 9108 | settings: DEFAULT_CLAUDE_SETTINGS, | 9401 | settings: DEFAULT_CLAUDE_SETTINGS, |
| 9109 | environment: {}, | 9402 | environment: {}, |
| ... | @@ -9215,7 +9508,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { | ... | @@ -9215,7 +9508,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { |
| 9215 | // no replacement is opened. | 9508 | // no replacement is opened. |
| 9216 | assert.equal(openCount, 1); | 9509 | assert.equal(openCount, 1); |
| 9217 | assert.isTrue(yield* hasPendingBackgroundWork); | 9510 | assert.isTrue(yield* hasPendingBackgroundWork); |
| 9218 | }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 9511 | }).pipe( |
| 9512 | Effect.provide( | ||
| 9513 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 9514 | ), | ||
| 9515 | ), | ||
| 9219 | ), | 9516 | ), |
| 9220 | ); | 9517 | ); |
| 9221 | 9518 | ||
| ... | @@ -9234,7 +9531,7 @@ describe("ClaudeAdapterV2 background wake turns", () => { | ... | @@ -9234,7 +9531,7 @@ describe("ClaudeAdapterV2 background wake turns", () => { |
| 9234 | const events: Array<ProviderAdapter.ProviderAdapterV2Event> = []; | 9531 | const events: Array<ProviderAdapter.ProviderAdapterV2Event> = []; |
| 9235 | const continuationRequests: Array<ProviderContinuationRequests.ProviderContinuationRequest> = | 9532 | const continuationRequests: Array<ProviderContinuationRequests.ProviderContinuationRequest> = |
| 9236 | []; | 9533 | []; |
| 9237 | const adapter = ClaudeAdapterV2.makeClaudeAdapterV2({ | 9534 | const adapter = yield* ClaudeAdapterV2.makeClaudeAdapterV2({ |
| 9238 | instanceId: ClaudeAdapterV2.CLAUDE_DEFAULT_INSTANCE_ID, | 9535 | instanceId: ClaudeAdapterV2.CLAUDE_DEFAULT_INSTANCE_ID, |
| 9239 | settings: DEFAULT_CLAUDE_SETTINGS, | 9536 | settings: DEFAULT_CLAUDE_SETTINGS, |
| 9240 | environment: {}, | 9537 | environment: {}, |
| ... | @@ -9416,7 +9713,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { | ... | @@ -9416,7 +9713,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { |
| 9416 | "continuation request after retry", | 9713 | "continuation request after retry", |
| 9417 | ); | 9714 | ); |
| 9418 | assert.equal(continuationRequests[1]?.detail, "Retry build completed successfully"); | 9715 | assert.equal(continuationRequests[1]?.detail, "Retry build completed successfully"); |
| 9419 | }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 9716 | }).pipe( |
| 9717 | Effect.provide( | ||
| 9718 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 9719 | ), | ||
| 9720 | ), | ||
| 9420 | ), | 9721 | ), |
| 9421 | ); | 9722 | ); |
| 9422 | 9723 | ||
| ... | @@ -9429,7 +9730,7 @@ describe("ClaudeAdapterV2 background wake turns", () => { | ... | @@ -9429,7 +9730,7 @@ describe("ClaudeAdapterV2 background wake turns", () => { |
| 9429 | prefix: "t3-claude-v2-first-open-fail-", | 9730 | prefix: "t3-claude-v2-first-open-fail-", |
| 9430 | }); | 9731 | }); |
| 9431 | const events: Array<ProviderAdapter.ProviderAdapterV2Event> = []; | 9732 | const events: Array<ProviderAdapter.ProviderAdapterV2Event> = []; |
| 9432 | const adapter = ClaudeAdapterV2.makeClaudeAdapterV2({ | 9733 | const adapter = yield* ClaudeAdapterV2.makeClaudeAdapterV2({ |
| 9433 | instanceId: ClaudeAdapterV2.CLAUDE_DEFAULT_INSTANCE_ID, | 9734 | instanceId: ClaudeAdapterV2.CLAUDE_DEFAULT_INSTANCE_ID, |
| 9434 | settings: DEFAULT_CLAUDE_SETTINGS, | 9735 | settings: DEFAULT_CLAUDE_SETTINGS, |
| 9435 | environment: {}, | 9736 | environment: {}, |
| ... | @@ -9491,7 +9792,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { | ... | @@ -9491,7 +9792,11 @@ describe("ClaudeAdapterV2 background wake turns", () => { |
| 9491 | assert.isTrue(Exit.isFailure(failedStart)); | 9792 | assert.isTrue(Exit.isFailure(failedStart)); |
| 9492 | // No live process ever existed: do not emit a fabricated empty roster. | 9793 | // No live process ever existed: do not emit a fabricated empty roster. |
| 9493 | assert.lengthOf(providerThreadRosterEvents(events), 0); | 9794 | assert.lengthOf(providerThreadRosterEvents(events), 0); |
| 9494 | }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 9795 | }).pipe( |
| 9796 | Effect.provide( | ||
| 9797 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 9798 | ), | ||
| 9799 | ), | ||
| 9495 | ), | 9800 | ), |
| 9496 | ); | 9801 | ); |
| 9497 | 9802 | ||
| ... | @@ -9572,7 +9877,12 @@ describe("ClaudeAdapterV2 background wake turns", () => { | ... | @@ -9572,7 +9877,12 @@ describe("ClaudeAdapterV2 background wake turns", () => { |
| 9572 | }, | 9877 | }, |
| 9573 | { objective: condition, status: "complete", checks: 1 }, | 9878 | { objective: condition, status: "complete", checks: 1 }, |
| 9574 | ]); | 9879 | ]); |
| 9575 | }).pipe(Effect.scoped, Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 9880 | }).pipe( |
| 9881 | Effect.scoped, | ||
| 9882 | Effect.provide( | ||
| 9883 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 9884 | ), | ||
| 9885 | ), | ||
| 9576 | ); | 9886 | ); |
| 9577 | 9887 | ||
| 9578 | it.effect("keeps a goal active when a hook stops the turn before the goal passes", () => | 9888 | it.effect("keeps a goal active when a hook stops the turn before the goal passes", () => |
| ... | @@ -9606,7 +9916,12 @@ describe("ClaudeAdapterV2 background wake turns", () => { | ... | @@ -9606,7 +9916,12 @@ describe("ClaudeAdapterV2 background wake turns", () => { |
| 9606 | status: "active", | 9916 | status: "active", |
| 9607 | checks: 0, | 9917 | checks: 0, |
| 9608 | }); | 9918 | }); |
| 9609 | }).pipe(Effect.scoped, Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 9919 | }).pipe( |
| 9920 | Effect.scoped, | ||
| 9921 | Effect.provide( | ||
| 9922 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 9923 | ), | ||
| 9924 | ), | ||
| 9610 | ); | 9925 | ); |
| 9611 | 9926 | ||
| 9612 | it.effect("keeps a goal active after a command turn with no model output", () => | 9927 | it.effect("keeps a goal active after a command turn with no model output", () => |
| ... | @@ -9640,7 +9955,12 @@ describe("ClaudeAdapterV2 background wake turns", () => { | ... | @@ -9640,7 +9955,12 @@ describe("ClaudeAdapterV2 background wake turns", () => { |
| 9640 | status: "active", | 9955 | status: "active", |
| 9641 | checks: 2, | 9956 | checks: 2, |
| 9642 | }); | 9957 | }); |
| 9643 | }).pipe(Effect.scoped, Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 9958 | }).pipe( |
| 9959 | Effect.scoped, | ||
| 9960 | Effect.provide( | ||
| 9961 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 9962 | ), | ||
| 9963 | ), | ||
| 9644 | ); | 9964 | ); |
| 9645 | }); | 9965 | }); |
| 9646 | }); | 9966 | }); |
apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts+12-8| ... | @@ -117,6 +117,7 @@ import { | ... | @@ -117,6 +117,7 @@ import { |
| 117 | normalizeMcpText, | 117 | normalizeMcpText, |
| 118 | } from "@t3tools/provider-core/server/mcpToolPresentation"; | 118 | } from "@t3tools/provider-core/server/mcpToolPresentation"; |
| 119 | import * as McpProviderSession from "@t3tools/provider-core/server/mcpSession"; | 119 | import * as McpProviderSession from "@t3tools/provider-core/server/mcpSession"; |
| 120 | import * as McpProviderSessions from "@t3tools/provider-core/server/McpProviderSessions"; | ||
| 120 | import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator"; | 121 | import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator"; |
| 121 | import { | 122 | import { |
| 122 | makeProviderFailure, | 123 | makeProviderFailure, |
| ... | @@ -973,7 +974,7 @@ export const CLAUDE_T3_MCP_TOOL_TIMEOUT_MS = 65 * 60 * 1_000; | ... | @@ -973,7 +974,7 @@ export const CLAUDE_T3_MCP_TOOL_TIMEOUT_MS = 65 * 60 * 1_000; |
| 973 | const CLAUDE_T3_MCP_AUTHORIZATION_ENV = "T3_CODE_MCP_AUTHORIZATION"; | 974 | const CLAUDE_T3_MCP_AUTHORIZATION_ENV = "T3_CODE_MCP_AUTHORIZATION"; |
| 974 | 975 | ||
| 975 | export function claudeMcpQueryOverrides(input: { | 976 | export function claudeMcpQueryOverrides(input: { |
| 976 | readonly threadId: ThreadId; | 977 | readonly mcpSession: McpProviderSession.McpProviderSessionConfig | undefined; |
| 977 | readonly readOnlySandbox: boolean; | 978 | readonly readOnlySandbox: boolean; |
| 978 | readonly allowedTools?: ReadonlyArray<string>; | 979 | readonly allowedTools?: ReadonlyArray<string>; |
| 979 | }): { | 980 | }): { |
| ... | @@ -981,7 +982,7 @@ export function claudeMcpQueryOverrides(input: { | ... | @@ -981,7 +982,7 @@ export function claudeMcpQueryOverrides(input: { |
| 981 | readonly mcpServers?: ClaudeQueryOptions["mcpServers"]; | 982 | readonly mcpServers?: ClaudeQueryOptions["mcpServers"]; |
| 982 | readonly mcpEnvironment?: Readonly<Record<string, string>>; | 983 | readonly mcpEnvironment?: Readonly<Record<string, string>>; |
| 983 | } { | 984 | } { |
| 984 | const session = McpProviderSession.readMcpProviderSession(input.threadId); | 985 | const session = input.mcpSession; |
| 985 | if (session === undefined) { | 986 | if (session === undefined) { |
| 986 | return input.allowedTools === undefined ? {} : { allowedTools: input.allowedTools }; | 987 | return input.allowedTools === undefined ? {} : { allowedTools: input.allowedTools }; |
| 987 | } | 988 | } |
| ... | @@ -3026,9 +3027,10 @@ export interface ClaudeAdapterV2Options { | ... | @@ -3026,9 +3027,10 @@ export interface ClaudeAdapterV2Options { |
| 3026 | }; | 3027 | }; |
| 3027 | } | 3028 | } |
| 3028 | 3029 | ||
| 3029 | export function makeClaudeAdapterV2( | 3030 | export const makeClaudeAdapterV2 = Effect.fn("makeClaudeAdapterV2")(function* ( |
| 3030 | adapterOptions: ClaudeAdapterV2Options, | 3031 | adapterOptions: ClaudeAdapterV2Options, |
| 3031 | ): ProviderAdapter.ProviderAdapterV2["Service"] { | 3032 | ) { |
| 3033 | const mcpSessions = yield* McpProviderSessions.McpProviderSessions; | ||
| 3032 | const { attachmentsDir, fileSystem, path, crypto, idAllocator, queryRunner } = adapterOptions; | 3034 | const { attachmentsDir, fileSystem, path, crypto, idAllocator, queryRunner } = adapterOptions; |
| 3033 | const continuationRequests = adapterOptions.continuationRequests ?? { | 3035 | const continuationRequests = adapterOptions.continuationRequests ?? { |
| 3034 | offer: () => Effect.void, | 3036 | offer: () => Effect.void, |
| ... | @@ -7261,7 +7263,7 @@ export function makeClaudeAdapterV2( | ... | @@ -7261,7 +7263,7 @@ export function makeClaudeAdapterV2( |
| 7261 | ) { | 7263 | ) { |
| 7262 | const queryPolicy = claudeRuntimeQueryPolicyForRuntimePolicy(turnInput.runtimePolicy); | 7264 | const queryPolicy = claudeRuntimeQueryPolicyForRuntimePolicy(turnInput.runtimePolicy); |
| 7263 | const mcpOverrides = claudeMcpQueryOverrides({ | 7265 | const mcpOverrides = claudeMcpQueryOverrides({ |
| 7264 | threadId: turnInput.threadId, | 7266 | mcpSession: yield* mcpSessions.read(turnInput.threadId), |
| 7265 | readOnlySandbox: | 7267 | readOnlySandbox: |
| 7266 | sandboxPolicyKindForClaudeRuntimePolicy(turnInput.runtimePolicy) === "readOnly", | 7268 | sandboxPolicyKindForClaudeRuntimePolicy(turnInput.runtimePolicy) === "readOnly", |
| 7267 | ...(queryPolicy.allowedTools === undefined | 7269 | ...(queryPolicy.allowedTools === undefined |
| ... | @@ -8156,13 +8158,14 @@ export function makeClaudeAdapterV2( | ... | @@ -8156,13 +8158,14 @@ export function makeClaudeAdapterV2( |
| 8156 | ), | 8158 | ), |
| 8157 | ), | 8159 | ), |
| 8158 | }); | 8160 | }); |
| 8159 | } | 8161 | }); |
| 8160 | 8162 | ||
| 8161 | export type ClaudeAdapterV2DriverEnv = | 8163 | export type ClaudeAdapterV2DriverEnv = |
| 8162 | | ClaudeAgentSdkQueryRunner | 8164 | | ClaudeAgentSdkQueryRunner |
| 8163 | | Crypto.Crypto | 8165 | | Crypto.Crypto |
| 8164 | | FileSystem.FileSystem | 8166 | | FileSystem.FileSystem |
| 8165 | | IdAllocator.IdAllocatorV2 | 8167 | | IdAllocator.IdAllocatorV2 |
| 8168 | | McpProviderSessions.McpProviderSessions | ||
| 8166 | | Path.Path | 8169 | | Path.Path |
| 8167 | | ServerConfig.ServerConfig; | 8170 | | ServerConfig.ServerConfig; |
| 8168 | 8171 | ||
| ... | @@ -8186,7 +8189,7 @@ export const createClaudeAdapterV2 = Effect.fn("ClaudeAdapterV2Driver.create")( | ... | @@ -8186,7 +8189,7 @@ export const createClaudeAdapterV2 = Effect.fn("ClaudeAdapterV2Driver.create")( |
| 8186 | expandHomePath(config.binaryPath), | 8189 | expandHomePath(config.binaryPath), |
| 8187 | claudeEnvironment, | 8190 | claudeEnvironment, |
| 8188 | ); | 8191 | ); |
| 8189 | return makeClaudeAdapterV2({ | 8192 | return yield* makeClaudeAdapterV2({ |
| 8190 | instanceId, | 8193 | instanceId, |
| 8191 | settings: { ...config, enabled, binaryPath }, | 8194 | settings: { ...config, enabled, binaryPath }, |
| 8192 | environment: claudeEnvironment, | 8195 | environment: claudeEnvironment, |
| ... | @@ -8234,7 +8237,7 @@ const makeDefaultClaudeAdapterV2 = Effect.fn("ClaudeAdapterV2.layer")(function* | ... | @@ -8234,7 +8237,7 @@ const makeDefaultClaudeAdapterV2 = Effect.fn("ClaudeAdapterV2.layer")(function* |
| 8234 | const serverConfig = yield* ServerConfig.ServerConfig; | 8237 | const serverConfig = yield* ServerConfig.ServerConfig; |
| 8235 | const continuationRequests = yield* ProviderContinuationRequests.ProviderContinuationRequests; | 8238 | const continuationRequests = yield* ProviderContinuationRequests.ProviderContinuationRequests; |
| 8236 | 8239 | ||
| 8237 | return makeClaudeAdapterV2({ | 8240 | return yield* makeClaudeAdapterV2({ |
| 8238 | instanceId: CLAUDE_DEFAULT_INSTANCE_ID, | 8241 | instanceId: CLAUDE_DEFAULT_INSTANCE_ID, |
| 8239 | settings: DEFAULT_CLAUDE_SETTINGS, | 8242 | settings: DEFAULT_CLAUDE_SETTINGS, |
| 8240 | environment: hostEnvironment, | 8243 | environment: hostEnvironment, |
| ... | @@ -8255,6 +8258,7 @@ const layer: Layer.Layer< | ... | @@ -8255,6 +8258,7 @@ const layer: Layer.Layer< |
| 8255 | | Crypto.Crypto | 8258 | | Crypto.Crypto |
| 8256 | | FileSystem.FileSystem | 8259 | | FileSystem.FileSystem |
| 8257 | | IdAllocator.IdAllocatorV2 | 8260 | | IdAllocator.IdAllocatorV2 |
| 8261 | | McpProviderSessions.McpProviderSessions | ||
| 8258 | | Path.Path | 8262 | | Path.Path |
| 8259 | | ServerConfig.ServerConfig | 8263 | | ServerConfig.ServerConfig |
| 8260 | > = Layer.effect(ProviderAdapter.ProviderAdapterV2, makeDefaultClaudeAdapterV2()); | 8264 | > = Layer.effect(ProviderAdapter.ProviderAdapterV2, makeDefaultClaudeAdapterV2()); |
apps/server/src/orchestration-v2/Adapters/CodexAdapterV2.test.ts+379-100| ... | @@ -54,7 +54,7 @@ import { ChildProcess, ChildProcessSpawner } from "effect/process"; | ... | @@ -54,7 +54,7 @@ import { ChildProcess, ChildProcessSpawner } from "effect/process"; |
| 54 | 54 | ||
| 55 | import packageJson from "../../../package.json" with { type: "json" }; | 55 | import packageJson from "../../../package.json" with { type: "json" }; |
| 56 | import * as ServerConfig from "../../config.ts"; | 56 | import * as ServerConfig from "../../config.ts"; |
| 57 | import * as McpProviderSession from "@t3tools/provider-core/server/mcpSession"; | 57 | import * as McpProviderSessions from "@t3tools/provider-core/server/McpProviderSessions"; |
| 58 | import * as ProviderEventLoggers from "@t3tools/provider-core/server/ProviderEventLoggers"; | 58 | import * as ProviderEventLoggers from "@t3tools/provider-core/server/ProviderEventLoggers"; |
| 59 | import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator"; | 59 | import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator"; |
| 60 | import * as EffectWorker from "../EffectWorker.ts"; | 60 | import * as EffectWorker from "../EffectWorker.ts"; |
| ... | @@ -639,7 +639,7 @@ describe("CodexAdapterV2 runtime policy", () => { | ... | @@ -639,7 +639,7 @@ describe("CodexAdapterV2 runtime policy", () => { |
| 639 | describe("CodexAdapterV2 process spawning", () => { | 639 | describe("CodexAdapterV2 process spawning", () => { |
| 640 | it("injects cwd, model, and MCP authorization into thread-scoped params", () => { | 640 | it("injects cwd, model, and MCP authorization into thread-scoped params", () => { |
| 641 | const threadId = ThreadId.make("thread-codex-mcp"); | 641 | const threadId = ThreadId.make("thread-codex-mcp"); |
| 642 | McpProviderSession.setMcpProviderSession({ | 642 | const mcpSession = { |
| 643 | environmentId: EnvironmentId.make("environment-codex-mcp"), | 643 | environmentId: EnvironmentId.make("environment-codex-mcp"), |
| 644 | threadId, | 644 | threadId, |
| 645 | providerSessionId: "mcp-session-codex", | 645 | providerSessionId: "mcp-session-codex", |
| ... | @@ -647,38 +647,34 @@ describe("CodexAdapterV2 process spawning", () => { | ... | @@ -647,38 +647,34 @@ describe("CodexAdapterV2 process spawning", () => { |
| 647 | endpoint: "http://127.0.0.1:43123/mcp", | 647 | endpoint: "http://127.0.0.1:43123/mcp", |
| 648 | authorizationHeader: "Bearer secret-codex-token", | 648 | authorizationHeader: "Bearer secret-codex-token", |
| 649 | browserToolsAvailable: true, | 649 | browserToolsAvailable: true, |
| 650 | }); | 650 | }; |
| 651 | 651 | ||
| 652 | try { | 652 | assert.deepEqual( |
| 653 | assert.deepEqual( | 653 | CodexAdapterV2.codexThreadRuntimeParams({ |
| 654 | CodexAdapterV2.codexThreadRuntimeParams({ | 654 | mcpSession, |
| 655 | threadId, | 655 | modelSelection: { model: "gpt-5.4" }, |
| 656 | modelSelection: { model: "gpt-5.4" }, | 656 | runtimePolicy: { |
| 657 | runtimePolicy: { | 657 | runtimeMode: "full-access", |
| 658 | runtimeMode: "full-access", | 658 | interactionMode: "default", |
| 659 | interactionMode: "default", | ||
| 660 | cwd: "/workspace/thread-codex-mcp", | ||
| 661 | }, | ||
| 662 | }), | ||
| 663 | { | ||
| 664 | cwd: "/workspace/thread-codex-mcp", | 659 | cwd: "/workspace/thread-codex-mcp", |
| 665 | model: "gpt-5.4", | 660 | }, |
| 666 | config: { | 661 | }), |
| 667 | "tools.update_plan.enabled": true, | 662 | { |
| 668 | mcp_servers: { | 663 | cwd: "/workspace/thread-codex-mcp", |
| 669 | "t3-code": { | 664 | model: "gpt-5.4", |
| 670 | url: "http://127.0.0.1:43123/mcp", | 665 | config: { |
| 671 | http_headers: { | 666 | "tools.update_plan.enabled": true, |
| 672 | Authorization: "Bearer secret-codex-token", | 667 | mcp_servers: { |
| 673 | }, | 668 | "t3-code": { |
| 669 | url: "http://127.0.0.1:43123/mcp", | ||
| 670 | http_headers: { | ||
| 671 | Authorization: "Bearer secret-codex-token", | ||
| 674 | }, | 672 | }, |
| 675 | }, | 673 | }, |
| 676 | }, | 674 | }, |
| 677 | }, | 675 | }, |
| 678 | ); | 676 | }, |
| 679 | } finally { | 677 | ); |
| 680 | McpProviderSession.clearMcpProviderSession(threadId); | ||
| 681 | } | ||
| 682 | }); | 678 | }); |
| 683 | 679 | ||
| 684 | it.effect("resolves Windows command shims through the shared spawn policy", () => | 680 | it.effect("resolves Windows command shims through the shared spawn policy", () => |
| ... | @@ -822,7 +818,9 @@ describe("CodexAdapterV2 process spawning", () => { | ... | @@ -822,7 +818,9 @@ describe("CodexAdapterV2 process spawning", () => { |
| 822 | 818 | ||
| 823 | assert.deepEqual(spawnedCommands, [path.join(NodeOS.homedir(), "bin", "codex")]); | 819 | assert.deepEqual(spawnedCommands, [path.join(NodeOS.homedir(), "bin", "codex")]); |
| 824 | }).pipe( | 820 | }).pipe( |
| 825 | Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer)), | 821 | Effect.provide( |
| 822 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 823 | ), | ||
| 826 | Effect.provideService(HostProcessPlatform, "linux"), | 824 | Effect.provideService(HostProcessPlatform, "linux"), |
| 827 | ), | 825 | ), |
| 828 | ); | 826 | ); |
| ... | @@ -1716,7 +1714,7 @@ describe("CodexAdapterV2 session initialize", () => { | ... | @@ -1716,7 +1714,7 @@ describe("CodexAdapterV2 session initialize", () => { |
| 1716 | beforeEmitInbound === undefined ? {} : { beforeEmitInbound }, | 1714 | beforeEmitInbound === undefined ? {} : { beforeEmitInbound }, |
| 1717 | ); | 1715 | ); |
| 1718 | let initializeRequests = 0; | 1716 | let initializeRequests = 0; |
| 1719 | const adapter = CodexAdapterV2.makeCodexAdapterV2({ | 1717 | const adapter = yield* CodexAdapterV2.makeCodexAdapterV2({ |
| 1720 | instanceId: CodexAdapterV2.CODEX_DEFAULT_INSTANCE_ID, | 1718 | instanceId: CodexAdapterV2.CODEX_DEFAULT_INSTANCE_ID, |
| 1721 | settings: DEFAULT_CODEX_SETTINGS, | 1719 | settings: DEFAULT_CODEX_SETTINGS, |
| 1722 | environment: {}, | 1720 | environment: {}, |
| ... | @@ -1811,7 +1809,12 @@ describe("CodexAdapterV2 session initialize", () => { | ... | @@ -1811,7 +1809,12 @@ describe("CodexAdapterV2 session initialize", () => { |
| 1811 | providerThreads.map((providerThread) => providerThread.nativeThreadRef?.nativeId), | 1809 | providerThreads.map((providerThread) => providerThread.nativeThreadRef?.nativeId), |
| 1812 | ["concurrent-first", "concurrent-second"], | 1810 | ["concurrent-first", "concurrent-second"], |
| 1813 | ); | 1811 | ); |
| 1814 | }).pipe(Effect.scoped, Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 1812 | }).pipe( |
| 1813 | Effect.scoped, | ||
| 1814 | Effect.provide( | ||
| 1815 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 1816 | ), | ||
| 1817 | ), | ||
| 1815 | ); | 1818 | ); |
| 1816 | 1819 | ||
| 1817 | it.effect("retries initialize after a failed handshake", () => | 1820 | it.effect("retries initialize after a failed handshake", () => |
| ... | @@ -1837,7 +1840,12 @@ describe("CodexAdapterV2 session initialize", () => { | ... | @@ -1837,7 +1840,12 @@ describe("CodexAdapterV2 session initialize", () => { |
| 1837 | const providerThread = yield* session.ensureThread("thread-initialize-retry"); | 1840 | const providerThread = yield* session.ensureThread("thread-initialize-retry"); |
| 1838 | assert.equal(providerThread.nativeThreadRef?.nativeId, "initialize-retry"); | 1841 | assert.equal(providerThread.nativeThreadRef?.nativeId, "initialize-retry"); |
| 1839 | assert.equal(session.initializeRequests(), 2); | 1842 | assert.equal(session.initializeRequests(), 2); |
| 1840 | }).pipe(Effect.scoped, Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 1843 | }).pipe( |
| 1844 | Effect.scoped, | ||
| 1845 | Effect.provide( | ||
| 1846 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 1847 | ), | ||
| 1848 | ), | ||
| 1841 | ); | 1849 | ); |
| 1842 | 1850 | ||
| 1843 | it.effect("completes the handshake after a caller is interrupted mid-initialize", () => | 1851 | it.effect("completes the handshake after a caller is interrupted mid-initialize", () => |
| ... | @@ -1880,7 +1888,12 @@ describe("CodexAdapterV2 session initialize", () => { | ... | @@ -1880,7 +1888,12 @@ describe("CodexAdapterV2 session initialize", () => { |
| 1880 | const providerThread = yield* session.ensureThread("thread-initialize-interrupted"); | 1888 | const providerThread = yield* session.ensureThread("thread-initialize-interrupted"); |
| 1881 | assert.equal(providerThread.nativeThreadRef?.nativeId, "initialize-interrupted"); | 1889 | assert.equal(providerThread.nativeThreadRef?.nativeId, "initialize-interrupted"); |
| 1882 | assert.equal(session.initializeRequests(), 2); | 1890 | assert.equal(session.initializeRequests(), 2); |
| 1883 | }).pipe(Effect.scoped, Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 1891 | }).pipe( |
| 1892 | Effect.scoped, | ||
| 1893 | Effect.provide( | ||
| 1894 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 1895 | ), | ||
| 1896 | ), | ||
| 1884 | ); | 1897 | ); |
| 1885 | }); | 1898 | }); |
| 1886 | 1899 | ||
| ... | @@ -1940,7 +1953,7 @@ describe("CodexAdapterV2 post-settle continuation", () => { | ... | @@ -1940,7 +1953,7 @@ describe("CodexAdapterV2 post-settle continuation", () => { |
| 1940 | ), | 1953 | ), |
| 1941 | ), | 1954 | ), |
| 1942 | }; | 1955 | }; |
| 1943 | const adapter = CodexAdapterV2.makeCodexAdapterV2({ | 1956 | const adapter = yield* CodexAdapterV2.makeCodexAdapterV2({ |
| 1944 | instanceId: CodexAdapterV2.CODEX_DEFAULT_INSTANCE_ID, | 1957 | instanceId: CodexAdapterV2.CODEX_DEFAULT_INSTANCE_ID, |
| 1945 | settings: DEFAULT_CODEX_SETTINGS, | 1958 | settings: DEFAULT_CODEX_SETTINGS, |
| 1946 | environment: {}, | 1959 | environment: {}, |
| ... | @@ -2121,7 +2134,12 @@ describe("CodexAdapterV2 post-settle continuation", () => { | ... | @@ -2121,7 +2134,12 @@ describe("CodexAdapterV2 post-settle continuation", () => { |
| 2121 | ); | 2134 | ); |
| 2122 | assert.equal(requests.filter((method) => method === "turn/start").length, 1); | 2135 | assert.equal(requests.filter((method) => method === "turn/start").length, 1); |
| 2123 | assert.isBelow(requests.indexOf("thread/inject_items"), requests.indexOf("turn/start")); | 2136 | assert.isBelow(requests.indexOf("thread/inject_items"), requests.indexOf("turn/start")); |
| 2124 | }).pipe(Effect.scoped, Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 2137 | }).pipe( |
| 2138 | Effect.scoped, | ||
| 2139 | Effect.provide( | ||
| 2140 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 2141 | ), | ||
| 2142 | ), | ||
| 2125 | ); | 2143 | ); |
| 2126 | 2144 | ||
| 2127 | it.effect("identifies sessions to Codex with the same client info as main", () => | 2145 | it.effect("identifies sessions to Codex with the same client info as main", () => |
| ... | @@ -2157,7 +2175,12 @@ describe("CodexAdapterV2 post-settle continuation", () => { | ... | @@ -2157,7 +2175,12 @@ describe("CodexAdapterV2 post-settle continuation", () => { |
| 2157 | }, | 2175 | }, |
| 2158 | }, | 2176 | }, |
| 2159 | ]); | 2177 | ]); |
| 2160 | }).pipe(Effect.scoped, Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 2178 | }).pipe( |
| 2179 | Effect.scoped, | ||
| 2180 | Effect.provide( | ||
| 2181 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 2182 | ), | ||
| 2183 | ), | ||
| 2161 | ); | 2184 | ); |
| 2162 | 2185 | ||
| 2163 | it.effect("unsubscribes from the native thread when it is unloaded", () => | 2186 | it.effect("unsubscribes from the native thread when it is unloaded", () => |
| ... | @@ -2195,7 +2218,12 @@ describe("CodexAdapterV2 post-settle continuation", () => { | ... | @@ -2195,7 +2218,12 @@ describe("CodexAdapterV2 post-settle continuation", () => { |
| 2195 | assert.isDefined(harness.runtime.unloadThread); | 2218 | assert.isDefined(harness.runtime.unloadThread); |
| 2196 | yield* harness.runtime.unloadThread!({ providerThread: harness.providerThread }); | 2219 | yield* harness.runtime.unloadThread!({ providerThread: harness.providerThread }); |
| 2197 | assert.deepEqual(requests, ["initialize", "thread/start", "thread/unsubscribe"]); | 2220 | assert.deepEqual(requests, ["initialize", "thread/start", "thread/unsubscribe"]); |
| 2198 | }).pipe(Effect.scoped, Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 2221 | }).pipe( |
| 2222 | Effect.scoped, | ||
| 2223 | Effect.provide( | ||
| 2224 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 2225 | ), | ||
| 2226 | ), | ||
| 2199 | ); | 2227 | ); |
| 2200 | 2228 | ||
| 2201 | it.effect("keeps the app-server failure as the cause when an unload is rejected", () => | 2229 | it.effect("keeps the app-server failure as the cause when an unload is rejected", () => |
| ... | @@ -2229,7 +2257,12 @@ describe("CodexAdapterV2 post-settle continuation", () => { | ... | @@ -2229,7 +2257,12 @@ describe("CodexAdapterV2 post-settle continuation", () => { |
| 2229 | assert.equal(error._tag, "ProviderAdapterProtocolError"); | 2257 | assert.equal(error._tag, "ProviderAdapterProtocolError"); |
| 2230 | const cause = error._tag === "ProviderAdapterProtocolError" ? error.cause : undefined; | 2258 | const cause = error._tag === "ProviderAdapterProtocolError" ? error.cause : undefined; |
| 2231 | assert.equal((cause as { _tag?: string } | undefined)?._tag, "CodexAppServerRequestError"); | 2259 | assert.equal((cause as { _tag?: string } | undefined)?._tag, "CodexAppServerRequestError"); |
| 2232 | }).pipe(Effect.scoped, Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 2260 | }).pipe( |
| 2261 | Effect.scoped, | ||
| 2262 | Effect.provide( | ||
| 2263 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 2264 | ), | ||
| 2265 | ), | ||
| 2233 | ); | 2266 | ); |
| 2234 | 2267 | ||
| 2235 | it.effect("waits for native start before interrupting an acknowledged queued turn", () => | 2268 | it.effect("waits for native start before interrupting an acknowledged queued turn", () => |
| ... | @@ -2323,7 +2356,12 @@ describe("CodexAdapterV2 post-settle continuation", () => { | ... | @@ -2323,7 +2356,12 @@ describe("CodexAdapterV2 post-settle continuation", () => { |
| 2323 | assert.equal(harness.terminalEvents()[0]?.status, "interrupted"); | 2356 | assert.equal(harness.terminalEvents()[0]?.status, "interrupted"); |
| 2324 | assert.lengthOf(harness.terminalEvents(), 1); | 2357 | assert.lengthOf(harness.terminalEvents(), 1); |
| 2325 | assert.isFalse(yield* harness.hasPendingBackgroundWork); | 2358 | assert.isFalse(yield* harness.hasPendingBackgroundWork); |
| 2326 | }).pipe(Effect.scoped, Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 2359 | }).pipe( |
| 2360 | Effect.scoped, | ||
| 2361 | Effect.provide( | ||
| 2362 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 2363 | ), | ||
| 2364 | ), | ||
| 2327 | ); | 2365 | ); |
| 2328 | 2366 | ||
| 2329 | it.effect("settles Stop when a queued native turn fails before starting", () => | 2367 | it.effect("settles Stop when a queued native turn fails before starting", () => |
| ... | @@ -2405,7 +2443,12 @@ describe("CodexAdapterV2 post-settle continuation", () => { | ... | @@ -2405,7 +2443,12 @@ describe("CodexAdapterV2 post-settle continuation", () => { |
| 2405 | assert.lengthOf(harness.terminalEvents(), 1); | 2443 | assert.lengthOf(harness.terminalEvents(), 1); |
| 2406 | assert.isFalse(interruptSent, "A terminal native turn must not receive turn/interrupt"); | 2444 | assert.isFalse(interruptSent, "A terminal native turn must not receive turn/interrupt"); |
| 2407 | assert.isFalse(yield* harness.hasPendingBackgroundWork); | 2445 | assert.isFalse(yield* harness.hasPendingBackgroundWork); |
| 2408 | }).pipe(Effect.scoped, Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 2446 | }).pipe( |
| 2447 | Effect.scoped, | ||
| 2448 | Effect.provide( | ||
| 2449 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 2450 | ), | ||
| 2451 | ), | ||
| 2409 | ); | 2452 | ); |
| 2410 | 2453 | ||
| 2411 | it.effect("bounds Stop when a queued native turn never starts", () => | 2454 | it.effect("bounds Stop when a queued native turn never starts", () => |
| ... | @@ -2465,7 +2508,12 @@ describe("CodexAdapterV2 post-settle continuation", () => { | ... | @@ -2465,7 +2508,12 @@ describe("CodexAdapterV2 post-settle continuation", () => { |
| 2465 | assert.lengthOf(harness.terminalEvents(), 1); | 2508 | assert.lengthOf(harness.terminalEvents(), 1); |
| 2466 | assert.isFalse(interruptSent, "An unstarted native turn must not receive turn/interrupt"); | 2509 | assert.isFalse(interruptSent, "An unstarted native turn must not receive turn/interrupt"); |
| 2467 | assert.isFalse(yield* harness.hasPendingBackgroundWork); | 2510 | assert.isFalse(yield* harness.hasPendingBackgroundWork); |
| 2468 | }).pipe(Effect.scoped, Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 2511 | }).pipe( |
| 2512 | Effect.scoped, | ||
| 2513 | Effect.provide( | ||
| 2514 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 2515 | ), | ||
| 2516 | ), | ||
| 2469 | ); | 2517 | ); |
| 2470 | 2518 | ||
| 2471 | it.effect("sends currency-sigil skill mentions to Codex as $ mentions", () => | 2519 | it.effect("sends currency-sigil skill mentions to Codex as $ mentions", () => |
| ... | @@ -2524,7 +2572,12 @@ describe("CodexAdapterV2 post-settle continuation", () => { | ... | @@ -2524,7 +2572,12 @@ describe("CodexAdapterV2 post-settle continuation", () => { |
| 2524 | text: "then £ship it", | 2572 | text: "then £ship it", |
| 2525 | }, | 2573 | }, |
| 2526 | }); | 2574 | }); |
| 2527 | }).pipe(Effect.scoped, Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 2575 | }).pipe( |
| 2576 | Effect.scoped, | ||
| 2577 | Effect.provide( | ||
| 2578 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 2579 | ), | ||
| 2580 | ), | ||
| 2528 | ); | 2581 | ); |
| 2529 | 2582 | ||
| 2530 | const assistantMessages = (events: ReadonlyArray<ProviderAdapter.ProviderAdapterV2Event>) => | 2583 | const assistantMessages = (events: ReadonlyArray<ProviderAdapter.ProviderAdapterV2Event>) => |
| ... | @@ -2674,7 +2727,11 @@ describe("CodexAdapterV2 post-settle continuation", () => { | ... | @@ -2674,7 +2727,11 @@ describe("CodexAdapterV2 post-settle continuation", () => { |
| 2674 | }); | 2727 | }); |
| 2675 | } | 2728 | } |
| 2676 | assert.isEmpty(assistantMessages(harness.events)); | 2729 | assert.isEmpty(assistantMessages(harness.events)); |
| 2677 | }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 2730 | }).pipe( |
| 2731 | Effect.provide( | ||
| 2732 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 2733 | ), | ||
| 2734 | ), | ||
| 2678 | ), | 2735 | ), |
| 2679 | ); | 2736 | ); |
| 2680 | 2737 | ||
| ... | @@ -2748,7 +2805,7 @@ describe("CodexAdapterV2 post-settle continuation", () => { | ... | @@ -2748,7 +2805,7 @@ describe("CodexAdapterV2 post-settle continuation", () => { |
| 2748 | ], | 2805 | ], |
| 2749 | }); | 2806 | }); |
| 2750 | const harness = yield* makeCodexReplayHarness(transcript); | 2807 | const harness = yield* makeCodexReplayHarness(transcript); |
| 2751 | McpProviderSession.setMcpProviderSession({ | 2808 | yield* (yield* McpProviderSessions.McpProviderSessions).set({ |
| 2752 | environmentId: EnvironmentId.make("test"), | 2809 | environmentId: EnvironmentId.make("test"), |
| 2753 | threadId: harness.threadId, | 2810 | threadId: harness.threadId, |
| 2754 | providerSessionId: "context-session", | 2811 | providerSessionId: "context-session", |
| ... | @@ -2757,9 +2814,6 @@ describe("CodexAdapterV2 post-settle continuation", () => { | ... | @@ -2757,9 +2814,6 @@ describe("CodexAdapterV2 post-settle continuation", () => { |
| 2757 | authorizationHeader: "Bearer test", | 2814 | authorizationHeader: "Bearer test", |
| 2758 | browserToolsAvailable: true, | 2815 | browserToolsAvailable: true, |
| 2759 | }); | 2816 | }); |
| 2760 | yield* Effect.addFinalizer(() => | ||
| 2761 | Effect.sync(() => McpProviderSession.clearMcpProviderSession(harness.threadId)), | ||
| 2762 | ); | ||
| 2763 | yield* harness.runtime.startTurn( | 2817 | yield* harness.runtime.startTurn( |
| 2764 | makeCodexTestTurnInput({ | 2818 | makeCodexTestTurnInput({ |
| 2765 | threadId: harness.threadId, | 2819 | threadId: harness.threadId, |
| ... | @@ -2771,7 +2825,11 @@ describe("CodexAdapterV2 post-settle continuation", () => { | ... | @@ -2771,7 +2825,11 @@ describe("CodexAdapterV2 post-settle continuation", () => { |
| 2771 | ); | 2825 | ); |
| 2772 | yield* harness.firstTerminal; | 2826 | yield* harness.firstTerminal; |
| 2773 | assert.equal(harness.terminalEvents()[0]?.status, "completed"); | 2827 | assert.equal(harness.terminalEvents()[0]?.status, "completed"); |
| 2774 | }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 2828 | }).pipe( |
| 2829 | Effect.provide( | ||
| 2830 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 2831 | ), | ||
| 2832 | ), | ||
| 2775 | ), | 2833 | ), |
| 2776 | ); | 2834 | ); |
| 2777 | 2835 | ||
| ... | @@ -2847,7 +2905,11 @@ describe("CodexAdapterV2 post-settle continuation", () => { | ... | @@ -2847,7 +2905,11 @@ describe("CodexAdapterV2 post-settle continuation", () => { |
| 2847 | ); | 2905 | ); |
| 2848 | assert.equal(items[0]?.id, items[1]?.id); | 2906 | assert.equal(items[0]?.id, items[1]?.id); |
| 2849 | assert.equal(harness.terminalEvents()[0]?.status, "completed"); | 2907 | assert.equal(harness.terminalEvents()[0]?.status, "completed"); |
| 2850 | }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 2908 | }).pipe( |
| 2909 | Effect.provide( | ||
| 2910 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 2911 | ), | ||
| 2912 | ), | ||
| 2851 | ), | 2913 | ), |
| 2852 | ); | 2914 | ); |
| 2853 | 2915 | ||
| ... | @@ -2895,7 +2957,11 @@ describe("CodexAdapterV2 post-settle continuation", () => { | ... | @@ -2895,7 +2957,11 @@ describe("CodexAdapterV2 post-settle continuation", () => { |
| 2895 | assert.equal(resumed.nativeThreadRef?.nativeId, nativeThreadId); | 2957 | assert.equal(resumed.nativeThreadRef?.nativeId, nativeThreadId); |
| 2896 | assert.equal(resumed.status, "idle"); | 2958 | assert.equal(resumed.status, "idle"); |
| 2897 | assert.equal(DateTime.toEpochMillis(resumed.updatedAt), 1782622450000); | 2959 | assert.equal(DateTime.toEpochMillis(resumed.updatedAt), 1782622450000); |
| 2898 | }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 2960 | }).pipe( |
| 2961 | Effect.provide( | ||
| 2962 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 2963 | ), | ||
| 2964 | ), | ||
| 2899 | ), | 2965 | ), |
| 2900 | ); | 2966 | ); |
| 2901 | 2967 | ||
| ... | @@ -3050,7 +3116,11 @@ describe("CodexAdapterV2 post-settle continuation", () => { | ... | @@ -3050,7 +3116,11 @@ describe("CodexAdapterV2 post-settle continuation", () => { |
| 3050 | ); | 3116 | ); |
| 3051 | assert.equal(resumed.status, "idle"); | 3117 | assert.equal(resumed.status, "idle"); |
| 3052 | assert.equal(DateTime.toEpochMillis(resumed.updatedAt), 1782622450000); | 3118 | assert.equal(DateTime.toEpochMillis(resumed.updatedAt), 1782622450000); |
| 3053 | }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 3119 | }).pipe( |
| 3120 | Effect.provide( | ||
| 3121 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 3122 | ), | ||
| 3123 | ), | ||
| 3054 | ), | 3124 | ), |
| 3055 | ); | 3125 | ); |
| 3056 | 3126 | ||
| ... | @@ -3132,7 +3202,11 @@ describe("CodexAdapterV2 post-settle continuation", () => { | ... | @@ -3132,7 +3202,11 @@ describe("CodexAdapterV2 post-settle continuation", () => { |
| 3132 | restartContinuationOfRunId: RunId.make("run-before-restart"), | 3202 | restartContinuationOfRunId: RunId.make("run-before-restart"), |
| 3133 | }); | 3203 | }); |
| 3134 | assert.equal(resumed.nativeThreadRef?.nativeId, nativeThreadId); | 3204 | assert.equal(resumed.nativeThreadRef?.nativeId, nativeThreadId); |
| 3135 | }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 3205 | }).pipe( |
| 3206 | Effect.provide( | ||
| 3207 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 3208 | ), | ||
| 3209 | ), | ||
| 3136 | ), | 3210 | ), |
| 3137 | ); | 3211 | ); |
| 3138 | 3212 | ||
| ... | @@ -3280,7 +3354,11 @@ describe("CodexAdapterV2 post-settle continuation", () => { | ... | @@ -3280,7 +3354,11 @@ describe("CodexAdapterV2 post-settle continuation", () => { |
| 3280 | assert.isAtLeast(recoveredIndex, 0); | 3354 | assert.isAtLeast(recoveredIndex, 0); |
| 3281 | assert.isAbove(resumedCommandIndex, recoveredIndex); | 3355 | assert.isAbove(resumedCommandIndex, recoveredIndex); |
| 3282 | assert.isAbove(terminalIndex, resumedCommandIndex); | 3356 | assert.isAbove(terminalIndex, resumedCommandIndex); |
| 3283 | }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 3357 | }).pipe( |
| 3358 | Effect.provide( | ||
| 3359 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 3360 | ), | ||
| 3361 | ), | ||
| 3284 | ), | 3362 | ), |
| 3285 | ); | 3363 | ); |
| 3286 | 3364 | ||
| ... | @@ -3379,7 +3457,11 @@ describe("CodexAdapterV2 post-settle continuation", () => { | ... | @@ -3379,7 +3457,11 @@ describe("CodexAdapterV2 post-settle continuation", () => { |
| 3379 | ["compaction", 1782622565000], | 3457 | ["compaction", 1782622565000], |
| 3380 | ], | 3458 | ], |
| 3381 | ); | 3459 | ); |
| 3382 | }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 3460 | }).pipe( |
| 3461 | Effect.provide( | ||
| 3462 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 3463 | ), | ||
| 3464 | ), | ||
| 3383 | ), | 3465 | ), |
| 3384 | ); | 3466 | ); |
| 3385 | 3467 | ||
| ... | @@ -3543,7 +3625,11 @@ describe("CodexAdapterV2 post-settle continuation", () => { | ... | @@ -3543,7 +3625,11 @@ describe("CodexAdapterV2 post-settle continuation", () => { |
| 3543 | ); | 3625 | ); |
| 3544 | } | 3626 | } |
| 3545 | assert.deepEqual(assistantMessages(harness.events), []); | 3627 | assert.deepEqual(assistantMessages(harness.events), []); |
| 3546 | }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 3628 | }).pipe( |
| 3629 | Effect.provide( | ||
| 3630 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 3631 | ), | ||
| 3632 | ), | ||
| 3547 | ), | 3633 | ), |
| 3548 | ); | 3634 | ); |
| 3549 | 3635 | ||
| ... | @@ -3672,7 +3758,11 @@ describe("CodexAdapterV2 post-settle continuation", () => { | ... | @@ -3672,7 +3758,11 @@ describe("CodexAdapterV2 post-settle continuation", () => { |
| 3672 | assistantMessages(harness.events).map((event) => event.message.text), | 3758 | assistantMessages(harness.events).map((event) => event.message.text), |
| 3673 | ["CODEX_RECOVERY_OK"], | 3759 | ["CODEX_RECOVERY_OK"], |
| 3674 | ); | 3760 | ); |
| 3675 | }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 3761 | }).pipe( |
| 3762 | Effect.provide( | ||
| 3763 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 3764 | ), | ||
| 3765 | ), | ||
| 3676 | ), | 3766 | ), |
| 3677 | ); | 3767 | ); |
| 3678 | 3768 | ||
| ... | @@ -3715,7 +3805,11 @@ describe("CodexAdapterV2 post-settle continuation", () => { | ... | @@ -3715,7 +3805,11 @@ describe("CodexAdapterV2 post-settle continuation", () => { |
| 3715 | })), | 3805 | })), |
| 3716 | [{ text: "CODEX_RECOVERY_OK", streaming: false }], | 3806 | [{ text: "CODEX_RECOVERY_OK", streaming: false }], |
| 3717 | ); | 3807 | ); |
| 3718 | }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 3808 | }).pipe( |
| 3809 | Effect.provide( | ||
| 3810 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 3811 | ), | ||
| 3812 | ), | ||
| 3719 | ), | 3813 | ), |
| 3720 | ); | 3814 | ); |
| 3721 | 3815 | ||
| ... | @@ -3759,7 +3853,11 @@ describe("CodexAdapterV2 post-settle continuation", () => { | ... | @@ -3759,7 +3853,11 @@ describe("CodexAdapterV2 post-settle continuation", () => { |
| 3759 | assistantMessages(harness.events).map((event) => event.message.text), | 3853 | assistantMessages(harness.events).map((event) => event.message.text), |
| 3760 | ["CODEX_RECOVERY_OK"], | 3854 | ["CODEX_RECOVERY_OK"], |
| 3761 | ); | 3855 | ); |
| 3762 | }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 3856 | }).pipe( |
| 3857 | Effect.provide( | ||
| 3858 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 3859 | ), | ||
| 3860 | ), | ||
| 3763 | ), | 3861 | ), |
| 3764 | ); | 3862 | ); |
| 3765 | 3863 | ||
| ... | @@ -3878,7 +3976,11 @@ describe("CodexAdapterV2 post-settle continuation", () => { | ... | @@ -3878,7 +3976,11 @@ describe("CodexAdapterV2 post-settle continuation", () => { |
| 3878 | new Set(assistantMessages(harness.events).map((event) => event.message.id)).size, | 3976 | new Set(assistantMessages(harness.events).map((event) => event.message.id)).size, |
| 3879 | 1, | 3977 | 1, |
| 3880 | ); | 3978 | ); |
| 3881 | }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 3979 | }).pipe( |
| 3980 | Effect.provide( | ||
| 3981 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 3982 | ), | ||
| 3983 | ), | ||
| 3882 | ), | 3984 | ), |
| 3883 | ); | 3985 | ); |
| 3884 | 3986 | ||
| ... | @@ -3906,7 +4008,11 @@ describe("CodexAdapterV2 post-settle continuation", () => { | ... | @@ -3906,7 +4008,11 @@ describe("CodexAdapterV2 post-settle continuation", () => { |
| 3906 | assistantMessages(harness.events).map((event) => event.message.text), | 4008 | assistantMessages(harness.events).map((event) => event.message.text), |
| 3907 | [""], | 4009 | [""], |
| 3908 | ); | 4010 | ); |
| 3909 | }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 4011 | }).pipe( |
| 4012 | Effect.provide( | ||
| 4013 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 4014 | ), | ||
| 4015 | ), | ||
| 3910 | ), | 4016 | ), |
| 3911 | ); | 4017 | ); |
| 3912 | 4018 | ||
| ... | @@ -3935,7 +4041,11 @@ describe("CodexAdapterV2 post-settle continuation", () => { | ... | @@ -3935,7 +4041,11 @@ describe("CodexAdapterV2 post-settle continuation", () => { |
| 3935 | assistantMessages(harness.events).map((event) => event.message.text), | 4041 | assistantMessages(harness.events).map((event) => event.message.text), |
| 3936 | [""], | 4042 | [""], |
| 3937 | ); | 4043 | ); |
| 3938 | }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 4044 | }).pipe( |
| 4045 | Effect.provide( | ||
| 4046 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 4047 | ), | ||
| 4048 | ), | ||
| 3939 | ), | 4049 | ), |
| 3940 | ); | 4050 | ); |
| 3941 | 4051 | ||
| ... | @@ -3964,7 +4074,11 @@ describe("CodexAdapterV2 post-settle continuation", () => { | ... | @@ -3964,7 +4074,11 @@ describe("CodexAdapterV2 post-settle continuation", () => { |
| 3964 | assistantMessages(harness.events).map((event) => event.message.text), | 4074 | assistantMessages(harness.events).map((event) => event.message.text), |
| 3965 | ["Working on it.", ""], | 4075 | ["Working on it.", ""], |
| 3966 | ); | 4076 | ); |
| 3967 | }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 4077 | }).pipe( |
| 4078 | Effect.provide( | ||
| 4079 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 4080 | ), | ||
| 4081 | ), | ||
| 3968 | ), | 4082 | ), |
| 3969 | ); | 4083 | ); |
| 3970 | 4084 | ||
| ... | @@ -3993,7 +4107,11 @@ describe("CodexAdapterV2 post-settle continuation", () => { | ... | @@ -3993,7 +4107,11 @@ describe("CodexAdapterV2 post-settle continuation", () => { |
| 3993 | assistantMessages(harness.events).map((event) => event.message.text), | 4107 | assistantMessages(harness.events).map((event) => event.message.text), |
| 3994 | ["CODEX_RECOVERY_OK"], | 4108 | ["CODEX_RECOVERY_OK"], |
| 3995 | ); | 4109 | ); |
| 3996 | }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 4110 | }).pipe( |
| 4111 | Effect.provide( | ||
| 4112 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 4113 | ), | ||
| 4114 | ), | ||
| 3997 | ), | 4115 | ), |
| 3998 | ); | 4116 | ); |
| 3999 | 4117 | ||
| ... | @@ -4022,7 +4140,11 @@ describe("CodexAdapterV2 post-settle continuation", () => { | ... | @@ -4022,7 +4140,11 @@ describe("CodexAdapterV2 post-settle continuation", () => { |
| 4022 | assistantMessages(harness.events).map((event) => event.message.text), | 4140 | assistantMessages(harness.events).map((event) => event.message.text), |
| 4023 | ["CODEX_RECOVERY_OK"], | 4141 | ["CODEX_RECOVERY_OK"], |
| 4024 | ); | 4142 | ); |
| 4025 | }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 4143 | }).pipe( |
| 4144 | Effect.provide( | ||
| 4145 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 4146 | ), | ||
| 4147 | ), | ||
| 4026 | ), | 4148 | ), |
| 4027 | ); | 4149 | ); |
| 4028 | 4150 | ||
| ... | @@ -4051,7 +4173,11 @@ describe("CodexAdapterV2 post-settle continuation", () => { | ... | @@ -4051,7 +4173,11 @@ describe("CodexAdapterV2 post-settle continuation", () => { |
| 4051 | assistantMessages(harness.events).map((event) => event.message.text), | 4173 | assistantMessages(harness.events).map((event) => event.message.text), |
| 4052 | ["", "CODEX_RECOVERY_OK"], | 4174 | ["", "CODEX_RECOVERY_OK"], |
| 4053 | ); | 4175 | ); |
| 4054 | }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 4176 | }).pipe( |
| 4177 | Effect.provide( | ||
| 4178 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 4179 | ), | ||
| 4180 | ), | ||
| 4055 | ), | 4181 | ), |
| 4056 | ); | 4182 | ); |
| 4057 | 4183 | ||
| ... | @@ -4207,7 +4333,11 @@ describe("CodexAdapterV2 post-settle continuation", () => { | ... | @@ -4207,7 +4333,11 @@ describe("CodexAdapterV2 post-settle continuation", () => { |
| 4207 | ); | 4333 | ); |
| 4208 | assert.lengthOf(harness.terminalEvents(), 1); | 4334 | assert.lengthOf(harness.terminalEvents(), 1); |
| 4209 | assert.isFalse(yield* harness.hasPendingBackgroundWork); | 4335 | assert.isFalse(yield* harness.hasPendingBackgroundWork); |
| 4210 | }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 4336 | }).pipe( |
| 4337 | Effect.provide( | ||
| 4338 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 4339 | ), | ||
| 4340 | ), | ||
| 4211 | ), | 4341 | ), |
| 4212 | ); | 4342 | ); |
| 4213 | 4343 | ||
| ... | @@ -4362,7 +4492,11 @@ describe("CodexAdapterV2 post-settle continuation", () => { | ... | @@ -4362,7 +4492,11 @@ describe("CodexAdapterV2 post-settle continuation", () => { |
| 4362 | assert.lengthOf(harness.terminalEvents(), 1); | 4492 | assert.lengthOf(harness.terminalEvents(), 1); |
| 4363 | assert.equal(harness.terminalEvents()[0]?.status, "completed"); | 4493 | assert.equal(harness.terminalEvents()[0]?.status, "completed"); |
| 4364 | assert.lengthOf(harness.continuationRequests, 0); | 4494 | assert.lengthOf(harness.continuationRequests, 0); |
| 4365 | }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 4495 | }).pipe( |
| 4496 | Effect.provide( | ||
| 4497 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 4498 | ), | ||
| 4499 | ), | ||
| 4366 | ); | 4500 | ); |
| 4367 | }, | 4501 | }, |
| 4368 | ); | 4502 | ); |
| ... | @@ -4722,7 +4856,11 @@ describe("CodexAdapterV2 post-settle continuation", () => { | ... | @@ -4722,7 +4856,11 @@ describe("CodexAdapterV2 post-settle continuation", () => { |
| 4722 | assert.lengthOf(harness.continuationRequests, 0); | 4856 | assert.lengthOf(harness.continuationRequests, 0); |
| 4723 | assert.isFalse(yield* harness.hasPendingBackgroundWork); | 4857 | assert.isFalse(yield* harness.hasPendingBackgroundWork); |
| 4724 | assert.lengthOf(harness.terminalEvents(), 1); | 4858 | assert.lengthOf(harness.terminalEvents(), 1); |
| 4725 | }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 4859 | }).pipe( |
| 4860 | Effect.provide( | ||
| 4861 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 4862 | ), | ||
| 4863 | ), | ||
| 4726 | ), | 4864 | ), |
| 4727 | ); | 4865 | ); |
| 4728 | 4866 | ||
| ... | @@ -5011,7 +5149,11 @@ describe("CodexAdapterV2 post-settle continuation", () => { | ... | @@ -5011,7 +5149,11 @@ describe("CodexAdapterV2 post-settle continuation", () => { |
| 5011 | ); | 5149 | ); |
| 5012 | assert.isAtLeast(commandUpdates.length, 2, "start + interrupt terminalization"); | 5150 | assert.isAtLeast(commandUpdates.length, 2, "start + interrupt terminalization"); |
| 5013 | assert.equal(commandUpdates[commandUpdates.length - 1]?.turnItem.status, "interrupted"); | 5151 | assert.equal(commandUpdates[commandUpdates.length - 1]?.turnItem.status, "interrupted"); |
| 5014 | }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 5152 | }).pipe( |
| 5153 | Effect.provide( | ||
| 5154 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 5155 | ), | ||
| 5156 | ), | ||
| 5015 | ), | 5157 | ), |
| 5016 | ); | 5158 | ); |
| 5017 | 5159 | ||
| ... | @@ -5239,7 +5381,11 @@ describe("CodexAdapterV2 post-settle continuation", () => { | ... | @@ -5239,7 +5381,11 @@ describe("CodexAdapterV2 post-settle continuation", () => { |
| 5239 | assert.equal(harness.subagentUpdates().at(-1)?.subagent.status, "interrupted"); | 5381 | assert.equal(harness.subagentUpdates().at(-1)?.subagent.status, "interrupted"); |
| 5240 | assertChildProviderTerminalBeforeRoot(harness.events, harness.threadId); | 5382 | assertChildProviderTerminalBeforeRoot(harness.events, harness.threadId); |
| 5241 | assert.isFalse(yield* harness.hasPendingBackgroundWork); | 5383 | assert.isFalse(yield* harness.hasPendingBackgroundWork); |
| 5242 | }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 5384 | }).pipe( |
| 5385 | Effect.provide( | ||
| 5386 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 5387 | ), | ||
| 5388 | ), | ||
| 5243 | ), | 5389 | ), |
| 5244 | ); | 5390 | ); |
| 5245 | 5391 | ||
| ... | @@ -5324,7 +5470,11 @@ describe("CodexAdapterV2 post-settle continuation", () => { | ... | @@ -5324,7 +5470,11 @@ describe("CodexAdapterV2 post-settle continuation", () => { |
| 5324 | yield* awaitUntil(() => harness.terminalEvents().length === 1, "interrupted root terminal"); | 5470 | yield* awaitUntil(() => harness.terminalEvents().length === 1, "interrupted root terminal"); |
| 5325 | assertChildProviderTerminalBeforeRoot(harness.events, harness.threadId); | 5471 | assertChildProviderTerminalBeforeRoot(harness.events, harness.threadId); |
| 5326 | assert.isFalse(yield* harness.hasPendingBackgroundWork); | 5472 | assert.isFalse(yield* harness.hasPendingBackgroundWork); |
| 5327 | }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 5473 | }).pipe( |
| 5474 | Effect.provide( | ||
| 5475 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 5476 | ), | ||
| 5477 | ), | ||
| 5328 | ), | 5478 | ), |
| 5329 | ); | 5479 | ); |
| 5330 | 5480 | ||
| ... | @@ -5412,7 +5562,11 @@ describe("CodexAdapterV2 post-settle continuation", () => { | ... | @@ -5412,7 +5562,11 @@ describe("CodexAdapterV2 post-settle continuation", () => { |
| 5412 | yield* awaitUntil(() => harness.terminalEvents().length === 1, "interrupted root terminal"); | 5562 | yield* awaitUntil(() => harness.terminalEvents().length === 1, "interrupted root terminal"); |
| 5413 | assertChildProviderTerminalBeforeRoot(harness.events, harness.threadId); | 5563 | assertChildProviderTerminalBeforeRoot(harness.events, harness.threadId); |
| 5414 | assert.isFalse(yield* harness.hasPendingBackgroundWork); | 5564 | assert.isFalse(yield* harness.hasPendingBackgroundWork); |
| 5415 | }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 5565 | }).pipe( |
| 5566 | Effect.provide( | ||
| 5567 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 5568 | ), | ||
| 5569 | ), | ||
| 5416 | ), | 5570 | ), |
| 5417 | ); | 5571 | ); |
| 5418 | 5572 | ||
| ... | @@ -5523,7 +5677,11 @@ describe("CodexAdapterV2 post-settle continuation", () => { | ... | @@ -5523,7 +5677,11 @@ describe("CodexAdapterV2 post-settle continuation", () => { |
| 5523 | assert.equal(lateChildUpdates.at(-1)?.providerTurn.status, "interrupted"); | 5677 | assert.equal(lateChildUpdates.at(-1)?.providerTurn.status, "interrupted"); |
| 5524 | assertChildProviderTerminalBeforeRoot(harness.events, harness.threadId); | 5678 | assertChildProviderTerminalBeforeRoot(harness.events, harness.threadId); |
| 5525 | assert.isFalse(yield* harness.hasPendingBackgroundWork); | 5679 | assert.isFalse(yield* harness.hasPendingBackgroundWork); |
| 5526 | }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 5680 | }).pipe( |
| 5681 | Effect.provide( | ||
| 5682 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 5683 | ), | ||
| 5684 | ), | ||
| 5527 | ), | 5685 | ), |
| 5528 | ); | 5686 | ); |
| 5529 | 5687 | ||
| ... | @@ -5688,7 +5846,11 @@ describe("CodexAdapterV2 post-settle continuation", () => { | ... | @@ -5688,7 +5846,11 @@ describe("CodexAdapterV2 post-settle continuation", () => { |
| 5688 | ); | 5846 | ); |
| 5689 | assertChildProviderTerminalBeforeRoot(harness.events, harness.threadId); | 5847 | assertChildProviderTerminalBeforeRoot(harness.events, harness.threadId); |
| 5690 | assert.isFalse(yield* harness.hasPendingBackgroundWork); | 5848 | assert.isFalse(yield* harness.hasPendingBackgroundWork); |
| 5691 | }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 5849 | }).pipe( |
| 5850 | Effect.provide( | ||
| 5851 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 5852 | ), | ||
| 5853 | ), | ||
| 5692 | ), | 5854 | ), |
| 5693 | ); | 5855 | ); |
| 5694 | 5856 | ||
| ... | @@ -5953,7 +6115,11 @@ describe("CodexAdapterV2 post-settle continuation", () => { | ... | @@ -5953,7 +6115,11 @@ describe("CodexAdapterV2 post-settle continuation", () => { |
| 5953 | commandUpdatesBeforeLateEvents.length, | 6115 | commandUpdatesBeforeLateEvents.length, |
| 5954 | "late starts and completions must not project after timeout", | 6116 | "late starts and completions must not project after timeout", |
| 5955 | ); | 6117 | ); |
| 5956 | }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 6118 | }).pipe( |
| 6119 | Effect.provide( | ||
| 6120 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 6121 | ), | ||
| 6122 | ), | ||
| 5957 | ), | 6123 | ), |
| 5958 | ); | 6124 | ); |
| 5959 | 6125 | ||
| ... | @@ -6060,7 +6226,11 @@ describe("CodexAdapterV2 post-settle continuation", () => { | ... | @@ -6060,7 +6226,11 @@ describe("CodexAdapterV2 post-settle continuation", () => { |
| 6060 | yield* awaitUntil(() => harness.terminalEvents().length === 1, "interrupted terminal"); | 6226 | yield* awaitUntil(() => harness.terminalEvents().length === 1, "interrupted terminal"); |
| 6061 | assert.equal(harness.terminalEvents()[0]?.status, "interrupted"); | 6227 | assert.equal(harness.terminalEvents()[0]?.status, "interrupted"); |
| 6062 | assert.isFalse(yield* harness.hasPendingBackgroundWork); | 6228 | assert.isFalse(yield* harness.hasPendingBackgroundWork); |
| 6063 | }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 6229 | }).pipe( |
| 6230 | Effect.provide( | ||
| 6231 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 6232 | ), | ||
| 6233 | ), | ||
| 6064 | ), | 6234 | ), |
| 6065 | ); | 6235 | ); |
| 6066 | 6236 | ||
| ... | @@ -6296,7 +6466,11 @@ describe("CodexAdapterV2 post-settle continuation", () => { | ... | @@ -6296,7 +6466,11 @@ describe("CodexAdapterV2 post-settle continuation", () => { |
| 6296 | assert.isDefined(item); | 6466 | assert.isDefined(item); |
| 6297 | } | 6467 | } |
| 6298 | if (scenario.name === "retry") assert.equal(terminal.retry?.attempt, 1); | 6468 | if (scenario.name === "retry") assert.equal(terminal.retry?.attempt, 1); |
| 6299 | }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 6469 | }).pipe( |
| 6470 | Effect.provide( | ||
| 6471 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 6472 | ), | ||
| 6473 | ), | ||
| 6300 | ), | 6474 | ), |
| 6301 | ); | 6475 | ); |
| 6302 | 6476 | ||
| ... | @@ -6394,7 +6568,11 @@ describe("CodexAdapterV2 post-settle continuation", () => { | ... | @@ -6394,7 +6568,11 @@ describe("CodexAdapterV2 post-settle continuation", () => { |
| 6394 | ); | 6568 | ); |
| 6395 | assert.isFalse(yield* harness.hasPendingBackgroundWork); | 6569 | assert.isFalse(yield* harness.hasPendingBackgroundWork); |
| 6396 | assert.lengthOf(harness.continuationRequests, 0); | 6570 | assert.lengthOf(harness.continuationRequests, 0); |
| 6397 | }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 6571 | }).pipe( |
| 6572 | Effect.provide( | ||
| 6573 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 6574 | ), | ||
| 6575 | ), | ||
| 6398 | ), | 6576 | ), |
| 6399 | ); | 6577 | ); |
| 6400 | 6578 | ||
| ... | @@ -6555,7 +6733,11 @@ describe("CodexAdapterV2 post-settle continuation", () => { | ... | @@ -6555,7 +6733,11 @@ describe("CodexAdapterV2 post-settle continuation", () => { |
| 6555 | assert.include(stored, "connect-src https://api.weather.test"); | 6733 | assert.include(stored, "connect-src https://api.weather.test"); |
| 6556 | assert.isBelow(stored.indexOf("Content-Security-Policy"), stored.indexOf("<script>")); | 6734 | assert.isBelow(stored.indexOf("Content-Security-Policy"), stored.indexOf("<script>")); |
| 6557 | assert.isFalse(yield* harness.hasPendingBackgroundWork); | 6735 | assert.isFalse(yield* harness.hasPendingBackgroundWork); |
| 6558 | }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 6736 | }).pipe( |
| 6737 | Effect.provide( | ||
| 6738 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 6739 | ), | ||
| 6740 | ), | ||
| 6559 | ), | 6741 | ), |
| 6560 | ); | 6742 | ); |
| 6561 | 6743 | ||
| ... | @@ -6641,7 +6823,11 @@ describe("CodexAdapterV2 post-settle continuation", () => { | ... | @@ -6641,7 +6823,11 @@ describe("CodexAdapterV2 post-settle continuation", () => { |
| 6641 | const output = rows[0]!.turnItem.type === "dynamic_tool" ? rows[0]!.turnItem.output : null; | 6823 | const output = rows[0]!.turnItem.type === "dynamic_tool" ? rows[0]!.turnItem.output : null; |
| 6642 | assert.isUndefined((output as Record<string, unknown> | null)?.[MCP_APP_OUTPUT_KEY]); | 6824 | assert.isUndefined((output as Record<string, unknown> | null)?.[MCP_APP_OUTPUT_KEY]); |
| 6643 | assert.isFalse(yield* harness.hasPendingBackgroundWork); | 6825 | assert.isFalse(yield* harness.hasPendingBackgroundWork); |
| 6644 | }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 6826 | }).pipe( |
| 6827 | Effect.provide( | ||
| 6828 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 6829 | ), | ||
| 6830 | ), | ||
| 6645 | ), | 6831 | ), |
| 6646 | ); | 6832 | ); |
| 6647 | 6833 | ||
| ... | @@ -6883,7 +7069,11 @@ describe("CodexAdapterV2 post-settle continuation", () => { | ... | @@ -6883,7 +7069,11 @@ describe("CodexAdapterV2 post-settle continuation", () => { |
| 6883 | ); | 7069 | ); |
| 6884 | assert.lengthOf(harness.terminalEvents(), 1); | 7070 | assert.lengthOf(harness.terminalEvents(), 1); |
| 6885 | assert.isFalse(yield* harness.hasPendingBackgroundWork); | 7071 | assert.isFalse(yield* harness.hasPendingBackgroundWork); |
| 6886 | }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 7072 | }).pipe( |
| 7073 | Effect.provide( | ||
| 7074 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 7075 | ), | ||
| 7076 | ), | ||
| 6887 | ), | 7077 | ), |
| 6888 | ); | 7078 | ); |
| 6889 | 7079 | ||
| ... | @@ -7117,7 +7307,11 @@ describe("CodexAdapterV2 post-settle continuation", () => { | ... | @@ -7117,7 +7307,11 @@ describe("CodexAdapterV2 post-settle continuation", () => { |
| 7117 | ]); | 7307 | ]); |
| 7118 | } | 7308 | } |
| 7119 | assert.equal(metadataRequests, name === "current Codex Sol" ? 1 : 2); | 7309 | assert.equal(metadataRequests, name === "current Codex Sol" ? 1 : 2); |
| 7120 | }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 7310 | }).pipe( |
| 7311 | Effect.provide( | ||
| 7312 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 7313 | ), | ||
| 7314 | ), | ||
| 7121 | ), | 7315 | ), |
| 7122 | ); | 7316 | ); |
| 7123 | 7317 | ||
| ... | @@ -7174,7 +7368,11 @@ describe("CodexAdapterV2 post-settle continuation", () => { | ... | @@ -7174,7 +7368,11 @@ describe("CodexAdapterV2 post-settle continuation", () => { |
| 7174 | yield* harness.firstTerminal; | 7368 | yield* harness.firstTerminal; |
| 7175 | assert.equal(harness.subagentUpdates().at(-1)?.subagent.model, model); | 7369 | assert.equal(harness.subagentUpdates().at(-1)?.subagent.model, model); |
| 7176 | assert.deepEqual(metadataRequests, ["thread/read", "thread/resume"]); | 7370 | assert.deepEqual(metadataRequests, ["thread/read", "thread/resume"]); |
| 7177 | }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 7371 | }).pipe( |
| 7372 | Effect.provide( | ||
| 7373 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 7374 | ), | ||
| 7375 | ), | ||
| 7178 | ), | 7376 | ), |
| 7179 | ); | 7377 | ); |
| 7180 | 7378 | ||
| ... | @@ -7273,7 +7471,11 @@ describe("CodexAdapterV2 post-settle continuation", () => { | ... | @@ -7273,7 +7471,11 @@ describe("CodexAdapterV2 post-settle continuation", () => { |
| 7273 | { id: "reasoningEffort", value: "low" }, | 7471 | { id: "reasoningEffort", value: "low" }, |
| 7274 | { id: "serviceTier", value: "ultrafast" }, | 7472 | { id: "serviceTier", value: "ultrafast" }, |
| 7275 | ]); | 7473 | ]); |
| 7276 | }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 7474 | }).pipe( |
| 7475 | Effect.provide( | ||
| 7476 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 7477 | ), | ||
| 7478 | ), | ||
| 7277 | ), | 7479 | ), |
| 7278 | ); | 7480 | ); |
| 7279 | 7481 | ||
| ... | @@ -7335,7 +7537,11 @@ describe("CodexAdapterV2 post-settle continuation", () => { | ... | @@ -7335,7 +7537,11 @@ describe("CodexAdapterV2 post-settle continuation", () => { |
| 7335 | assert.isFalse(yield* harness.hasPendingBackgroundWork); | 7537 | assert.isFalse(yield* harness.hasPendingBackgroundWork); |
| 7336 | assert.lengthOf(harness.terminalEvents(), 1); | 7538 | assert.lengthOf(harness.terminalEvents(), 1); |
| 7337 | assert.lengthOf(harness.continuationRequests, 0); | 7539 | assert.lengthOf(harness.continuationRequests, 0); |
| 7338 | }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 7540 | }).pipe( |
| 7541 | Effect.provide( | ||
| 7542 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 7543 | ), | ||
| 7544 | ), | ||
| 7339 | ), | 7545 | ), |
| 7340 | ); | 7546 | ); |
| 7341 | 7547 | ||
| ... | @@ -7415,7 +7621,11 @@ describe("CodexAdapterV2 post-settle continuation", () => { | ... | @@ -7415,7 +7621,11 @@ describe("CodexAdapterV2 post-settle continuation", () => { |
| 7415 | assert.isNull(row?.completedAt); | 7621 | assert.isNull(row?.completedAt); |
| 7416 | assert.isNotNull(row?.startedAt); | 7622 | assert.isNotNull(row?.startedAt); |
| 7417 | assert.equal(DateTime.toEpochMillis(row!.startedAt!), 1782622470000); | 7623 | assert.equal(DateTime.toEpochMillis(row!.startedAt!), 1782622470000); |
| 7418 | }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 7624 | }).pipe( |
| 7625 | Effect.provide( | ||
| 7626 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 7627 | ), | ||
| 7628 | ), | ||
| 7419 | ), | 7629 | ), |
| 7420 | ); | 7630 | ); |
| 7421 | 7631 | ||
| ... | @@ -7577,7 +7787,11 @@ describe("CodexAdapterV2 post-settle continuation", () => { | ... | @@ -7577,7 +7787,11 @@ describe("CodexAdapterV2 post-settle continuation", () => { |
| 7577 | DateTime.toEpochMillis(first.subagent.completedAt!), | 7787 | DateTime.toEpochMillis(first.subagent.completedAt!), |
| 7578 | ); | 7788 | ); |
| 7579 | } | 7789 | } |
| 7580 | }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 7790 | }).pipe( |
| 7791 | Effect.provide( | ||
| 7792 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 7793 | ), | ||
| 7794 | ), | ||
| 7581 | ), | 7795 | ), |
| 7582 | ); | 7796 | ); |
| 7583 | 7797 | ||
| ... | @@ -7725,7 +7939,12 @@ describe("CodexAdapterV2 post-settle continuation", () => { | ... | @@ -7725,7 +7939,12 @@ describe("CodexAdapterV2 post-settle continuation", () => { |
| 7725 | "thread/rollback", | 7939 | "thread/rollback", |
| 7726 | "thread/rollback must not be sent to a legacy Codex thread", | 7940 | "thread/rollback must not be sent to a legacy Codex thread", |
| 7727 | ); | 7941 | ); |
| 7728 | }).pipe(Effect.scoped, Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 7942 | }).pipe( |
| 7943 | Effect.scoped, | ||
| 7944 | Effect.provide( | ||
| 7945 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 7946 | ), | ||
| 7947 | ), | ||
| 7729 | ); | 7948 | ); |
| 7730 | 7949 | ||
| 7731 | it.effect( | 7950 | it.effect( |
| ... | @@ -7864,7 +8083,12 @@ describe("CodexAdapterV2 post-settle continuation", () => { | ... | @@ -7864,7 +8083,12 @@ describe("CodexAdapterV2 post-settle continuation", () => { |
| 7864 | assert.notEqual(forkedProviderThread.id, harness.providerThread.id); | 8083 | assert.notEqual(forkedProviderThread.id, harness.providerThread.id); |
| 7865 | assert.equal(forkedProviderThread.forkedFrom?.providerTurnId, firstTurn.id); | 8084 | assert.equal(forkedProviderThread.forkedFrom?.providerTurnId, firstTurn.id); |
| 7866 | assert.deepEqual(outbound.slice(-2), ["thread/turns/list", "thread/revert"]); | 8085 | assert.deepEqual(outbound.slice(-2), ["thread/turns/list", "thread/revert"]); |
| 7867 | }).pipe(Effect.scoped, Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 8086 | }).pipe( |
| 8087 | Effect.scoped, | ||
| 8088 | Effect.provide( | ||
| 8089 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 8090 | ), | ||
| 8091 | ), | ||
| 7868 | ); | 8092 | ); |
| 7869 | 8093 | ||
| 7870 | it.effect( | 8094 | it.effect( |
| ... | @@ -7966,7 +8190,12 @@ describe("CodexAdapterV2 post-settle continuation", () => { | ... | @@ -7966,7 +8190,12 @@ describe("CodexAdapterV2 post-settle continuation", () => { |
| 7966 | "thread/rollback", | 8190 | "thread/rollback", |
| 7967 | "thread/rollback must not be sent to a legacy Codex fork", | 8191 | "thread/rollback must not be sent to a legacy Codex fork", |
| 7968 | ); | 8192 | ); |
| 7969 | }).pipe(Effect.scoped, Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 8193 | }).pipe( |
| 8194 | Effect.scoped, | ||
| 8195 | Effect.provide( | ||
| 8196 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 8197 | ), | ||
| 8198 | ), | ||
| 7970 | ); | 8199 | ); |
| 7971 | 8200 | ||
| 7972 | it.effect("propagates native thread/fork failures as typed fork errors", () => | 8201 | it.effect("propagates native thread/fork failures as typed fork errors", () => |
| ... | @@ -8022,7 +8251,12 @@ describe("CodexAdapterV2 post-settle continuation", () => { | ... | @@ -8022,7 +8251,12 @@ describe("CodexAdapterV2 post-settle continuation", () => { |
| 8022 | 8251 | ||
| 8023 | assert.instanceOf(error, ProviderAdapter.ProviderAdapterForkThreadError); | 8252 | assert.instanceOf(error, ProviderAdapter.ProviderAdapterForkThreadError); |
| 8024 | assert.include(errorCauseChainText(error), "fork exploded"); | 8253 | assert.include(errorCauseChainText(error), "fork exploded"); |
| 8025 | }).pipe(Effect.scoped, Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 8254 | }).pipe( |
| 8255 | Effect.scoped, | ||
| 8256 | Effect.provide( | ||
| 8257 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 8258 | ), | ||
| 8259 | ), | ||
| 8026 | ); | 8260 | ); |
| 8027 | 8261 | ||
| 8028 | describe("native goals", () => { | 8262 | describe("native goals", () => { |
| ... | @@ -8186,7 +8420,12 @@ describe("CodexAdapterV2 post-settle continuation", () => { | ... | @@ -8186,7 +8420,12 @@ describe("CodexAdapterV2 post-settle continuation", () => { |
| 8186 | ], | 8420 | ], |
| 8187 | ); | 8421 | ); |
| 8188 | assert.equal(providerGoals(harness.events).at(-1), "complete"); | 8422 | assert.equal(providerGoals(harness.events).at(-1), "complete"); |
| 8189 | }).pipe(Effect.scoped, Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 8423 | }).pipe( |
| 8424 | Effect.scoped, | ||
| 8425 | Effect.provide( | ||
| 8426 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 8427 | ), | ||
| 8428 | ), | ||
| 8190 | ); | 8429 | ); |
| 8191 | 8430 | ||
| 8192 | it.effect("settles /goal pause with a reply instead of a native turn", () => | 8431 | it.effect("settles /goal pause with a reply instead of a native turn", () => |
| ... | @@ -8230,7 +8469,12 @@ describe("CodexAdapterV2 post-settle continuation", () => { | ... | @@ -8230,7 +8469,12 @@ describe("CodexAdapterV2 post-settle continuation", () => { |
| 8230 | "Goal paused. Send /goal resume to continue.", | 8469 | "Goal paused. Send /goal resume to continue.", |
| 8231 | ); | 8470 | ); |
| 8232 | assert.equal(providerGoals(harness.events).at(-1), "paused"); | 8471 | assert.equal(providerGoals(harness.events).at(-1), "paused"); |
| 8233 | }).pipe(Effect.scoped, Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 8472 | }).pipe( |
| 8473 | Effect.scoped, | ||
| 8474 | Effect.provide( | ||
| 8475 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 8476 | ), | ||
| 8477 | ), | ||
| 8234 | ); | 8478 | ); |
| 8235 | 8479 | ||
| 8236 | it.effect("settles a run held for the next goal turn when Stop arrives in between", () => | 8480 | it.effect("settles a run held for the next goal turn when Stop arrives in between", () => |
| ... | @@ -8321,7 +8565,12 @@ describe("CodexAdapterV2 post-settle continuation", () => { | ... | @@ -8321,7 +8565,12 @@ describe("CodexAdapterV2 post-settle continuation", () => { |
| 8321 | harness.terminalEvents().map((event) => event.status), | 8565 | harness.terminalEvents().map((event) => event.status), |
| 8322 | ["interrupted"], | 8566 | ["interrupted"], |
| 8323 | ); | 8567 | ); |
| 8324 | }).pipe(Effect.scoped, Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 8568 | }).pipe( |
| 8569 | Effect.scoped, | ||
| 8570 | Effect.provide( | ||
| 8571 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 8572 | ), | ||
| 8573 | ), | ||
| 8325 | ); | 8574 | ); |
| 8326 | 8575 | ||
| 8327 | it.effect("moves a Stop that names an earlier goal turn to the turn Codex continued with", () => | 8576 | it.effect("moves a Stop that names an earlier goal turn to the turn Codex continued with", () => |
| ... | @@ -8400,7 +8649,12 @@ describe("CodexAdapterV2 post-settle continuation", () => { | ... | @@ -8400,7 +8649,12 @@ describe("CodexAdapterV2 post-settle continuation", () => { |
| 8400 | harness.terminalEvents().map((event) => event.status), | 8649 | harness.terminalEvents().map((event) => event.status), |
| 8401 | ["interrupted"], | 8650 | ["interrupted"], |
| 8402 | ); | 8651 | ); |
| 8403 | }).pipe(Effect.scoped, Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 8652 | }).pipe( |
| 8653 | Effect.scoped, | ||
| 8654 | Effect.provide( | ||
| 8655 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 8656 | ), | ||
| 8657 | ), | ||
| 8404 | ); | 8658 | ); |
| 8405 | 8659 | ||
| 8406 | it.effect("stops a held goal run's background command when Stop arrives between turns", () => | 8660 | it.effect("stops a held goal run's background command when Stop arrives between turns", () => |
| ... | @@ -8502,7 +8756,12 @@ describe("CodexAdapterV2 post-settle continuation", () => { | ... | @@ -8502,7 +8756,12 @@ describe("CodexAdapterV2 post-settle continuation", () => { |
| 8502 | ), | 8756 | ), |
| 8503 | "the background command to be interrupted", | 8757 | "the background command to be interrupted", |
| 8504 | ); | 8758 | ); |
| 8505 | }).pipe(Effect.scoped, Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 8759 | }).pipe( |
| 8760 | Effect.scoped, | ||
| 8761 | Effect.provide( | ||
| 8762 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 8763 | ), | ||
| 8764 | ), | ||
| 8506 | ); | 8765 | ); |
| 8507 | 8766 | ||
| 8508 | it.effect("moves Stop to a continuation Codex starts while the goal pause is in flight", () => | 8767 | it.effect("moves Stop to a continuation Codex starts while the goal pause is in flight", () => |
| ... | @@ -8584,7 +8843,12 @@ describe("CodexAdapterV2 post-settle continuation", () => { | ... | @@ -8584,7 +8843,12 @@ describe("CodexAdapterV2 post-settle continuation", () => { |
| 8584 | harness.terminalEvents().map((event) => event.status), | 8843 | harness.terminalEvents().map((event) => event.status), |
| 8585 | ["interrupted"], | 8844 | ["interrupted"], |
| 8586 | ); | 8845 | ); |
| 8587 | }).pipe(Effect.scoped, Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 8846 | }).pipe( |
| 8847 | Effect.scoped, | ||
| 8848 | Effect.provide( | ||
| 8849 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 8850 | ), | ||
| 8851 | ), | ||
| 8588 | ); | 8852 | ); |
| 8589 | 8853 | ||
| 8590 | it.effect("stops a subagent an earlier goal turn left running when Stop settles the run", () => | 8854 | it.effect("stops a subagent an earlier goal turn left running when Stop settles the run", () => |
| ... | @@ -8681,7 +8945,12 @@ describe("CodexAdapterV2 post-settle continuation", () => { | ... | @@ -8681,7 +8945,12 @@ describe("CodexAdapterV2 post-settle continuation", () => { |
| 8681 | ), | 8945 | ), |
| 8682 | "the subagent turn to be interrupted", | 8946 | "the subagent turn to be interrupted", |
| 8683 | ); | 8947 | ); |
| 8684 | }).pipe(Effect.scoped, Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 8948 | }).pipe( |
| 8949 | Effect.scoped, | ||
| 8950 | Effect.provide( | ||
| 8951 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 8952 | ), | ||
| 8953 | ), | ||
| 8685 | ); | 8954 | ); |
| 8686 | 8955 | ||
| 8687 | it.effect("stops an earlier goal turn's subagent when the run settles during the pause", () => | 8956 | it.effect("stops an earlier goal turn's subagent when the run settles during the pause", () => |
| ... | @@ -8794,7 +9063,12 @@ describe("CodexAdapterV2 post-settle continuation", () => { | ... | @@ -8794,7 +9063,12 @@ describe("CodexAdapterV2 post-settle continuation", () => { |
| 8794 | ), | 9063 | ), |
| 8795 | "the subagent turn to be interrupted", | 9064 | "the subagent turn to be interrupted", |
| 8796 | ); | 9065 | ); |
| 8797 | }).pipe(Effect.scoped, Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 9066 | }).pipe( |
| 9067 | Effect.scoped, | ||
| 9068 | Effect.provide( | ||
| 9069 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 9070 | ), | ||
| 9071 | ), | ||
| 8798 | ); | 9072 | ); |
| 8799 | 9073 | ||
| 8800 | it.effect("pauses an active goal before Stop interrupts its turn", () => | 9074 | it.effect("pauses an active goal before Stop interrupts its turn", () => |
| ... | @@ -8858,7 +9132,12 @@ describe("CodexAdapterV2 post-settle continuation", () => { | ... | @@ -8858,7 +9132,12 @@ describe("CodexAdapterV2 post-settle continuation", () => { |
| 8858 | harness.terminalEvents().map((event) => event.status), | 9132 | harness.terminalEvents().map((event) => event.status), |
| 8859 | ["interrupted"], | 9133 | ["interrupted"], |
| 8860 | ); | 9134 | ); |
| 8861 | }).pipe(Effect.scoped, Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 9135 | }).pipe( |
| 9136 | Effect.scoped, | ||
| 9137 | Effect.provide( | ||
| 9138 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 9139 | ), | ||
| 9140 | ), | ||
| 8862 | ); | 9141 | ); |
| 8863 | }); | 9142 | }); |
| 8864 | }); | 9143 | }); |
apps/server/src/orchestration-v2/Adapters/CodexAdapterV2.ts+22-14| ... | @@ -108,6 +108,7 @@ import * as ProviderEventLoggers from "@t3tools/provider-core/server/ProviderEve | ... | @@ -108,6 +108,7 @@ import * as ProviderEventLoggers from "@t3tools/provider-core/server/ProviderEve |
| 108 | import { codexAppServerArgs, resolveCodexLaunchArgs } from "../../provider/codexLaunchArgs.ts"; | 108 | import { codexAppServerArgs, resolveCodexLaunchArgs } from "../../provider/codexLaunchArgs.ts"; |
| 109 | import { mergeProviderInstanceEnvironment } from "@t3tools/provider-core/server/instanceEnvironment"; | 109 | import { mergeProviderInstanceEnvironment } from "@t3tools/provider-core/server/instanceEnvironment"; |
| 110 | import * as McpProviderSession from "@t3tools/provider-core/server/mcpSession"; | 110 | import * as McpProviderSession from "@t3tools/provider-core/server/mcpSession"; |
| 111 | import * as McpProviderSessions from "@t3tools/provider-core/server/McpProviderSessions"; | ||
| 111 | import { | 112 | import { |
| 112 | MCP_APP_EXTENSION_ID, | 113 | MCP_APP_EXTENSION_ID, |
| 113 | MCP_APP_MIME_TYPE, | 114 | MCP_APP_MIME_TYPE, |
| ... | @@ -1296,7 +1297,7 @@ export class CodexAppServerClientFactory extends Context.Service< | ... | @@ -1296,7 +1297,7 @@ export class CodexAppServerClientFactory extends Context.Service< |
| 1296 | export const CODEX_THREAD_CONFIG = { "tools.update_plan.enabled": true } as const; | 1297 | export const CODEX_THREAD_CONFIG = { "tools.update_plan.enabled": true } as const; |
| 1297 | 1298 | ||
| 1298 | export function codexThreadRuntimeParams(input: { | 1299 | export function codexThreadRuntimeParams(input: { |
| 1299 | readonly threadId: ThreadId | null; | 1300 | readonly mcpSession: McpProviderSession.McpProviderSessionConfig | undefined; |
| 1300 | readonly modelSelection?: { readonly model: string }; | 1301 | readonly modelSelection?: { readonly model: string }; |
| 1301 | readonly runtimePolicy?: ProviderAdapter.ProviderAdapterV2RuntimePolicy; | 1302 | readonly runtimePolicy?: ProviderAdapter.ProviderAdapterV2RuntimePolicy; |
| 1302 | }): { | 1303 | }): { |
| ... | @@ -1304,8 +1305,7 @@ export function codexThreadRuntimeParams(input: { | ... | @@ -1304,8 +1305,7 @@ export function codexThreadRuntimeParams(input: { |
| 1304 | readonly model?: string; | 1305 | readonly model?: string; |
| 1305 | readonly config: Readonly<Record<string, Schema.Json>>; | 1306 | readonly config: Readonly<Record<string, Schema.Json>>; |
| 1306 | } { | 1307 | } { |
| 1307 | const mcpSession = | 1308 | const { mcpSession } = input; |
| 1308 | input.threadId === null ? undefined : McpProviderSession.readMcpProviderSession(input.threadId); | ||
| 1309 | return { | 1309 | return { |
| 1310 | ...(input.runtimePolicy?.cwd == null ? {} : { cwd: input.runtimePolicy.cwd }), | 1310 | ...(input.runtimePolicy?.cwd == null ? {} : { cwd: input.runtimePolicy.cwd }), |
| 1311 | ...(input.modelSelection === undefined ? {} : { model: input.modelSelection.model }), | 1311 | ...(input.modelSelection === undefined ? {} : { model: input.modelSelection.model }), |
| ... | @@ -1549,6 +1549,7 @@ export type CodexAdapterV2DriverEnv = | ... | @@ -1549,6 +1549,7 @@ export type CodexAdapterV2DriverEnv = |
| 1549 | | Crypto.Crypto | 1549 | | Crypto.Crypto |
| 1550 | | FileSystem.FileSystem | 1550 | | FileSystem.FileSystem |
| 1551 | | IdAllocator.IdAllocatorV2 | 1551 | | IdAllocator.IdAllocatorV2 |
| 1552 | | McpProviderSessions.McpProviderSessions | ||
| 1552 | | Path.Path | 1553 | | Path.Path |
| 1553 | | ServerConfig; | 1554 | | ServerConfig; |
| 1554 | 1555 | ||
| ... | @@ -1585,7 +1586,7 @@ export const createCodexAdapterV2 = ( | ... | @@ -1585,7 +1586,7 @@ export const createCodexAdapterV2 = ( |
| 1585 | homePath: homeLayout.effectiveHomePath ?? "", | 1586 | homePath: homeLayout.effectiveHomePath ?? "", |
| 1586 | } satisfies CodexSettings; | 1587 | } satisfies CodexSettings; |
| 1587 | 1588 | ||
| 1588 | return makeCodexAdapterV2({ | 1589 | return yield* makeCodexAdapterV2({ |
| 1589 | instanceId, | 1590 | instanceId, |
| 1590 | settings, | 1591 | settings, |
| 1591 | environment: mergeProviderInstanceEnvironment(environment, hostEnvironment), | 1592 | environment: mergeProviderInstanceEnvironment(environment, hostEnvironment), |
| ... | @@ -1613,6 +1614,7 @@ const layer: Layer.Layer< | ... | @@ -1613,6 +1614,7 @@ const layer: Layer.Layer< |
| 1613 | | Crypto.Crypto | 1614 | | Crypto.Crypto |
| 1614 | | FileSystem.FileSystem | 1615 | | FileSystem.FileSystem |
| 1615 | | IdAllocator.IdAllocatorV2 | 1616 | | IdAllocator.IdAllocatorV2 |
| 1617 | | McpProviderSessions.McpProviderSessions | ||
| 1616 | | ServerConfig | 1618 | | ServerConfig |
| 1617 | > = Layer.effect( | 1619 | > = Layer.effect( |
| 1618 | ProviderAdapter.ProviderAdapterV2, | 1620 | ProviderAdapter.ProviderAdapterV2, |
| ... | @@ -1625,7 +1627,7 @@ const layer: Layer.Layer< | ... | @@ -1625,7 +1627,7 @@ const layer: Layer.Layer< |
| 1625 | const idAllocator = yield* IdAllocator.IdAllocatorV2; | 1627 | const idAllocator = yield* IdAllocator.IdAllocatorV2; |
| 1626 | const serverConfig = yield* ServerConfig; | 1628 | const serverConfig = yield* ServerConfig; |
| 1627 | 1629 | ||
| 1628 | return makeCodexAdapterV2({ | 1630 | return yield* makeCodexAdapterV2({ |
| 1629 | instanceId: CODEX_DEFAULT_INSTANCE_ID, | 1631 | instanceId: CODEX_DEFAULT_INSTANCE_ID, |
| 1630 | settings: DEFAULT_CODEX_SETTINGS, | 1632 | settings: DEFAULT_CODEX_SETTINGS, |
| 1631 | environment: hostEnvironment, | 1633 | environment: hostEnvironment, |
| ... | @@ -1667,9 +1669,12 @@ export interface CodexAdapterV2Options { | ... | @@ -1667,9 +1669,12 @@ export interface CodexAdapterV2Options { |
| 1667 | }; | 1669 | }; |
| 1668 | } | 1670 | } |
| 1669 | 1671 | ||
| 1670 | export function makeCodexAdapterV2( | 1672 | export const makeCodexAdapterV2 = Effect.fn("makeCodexAdapterV2")(function* ( |
| 1671 | adapterOptions: CodexAdapterV2Options, | 1673 | adapterOptions: CodexAdapterV2Options, |
| 1672 | ): ProviderAdapter.ProviderAdapterV2["Service"] { | 1674 | ) { |
| 1675 | const mcpSessions = yield* McpProviderSessions.McpProviderSessions; | ||
| 1676 | const readMcpSession = (threadId: ThreadId | null) => | ||
| 1677 | threadId === null ? Effect.succeed(undefined) : mcpSessions.read(threadId); | ||
| 1673 | const { clientFactory, crypto, fileSystem, idAllocator, serverConfig } = adapterOptions; | 1678 | const { clientFactory, crypto, fileSystem, idAllocator, serverConfig } = adapterOptions; |
| 1674 | const continuationRequests = adapterOptions.continuationRequests; | 1679 | const continuationRequests = adapterOptions.continuationRequests; |
| 1675 | 1680 | ||
| ... | @@ -6166,7 +6171,7 @@ export function makeCodexAdapterV2( | ... | @@ -6166,7 +6171,7 @@ export function makeCodexAdapterV2( |
| 6166 | ) => | 6171 | ) => |
| 6167 | Effect.gen(function* () { | 6172 | Effect.gen(function* () { |
| 6168 | const threadId = yield* getNativeThreadId(turnInput.providerThread); | 6173 | const threadId = yield* getNativeThreadId(turnInput.providerThread); |
| 6169 | const mcpSession = McpProviderSession.readMcpProviderSession(turnInput.threadId); | 6174 | const mcpSession = yield* mcpSessions.read(turnInput.threadId); |
| 6170 | const turnStartParams = yield* buildCodexTurnStartParams({ | 6175 | const turnStartParams = yield* buildCodexTurnStartParams({ |
| 6171 | nativeThreadId: threadId, | 6176 | nativeThreadId: threadId, |
| 6172 | codexInput, | 6177 | codexInput, |
| ... | @@ -6445,11 +6450,12 @@ export function makeCodexAdapterV2( | ... | @@ -6445,11 +6450,12 @@ export function makeCodexAdapterV2( |
| 6445 | }), | 6450 | }), |
| 6446 | ensureThread: (threadInput) => | 6451 | ensureThread: (threadInput) => |
| 6447 | ensureInitialized.pipe( | 6452 | ensureInitialized.pipe( |
| 6448 | Effect.andThen( | 6453 | Effect.andThen(mcpSessions.read(threadInput.threadId)), |
| 6454 | Effect.flatMap((mcpSession) => | ||
| 6449 | client.request( | 6455 | client.request( |
| 6450 | "thread/start", | 6456 | "thread/start", |
| 6451 | codexThreadRuntimeParams({ | 6457 | codexThreadRuntimeParams({ |
| 6452 | threadId: threadInput.threadId, | 6458 | mcpSession, |
| 6453 | modelSelection: threadInput.modelSelection, | 6459 | modelSelection: threadInput.modelSelection, |
| 6454 | runtimePolicy: threadInput.runtimePolicy, | 6460 | runtimePolicy: threadInput.runtimePolicy, |
| 6455 | }), | 6461 | }), |
| ... | @@ -6482,7 +6488,9 @@ export function makeCodexAdapterV2( | ... | @@ -6482,7 +6488,9 @@ export function makeCodexAdapterV2( |
| 6482 | threadId: nativeThreadId, | 6488 | threadId: nativeThreadId, |
| 6483 | excludeTurns: true, | 6489 | excludeTurns: true, |
| 6484 | ...codexThreadRuntimeParams({ | 6490 | ...codexThreadRuntimeParams({ |
| 6485 | threadId: threadInput.threadId ?? threadInput.providerThread.appThreadId, | 6491 | mcpSession: yield* readMcpSession( |
| 6492 | threadInput.threadId ?? threadInput.providerThread.appThreadId, | ||
| 6493 | ), | ||
| 6486 | ...(threadInput.modelSelection === undefined | 6494 | ...(threadInput.modelSelection === undefined |
| 6487 | ? {} | 6495 | ? {} |
| 6488 | : { modelSelection: threadInput.modelSelection }), | 6496 | : { modelSelection: threadInput.modelSelection }), |
| ... | @@ -7210,7 +7218,7 @@ export function makeCodexAdapterV2( | ... | @@ -7210,7 +7218,7 @@ export function makeCodexAdapterV2( |
| 7210 | threadId, | 7218 | threadId, |
| 7211 | excludeTurns: true, | 7219 | excludeTurns: true, |
| 7212 | ...codexThreadRuntimeParams({ | 7220 | ...codexThreadRuntimeParams({ |
| 7213 | threadId: threadInput.providerThread.appThreadId, | 7221 | mcpSession: yield* readMcpSession(threadInput.providerThread.appThreadId), |
| 7214 | modelSelection: input.modelSelection, | 7222 | modelSelection: input.modelSelection, |
| 7215 | runtimePolicy: input.runtimePolicy, | 7223 | runtimePolicy: input.runtimePolicy, |
| 7216 | }), | 7224 | }), |
| ... | @@ -7259,7 +7267,7 @@ export function makeCodexAdapterV2( | ... | @@ -7259,7 +7267,7 @@ export function makeCodexAdapterV2( |
| 7259 | ? {} | 7267 | ? {} |
| 7260 | : { lastTurnId: boundary.lastTurnId }), | 7268 | : { lastTurnId: boundary.lastTurnId }), |
| 7261 | ...codexThreadRuntimeParams({ | 7269 | ...codexThreadRuntimeParams({ |
| 7262 | threadId: threadInput.targetThreadId, | 7270 | mcpSession: yield* mcpSessions.read(threadInput.targetThreadId), |
| 7263 | ...(threadInput.modelSelection === undefined | 7271 | ...(threadInput.modelSelection === undefined |
| 7264 | ? {} | 7272 | ? {} |
| 7265 | : { modelSelection: threadInput.modelSelection }), | 7273 | : { modelSelection: threadInput.modelSelection }), |
| ... | @@ -7344,4 +7352,4 @@ export function makeCodexAdapterV2( | ... | @@ -7344,4 +7352,4 @@ export function makeCodexAdapterV2( |
| 7344 | ), | 7352 | ), |
| 7345 | ), | 7353 | ), |
| 7346 | }); | 7354 | }); |
| 7347 | } | 7355 | }); |
apps/server/src/orchestration-v2/Adapters/CursorAdapterV2.testkit.ts+1| ... | @@ -719,6 +719,7 @@ export const recordCursorAgentSdkReplayTranscript = Effect.fn( | ... | @@ -719,6 +719,7 @@ export const recordCursorAgentSdkReplayTranscript = Effect.fn( |
| 719 | : { override: input.runtimePolicyOverride }), | 719 | : { override: input.runtimePolicyOverride }), |
| 720 | }), | 720 | }), |
| 721 | threadId, | 721 | threadId, |
| 722 | mcpSession: undefined, | ||
| 722 | }); | 723 | }); |
| 723 | const sendOptions = { | 724 | const sendOptions = { |
| 724 | model: cursorSdkModelSelection(input.modelSelection), | 725 | model: cursorSdkModelSelection(input.modelSelection), |
apps/server/src/orchestration-v2/Adapters/GrokAdapterV2.test.ts+7-1| ... | @@ -26,6 +26,7 @@ import { buildInitialGrokProviderSnapshot } from "@t3tools/provider-grok/testing | ... | @@ -26,6 +26,7 @@ import { buildInitialGrokProviderSnapshot } from "@t3tools/provider-grok/testing |
| 26 | import type { ProviderInstance } from "@t3tools/provider-core/server/driver"; | 26 | import type { ProviderInstance } from "@t3tools/provider-core/server/driver"; |
| 27 | import * as ProviderInstanceRegistry from "../../provider/ProviderInstanceRegistry.ts"; | 27 | import * as ProviderInstanceRegistry from "../../provider/ProviderInstanceRegistry.ts"; |
| 28 | import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator"; | 28 | import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator"; |
| 29 | import * as McpProviderSessions from "@t3tools/provider-core/server/McpProviderSessions"; | ||
| 29 | import * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter"; | 30 | import * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter"; |
| 30 | import * as RuntimePolicy from "../RuntimePolicy.ts"; | 31 | import * as RuntimePolicy from "../RuntimePolicy.ts"; |
| 31 | import { acpPermissionDisposition } from "@t3tools/provider-acp/server/clientPolicy"; | 32 | import { acpPermissionDisposition } from "@t3tools/provider-acp/server/clientPolicy"; |
| ... | @@ -309,7 +310,12 @@ describe("Grok permission prompts", () => { | ... | @@ -309,7 +310,12 @@ describe("Grok permission prompts", () => { |
| 309 | 310 | ||
| 310 | describe("Grok launch permission mode", () => { | 311 | describe("Grok launch permission mode", () => { |
| 311 | const layerHost = layerTestProviderHost().pipe(Layer.provide(NodeServices.layer)); | 312 | const layerHost = layerTestProviderHost().pipe(Layer.provide(NodeServices.layer)); |
| 312 | const layerTest = Layer.mergeAll(NodeServices.layer, IdAllocator.layer, layerHost); | 313 | const layerTest = Layer.mergeAll( |
| 314 | NodeServices.layer, | ||
| 315 | IdAllocator.layer, | ||
| 316 | McpProviderSessions.layer, | ||
| 317 | layerHost, | ||
| 318 | ); | ||
| 313 | 319 | ||
| 314 | // Opens a session through the adapter's own Grok runtime factory and returns | 320 | // Opens a session through the adapter's own Grok runtime factory and returns |
| 315 | // the argv it tried to launch. The spawn fails after recording, so no | 321 | // the argv it tried to launch. The spawn fails after recording, so no |
apps/server/src/orchestration-v2/Adapters/OpenCode2AdapterV2.test.ts+34-34| ... | @@ -39,7 +39,7 @@ import { TestClock } from "effect/testing"; | ... | @@ -39,7 +39,7 @@ import { TestClock } from "effect/testing"; |
| 39 | import { describe } from "vite-plus/test"; | 39 | import { describe } from "vite-plus/test"; |
| 40 | 40 | ||
| 41 | import type * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter"; | 41 | import type * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter"; |
| 42 | import * as McpProviderSession from "@t3tools/provider-core/server/mcpSession"; | 42 | import * as McpProviderSessions from "@t3tools/provider-core/server/McpProviderSessions"; |
| 43 | import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator"; | 43 | import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator"; |
| 44 | import * as ProviderContinuationRequests from "@t3tools/provider-core/server/ProviderContinuationRequests"; | 44 | import * as ProviderContinuationRequests from "@t3tools/provider-core/server/ProviderContinuationRequests"; |
| 45 | import { | 45 | import { |
| ... | @@ -403,7 +403,7 @@ const history = { | ... | @@ -403,7 +403,7 @@ const history = { |
| 403 | cursor: {}, | 403 | cursor: {}, |
| 404 | }; | 404 | }; |
| 405 | 405 | ||
| 406 | describe("OpenCode2 adapter", () => { | 406 | it.layer(McpProviderSessions.layer)("OpenCode2 adapter", (it) => { |
| 407 | it.effect("switches the session's model and variant before a turn that changed them", () => | 407 | it.effect("switches the session's model and variant before a turn that changed them", () => |
| 408 | Effect.gen(function* () { | 408 | Effect.gen(function* () { |
| 409 | const { runtime, thread } = yield* resumed([ | 409 | const { runtime, thread } = yield* resumed([ |
| ... | @@ -2486,29 +2486,6 @@ describe("OpenCode2 adapter", () => { | ... | @@ -2486,29 +2486,6 @@ describe("OpenCode2 adapter", () => { |
| 2486 | }).pipe(Effect.scoped), | 2486 | }).pipe(Effect.scoped), |
| 2487 | ); | 2487 | ); |
| 2488 | 2488 | ||
| 2489 | it.live("returns the server it reconnected to once the session closes", () => | ||
| 2490 | Effect.gen(function* () { | ||
| 2491 | // A spawned server stops after it has no borrowers for a while, so a | ||
| 2492 | // session must not keep holding the connection it reconnected with. | ||
| 2493 | const borrowers = { current: 0 }; | ||
| 2494 | yield* Effect.gen(function* () { | ||
| 2495 | yield* openCode2ReplayRuntime( | ||
| 2496 | [ | ||
| 2497 | ...opening, | ||
| 2498 | { type: "runtime_exit", status: "success" }, | ||
| 2499 | out("event.subscribe"), | ||
| 2500 | event("server.connected", {}), | ||
| 2501 | ], | ||
| 2502 | { borrowers }, | ||
| 2503 | ); | ||
| 2504 | // Reconnected: the dropped connection is returned and the new one is held. | ||
| 2505 | yield* Effect.sleep("200 millis"); | ||
| 2506 | assert.equal(borrowers.current, 1); | ||
| 2507 | }).pipe(Effect.scoped); | ||
| 2508 | assert.equal(borrowers.current, 0); | ||
| 2509 | }), | ||
| 2510 | ); | ||
| 2511 | |||
| 2512 | it.effect( | 2489 | it.effect( |
| 2513 | "backfills and ends a subagent's turn whose execution ended while the stream was down", | 2490 | "backfills and ends a subagent's turn whose execution ended while the stream was down", |
| 2514 | () => | 2491 | () => |
| ... | @@ -2697,7 +2674,8 @@ describe("OpenCode2 adapter", () => { | ... | @@ -2697,7 +2674,8 @@ describe("OpenCode2 adapter", () => { |
| 2697 | "registers T3's MCP server for the thread alone and removes it when the thread unloads", | 2674 | "registers T3's MCP server for the thread alone and removes it when the thread unloads", |
| 2698 | () => | 2675 | () => |
| 2699 | Effect.gen(function* () { | 2676 | Effect.gen(function* () { |
| 2700 | McpProviderSession.setMcpProviderSession({ | 2677 | const mcpSessions = yield* McpProviderSessions.McpProviderSessions; |
| 2678 | yield* mcpSessions.set({ | ||
| 2701 | environmentId: EnvironmentId.make("environment:opencode2-adapter"), | 2679 | environmentId: EnvironmentId.make("environment:opencode2-adapter"), |
| 2702 | threadId, | 2680 | threadId, |
| 2703 | providerSessionId: "mcp:opencode2-adapter", | 2681 | providerSessionId: "mcp:opencode2-adapter", |
| ... | @@ -2706,9 +2684,7 @@ describe("OpenCode2 adapter", () => { | ... | @@ -2706,9 +2684,7 @@ describe("OpenCode2 adapter", () => { |
| 2706 | authorizationHeader: "Bearer thread-credential", | 2684 | authorizationHeader: "Bearer thread-credential", |
| 2707 | browserToolsAvailable: false, | 2685 | browserToolsAvailable: false, |
| 2708 | }); | 2686 | }); |
| 2709 | yield* Effect.addFinalizer(() => | 2687 | yield* Effect.addFinalizer(() => mcpSessions.clear(threadId)); |
| 2710 | Effect.sync(() => McpProviderSession.clearMcpProviderSession(threadId)), | ||
| 2711 | ); | ||
| 2712 | const server = "t3-code-thread_opencode2-adapter"; | 2688 | const server = "t3-code-thread_opencode2-adapter"; |
| 2713 | const { runtime, thread } = yield* resumed([ | 2689 | const { runtime, thread } = yield* resumed([ |
| 2714 | // Registered for the session's directory under the thread's own name; | 2690 | // Registered for the session's directory under the thread's own name; |
| ... | @@ -2744,7 +2720,8 @@ describe("OpenCode2 adapter", () => { | ... | @@ -2744,7 +2720,8 @@ describe("OpenCode2 adapter", () => { |
| 2744 | "thread:delegated-task:command%3Amcp%3A48bef2bf-6d0e-4f7a-9c3b-2e5d8a1f7c40%3Adelegate-task%3Asubproject-b-round1", | 2720 | "thread:delegated-task:command%3Amcp%3A48bef2bf-6d0e-4f7a-9c3b-2e5d8a1f7c40%3Adelegate-task%3Asubproject-b-round1", |
| 2745 | ); | 2721 | ); |
| 2746 | const server = "t3-code-aa73fa1e03099934"; | 2722 | const server = "t3-code-aa73fa1e03099934"; |
| 2747 | McpProviderSession.setMcpProviderSession({ | 2723 | const mcpSessions = yield* McpProviderSessions.McpProviderSessions; |
| 2724 | yield* mcpSessions.set({ | ||
| 2748 | environmentId: EnvironmentId.make("environment:opencode2-adapter"), | 2725 | environmentId: EnvironmentId.make("environment:opencode2-adapter"), |
| 2749 | threadId: child, | 2726 | threadId: child, |
| 2750 | providerSessionId: "mcp:opencode2-adapter", | 2727 | providerSessionId: "mcp:opencode2-adapter", |
| ... | @@ -2753,9 +2730,7 @@ describe("OpenCode2 adapter", () => { | ... | @@ -2753,9 +2730,7 @@ describe("OpenCode2 adapter", () => { |
| 2753 | authorizationHeader: "Bearer thread-credential", | 2730 | authorizationHeader: "Bearer thread-credential", |
| 2754 | browserToolsAvailable: false, | 2731 | browserToolsAvailable: false, |
| 2755 | }); | 2732 | }); |
| 2756 | yield* Effect.addFinalizer(() => | 2733 | yield* Effect.addFinalizer(() => mcpSessions.clear(child)); |
| 2757 | Effect.sync(() => McpProviderSession.clearMcpProviderSession(child)), | ||
| 2758 | ); | ||
| 2759 | const runtime = yield* openCode2ReplayRuntimeWithInstructions([ | 2734 | const runtime = yield* openCode2ReplayRuntimeWithInstructions([ |
| 2760 | ...opening, | 2735 | ...opening, |
| 2761 | out("session.get", { sessionID: SESSION }), | 2736 | out("session.get", { sessionID: SESSION }), |
| ... | @@ -3992,6 +3967,31 @@ describe("OpenCode2 adapter", () => { | ... | @@ -3992,6 +3967,31 @@ describe("OpenCode2 adapter", () => { |
| 3992 | ); | 3967 | ); |
| 3993 | }); | 3968 | }); |
| 3994 | 3969 | ||
| 3970 | describe("OpenCode2 adapter server connection", () => { | ||
| 3971 | it.live("returns the server it reconnected to once the session closes", () => | ||
| 3972 | Effect.gen(function* () { | ||
| 3973 | // A spawned server stops after it has no borrowers for a while, so a | ||
| 3974 | // session must not keep holding the connection it reconnected with. | ||
| 3975 | const borrowers = { current: 0 }; | ||
| 3976 | yield* Effect.gen(function* () { | ||
| 3977 | yield* openCode2ReplayRuntime( | ||
| 3978 | [ | ||
| 3979 | ...opening, | ||
| 3980 | { type: "runtime_exit", status: "success" }, | ||
| 3981 | out("event.subscribe"), | ||
| 3982 | event("server.connected", {}), | ||
| 3983 | ], | ||
| 3984 | { borrowers }, | ||
| 3985 | ); | ||
| 3986 | // Reconnected: the dropped connection is returned and the new one is held. | ||
| 3987 | yield* Effect.sleep("200 millis"); | ||
| 3988 | assert.equal(borrowers.current, 1); | ||
| 3989 | }).pipe(Effect.scoped); | ||
| 3990 | assert.equal(borrowers.current, 0); | ||
| 3991 | }).pipe(Effect.provide(McpProviderSessions.layer)), | ||
| 3992 | ); | ||
| 3993 | }); | ||
| 3994 | |||
| 3995 | /** The provider turn the adapter derives for `turnInput`'s attempt. */ | 3995 | /** The provider turn the adapter derives for `turnInput`'s attempt. */ |
| 3996 | const providerTurnId = Effect.gen(function* () { | 3996 | const providerTurnId = Effect.gen(function* () { |
| 3997 | const ids = yield* IdAllocator.IdAllocatorV2; | 3997 | const ids = yield* IdAllocator.IdAllocatorV2; |
| ... | @@ -4001,7 +4001,7 @@ const providerTurnId = Effect.gen(function* () { | ... | @@ -4001,7 +4001,7 @@ const providerTurnId = Effect.gen(function* () { |
| 4001 | }); | 4001 | }); |
| 4002 | }).pipe(Effect.provide(IdAllocator.layer)); | 4002 | }).pipe(Effect.provide(IdAllocator.layer)); |
| 4003 | 4003 | ||
| 4004 | describe("OpenCode reported model variants", () => { | 4004 | it.layer(McpProviderSessions.layer)("OpenCode reported model variants", (it) => { |
| 4005 | it.effect( | 4005 | it.effect( |
| 4006 | "updates reported variants from selected-model and step events without duplicate updates", | 4006 | "updates reported variants from selected-model and step events without duplicate updates", |
| 4007 | () => | 4007 | () => |
apps/server/src/orchestration-v2/ClaudeAutomaticDelivery.integration.test.ts+7-2| ... | @@ -32,6 +32,7 @@ import * as ClaudeAdapterV2 from "./Adapters/ClaudeAdapterV2.ts"; | ... | @@ -32,6 +32,7 @@ import * as ClaudeAdapterV2 from "./Adapters/ClaudeAdapterV2.ts"; |
| 32 | import * as EffectWorker from "./EffectWorker.ts"; | 32 | import * as EffectWorker from "./EffectWorker.ts"; |
| 33 | import * as EventSink from "./EventSink.ts"; | 33 | import * as EventSink from "./EventSink.ts"; |
| 34 | import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator"; | 34 | import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator"; |
| 35 | import * as McpProviderSessions from "@t3tools/provider-core/server/McpProviderSessions"; | ||
| 35 | import * as LegacyV1ThreadImporter from "./legacy/LegacyV1ThreadImporter.ts"; | 36 | import * as LegacyV1ThreadImporter from "./legacy/LegacyV1ThreadImporter.ts"; |
| 36 | import * as Orchestrator from "./Orchestrator.ts"; | 37 | import * as Orchestrator from "./Orchestrator.ts"; |
| 37 | import * as ProviderAdapterRegistry from "./ProviderAdapterRegistry.ts"; | 38 | import * as ProviderAdapterRegistry from "./ProviderAdapterRegistry.ts"; |
| ... | @@ -131,7 +132,7 @@ it.effect.each(["child completion", "scheduled message", "user steering"] as con | ... | @@ -131,7 +132,7 @@ it.effect.each(["child completion", "scheduled message", "user steering"] as con |
| 131 | const offers: SDKUserMessage[] = []; | 132 | const offers: SDKUserMessage[] = []; |
| 132 | const nativeQueue: SDKUserMessage[] = []; | 133 | const nativeQueue: SDKUserMessage[] = []; |
| 133 | const batchAbort = new AbortController(); | 134 | const batchAbort = new AbortController(); |
| 134 | const adapter = ClaudeAdapterV2.makeClaudeAdapterV2({ | 135 | const adapter = yield* ClaudeAdapterV2.makeClaudeAdapterV2({ |
| 135 | instanceId: modelSelection.instanceId, | 136 | instanceId: modelSelection.instanceId, |
| 136 | settings, | 137 | settings, |
| 137 | environment: {}, | 138 | environment: {}, |
| ... | @@ -423,6 +424,10 @@ it.effect.each(["child completion", "scheduled message", "user steering"] as con | ... | @@ -423,6 +424,10 @@ it.effect.each(["child completion", "scheduled message", "user steering"] as con |
| 423 | ), | 424 | ), |
| 424 | ), | 425 | ), |
| 425 | ); | 426 | ); |
| 426 | }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), | 427 | }).pipe( |
| 428 | Effect.provide( | ||
| 429 | Layer.mergeAll(IdAllocator.layer, McpProviderSessions.layer, NodeServices.layer), | ||
| 430 | ), | ||
| 431 | ), | ||
| 427 | ), | 432 | ), |
| 428 | ); | 433 | ); |
apps/server/src/orchestration-v2/CursorOrchestratorV2.live.test.ts+2| ... | @@ -30,6 +30,7 @@ import * as ServerEnvironment from "../environment/ServerEnvironment.ts"; | ... | @@ -30,6 +30,7 @@ import * as ServerEnvironment from "../environment/ServerEnvironment.ts"; |
| 30 | import * as ModelManifest from "../provider/ModelManifest.ts"; | 30 | import * as ModelManifest from "../provider/ModelManifest.ts"; |
| 31 | import * as ProviderInstanceRegistryHydration from "../provider/ProviderInstanceRegistryHydration.ts"; | 31 | import * as ProviderInstanceRegistryHydration from "../provider/ProviderInstanceRegistryHydration.ts"; |
| 32 | import * as ProviderLatestVersions from "@t3tools/provider-core/server/ProviderLatestVersions"; | 32 | import * as ProviderLatestVersions from "@t3tools/provider-core/server/ProviderLatestVersions"; |
| 33 | import * as McpProviderSessions from "@t3tools/provider-core/server/McpProviderSessions"; | ||
| 33 | import * as ProviderEventLoggers from "@t3tools/provider-core/server/ProviderEventLoggers"; | 34 | import * as ProviderEventLoggers from "@t3tools/provider-core/server/ProviderEventLoggers"; |
| 34 | import * as OpenCodeRuntime from "@t3tools/provider-opencode/server/OpenCodeRuntime"; | 35 | import * as OpenCodeRuntime from "@t3tools/provider-opencode/server/OpenCodeRuntime"; |
| 35 | import * as OpenCodeServerLedger from "@t3tools/provider-opencode/server/OpenCodeServerLedger"; | 36 | import * as OpenCodeServerLedger from "@t3tools/provider-opencode/server/OpenCodeServerLedger"; |
| ... | @@ -124,6 +125,7 @@ const layerLive = RuntimeLayer.layer.pipe( | ... | @@ -124,6 +125,7 @@ const layerLive = RuntimeLayer.layer.pipe( |
| 124 | Layer.provide(ResetCreditCoordinator.layer), | 125 | Layer.provide(ResetCreditCoordinator.layer), |
| 125 | Layer.provide(layerBackgroundPolicy), | 126 | Layer.provide(layerBackgroundPolicy), |
| 126 | Layer.provide(layerPlatformTest), | 127 | Layer.provide(layerPlatformTest), |
| 128 | Layer.provide(McpProviderSessions.layer), | ||
| 127 | ); | 129 | ); |
| 128 | 130 | ||
| 129 | const waitForIdle = Effect.fn("CursorOrchestratorV2Live.waitForIdle")(function* ( | 131 | const waitForIdle = Effect.fn("CursorOrchestratorV2Live.waitForIdle")(function* ( |
apps/server/src/orchestration-v2/DelegatedCompletionDelivery.test.ts+2| ... | @@ -1,4 +1,5 @@ | ... | @@ -1,4 +1,5 @@ |
| 1 | import * as SourceControlProviderRegistry from "../sourceControl/SourceControlProviderRegistry.ts"; | 1 | import * as SourceControlProviderRegistry from "../sourceControl/SourceControlProviderRegistry.ts"; |
| 2 | import * as McpProviderSessions from "@t3tools/provider-core/server/McpProviderSessions"; | ||
| 2 | import * as NodeServices from "@effect/platform-node/NodeServices"; | 3 | import * as NodeServices from "@effect/platform-node/NodeServices"; |
| 3 | import { assert, it } from "@effect/vitest"; | 4 | import { assert, it } from "@effect/vitest"; |
| 4 | import { | 5 | import { |
| ... | @@ -130,6 +131,7 @@ const layerTest = Layer.mergeAll(RuntimeLayer.layer, RuntimeLayer.layerEventSink | ... | @@ -130,6 +131,7 @@ const layerTest = Layer.mergeAll(RuntimeLayer.layer, RuntimeLayer.layerEventSink |
| 130 | }), | 131 | }), |
| 131 | ), | 132 | ), |
| 132 | Layer.provide(McpSessionRegistryTestkit.layer), | 133 | Layer.provide(McpSessionRegistryTestkit.layer), |
| 134 | Layer.provide(McpProviderSessions.layer), | ||
| 133 | Layer.provide(SqlitePersistence.layerMemory), | 135 | Layer.provide(SqlitePersistence.layerMemory), |
| 134 | Layer.provide(layerCheckpointStoreTest), | 136 | Layer.provide(layerCheckpointStoreTest), |
| 135 | Layer.provide(layerServerConfig), | 137 | Layer.provide(layerServerConfig), |
apps/server/src/orchestration-v2/GrokOrchestratorV2.live.test.ts+2| ... | @@ -30,6 +30,7 @@ import * as ServerEnvironment from "../environment/ServerEnvironment.ts"; | ... | @@ -30,6 +30,7 @@ import * as ServerEnvironment from "../environment/ServerEnvironment.ts"; |
| 30 | import * as ModelManifest from "../provider/ModelManifest.ts"; | 30 | import * as ModelManifest from "../provider/ModelManifest.ts"; |
| 31 | import * as ProviderInstanceRegistryHydration from "../provider/ProviderInstanceRegistryHydration.ts"; | 31 | import * as ProviderInstanceRegistryHydration from "../provider/ProviderInstanceRegistryHydration.ts"; |
| 32 | import * as ProviderLatestVersions from "@t3tools/provider-core/server/ProviderLatestVersions"; | 32 | import * as ProviderLatestVersions from "@t3tools/provider-core/server/ProviderLatestVersions"; |
| 33 | import * as McpProviderSessions from "@t3tools/provider-core/server/McpProviderSessions"; | ||
| 33 | import * as ProviderEventLoggers from "@t3tools/provider-core/server/ProviderEventLoggers"; | 34 | import * as ProviderEventLoggers from "@t3tools/provider-core/server/ProviderEventLoggers"; |
| 34 | import * as OpenCodeRuntime from "@t3tools/provider-opencode/server/OpenCodeRuntime"; | 35 | import * as OpenCodeRuntime from "@t3tools/provider-opencode/server/OpenCodeRuntime"; |
| 35 | import * as OpenCodeServerLedger from "@t3tools/provider-opencode/server/OpenCodeServerLedger"; | 36 | import * as OpenCodeServerLedger from "@t3tools/provider-opencode/server/OpenCodeServerLedger"; |
| ... | @@ -123,6 +124,7 @@ const layerLive = RuntimeLayer.layer.pipe( | ... | @@ -123,6 +124,7 @@ const layerLive = RuntimeLayer.layer.pipe( |
| 123 | Layer.provide(ResetCreditCoordinator.layer), | 124 | Layer.provide(ResetCreditCoordinator.layer), |
| 124 | Layer.provide(layerBackgroundPolicy), | 125 | Layer.provide(layerBackgroundPolicy), |
| 125 | Layer.provide(layerPlatformTest), | 126 | Layer.provide(layerPlatformTest), |
| 127 | Layer.provide(McpProviderSessions.layer), | ||
| 126 | ); | 128 | ); |
| 127 | 129 | ||
| 128 | const waitForIdle = Effect.fn("GrokOrchestratorV2Live.waitForIdle")(function* (threadId: ThreadId) { | 130 | const waitForIdle = Effect.fn("GrokOrchestratorV2Live.waitForIdle")(function* (threadId: ThreadId) { |
apps/server/src/orchestration-v2/OpenCode2OrchestratorV2.live.test.ts+2| ... | @@ -55,6 +55,7 @@ import * as ServerEnvironment from "../environment/ServerEnvironment.ts"; | ... | @@ -55,6 +55,7 @@ import * as ServerEnvironment from "../environment/ServerEnvironment.ts"; |
| 55 | import * as ModelManifest from "../provider/ModelManifest.ts"; | 55 | import * as ModelManifest from "../provider/ModelManifest.ts"; |
| 56 | import * as ProviderInstanceRegistryHydration from "../provider/ProviderInstanceRegistryHydration.ts"; | 56 | import * as ProviderInstanceRegistryHydration from "../provider/ProviderInstanceRegistryHydration.ts"; |
| 57 | import * as ProviderLatestVersions from "@t3tools/provider-core/server/ProviderLatestVersions"; | 57 | import * as ProviderLatestVersions from "@t3tools/provider-core/server/ProviderLatestVersions"; |
| 58 | import * as McpProviderSessions from "@t3tools/provider-core/server/McpProviderSessions"; | ||
| 58 | import * as ProviderEventLoggers from "@t3tools/provider-core/server/ProviderEventLoggers"; | 59 | import * as ProviderEventLoggers from "@t3tools/provider-core/server/ProviderEventLoggers"; |
| 59 | import * as OpenCode2Client from "@t3tools/provider-opencode/server/v2/OpenCode2Client"; | 60 | import * as OpenCode2Client from "@t3tools/provider-opencode/server/v2/OpenCode2Client"; |
| 60 | import * as OpenCodeRuntime from "@t3tools/provider-opencode/server/OpenCodeRuntime"; | 61 | import * as OpenCodeRuntime from "@t3tools/provider-opencode/server/OpenCodeRuntime"; |
| ... | @@ -217,6 +218,7 @@ const layerOrchestration = RuntimeLayer.layer.pipe( | ... | @@ -217,6 +218,7 @@ const layerOrchestration = RuntimeLayer.layer.pipe( |
| 217 | Layer.provide(ResetCreditCoordinator.layer), | 218 | Layer.provide(ResetCreditCoordinator.layer), |
| 218 | Layer.provide(layerBackgroundPolicy), | 219 | Layer.provide(layerBackgroundPolicy), |
| 219 | Layer.provide(layerPlatformTest), | 220 | Layer.provide(layerPlatformTest), |
| 221 | Layer.provide(McpProviderSessions.layer), | ||
| 220 | ); | 222 | ); |
| 221 | 223 | ||
| 222 | // Starts the continuation run a provider wake asks for, as the production layer does. | 224 | // Starts the continuation run a provider wake asks for, as the production layer does. |
apps/server/src/orchestration-v2/ProjectSettingsUpgrade.integration.test.ts+2| ... | @@ -1,4 +1,5 @@ | ... | @@ -1,4 +1,5 @@ |
| 1 | import * as NodeServices from "@effect/platform-node/NodeServices"; | 1 | import * as NodeServices from "@effect/platform-node/NodeServices"; |
| 2 | import * as McpProviderSessions from "@t3tools/provider-core/server/McpProviderSessions"; | ||
| 2 | import { assert, it } from "@effect/vitest"; | 3 | import { assert, it } from "@effect/vitest"; |
| 3 | import { ProjectId } from "@t3tools/contracts"; | 4 | import { ProjectId } from "@t3tools/contracts"; |
| 4 | import * as Effect from "effect/Effect"; | 5 | import * as Effect from "effect/Effect"; |
| ... | @@ -152,6 +153,7 @@ const layerRuntime = (dbPath: string) => { | ... | @@ -152,6 +153,7 @@ const layerRuntime = (dbPath: string) => { |
| 152 | }), | 153 | }), |
| 153 | ), | 154 | ), |
| 154 | Layer.provide(McpSessionRegistryTestkit.layer), | 155 | Layer.provide(McpSessionRegistryTestkit.layer), |
| 156 | Layer.provide(McpProviderSessions.layer), | ||
| 155 | Layer.provideMerge(SqlitePersistence.layerFromPath(dbPath)), | 157 | Layer.provideMerge(SqlitePersistence.layerFromPath(dbPath)), |
| 156 | Layer.provide(layerCheckpointStore), | 158 | Layer.provide(layerCheckpointStore), |
| 157 | Layer.provide(layerServerConfig), | 159 | Layer.provide(layerServerConfig), |
apps/server/src/orchestration-v2/ProviderSessionManager.test.ts+44-32| ... | @@ -36,6 +36,7 @@ import { ProviderWorkspaceMissingError } from "../provider/Errors.ts"; | ... | @@ -36,6 +36,7 @@ import { ProviderWorkspaceMissingError } from "../provider/Errors.ts"; |
| 36 | import * as ServerEnvironment from "../environment/ServerEnvironment.ts"; | 36 | import * as ServerEnvironment from "../environment/ServerEnvironment.ts"; |
| 37 | import * as ProjectService from "../project/ProjectService.ts"; | 37 | import * as ProjectService from "../project/ProjectService.ts"; |
| 38 | import * as McpProviderSession from "@t3tools/provider-core/server/mcpSession"; | 38 | import * as McpProviderSession from "@t3tools/provider-core/server/mcpSession"; |
| 39 | import * as McpProviderSessions from "@t3tools/provider-core/server/McpProviderSessions"; | ||
| 39 | import * as McpSessionRegistry from "../mcp/McpSessionRegistry.ts"; | 40 | import * as McpSessionRegistry from "../mcp/McpSessionRegistry.ts"; |
| 40 | import * as SqlitePersistence from "../persistence/Sqlite.ts"; | 41 | import * as SqlitePersistence from "../persistence/Sqlite.ts"; |
| 41 | import * as ServerSettings from "../serverSettings.ts"; | 42 | import * as ServerSettings from "../serverSettings.ts"; |
| ... | @@ -302,7 +303,7 @@ function unimplemented(detail: string) { | ... | @@ -302,7 +303,7 @@ function unimplemented(detail: string) { |
| 302 | ); | 303 | ); |
| 303 | } | 304 | } |
| 304 | 305 | ||
| 305 | function makeProviderAdapter( | 306 | const makeProviderAdapter = Effect.fnUntraced(function* ( |
| 306 | state: Ref.Ref<TestProviderRuntimeState>, | 307 | state: Ref.Ref<TestProviderRuntimeState>, |
| 307 | options: { | 308 | options: { |
| 308 | readonly failEventStream?: boolean; | 309 | readonly failEventStream?: boolean; |
| ... | @@ -324,7 +325,8 @@ function makeProviderAdapter( | ... | @@ -324,7 +325,8 @@ function makeProviderAdapter( |
| 324 | /** Completed when a hanging scope close reaches its wedged finalizer. */ | 325 | /** Completed when a hanging scope close reaches its wedged finalizer. */ |
| 325 | readonly scopeCloseReached?: Deferred.Deferred<void>; | 326 | readonly scopeCloseReached?: Deferred.Deferred<void>; |
| 326 | } = {}, | 327 | } = {}, |
| 327 | ): ProviderAdapter.ProviderAdapterV2["Service"] { | 328 | ) { |
| 329 | const mcpSessions = yield* McpProviderSessions.McpProviderSessions; | ||
| 328 | const countClose = Effect.addFinalizer(() => | 330 | const countClose = Effect.addFinalizer(() => |
| 329 | Ref.update(state, (current) => ({ | 331 | Ref.update(state, (current) => ({ |
| 330 | ...current, | 332 | ...current, |
| ... | @@ -340,7 +342,7 @@ function makeProviderAdapter( | ... | @@ -340,7 +342,7 @@ function makeProviderAdapter( |
| 340 | : Deferred.succeed(options.scopeCloseReached, undefined) | 342 | : Deferred.succeed(options.scopeCloseReached, undefined) |
| 341 | ).pipe(Effect.andThen(Effect.never)), | 343 | ).pipe(Effect.andThen(Effect.never)), |
| 342 | ); | 344 | ); |
| 343 | return { | 345 | return ProviderAdapter.ProviderAdapterV2.of({ |
| 344 | instanceId: ProviderInstanceId.make("codex"), | 346 | instanceId: ProviderInstanceId.make("codex"), |
| 345 | driver: CODEX_DRIVER, | 347 | driver: CODEX_DRIVER, |
| 346 | getCapabilities: () => Effect.succeed(options.capabilities ?? CodexCapabilities), | 348 | getCapabilities: () => Effect.succeed(options.capabilities ?? CodexCapabilities), |
| ... | @@ -352,19 +354,15 @@ function makeProviderAdapter( | ... | @@ -352,19 +354,15 @@ function makeProviderAdapter( |
| 352 | if (options.hangSessionScopeClose === true) yield* hangClose; | 354 | if (options.hangSessionScopeClose === true) yield* hangClose; |
| 353 | } | 355 | } |
| 354 | if (options.mcpConfigs !== undefined && options.spawnBeforeOpen === true) { | 356 | if (options.mcpConfigs !== undefined && options.spawnBeforeOpen === true) { |
| 355 | yield* Ref.update(options.mcpConfigs, (configs) => [ | 357 | const mcpConfig = yield* mcpSessions.read(input.threadId); |
| 356 | ...configs, | 358 | yield* Ref.update(options.mcpConfigs, (configs) => [...configs, mcpConfig]); |
| 357 | McpProviderSession.readMcpProviderSession(input.threadId), | ||
| 358 | ]); | ||
| 359 | } | 359 | } |
| 360 | if (options.beforeOpen !== undefined) { | 360 | if (options.beforeOpen !== undefined) { |
| 361 | yield* options.beforeOpen(input); | 361 | yield* options.beforeOpen(input); |
| 362 | } | 362 | } |
| 363 | if (options.mcpConfigs !== undefined && options.spawnBeforeOpen !== true) { | 363 | if (options.mcpConfigs !== undefined && options.spawnBeforeOpen !== true) { |
| 364 | yield* Ref.update(options.mcpConfigs, (configs) => [ | 364 | const mcpConfig = yield* mcpSessions.read(input.threadId); |
| 365 | ...configs, | 365 | yield* Ref.update(options.mcpConfigs, (configs) => [...configs, mcpConfig]); |
| 366 | McpProviderSession.readMcpProviderSession(input.threadId), | ||
| 367 | ]); | ||
| 368 | } | 366 | } |
| 369 | const now = yield* DateTime.now; | 367 | const now = yield* DateTime.now; |
| 370 | const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event, Cause.Done>(); | 368 | const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event, Cause.Done>(); |
| ... | @@ -440,8 +438,8 @@ function makeProviderAdapter( | ... | @@ -440,8 +438,8 @@ function makeProviderAdapter( |
| 440 | forkThread: () => unimplemented("forkThread unused in test"), | 438 | forkThread: () => unimplemented("forkThread unused in test"), |
| 441 | } satisfies ProviderAdapter.ProviderAdapterV2SessionRuntime; | 439 | } satisfies ProviderAdapter.ProviderAdapterV2SessionRuntime; |
| 442 | }), | 440 | }), |
| 443 | }; | 441 | }); |
| 444 | } | 442 | }); |
| 445 | 443 | ||
| 446 | function layerTest(input: { | 444 | function layerTest(input: { |
| 447 | readonly state: Ref.Ref<TestProviderRuntimeState>; | 445 | readonly state: Ref.Ref<TestProviderRuntimeState>; |
| ... | @@ -482,7 +480,7 @@ function layerTest(input: { | ... | @@ -482,7 +480,7 @@ function layerTest(input: { |
| 482 | : input.failReleaseEventWrites | 480 | : input.failReleaseEventWrites |
| 483 | ? layerFailingReleaseEventSink | 481 | ? layerFailingReleaseEventSink |
| 484 | : layerTestEventSink; | 482 | : layerTestEventSink; |
| 485 | const layerRegistry = ProviderAdapterRegistry.layerSingle( | 483 | const layerRegistry = Layer.unwrap( |
| 486 | makeProviderAdapter(input.state, { | 484 | makeProviderAdapter(input.state, { |
| 487 | failEventStream: input.failEventStream ?? false, | 485 | failEventStream: input.failEventStream ?? false, |
| 488 | ...(input.capabilities === undefined ? {} : { capabilities: input.capabilities }), | 486 | ...(input.capabilities === undefined ? {} : { capabilities: input.capabilities }), |
| ... | @@ -503,7 +501,7 @@ function layerTest(input: { | ... | @@ -503,7 +501,7 @@ function layerTest(input: { |
| 503 | ...(input.scopeCloseReached === undefined | 501 | ...(input.scopeCloseReached === undefined |
| 504 | ? {} | 502 | ? {} |
| 505 | : { scopeCloseReached: input.scopeCloseReached }), | 503 | : { scopeCloseReached: input.scopeCloseReached }), |
| 506 | }), | 504 | }).pipe(Effect.map(ProviderAdapterRegistry.layerSingle)), |
| 507 | ); | 505 | ); |
| 508 | const layerConfiguredMcpRegistry = | 506 | const layerConfiguredMcpRegistry = |
| 509 | input.pauseResolve === undefined | 507 | input.pauseResolve === undefined |
| ... | @@ -524,13 +522,14 @@ function layerTest(input: { | ... | @@ -524,13 +522,14 @@ function layerTest(input: { |
| 524 | layerConfiguredEventSink, | 522 | layerConfiguredEventSink, |
| 525 | IdAllocator.layer, | 523 | IdAllocator.layer, |
| 526 | layerConfiguredMcpRegistry, | 524 | layerConfiguredMcpRegistry, |
| 525 | McpProviderSessions.layer, | ||
| 527 | ProviderSessionManager.layerWithOptions({ | 526 | ProviderSessionManager.layerWithOptions({ |
| 528 | idleTimeoutMs: input.idleTimeoutMs, | 527 | idleTimeoutMs: input.idleTimeoutMs, |
| 529 | ...(input.maxIdlePinMs === undefined ? {} : { maxIdlePinMs: input.maxIdlePinMs }), | 528 | ...(input.maxIdlePinMs === undefined ? {} : { maxIdlePinMs: input.maxIdlePinMs }), |
| 530 | }).pipe( | 529 | }).pipe( |
| 531 | Layer.provide( | 530 | Layer.provide( |
| 532 | Layer.mergeAll( | 531 | Layer.mergeAll( |
| 533 | layerRegistry, | 532 | layerRegistry.pipe(Layer.provideMerge(McpProviderSessions.layer)), |
| 534 | layerConfiguredEventSink, | 533 | layerConfiguredEventSink, |
| 535 | IdAllocator.layer, | 534 | IdAllocator.layer, |
| 536 | layerProviderEventIngestorTest, | 535 | layerProviderEventIngestorTest, |
| ... | @@ -1106,6 +1105,7 @@ it.effect("ProviderSessionManagerV2 cleans up an open interrupted mid-handshake" | ... | @@ -1106,6 +1105,7 @@ it.effect("ProviderSessionManagerV2 cleans up an open interrupted mid-handshake" |
| 1106 | const idAllocator = yield* IdAllocator.IdAllocatorV2; | 1105 | const idAllocator = yield* IdAllocator.IdAllocatorV2; |
| 1107 | const manager = yield* ProviderSessionManager.ProviderSessionManagerV2; | 1106 | const manager = yield* ProviderSessionManager.ProviderSessionManagerV2; |
| 1108 | const registry = yield* McpSessionRegistry.McpSessionRegistry; | 1107 | const registry = yield* McpSessionRegistry.McpSessionRegistry; |
| 1108 | const mcpSessions = yield* McpProviderSessions.McpProviderSessions; | ||
| 1109 | const projectionStore = yield* ProjectionStore.ProjectionStoreV2; | 1109 | const projectionStore = yield* ProjectionStore.ProjectionStoreV2; |
| 1110 | const now = yield* DateTime.now; | 1110 | const now = yield* DateTime.now; |
| 1111 | const threadId = ThreadId.make("thread-provider-session-manager-interrupted-open"); | 1111 | const threadId = ThreadId.make("thread-provider-session-manager-interrupted-open"); |
| ... | @@ -1133,7 +1133,7 @@ it.effect("ProviderSessionManagerV2 cleans up an open interrupted mid-handshake" | ... | @@ -1133,7 +1133,7 @@ it.effect("ProviderSessionManagerV2 cleans up an open interrupted mid-handshake" |
| 1133 | // for it revoked. | 1133 | // for it revoked. |
| 1134 | assert.equal((yield* Ref.get(state)).closeCount, 1); | 1134 | assert.equal((yield* Ref.get(state)).closeCount, 1); |
| 1135 | assert.isUndefined(yield* registry.resolve(token!)); | 1135 | assert.isUndefined(yield* registry.resolve(token!)); |
| 1136 | assert.isUndefined(McpProviderSession.readMcpProviderSession(threadId)); | 1136 | assert.isUndefined(yield* mcpSessions.read(threadId)); |
| 1137 | assert.isTrue(Option.isNone(yield* manager.get(providerSessionId))); | 1137 | assert.isTrue(Option.isNone(yield* manager.get(providerSessionId))); |
| 1138 | 1138 | ||
| 1139 | // Nothing of the interrupted open is left behind: the next open starts a | 1139 | // Nothing of the interrupted open is left behind: the next open starts a |
| ... | @@ -1188,6 +1188,7 @@ it.effect("ProviderSessionManagerV2 cleans up an interrupted open whose scope cl | ... | @@ -1188,6 +1188,7 @@ it.effect("ProviderSessionManagerV2 cleans up an interrupted open whose scope cl |
| 1188 | const idAllocator = yield* IdAllocator.IdAllocatorV2; | 1188 | const idAllocator = yield* IdAllocator.IdAllocatorV2; |
| 1189 | const manager = yield* ProviderSessionManager.ProviderSessionManagerV2; | 1189 | const manager = yield* ProviderSessionManager.ProviderSessionManagerV2; |
| 1190 | const registry = yield* McpSessionRegistry.McpSessionRegistry; | 1190 | const registry = yield* McpSessionRegistry.McpSessionRegistry; |
| 1191 | const mcpSessions = yield* McpProviderSessions.McpProviderSessions; | ||
| 1191 | const now = yield* DateTime.now; | 1192 | const now = yield* DateTime.now; |
| 1192 | const threadId = ThreadId.make("thread-provider-session-manager-interrupted-hung-open"); | 1193 | const threadId = ThreadId.make("thread-provider-session-manager-interrupted-hung-open"); |
| 1193 | const providerSessionId = yield* idAllocator.allocate.providerSession({ | 1194 | const providerSessionId = yield* idAllocator.allocate.providerSession({ |
| ... | @@ -1212,7 +1213,7 @@ it.effect("ProviderSessionManagerV2 cleans up an interrupted open whose scope cl | ... | @@ -1212,7 +1213,7 @@ it.effect("ProviderSessionManagerV2 cleans up an interrupted open whose scope cl |
| 1212 | 1213 | ||
| 1213 | // The session cleanup already ran, ahead of the stuck close. | 1214 | // The session cleanup already ran, ahead of the stuck close. |
| 1214 | assert.isUndefined(yield* registry.resolve(token!)); | 1215 | assert.isUndefined(yield* registry.resolve(token!)); |
| 1215 | assert.isUndefined(McpProviderSession.readMcpProviderSession(threadId)); | 1216 | assert.isUndefined(yield* mcpSessions.read(threadId)); |
| 1216 | assert.equal((yield* Ref.get(state)).closeCount, 0); | 1217 | assert.equal((yield* Ref.get(state)).closeCount, 0); |
| 1217 | 1218 | ||
| 1218 | // The close is time-boxed, so the interrupter returns and the session's | 1219 | // The close is time-boxed, so the interrupter returns and the session's |
| ... | @@ -1750,6 +1751,7 @@ it.effect( | ... | @@ -1750,6 +1751,7 @@ it.effect( |
| 1750 | const idAllocator = yield* IdAllocator.IdAllocatorV2; | 1751 | const idAllocator = yield* IdAllocator.IdAllocatorV2; |
| 1751 | const manager = yield* ProviderSessionManager.ProviderSessionManagerV2; | 1752 | const manager = yield* ProviderSessionManager.ProviderSessionManagerV2; |
| 1752 | const registry = yield* McpSessionRegistry.McpSessionRegistry; | 1753 | const registry = yield* McpSessionRegistry.McpSessionRegistry; |
| 1754 | const mcpSessions = yield* McpProviderSessions.McpProviderSessions; | ||
| 1753 | const now = yield* DateTime.now; | 1755 | const now = yield* DateTime.now; |
| 1754 | const threadId = ThreadId.make("thread-provider-session-manager-mcp"); | 1756 | const threadId = ThreadId.make("thread-provider-session-manager-mcp"); |
| 1755 | const providerSessionId = yield* idAllocator.allocate.providerSession({ | 1757 | const providerSessionId = yield* idAllocator.allocate.providerSession({ |
| ... | @@ -1782,7 +1784,7 @@ it.effect( | ... | @@ -1782,7 +1784,7 @@ it.effect( |
| 1782 | ); | 1784 | ); |
| 1783 | 1785 | ||
| 1784 | yield* manager.close(providerSessionId); | 1786 | yield* manager.close(providerSessionId); |
| 1785 | assert.isUndefined(McpProviderSession.readMcpProviderSession(threadId)); | 1787 | assert.isUndefined(yield* mcpSessions.read(threadId)); |
| 1786 | assert.isUndefined(yield* registry.resolve(token!)); | 1788 | assert.isUndefined(yield* registry.resolve(token!)); |
| 1787 | }); | 1789 | }); |
| 1788 | 1790 | ||
| ... | @@ -1915,6 +1917,7 @@ it.effect("ProviderSessionManagerV2 revokes MCP credentials when release persist | ... | @@ -1915,6 +1917,7 @@ it.effect("ProviderSessionManagerV2 revokes MCP credentials when release persist |
| 1915 | const idAllocator = yield* IdAllocator.IdAllocatorV2; | 1917 | const idAllocator = yield* IdAllocator.IdAllocatorV2; |
| 1916 | const manager = yield* ProviderSessionManager.ProviderSessionManagerV2; | 1918 | const manager = yield* ProviderSessionManager.ProviderSessionManagerV2; |
| 1917 | const registry = yield* McpSessionRegistry.McpSessionRegistry; | 1919 | const registry = yield* McpSessionRegistry.McpSessionRegistry; |
| 1920 | const mcpSessions = yield* McpProviderSessions.McpProviderSessions; | ||
| 1918 | const now = yield* DateTime.now; | 1921 | const now = yield* DateTime.now; |
| 1919 | const threadId = ThreadId.make("thread-provider-session-manager-mcp-release-failure"); | 1922 | const threadId = ThreadId.make("thread-provider-session-manager-mcp-release-failure"); |
| 1920 | const providerSessionId = yield* idAllocator.allocate.providerSession({ | 1923 | const providerSessionId = yield* idAllocator.allocate.providerSession({ |
| ... | @@ -1939,7 +1942,7 @@ it.effect("ProviderSessionManagerV2 revokes MCP credentials when release persist | ... | @@ -1939,7 +1942,7 @@ it.effect("ProviderSessionManagerV2 revokes MCP credentials when release persist |
| 1939 | 1942 | ||
| 1940 | const closeError = yield* manager.close(providerSessionId).pipe(Effect.flip); | 1943 | const closeError = yield* manager.close(providerSessionId).pipe(Effect.flip); |
| 1941 | assert.equal(closeError._tag, "ProviderSessionCloseError"); | 1944 | assert.equal(closeError._tag, "ProviderSessionCloseError"); |
| 1942 | assert.isUndefined(McpProviderSession.readMcpProviderSession(threadId)); | 1945 | assert.isUndefined(yield* mcpSessions.read(threadId)); |
| 1943 | assert.isUndefined(yield* registry.resolve(token!)); | 1946 | assert.isUndefined(yield* registry.resolve(token!)); |
| 1944 | }); | 1947 | }); |
| 1945 | 1948 | ||
| ... | @@ -1968,6 +1971,7 @@ it.effect( | ... | @@ -1968,6 +1971,7 @@ it.effect( |
| 1968 | const idAllocator = yield* IdAllocator.IdAllocatorV2; | 1971 | const idAllocator = yield* IdAllocator.IdAllocatorV2; |
| 1969 | const manager = yield* ProviderSessionManager.ProviderSessionManagerV2; | 1972 | const manager = yield* ProviderSessionManager.ProviderSessionManagerV2; |
| 1970 | const registry = yield* McpSessionRegistry.McpSessionRegistry; | 1973 | const registry = yield* McpSessionRegistry.McpSessionRegistry; |
| 1974 | const mcpSessions = yield* McpProviderSessions.McpProviderSessions; | ||
| 1971 | const now = yield* DateTime.now; | 1975 | const now = yield* DateTime.now; |
| 1972 | const owner = ThreadId.make("thread-provider-session-manager-attach-race-owner"); | 1976 | const owner = ThreadId.make("thread-provider-session-manager-attach-race-owner"); |
| 1973 | const threadId = ThreadId.make("thread-provider-session-manager-attach-race"); | 1977 | const threadId = ThreadId.make("thread-provider-session-manager-attach-race"); |
| ... | @@ -2007,7 +2011,7 @@ it.effect( | ... | @@ -2007,7 +2011,7 @@ it.effect( |
| 2007 | yield* Fiber.join(second); | 2011 | yield* Fiber.join(second); |
| 2008 | 2012 | ||
| 2009 | // The second attach owns the thread's attachment and credential. | 2013 | // The second attach owns the thread's attachment and credential. |
| 2010 | const config = McpProviderSession.readMcpProviderSession(threadId); | 2014 | const config = yield* mcpSessions.read(threadId); |
| 2011 | assert.isDefined(config); | 2015 | assert.isDefined(config); |
| 2012 | const token = config!.authorizationHeader.replace(/^Bearer\s+/, ""); | 2016 | const token = config!.authorizationHeader.replace(/^Bearer\s+/, ""); |
| 2013 | assert.equal((yield* registry.resolve(token))?.thread.threadId, threadId); | 2017 | assert.equal((yield* registry.resolve(token))?.thread.threadId, threadId); |
| ... | @@ -2031,6 +2035,7 @@ it.effect( | ... | @@ -2031,6 +2035,7 @@ it.effect( |
| 2031 | const idAllocator = yield* IdAllocator.IdAllocatorV2; | 2035 | const idAllocator = yield* IdAllocator.IdAllocatorV2; |
| 2032 | const manager = yield* ProviderSessionManager.ProviderSessionManagerV2; | 2036 | const manager = yield* ProviderSessionManager.ProviderSessionManagerV2; |
| 2033 | const registry = yield* McpSessionRegistry.McpSessionRegistry; | 2037 | const registry = yield* McpSessionRegistry.McpSessionRegistry; |
| 2038 | const mcpSessions = yield* McpProviderSessions.McpProviderSessions; | ||
| 2034 | const eventStore = yield* EventStore.EventStoreV2; | 2039 | const eventStore = yield* EventStore.EventStoreV2; |
| 2035 | const now = yield* DateTime.now; | 2040 | const now = yield* DateTime.now; |
| 2036 | const owner = ThreadId.make("thread-provider-session-manager-stale-attach-owner"); | 2041 | const owner = ThreadId.make("thread-provider-session-manager-stale-attach-owner"); |
| ... | @@ -2075,14 +2080,14 @@ it.effect( | ... | @@ -2075,14 +2080,14 @@ it.effect( |
| 2075 | modelSelection, | 2080 | modelSelection, |
| 2076 | runtimePolicy, | 2081 | runtimePolicy, |
| 2077 | }); | 2082 | }); |
| 2078 | const config = McpProviderSession.readMcpProviderSession(threadId); | 2083 | const config = yield* mcpSessions.read(threadId); |
| 2079 | assert.isDefined(config); | 2084 | assert.isDefined(config); |
| 2080 | 2085 | ||
| 2081 | yield* Fiber.interrupt(stale); | 2086 | yield* Fiber.interrupt(stale); |
| 2082 | 2087 | ||
| 2083 | // The replacement keeps the thread and its credential. | 2088 | // The replacement keeps the thread and its credential. |
| 2084 | assert.equal( | 2089 | assert.equal( |
| 2085 | McpProviderSession.readMcpProviderSession(threadId)?.providerSessionId, | 2090 | (yield* mcpSessions.read(threadId))?.providerSessionId, |
| 2086 | config!.providerSessionId, | 2091 | config!.providerSessionId, |
| 2087 | ); | 2092 | ); |
| 2088 | const token = config!.authorizationHeader.replace(/^Bearer\s+/, ""); | 2093 | const token = config!.authorizationHeader.replace(/^Bearer\s+/, ""); |
| ... | @@ -2126,6 +2131,7 @@ it.effect( | ... | @@ -2126,6 +2131,7 @@ it.effect( |
| 2126 | const idAllocator = yield* IdAllocator.IdAllocatorV2; | 2131 | const idAllocator = yield* IdAllocator.IdAllocatorV2; |
| 2127 | const manager = yield* ProviderSessionManager.ProviderSessionManagerV2; | 2132 | const manager = yield* ProviderSessionManager.ProviderSessionManagerV2; |
| 2128 | const registry = yield* McpSessionRegistry.McpSessionRegistry; | 2133 | const registry = yield* McpSessionRegistry.McpSessionRegistry; |
| 2134 | const mcpSessions = yield* McpProviderSessions.McpProviderSessions; | ||
| 2129 | const now = yield* DateTime.now; | 2135 | const now = yield* DateTime.now; |
| 2130 | const owner = ThreadId.make("thread-provider-session-manager-resolve-stop-owner"); | 2136 | const owner = ThreadId.make("thread-provider-session-manager-resolve-stop-owner"); |
| 2131 | const threadId = ThreadId.make("thread-provider-session-manager-resolve-stop"); | 2137 | const threadId = ThreadId.make("thread-provider-session-manager-resolve-stop"); |
| ... | @@ -2157,7 +2163,7 @@ it.effect( | ... | @@ -2157,7 +2163,7 @@ it.effect( |
| 2157 | // The thread gets a credential, then detaches and keeps it for a re-attach. | 2163 | // The thread gets a credential, then detaches and keeps it for a re-attach. |
| 2158 | yield* resume; | 2164 | yield* resume; |
| 2159 | yield* manager.detach({ providerSessionId, threadId }); | 2165 | yield* manager.detach({ providerSessionId, threadId }); |
| 2160 | const config = McpProviderSession.readMcpProviderSession(threadId); | 2166 | const config = yield* mcpSessions.read(threadId); |
| 2161 | assert.isDefined(config); | 2167 | assert.isDefined(config); |
| 2162 | const token = config!.authorizationHeader.replace(/^Bearer\s+/, ""); | 2168 | const token = config!.authorizationHeader.replace(/^Bearer\s+/, ""); |
| 2163 | 2169 | ||
| ... | @@ -2189,6 +2195,7 @@ it.effect("ProviderSessionManagerV2 duplicate detach preserves replacement MCP c | ... | @@ -2189,6 +2195,7 @@ it.effect("ProviderSessionManagerV2 duplicate detach preserves replacement MCP c |
| 2189 | const idAllocator = yield* IdAllocator.IdAllocatorV2; | 2195 | const idAllocator = yield* IdAllocator.IdAllocatorV2; |
| 2190 | const manager = yield* ProviderSessionManager.ProviderSessionManagerV2; | 2196 | const manager = yield* ProviderSessionManager.ProviderSessionManagerV2; |
| 2191 | const registry = yield* McpSessionRegistry.McpSessionRegistry; | 2197 | const registry = yield* McpSessionRegistry.McpSessionRegistry; |
| 2198 | const mcpSessions = yield* McpProviderSessions.McpProviderSessions; | ||
| 2192 | const now = yield* DateTime.now; | 2199 | const now = yield* DateTime.now; |
| 2193 | const threadId = ThreadId.make("thread-provider-session-manager-replacement-mcp"); | 2200 | const threadId = ThreadId.make("thread-provider-session-manager-replacement-mcp"); |
| 2194 | const oldSessionId = yield* idAllocator.allocate.providerSession({ | 2201 | const oldSessionId = yield* idAllocator.allocate.providerSession({ |
| ... | @@ -2222,14 +2229,14 @@ it.effect("ProviderSessionManagerV2 duplicate detach preserves replacement MCP c | ... | @@ -2222,14 +2229,14 @@ it.effect("ProviderSessionManagerV2 duplicate detach preserves replacement MCP c |
| 2222 | const replacementToken = replacement?.authorizationHeader.replace(/^Bearer\s+/, ""); | 2229 | const replacementToken = replacement?.authorizationHeader.replace(/^Bearer\s+/, ""); |
| 2223 | assert.isDefined(replacementToken); | 2230 | assert.isDefined(replacementToken); |
| 2224 | assert.equal( | 2231 | assert.equal( |
| 2225 | McpProviderSession.readMcpProviderSession(threadId)?.providerSessionId, | 2232 | (yield* mcpSessions.read(threadId))?.providerSessionId, |
| 2226 | replacement?.providerSessionId, | 2233 | replacement?.providerSessionId, |
| 2227 | ); | 2234 | ); |
| 2228 | 2235 | ||
| 2229 | yield* manager.detach({ providerSessionId: oldSessionId, threadId }); | 2236 | yield* manager.detach({ providerSessionId: oldSessionId, threadId }); |
| 2230 | 2237 | ||
| 2231 | assert.equal( | 2238 | assert.equal( |
| 2232 | McpProviderSession.readMcpProviderSession(threadId)?.providerSessionId, | 2239 | (yield* mcpSessions.read(threadId))?.providerSessionId, |
| 2233 | replacement?.providerSessionId, | 2240 | replacement?.providerSessionId, |
| 2234 | ); | 2241 | ); |
| 2235 | assert.equal((yield* registry.resolve(replacementToken!))?.thread.threadId, threadId); | 2242 | assert.equal((yield* registry.resolve(replacementToken!))?.thread.threadId, threadId); |
| ... | @@ -2261,6 +2268,7 @@ it.effect( | ... | @@ -2261,6 +2268,7 @@ it.effect( |
| 2261 | const idAllocator = yield* IdAllocator.IdAllocatorV2; | 2268 | const idAllocator = yield* IdAllocator.IdAllocatorV2; |
| 2262 | const manager = yield* ProviderSessionManager.ProviderSessionManagerV2; | 2269 | const manager = yield* ProviderSessionManager.ProviderSessionManagerV2; |
| 2263 | const registry = yield* McpSessionRegistry.McpSessionRegistry; | 2270 | const registry = yield* McpSessionRegistry.McpSessionRegistry; |
| 2271 | const mcpSessions = yield* McpProviderSessions.McpProviderSessions; | ||
| 2264 | const now = yield* DateTime.now; | 2272 | const now = yield* DateTime.now; |
| 2265 | const threadId = ThreadId.make("thread-provider-session-manager-superseded-mcp"); | 2273 | const threadId = ThreadId.make("thread-provider-session-manager-superseded-mcp"); |
| 2266 | const oldSessionId = yield* idAllocator.allocate.providerSession({ | 2274 | const oldSessionId = yield* idAllocator.allocate.providerSession({ |
| ... | @@ -2296,7 +2304,7 @@ it.effect( | ... | @@ -2296,7 +2304,7 @@ it.effect( |
| 2296 | const replacementToken = replacement?.authorizationHeader.replace(/^Bearer\s+/, ""); | 2304 | const replacementToken = replacement?.authorizationHeader.replace(/^Bearer\s+/, ""); |
| 2297 | assert.isDefined(replacementToken); | 2305 | assert.isDefined(replacementToken); |
| 2298 | assert.equal( | 2306 | assert.equal( |
| 2299 | McpProviderSession.readMcpProviderSession(threadId)?.providerSessionId, | 2307 | (yield* mcpSessions.read(threadId))?.providerSessionId, |
| 2300 | replacement?.providerSessionId, | 2308 | replacement?.providerSessionId, |
| 2301 | ); | 2309 | ); |
| 2302 | 2310 | ||
| ... | @@ -2305,7 +2313,7 @@ it.effect( | ... | @@ -2305,7 +2313,7 @@ it.effect( |
| 2305 | yield* manager.detach({ providerSessionId: oldSessionId, threadId }); | 2313 | yield* manager.detach({ providerSessionId: oldSessionId, threadId }); |
| 2306 | 2314 | ||
| 2307 | assert.equal( | 2315 | assert.equal( |
| 2308 | McpProviderSession.readMcpProviderSession(threadId)?.providerSessionId, | 2316 | (yield* mcpSessions.read(threadId))?.providerSessionId, |
| 2309 | replacement?.providerSessionId, | 2317 | replacement?.providerSessionId, |
| 2310 | ); | 2318 | ); |
| 2311 | assert.equal((yield* registry.resolve(replacementToken!))?.thread.threadId, threadId); | 2319 | assert.equal((yield* registry.resolve(replacementToken!))?.thread.threadId, threadId); |
| ... | @@ -2337,6 +2345,7 @@ it.effect( | ... | @@ -2337,6 +2345,7 @@ it.effect( |
| 2337 | const idAllocator = yield* IdAllocator.IdAllocatorV2; | 2345 | const idAllocator = yield* IdAllocator.IdAllocatorV2; |
| 2338 | const manager = yield* ProviderSessionManager.ProviderSessionManagerV2; | 2346 | const manager = yield* ProviderSessionManager.ProviderSessionManagerV2; |
| 2339 | const registry = yield* McpSessionRegistry.McpSessionRegistry; | 2347 | const registry = yield* McpSessionRegistry.McpSessionRegistry; |
| 2348 | const mcpSessions = yield* McpProviderSessions.McpProviderSessions; | ||
| 2340 | const now = yield* DateTime.now; | 2349 | const now = yield* DateTime.now; |
| 2341 | const threadId = ThreadId.make("thread-provider-session-manager-stable-mcp"); | 2350 | const threadId = ThreadId.make("thread-provider-session-manager-stable-mcp"); |
| 2342 | const providerSessionId = yield* idAllocator.allocate.providerSession({ | 2351 | const providerSessionId = yield* idAllocator.allocate.providerSession({ |
| ... | @@ -2379,7 +2388,7 @@ it.effect( | ... | @@ -2379,7 +2388,7 @@ it.effect( |
| 2379 | runtimePolicy, | 2388 | runtimePolicy, |
| 2380 | }); | 2389 | }); |
| 2381 | assert.equal( | 2390 | assert.equal( |
| 2382 | McpProviderSession.readMcpProviderSession(threadId)?.providerSessionId, | 2391 | (yield* mcpSessions.read(threadId))?.providerSessionId, |
| 2383 | original?.providerSessionId, | 2392 | original?.providerSessionId, |
| 2384 | "re-attach must reuse the existing credential, not rotate it", | 2393 | "re-attach must reuse the existing credential, not rotate it", |
| 2385 | ); | 2394 | ); |
| ... | @@ -2415,6 +2424,7 @@ it.effect( | ... | @@ -2415,6 +2424,7 @@ it.effect( |
| 2415 | const idAllocator = yield* IdAllocator.IdAllocatorV2; | 2424 | const idAllocator = yield* IdAllocator.IdAllocatorV2; |
| 2416 | const manager = yield* ProviderSessionManager.ProviderSessionManagerV2; | 2425 | const manager = yield* ProviderSessionManager.ProviderSessionManagerV2; |
| 2417 | const registry = yield* McpSessionRegistry.McpSessionRegistry; | 2426 | const registry = yield* McpSessionRegistry.McpSessionRegistry; |
| 2427 | const mcpSessions = yield* McpProviderSessions.McpProviderSessions; | ||
| 2418 | const now = yield* DateTime.now; | 2428 | const now = yield* DateTime.now; |
| 2419 | const threadId = ThreadId.make("thread-provider-session-manager-stale-record"); | 2429 | const threadId = ThreadId.make("thread-provider-session-manager-stale-record"); |
| 2420 | const s1 = yield* idAllocator.allocate.providerSession({ | 2430 | const s1 = yield* idAllocator.allocate.providerSession({ |
| ... | @@ -2437,7 +2447,7 @@ it.effect( | ... | @@ -2437,7 +2447,7 @@ it.effect( |
| 2437 | // The credential dies externally, so S2's attach must rotate to C2. | 2447 | // The credential dies externally, so S2's attach must rotate to C2. |
| 2438 | yield* registry.revokeThread(threadId); | 2448 | yield* registry.revokeThread(threadId); |
| 2439 | yield* manager.open({ threadId, providerSessionId: s2, modelSelection, runtimePolicy }); | 2449 | yield* manager.open({ threadId, providerSessionId: s2, modelSelection, runtimePolicy }); |
| 2440 | const rotated = McpProviderSession.readMcpProviderSession(threadId); | 2450 | const rotated = yield* mcpSessions.read(threadId); |
| 2441 | assert.isDefined(rotated); | 2451 | assert.isDefined(rotated); |
| 2442 | const rotatedToken = rotated?.authorizationHeader.replace(/^Bearer\s+/, ""); | 2452 | const rotatedToken = rotated?.authorizationHeader.replace(/^Bearer\s+/, ""); |
| 2443 | assert.isDefined(yield* registry.resolve(rotatedToken!)); | 2453 | assert.isDefined(yield* registry.resolve(rotatedToken!)); |
| ... | @@ -2470,6 +2480,7 @@ it.effect( | ... | @@ -2470,6 +2480,7 @@ it.effect( |
| 2470 | const idAllocator = yield* IdAllocator.IdAllocatorV2; | 2480 | const idAllocator = yield* IdAllocator.IdAllocatorV2; |
| 2471 | const manager = yield* ProviderSessionManager.ProviderSessionManagerV2; | 2481 | const manager = yield* ProviderSessionManager.ProviderSessionManagerV2; |
| 2472 | const registry = yield* McpSessionRegistry.McpSessionRegistry; | 2482 | const registry = yield* McpSessionRegistry.McpSessionRegistry; |
| 2483 | const mcpSessions = yield* McpProviderSessions.McpProviderSessions; | ||
| 2473 | const now = yield* DateTime.now; | 2484 | const now = yield* DateTime.now; |
| 2474 | const threadId = ThreadId.make("thread-provider-session-manager-open-race"); | 2485 | const threadId = ThreadId.make("thread-provider-session-manager-open-race"); |
| 2475 | const s1 = yield* idAllocator.allocate.providerSession({ | 2486 | const s1 = yield* idAllocator.allocate.providerSession({ |
| ... | @@ -2498,7 +2509,7 @@ it.effect( | ... | @@ -2498,7 +2509,7 @@ it.effect( |
| 2498 | yield* Ref.set(duringOpen, manager.close(s1).pipe(Effect.orDie)); | 2509 | yield* Ref.set(duringOpen, manager.close(s1).pipe(Effect.orDie)); |
| 2499 | yield* manager.open({ threadId, providerSessionId: s2, modelSelection, runtimePolicy }); | 2510 | yield* manager.open({ threadId, providerSessionId: s2, modelSelection, runtimePolicy }); |
| 2500 | 2511 | ||
| 2501 | const slot = McpProviderSession.readMcpProviderSession(threadId); | 2512 | const slot = yield* mcpSessions.read(threadId); |
| 2502 | assert.equal( | 2513 | assert.equal( |
| 2503 | slot?.providerSessionId, | 2514 | slot?.providerSessionId, |
| 2504 | original?.providerSessionId, | 2515 | original?.providerSessionId, |
| ... | @@ -2542,6 +2553,7 @@ it.effect("ProviderSessionManagerV2 terminal detach revokes the thread's MCP cre | ... | @@ -2542,6 +2553,7 @@ it.effect("ProviderSessionManagerV2 terminal detach revokes the thread's MCP cre |
| 2542 | const idAllocator = yield* IdAllocator.IdAllocatorV2; | 2553 | const idAllocator = yield* IdAllocator.IdAllocatorV2; |
| 2543 | const manager = yield* ProviderSessionManager.ProviderSessionManagerV2; | 2554 | const manager = yield* ProviderSessionManager.ProviderSessionManagerV2; |
| 2544 | const registry = yield* McpSessionRegistry.McpSessionRegistry; | 2555 | const registry = yield* McpSessionRegistry.McpSessionRegistry; |
| 2556 | const mcpSessions = yield* McpProviderSessions.McpProviderSessions; | ||
| 2545 | const now = yield* DateTime.now; | 2557 | const now = yield* DateTime.now; |
| 2546 | const threadId = ThreadId.make("thread-provider-session-manager-terminal-detach"); | 2558 | const threadId = ThreadId.make("thread-provider-session-manager-terminal-detach"); |
| 2547 | const providerSessionId = yield* idAllocator.allocate.providerSession({ | 2559 | const providerSessionId = yield* idAllocator.allocate.providerSession({ |
| ... | @@ -2566,7 +2578,7 @@ it.effect("ProviderSessionManagerV2 terminal detach revokes the thread's MCP cre | ... | @@ -2566,7 +2578,7 @@ it.effect("ProviderSessionManagerV2 terminal detach revokes the thread's MCP cre |
| 2566 | revokeMcpCredential: true, | 2578 | revokeMcpCredential: true, |
| 2567 | }); | 2579 | }); |
| 2568 | assert.isUndefined(yield* registry.resolve(token!)); | 2580 | assert.isUndefined(yield* registry.resolve(token!)); |
| 2569 | assert.isUndefined(McpProviderSession.readMcpProviderSession(threadId)); | 2581 | assert.isUndefined(yield* mcpSessions.read(threadId)); |
| 2570 | }); | 2582 | }); |
| 2571 | 2583 | ||
| 2572 | yield* effect.pipe(Effect.provide(layerTest({ state, idleTimeoutMs: 1_000, mcpConfigs }))); | 2584 | yield* effect.pipe(Effect.provide(layerTest({ state, idleTimeoutMs: 1_000, mcpConfigs }))); |
apps/server/src/orchestration-v2/ProviderSessionManager.ts+12-18| ... | @@ -40,7 +40,7 @@ import { | ... | @@ -40,7 +40,7 @@ import { |
| 40 | } from "../observability/Metrics.ts"; | 40 | } from "../observability/Metrics.ts"; |
| 41 | import { ProviderWorkspaceMissingError } from "../provider/Errors.ts"; | 41 | import { ProviderWorkspaceMissingError } from "../provider/Errors.ts"; |
| 42 | import * as ProjectService from "../project/ProjectService.ts"; | 42 | import * as ProjectService from "../project/ProjectService.ts"; |
| 43 | import * as McpProviderSession from "@t3tools/provider-core/server/mcpSession"; | 43 | import * as McpProviderSessions from "@t3tools/provider-core/server/McpProviderSessions"; |
| 44 | import * as ServerSettings from "../serverSettings.ts"; | 44 | import * as ServerSettings from "../serverSettings.ts"; |
| 45 | import * as McpSessionRegistry from "../mcp/McpSessionRegistry.ts"; | 45 | import * as McpSessionRegistry from "../mcp/McpSessionRegistry.ts"; |
| 46 | import * as EventSink from "./EventSink.ts"; | 46 | import * as EventSink from "./EventSink.ts"; |
| ... | @@ -331,6 +331,7 @@ export const layerWithOptions = ( | ... | @@ -331,6 +331,7 @@ export const layerWithOptions = ( |
| 331 | | EventSink.EventSinkV2 | 331 | | EventSink.EventSinkV2 |
| 332 | | FileSystem.FileSystem | 332 | | FileSystem.FileSystem |
| 333 | | IdAllocator.IdAllocatorV2 | 333 | | IdAllocator.IdAllocatorV2 |
| 334 | | McpProviderSessions.McpProviderSessions | ||
| 334 | | McpSessionRegistry.McpSessionRegistry | 335 | | McpSessionRegistry.McpSessionRegistry |
| 335 | | ProjectionStore.ProjectionStoreV2 | 336 | | ProjectionStore.ProjectionStoreV2 |
| 336 | | ProviderEventIngestor.ProviderEventIngestorV2 | 337 | | ProviderEventIngestor.ProviderEventIngestorV2 |
| ... | @@ -342,6 +343,7 @@ export const layerWithOptions = ( | ... | @@ -342,6 +343,7 @@ export const layerWithOptions = ( |
| 342 | const registry = yield* ProviderAdapterRegistry.ProviderAdapterRegistryV2; | 343 | const registry = yield* ProviderAdapterRegistry.ProviderAdapterRegistryV2; |
| 343 | const fileSystem = yield* FileSystem.FileSystem; | 344 | const fileSystem = yield* FileSystem.FileSystem; |
| 344 | const mcpSessionRegistry = yield* McpSessionRegistry.McpSessionRegistry; | 345 | const mcpSessionRegistry = yield* McpSessionRegistry.McpSessionRegistry; |
| 346 | const mcpSessions = yield* McpProviderSessions.McpProviderSessions; | ||
| 345 | /** | 347 | /** |
| 346 | * Optional so the many focused tests that assemble this layer by hand do | 348 | * Optional so the many focused tests that assemble this layer by hand do |
| 347 | * not each need a settings stub; the production composition always | 349 | * not each need a settings stub; the production composition always |
| ... | @@ -472,10 +474,9 @@ export const layerWithOptions = ( | ... | @@ -472,10 +474,9 @@ export const layerWithOptions = ( |
| 472 | providerInstanceId: ProviderInstanceId, | 474 | providerInstanceId: ProviderInstanceId, |
| 473 | ): Effect.Effect<PreparedMcpCredential> => | 475 | ): Effect.Effect<PreparedMcpCredential> => |
| 474 | options.configureMcp === false | 476 | options.configureMcp === false |
| 475 | ? Effect.sync((): PreparedMcpCredential => { | 477 | ? mcpSessions |
| 476 | McpProviderSession.clearMcpProviderSession(threadId); | 478 | .clear(threadId) |
| 477 | return { mcpCredentialId: undefined, issued: false }; | 479 | .pipe(Effect.as<PreparedMcpCredential>({ mcpCredentialId: undefined, issued: false })) |
| 478 | }) | ||
| 479 | : mcpPrepareLock.withLock( | 480 | : mcpPrepareLock.withLock( |
| 480 | threadId, | 481 | threadId, |
| 481 | Effect.gen(function* () { | 482 | Effect.gen(function* () { |
| ... | @@ -492,7 +493,7 @@ export const layerWithOptions = ( | ... | @@ -492,7 +493,7 @@ export const layerWithOptions = ( |
| 492 | >(["orchestration", "worktree", "pull-requests"]); | 493 | >(["orchestration", "worktree", "pull-requests"]); |
| 493 | if (browserToolsAvailable) capabilities.add("preview"); | 494 | if (browserToolsAvailable) capabilities.add("preview"); |
| 494 | if (deviceToolsAvailable) capabilities.add("device"); | 495 | if (deviceToolsAvailable) capabilities.add("device"); |
| 495 | const existing = McpProviderSession.readMcpProviderSession(threadId); | 496 | const existing = yield* mcpSessions.read(threadId); |
| 496 | if (existing !== undefined) { | 497 | if (existing !== undefined) { |
| 497 | // Reserve before the async resolve so a release cannot | 498 | // Reserve before the async resolve so a release cannot |
| 498 | // revoke the credential between validation and reservation. | 499 | // revoke the credential between validation and reservation. |
| ... | @@ -529,7 +530,7 @@ export const layerWithOptions = ( | ... | @@ -529,7 +530,7 @@ export const layerWithOptions = ( |
| 529 | browserToolsAvailable, | 530 | browserToolsAvailable, |
| 530 | capabilities, | 531 | capabilities, |
| 531 | }); | 532 | }); |
| 532 | McpProviderSession.setMcpProviderSession(credential.config); | 533 | yield* mcpSessions.set(credential.config); |
| 533 | reserveMcpCredential(threadId, credential.config.providerSessionId); | 534 | reserveMcpCredential(threadId, credential.config.providerSessionId); |
| 534 | return { mcpCredentialId: credential.config.providerSessionId, issued: true }; | 535 | return { mcpCredentialId: credential.config.providerSessionId, issued: true }; |
| 535 | }), | 536 | }), |
| ... | @@ -544,19 +545,12 @@ export const layerWithOptions = ( | ... | @@ -544,19 +545,12 @@ export const layerWithOptions = ( |
| 544 | mcpCredentialId === undefined | 545 | mcpCredentialId === undefined |
| 545 | ? mcpSessionRegistry | 546 | ? mcpSessionRegistry |
| 546 | .revokeThread(threadId) | 547 | .revokeThread(threadId) |
| 547 | .pipe( | 548 | .pipe(Effect.tap(() => mcpSessions.clear(threadId))) |
| 548 | Effect.tap(() => | ||
| 549 | Effect.sync(() => McpProviderSession.clearMcpProviderSession(threadId)), | ||
| 550 | ), | ||
| 551 | ) | ||
| 552 | : mcpSessionRegistry.revokeProviderSession(mcpCredentialId).pipe( | 549 | : mcpSessionRegistry.revokeProviderSession(mcpCredentialId).pipe( |
| 553 | Effect.tap(() => | 550 | Effect.tap(() => |
| 554 | Effect.sync(() => { | 551 | Effect.gen(function* () { |
| 555 | if ( | 552 | if ((yield* mcpSessions.read(threadId))?.providerSessionId === mcpCredentialId) { |
| 556 | McpProviderSession.readMcpProviderSession(threadId)?.providerSessionId === | 553 | yield* mcpSessions.clear(threadId); |
| 557 | mcpCredentialId | ||
| 558 | ) { | ||
| 559 | McpProviderSession.clearMcpProviderSession(threadId); | ||
| 560 | } | 554 | } |
| 561 | }), | 555 | }), |
| 562 | ), | 556 | ), |
apps/server/src/orchestration-v2/runtimeLayer.test.ts+5| ... | @@ -1,4 +1,5 @@ | ... | @@ -1,4 +1,5 @@ |
| 1 | import { limitRecoveryCommand } from "./UsageLimitRecoveryWorker.ts"; | 1 | import { limitRecoveryCommand } from "./UsageLimitRecoveryWorker.ts"; |
| 2 | import * as McpProviderSessions from "@t3tools/provider-core/server/McpProviderSessions"; | ||
| 2 | import * as SourceControlProviderRegistry from "../sourceControl/SourceControlProviderRegistry.ts"; | 3 | import * as SourceControlProviderRegistry from "../sourceControl/SourceControlProviderRegistry.ts"; |
| 3 | import * as NodeServices from "@effect/platform-node/NodeServices"; | 4 | import * as NodeServices from "@effect/platform-node/NodeServices"; |
| 4 | import { assert, it } from "@effect/vitest"; | 5 | import { assert, it } from "@effect/vitest"; |
| ... | @@ -272,6 +273,7 @@ const layerTest = Layer.mergeAll( | ... | @@ -272,6 +273,7 @@ const layerTest = Layer.mergeAll( |
| 272 | ThreadCommandExecutor.layer, | 273 | ThreadCommandExecutor.layer, |
| 273 | ).pipe( | 274 | ).pipe( |
| 274 | Layer.provide(McpSessionRegistryTestkit.layer), | 275 | Layer.provide(McpSessionRegistryTestkit.layer), |
| 276 | Layer.provide(McpProviderSessions.layer), | ||
| 275 | Layer.provide(SqlitePersistence.layerMemory), | 277 | Layer.provide(SqlitePersistence.layerMemory), |
| 276 | Layer.provide(layerCheckpointStoreTest), | 278 | Layer.provide(layerCheckpointStoreTest), |
| 277 | Layer.provide(layerServerConfig), | 279 | Layer.provide(layerServerConfig), |
| ... | @@ -284,6 +286,7 @@ const layerTest = Layer.mergeAll( | ... | @@ -284,6 +286,7 @@ const layerTest = Layer.mergeAll( |
| 284 | 286 | ||
| 285 | const layerLegacyImportTest = RuntimeLayer.layer.pipe( | 287 | const layerLegacyImportTest = RuntimeLayer.layer.pipe( |
| 286 | Layer.provide(McpSessionRegistryTestkit.layer), | 288 | Layer.provide(McpSessionRegistryTestkit.layer), |
| 289 | Layer.provide(McpProviderSessions.layer), | ||
| 287 | Layer.provideMerge(SqlitePersistence.layerMemory), | 290 | Layer.provideMerge(SqlitePersistence.layerMemory), |
| 288 | Layer.provide(layerCheckpointStoreTest), | 291 | Layer.provide(layerCheckpointStoreTest), |
| 289 | Layer.provide(layerServerConfig), | 292 | Layer.provide(layerServerConfig), |
| ... | @@ -323,6 +326,7 @@ const layerProjectDeletionTest = Layer.mergeAll( | ... | @@ -323,6 +326,7 @@ const layerProjectDeletionTest = Layer.mergeAll( |
| 323 | }), | 326 | }), |
| 324 | ), | 327 | ), |
| 325 | Layer.provide(McpSessionRegistryTestkit.layer), | 328 | Layer.provide(McpSessionRegistryTestkit.layer), |
| 329 | Layer.provide(McpProviderSessions.layer), | ||
| 326 | Layer.provide(SqlitePersistence.layerMemory), | 330 | Layer.provide(SqlitePersistence.layerMemory), |
| 327 | Layer.provide(layerCheckpointStoreTest), | 331 | Layer.provide(layerCheckpointStoreTest), |
| 328 | Layer.provide(layerServerConfig), | 332 | Layer.provide(layerServerConfig), |
| ... | @@ -467,6 +471,7 @@ const layerSharedApplicationDataPlaneTest = Layer.mergeAll( | ... | @@ -467,6 +471,7 @@ const layerSharedApplicationDataPlaneTest = Layer.mergeAll( |
| 467 | }), | 471 | }), |
| 468 | ), | 472 | ), |
| 469 | Layer.provide(McpSessionRegistryTestkit.layer), | 473 | Layer.provide(McpSessionRegistryTestkit.layer), |
| 474 | Layer.provide(McpProviderSessions.layer), | ||
| 470 | Layer.provideMerge(SqlitePersistence.layerMemory), | 475 | Layer.provideMerge(SqlitePersistence.layerMemory), |
| 471 | Layer.provide(layerCheckpointStoreTest), | 476 | Layer.provide(layerCheckpointStoreTest), |
| 472 | Layer.provide(layerServerConfig), | 477 | Layer.provide(layerServerConfig), |
apps/server/src/orchestration-v2/testkit/ProviderReplayHarness.ts+15-3| ... | @@ -16,6 +16,7 @@ import * as ServerConfig from "../../config.ts"; | ... | @@ -16,6 +16,7 @@ import * as ServerConfig from "../../config.ts"; |
| 16 | import * as SqlitePersistence from "../../persistence/Sqlite.ts"; | 16 | import * as SqlitePersistence from "../../persistence/Sqlite.ts"; |
| 17 | import * as ServerSettings from "../../serverSettings.ts"; | 17 | import * as ServerSettings from "../../serverSettings.ts"; |
| 18 | import * as ThreadManagementService from "../ThreadManagementService.ts"; | 18 | import * as ThreadManagementService from "../ThreadManagementService.ts"; |
| 19 | import * as McpProviderSessions from "@t3tools/provider-core/server/McpProviderSessions"; | ||
| 19 | import * as McpSessionRegistryTestkit from "../../mcp/McpSessionRegistry.testkit.ts"; | 20 | import * as McpSessionRegistryTestkit from "../../mcp/McpSessionRegistry.testkit.ts"; |
| 20 | import * as VcsDriverRegistry from "../../vcs/VcsDriverRegistry.ts"; | 21 | import * as VcsDriverRegistry from "../../vcs/VcsDriverRegistry.ts"; |
| 21 | import * as VcsProcess from "../../vcs/VcsProcess.ts"; | 22 | import * as VcsProcess from "../../vcs/VcsProcess.ts"; |
| ... | @@ -170,7 +171,11 @@ export interface OrchestratorV2ProviderReplayHarness< | ... | @@ -170,7 +171,11 @@ export interface OrchestratorV2ProviderReplayHarness< |
| 170 | readonly makeProviderAdapterRegistryLayer: ( | 171 | readonly makeProviderAdapterRegistryLayer: ( |
| 171 | transcript: Transcript, | 172 | transcript: Transcript, |
| 172 | options?: { readonly replayGate?: ProviderReplayGate }, | 173 | options?: { readonly replayGate?: ProviderReplayGate }, |
| 173 | ) => Layer.Layer<ProviderAdapterRegistry.ProviderAdapterRegistryV2, Error>; | 174 | ) => Layer.Layer< |
| 175 | ProviderAdapterRegistry.ProviderAdapterRegistryV2, | ||
| 176 | Error, | ||
| 177 | McpProviderSessions.McpProviderSessions | ||
| 178 | >; | ||
| 174 | } | 179 | } |
| 175 | 180 | ||
| 176 | export function runOrchestratorV2ProviderReplayScenario< | 181 | export function runOrchestratorV2ProviderReplayScenario< |
| ... | @@ -249,7 +254,11 @@ export function layerProviderReplay<Transcript extends ProviderReplayTranscript, | ... | @@ -249,7 +254,11 @@ export function layerProviderReplay<Transcript extends ProviderReplayTranscript, |
| 249 | 254 | ||
| 250 | export function layerWithRegistry<Error>( | 255 | export function layerWithRegistry<Error>( |
| 251 | scenario: Pick<OrchestratorV2ProviderReplayScenario, "name" | "runtimePolicyOverride">, | 256 | scenario: Pick<OrchestratorV2ProviderReplayScenario, "name" | "runtimePolicyOverride">, |
| 252 | registryLayer: Layer.Layer<ProviderAdapterRegistry.ProviderAdapterRegistryV2, Error>, | 257 | registryLayer: Layer.Layer< |
| 258 | ProviderAdapterRegistry.ProviderAdapterRegistryV2, | ||
| 259 | Error, | ||
| 260 | McpProviderSessions.McpProviderSessions | ||
| 261 | >, | ||
| 253 | options: { | 262 | options: { |
| 254 | readonly databaseLayer?: Layer.Layer< | 263 | readonly databaseLayer?: Layer.Layer< |
| 255 | SqlClient.SqlClient, | 264 | SqlClient.SqlClient, |
| ... | @@ -286,7 +295,9 @@ export function layerWithRegistry<Error>( | ... | @@ -286,7 +295,9 @@ export function layerWithRegistry<Error>( |
| 286 | // runtimeLayer.ts; layer memoization keeps it a single instance. | 295 | // runtimeLayer.ts; layer memoization keeps it a single instance. |
| 287 | const layerContinuationRequests = | 296 | const layerContinuationRequests = |
| 288 | options.runContinuationWorker === true ? ProviderContinuationRequests.layer : Layer.empty; | 297 | options.runContinuationWorker === true ? ProviderContinuationRequests.layer : Layer.empty; |
| 289 | const layerProvidedRegistry = registryLayer.pipe(Layer.provide(layerContinuationRequests)); | 298 | const layerProvidedRegistry = registryLayer.pipe( |
| 299 | Layer.provide(Layer.merge(layerContinuationRequests, McpProviderSessions.layer)), | ||
| 300 | ); | ||
| 290 | const layerServerSettings = ServerSettings.layerTest({ | 301 | const layerServerSettings = ServerSettings.layerTest({ |
| 291 | responseStreamingMode: "turn", | 302 | responseStreamingMode: "turn", |
| 292 | ...(options.continueThreadsAfterServerUpdate === undefined | 303 | ...(options.continueThreadsAfterServerUpdate === undefined |
| ... | @@ -347,6 +358,7 @@ export function layerWithRegistry<Error>( | ... | @@ -347,6 +358,7 @@ export function layerWithRegistry<Error>( |
| 347 | layerProvidedRegistry, | 358 | layerProvidedRegistry, |
| 348 | layerEventSinkProvided, | 359 | layerEventSinkProvided, |
| 349 | IdAllocator.layer, | 360 | IdAllocator.layer, |
| 361 | McpProviderSessions.layer, | ||
| 350 | McpSessionRegistryTestkit.layer, | 362 | McpSessionRegistryTestkit.layer, |
| 351 | layerProviderEventIngestorProvided, | 363 | layerProviderEventIngestorProvided, |
| 352 | layerStores, | 364 | layerStores, |
apps/server/src/provider/Drivers/AntigravityDriver.test.ts+2| ... | @@ -30,6 +30,7 @@ import { | ... | @@ -30,6 +30,7 @@ import { |
| 30 | resolveAntigravityInstanceDirectories, | 30 | resolveAntigravityInstanceDirectories, |
| 31 | } from "../antigravityAuthSupport.ts"; | 31 | } from "../antigravityAuthSupport.ts"; |
| 32 | import * as ProviderEventLoggers from "@t3tools/provider-core/server/ProviderEventLoggers"; | 32 | import * as ProviderEventLoggers from "@t3tools/provider-core/server/ProviderEventLoggers"; |
| 33 | import * as McpProviderSessions from "@t3tools/provider-core/server/McpProviderSessions"; | ||
| 33 | import * as ModelManifest from "../ModelManifest.ts"; | 34 | import * as ModelManifest from "../ModelManifest.ts"; |
| 34 | import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator"; | 35 | import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator"; |
| 35 | import { AntigravityDriver } from "./AntigravityDriver.ts"; | 36 | import { AntigravityDriver } from "./AntigravityDriver.ts"; |
| ... | @@ -264,6 +265,7 @@ const layerDeps = ServerConfig.layerTest(process.cwd(), { | ... | @@ -264,6 +265,7 @@ const layerDeps = ServerConfig.layerTest(process.cwd(), { |
| 264 | ), | 265 | ), |
| 265 | Layer.provideMerge(ModelManifest.layerTest), | 266 | Layer.provideMerge(ModelManifest.layerTest), |
| 266 | Layer.provideMerge(IdAllocator.layer), | 267 | Layer.provideMerge(IdAllocator.layer), |
| 268 | Layer.provideMerge(McpProviderSessions.layer), | ||
| 267 | ); | 269 | ); |
| 268 | const layerTest = ProviderHostLive.layer.pipe( | 270 | const layerTest = ProviderHostLive.layer.pipe( |
| 269 | Layer.provideMerge(ServerSecretStore.layer), | 271 | Layer.provideMerge(ServerSecretStore.layer), |
apps/server/src/provider/Drivers/AntigravityDriver.ts+2| ... | @@ -1,4 +1,5 @@ | ... | @@ -1,4 +1,5 @@ |
| 1 | import { withAgentDeviceEnvironment } from "@t3tools/provider-core/server/mcpSession"; | 1 | import { withAgentDeviceEnvironment } from "@t3tools/provider-core/server/mcpSession"; |
| 2 | import * as McpProviderSessions from "@t3tools/provider-core/server/McpProviderSessions"; | ||
| 2 | import { AntigravitySettings, ProviderDriverKind, ProviderSetupError } from "@t3tools/contracts"; | 3 | import { AntigravitySettings, ProviderDriverKind, ProviderSetupError } from "@t3tools/contracts"; |
| 3 | import { HostProcessPlatform } from "@t3tools/shared/hostProcess"; | 4 | import { HostProcessPlatform } from "@t3tools/shared/hostProcess"; |
| 4 | import { resolveSelfInvocation } from "@t3tools/shared/nodeRuntime"; | 5 | import { resolveSelfInvocation } from "@t3tools/shared/nodeRuntime"; |
| ... | @@ -73,6 +74,7 @@ export type AntigravityDriverEnv = | ... | @@ -73,6 +74,7 @@ export type AntigravityDriverEnv = |
| 73 | | Crypto.Crypto | 74 | | Crypto.Crypto |
| 74 | | FileSystem.FileSystem | 75 | | FileSystem.FileSystem |
| 75 | | IdAllocator.IdAllocatorV2 | 76 | | IdAllocator.IdAllocatorV2 |
| 77 | | McpProviderSessions.McpProviderSessions | ||
| 76 | | ModelCatalog.ModelCatalog | 78 | | ModelCatalog.ModelCatalog |
| 77 | | Path.Path | 79 | | Path.Path |
| 78 | | ProviderEventLoggers.ProviderEventLoggers; | 80 | | ProviderEventLoggers.ProviderEventLoggers; |
apps/server/src/provider/Drivers/CodexDriver.test.ts+2| ... | @@ -27,6 +27,7 @@ import * as ServerConfig from "../../config.ts"; | ... | @@ -27,6 +27,7 @@ import * as ServerConfig from "../../config.ts"; |
| 27 | import * as ServerSettings from "../../serverSettings.ts"; | 27 | import * as ServerSettings from "../../serverSettings.ts"; |
| 28 | import * as ResetCreditCoordinator from "../resetCreditCoordinator.ts"; | 28 | import * as ResetCreditCoordinator from "../resetCreditCoordinator.ts"; |
| 29 | import * as ProviderLatestVersions from "@t3tools/provider-core/server/ProviderLatestVersions"; | 29 | import * as ProviderLatestVersions from "@t3tools/provider-core/server/ProviderLatestVersions"; |
| 30 | import * as McpProviderSessions from "@t3tools/provider-core/server/McpProviderSessions"; | ||
| 30 | import * as ProviderEventLoggers from "@t3tools/provider-core/server/ProviderEventLoggers"; | 31 | import * as ProviderEventLoggers from "@t3tools/provider-core/server/ProviderEventLoggers"; |
| 31 | import * as ModelManifest from "../ModelManifest.ts"; | 32 | import * as ModelManifest from "../ModelManifest.ts"; |
| 32 | import { | 33 | import { |
| ... | @@ -45,6 +46,7 @@ const layerDeps = ServerConfig.layerTest(process.cwd(), { | ... | @@ -45,6 +46,7 @@ const layerDeps = ServerConfig.layerTest(process.cwd(), { |
| 45 | }).pipe( | 46 | }).pipe( |
| 46 | Layer.provideMerge(NodeServices.layer), | 47 | Layer.provideMerge(NodeServices.layer), |
| 47 | Layer.provideMerge(IdAllocator.layer), | 48 | Layer.provideMerge(IdAllocator.layer), |
| 49 | Layer.provideMerge(McpProviderSessions.layer), | ||
| 48 | Layer.provideMerge( | 50 | Layer.provideMerge( |
| 49 | Layer.mock(CodexAdapterV2.CodexAppServerClientFactory)({ | 51 | Layer.mock(CodexAdapterV2.CodexAppServerClientFactory)({ |
| 50 | open: () => Effect.die("Maintenance resolution must not open a Codex session"), | 52 | open: () => Effect.die("Maintenance resolution must not open a Codex session"), |
apps/server/src/provider/ProviderInstanceRegistry.test.ts+3| ... | @@ -36,6 +36,7 @@ import { | ... | @@ -36,6 +36,7 @@ import { |
| 36 | ProviderInstanceId, | 36 | ProviderInstanceId, |
| 37 | } from "@t3tools/contracts"; | 37 | } from "@t3tools/contracts"; |
| 38 | import * as ProviderLatestVersions from "@t3tools/provider-core/server/ProviderLatestVersions"; | 38 | import * as ProviderLatestVersions from "@t3tools/provider-core/server/ProviderLatestVersions"; |
| 39 | import * as McpProviderSessions from "@t3tools/provider-core/server/McpProviderSessions"; | ||
| 39 | import type { GrokSettings } from "@t3tools/provider-grok/settings"; | 40 | import type { GrokSettings } from "@t3tools/provider-grok/settings"; |
| 40 | import type { CursorSettings } from "@t3tools/provider-cursor/settings"; | 41 | import type { CursorSettings } from "@t3tools/provider-cursor/settings"; |
| 41 | import { HostProcessPlatform, isHostWindows } from "@t3tools/shared/hostProcess"; | 42 | import { HostProcessPlatform, isHostWindows } from "@t3tools/shared/hostProcess"; |
| ... | @@ -230,6 +231,7 @@ describe("ProviderInstanceRegistry — multi-instance codex slice", () => { | ... | @@ -230,6 +231,7 @@ describe("ProviderInstanceRegistry — multi-instance codex slice", () => { |
| 230 | ), | 231 | ), |
| 231 | ), | 232 | ), |
| 232 | Layer.provideMerge(ProviderLatestVersions.layer), | 233 | Layer.provideMerge(ProviderLatestVersions.layer), |
| 234 | Layer.provideMerge(McpProviderSessions.layer), | ||
| 233 | Layer.provideMerge(ModelManifest.layerTest), | 235 | Layer.provideMerge(ModelManifest.layerTest), |
| 234 | Layer.provideMerge(ResetCreditCoordinator.layerTest), | 236 | Layer.provideMerge(ResetCreditCoordinator.layerTest), |
| 235 | ); | 237 | ); |
| ... | @@ -612,6 +614,7 @@ describe("ProviderInstanceRegistry — all drivers slice", () => { | ... | @@ -612,6 +614,7 @@ describe("ProviderInstanceRegistry — all drivers slice", () => { |
| 612 | ), | 614 | ), |
| 613 | ), | 615 | ), |
| 614 | Layer.provideMerge(ProviderLatestVersions.layer), | 616 | Layer.provideMerge(ProviderLatestVersions.layer), |
| 617 | Layer.provideMerge(McpProviderSessions.layer), | ||
| 615 | Layer.provideMerge(ModelManifest.layerTest), | 618 | Layer.provideMerge(ModelManifest.layerTest), |
| 616 | Layer.provideMerge(ResetCreditCoordinator.layerTest), | 619 | Layer.provideMerge(ResetCreditCoordinator.layerTest), |
| 617 | ); | 620 | ); |
apps/server/src/provider/ProviderRegistry.test.ts+5| ... | @@ -46,6 +46,7 @@ import * as ModelManifest from "./ModelManifest.ts"; | ... | @@ -46,6 +46,7 @@ import * as ModelManifest from "./ModelManifest.ts"; |
| 46 | import { applyProviderCompatibility } from "./providerCompatibility.ts"; | 46 | import { applyProviderCompatibility } from "./providerCompatibility.ts"; |
| 47 | import * as ResetCreditCoordinator from "./resetCreditCoordinator.ts"; | 47 | import * as ResetCreditCoordinator from "./resetCreditCoordinator.ts"; |
| 48 | import * as ProviderLatestVersions from "@t3tools/provider-core/server/ProviderLatestVersions"; | 48 | import * as ProviderLatestVersions from "@t3tools/provider-core/server/ProviderLatestVersions"; |
| 49 | import * as McpProviderSessions from "@t3tools/provider-core/server/McpProviderSessions"; | ||
| 49 | import * as OpenCodeRuntime from "@t3tools/provider-opencode/server/OpenCodeRuntime"; | 50 | import * as OpenCodeRuntime from "@t3tools/provider-opencode/server/OpenCodeRuntime"; |
| 50 | import * as OpenCodeServerLedger from "@t3tools/provider-opencode/server/OpenCodeServerLedger"; | 51 | import * as OpenCodeServerLedger from "@t3tools/provider-opencode/server/OpenCodeServerLedger"; |
| 51 | import * as ProviderEventLoggers from "@t3tools/provider-core/server/ProviderEventLoggers"; | 52 | import * as ProviderEventLoggers from "@t3tools/provider-core/server/ProviderEventLoggers"; |
| ... | @@ -2761,6 +2762,7 @@ it.layer( | ... | @@ -2761,6 +2762,7 @@ it.layer( |
| 2761 | ), | 2762 | ), |
| 2762 | ), | 2763 | ), |
| 2763 | Layer.provideMerge(ProviderLatestVersions.layer), | 2764 | Layer.provideMerge(ProviderLatestVersions.layer), |
| 2765 | Layer.provideMerge(McpProviderSessions.layer), | ||
| 2764 | Layer.provideMerge(ModelManifest.layerTest), | 2766 | Layer.provideMerge(ModelManifest.layerTest), |
| 2765 | Layer.provideMerge(ResetCreditCoordinator.layerTest), | 2767 | Layer.provideMerge(ResetCreditCoordinator.layerTest), |
| 2766 | Layer.provideMerge( | 2768 | Layer.provideMerge( |
| ... | @@ -2865,6 +2867,7 @@ it.layer( | ... | @@ -2865,6 +2867,7 @@ it.layer( |
| 2865 | ), | 2867 | ), |
| 2866 | ), | 2868 | ), |
| 2867 | Layer.provideMerge(ProviderLatestVersions.layer), | 2869 | Layer.provideMerge(ProviderLatestVersions.layer), |
| 2870 | Layer.provideMerge(McpProviderSessions.layer), | ||
| 2868 | Layer.provideMerge(ModelManifest.layerTest), | 2871 | Layer.provideMerge(ModelManifest.layerTest), |
| 2869 | Layer.provideMerge(ResetCreditCoordinator.layerTest), | 2872 | Layer.provideMerge(ResetCreditCoordinator.layerTest), |
| 2870 | Layer.provideMerge( | 2873 | Layer.provideMerge( |
| ... | @@ -2984,6 +2987,7 @@ it.layer( | ... | @@ -2984,6 +2987,7 @@ it.layer( |
| 2984 | ), | 2987 | ), |
| 2985 | ), | 2988 | ), |
| 2986 | Layer.provideMerge(ProviderLatestVersions.layer), | 2989 | Layer.provideMerge(ProviderLatestVersions.layer), |
| 2990 | Layer.provideMerge(McpProviderSessions.layer), | ||
| 2987 | Layer.provideMerge(ModelManifest.layerTest), | 2991 | Layer.provideMerge(ModelManifest.layerTest), |
| 2988 | Layer.provideMerge(ResetCreditCoordinator.layerTest), | 2992 | Layer.provideMerge(ResetCreditCoordinator.layerTest), |
| 2989 | Layer.provideMerge( | 2993 | Layer.provideMerge( |
| ... | @@ -3043,6 +3047,7 @@ it.layer( | ... | @@ -3043,6 +3047,7 @@ it.layer( |
| 3043 | ), | 3047 | ), |
| 3044 | ), | 3048 | ), |
| 3045 | Layer.provideMerge(ProviderLatestVersions.layer), | 3049 | Layer.provideMerge(ProviderLatestVersions.layer), |
| 3050 | Layer.provideMerge(McpProviderSessions.layer), | ||
| 3046 | Layer.provideMerge(ModelManifest.layerTest), | 3051 | Layer.provideMerge(ModelManifest.layerTest), |
| 3047 | Layer.provideMerge(ResetCreditCoordinator.layerTest), | 3052 | Layer.provideMerge(ResetCreditCoordinator.layerTest), |
| 3048 | Layer.provideMerge( | 3053 | Layer.provideMerge( |
apps/server/src/server.ts+2| ... | @@ -45,6 +45,7 @@ import * as ModelManifest from "./provider/ModelManifest.ts"; | ... | @@ -45,6 +45,7 @@ import * as ModelManifest from "./provider/ModelManifest.ts"; |
| 45 | import * as ResetCreditCoordinator from "./provider/resetCreditCoordinator.ts"; | 45 | import * as ResetCreditCoordinator from "./provider/resetCreditCoordinator.ts"; |
| 46 | import * as EventNdjsonLogger from "./provider/EventNdjsonLogger.ts"; | 46 | import * as EventNdjsonLogger from "./provider/EventNdjsonLogger.ts"; |
| 47 | import * as ProviderLatestVersions from "@t3tools/provider-core/server/ProviderLatestVersions"; | 47 | import * as ProviderLatestVersions from "@t3tools/provider-core/server/ProviderLatestVersions"; |
| 48 | import * as McpProviderSessions from "@t3tools/provider-core/server/McpProviderSessions"; | ||
| 48 | import * as OpenCodeRuntime from "@t3tools/provider-opencode/server/OpenCodeRuntime"; | 49 | import * as OpenCodeRuntime from "@t3tools/provider-opencode/server/OpenCodeRuntime"; |
| 49 | import * as OpenCodeServerLedger from "@t3tools/provider-opencode/server/OpenCodeServerLedger"; | 50 | import * as OpenCodeServerLedger from "@t3tools/provider-opencode/server/OpenCodeServerLedger"; |
| 50 | import * as ProviderHostLive from "./provider/ProviderHostLive.ts"; | 51 | import * as ProviderHostLive from "./provider/ProviderHostLive.ts"; |
| ... | @@ -636,6 +637,7 @@ const layerRuntimeCoreDependencies = layerRuntimeCoreDependenciesBase.pipe( | ... | @@ -636,6 +637,7 @@ const layerRuntimeCoreDependencies = layerRuntimeCoreDependenciesBase.pipe( |
| 636 | ModelManifest.layerModelCatalog.pipe(Layer.provideMerge(ModelManifest.layer)), | 637 | ModelManifest.layerModelCatalog.pipe(Layer.provideMerge(ModelManifest.layer)), |
| 637 | ResetCreditCoordinator.layer, | 638 | ResetCreditCoordinator.layer, |
| 638 | ProviderLatestVersions.layer, | 639 | ProviderLatestVersions.layer, |
| 640 | McpProviderSessions.layer, | ||
| 639 | ), | 641 | ), |
| 640 | ), | 642 | ), |
| 641 | // `OpenCodeDriver.create()` yields `OpenCodeRuntime`; previously the old | 643 | // `OpenCodeDriver.create()` yields `OpenCodeRuntime`; previously the old |
packages/provider-acp-registry/src/server/adapter.ts+2| ... | @@ -18,6 +18,7 @@ import type * as Scope from "effect/Scope"; | ... | @@ -18,6 +18,7 @@ import type * as Scope from "effect/Scope"; |
| 18 | import { ChildProcessSpawner } from "effect/process"; | 18 | import { ChildProcessSpawner } from "effect/process"; |
| 19 | import * as EffectAcpErrors from "effect-acp/errors"; | 19 | import * as EffectAcpErrors from "effect-acp/errors"; |
| 20 | 20 | ||
| 21 | import * as McpProviderSessions from "@t3tools/provider-core/server/McpProviderSessions"; | ||
| 21 | import * as ProviderHost from "@t3tools/provider-core/server/ProviderHost"; | 22 | import * as ProviderHost from "@t3tools/provider-core/server/ProviderHost"; |
| 22 | import { | 23 | import { |
| 23 | normalizeAcpRegistryCommands, | 24 | normalizeAcpRegistryCommands, |
| ... | @@ -259,6 +260,7 @@ export type AcpRegistryAdapterV2DriverEnv = | ... | @@ -259,6 +260,7 @@ export type AcpRegistryAdapterV2DriverEnv = |
| 259 | | AcpRegistrySupport.AcpRegistryCatalog | 260 | | AcpRegistrySupport.AcpRegistryCatalog |
| 260 | | IdAllocator.IdAllocatorV2 | 261 | | IdAllocator.IdAllocatorV2 |
| 261 | | Path.Path | 262 | | Path.Path |
| 263 | | McpProviderSessions.McpProviderSessions | ||
| 262 | | ProviderEventLoggers.ProviderEventLoggers | 264 | | ProviderEventLoggers.ProviderEventLoggers |
| 263 | | ProviderHost.ProviderHost; | 265 | | ProviderHost.ProviderHost; |
| 264 | 266 |
packages/provider-acp/src/server/adapter.ts+58-43| ... | @@ -59,6 +59,7 @@ import { formatReadToolLabel, formatSearchToolLabel } from "@t3tools/shared/tool | ... | @@ -59,6 +59,7 @@ import { formatReadToolLabel, formatSearchToolLabel } from "@t3tools/shared/tool |
| 59 | import * as ProviderHost from "@t3tools/provider-core/server/ProviderHost"; | 59 | import * as ProviderHost from "@t3tools/provider-core/server/ProviderHost"; |
| 60 | import { makeAcpMcpOverAcpBridge, type AcpMcpOverAcpBridge } from "./mcpOverAcpBridge.ts"; | 60 | import { makeAcpMcpOverAcpBridge, type AcpMcpOverAcpBridge } from "./mcpOverAcpBridge.ts"; |
| 61 | import { mcpToolPresentation } from "@t3tools/provider-core/server/mcpToolPresentation"; | 61 | import { mcpToolPresentation } from "@t3tools/provider-core/server/mcpToolPresentation"; |
| 62 | import * as McpProviderSessions from "@t3tools/provider-core/server/McpProviderSessions"; | ||
| 62 | import * as McpProviderSession from "@t3tools/provider-core/server/mcpSession"; | 63 | import * as McpProviderSession from "@t3tools/provider-core/server/mcpSession"; |
| 63 | import { | 64 | import { |
| 64 | applyAcpAgentTerminalUpdate, | 65 | applyAcpAgentTerminalUpdate, |
| ... | @@ -677,9 +678,10 @@ interface AcpMcpContext { | ... | @@ -677,9 +678,10 @@ interface AcpMcpContext { |
| 677 | readonly authorization?: string; | 678 | readonly authorization?: string; |
| 678 | } | 679 | } |
| 679 | 680 | ||
| 680 | function acpMcpContext(threadId: ThreadId | null, self: SelfInvocation): AcpMcpContext { | 681 | function acpMcpContext( |
| 681 | if (threadId === null) return { servers: [], acpServers: [] }; | 682 | session: McpProviderSession.McpProviderSessionConfig | undefined, |
| 682 | const session = McpProviderSession.readMcpProviderSession(threadId); | 683 | self: SelfInvocation, |
| 684 | ): AcpMcpContext { | ||
| 683 | if (session === undefined) { | 685 | if (session === undefined) { |
| 684 | return { servers: [], acpServers: [] }; | 686 | return { servers: [], acpServers: [] }; |
| 685 | } | 687 | } |
| ... | @@ -714,15 +716,7 @@ function acpMcpContext(threadId: ThreadId | null, self: SelfInvocation): AcpMcpC | ... | @@ -714,15 +716,7 @@ function acpMcpContext(threadId: ThreadId | null, self: SelfInvocation): AcpMcpC |
| 714 | }; | 716 | }; |
| 715 | } | 717 | } |
| 716 | 718 | ||
| 717 | function acpMcpServers( | 719 | function acpMcpActivation(context: AcpMcpContext) { |
| 718 | threadId: ThreadId | null, | ||
| 719 | self: SelfInvocation, | ||
| 720 | ): ReadonlyArray<EffectAcpSchema.McpServer> { | ||
| 721 | return acpMcpContext(threadId, self).servers; | ||
| 722 | } | ||
| 723 | |||
| 724 | function acpMcpActivation(threadId: ThreadId | null, self: SelfInvocation) { | ||
| 725 | const context = acpMcpContext(threadId, self); | ||
| 726 | return { mcpServers: context.servers, acpMcpServers: context.acpServers }; | 720 | return { mcpServers: context.servers, acpMcpServers: context.acpServers }; |
| 727 | } | 721 | } |
| 728 | 722 | ||
| ... | @@ -1494,7 +1488,12 @@ export const makeAcpAdapterV2 = Effect.fn("makeAcpAdapterV2")(function* ( | ... | @@ -1494,7 +1488,12 @@ export const makeAcpAdapterV2 = Effect.fn("makeAcpAdapterV2")(function* ( |
| 1494 | const fileSystem = yield* FileSystem.FileSystem; | 1488 | const fileSystem = yield* FileSystem.FileSystem; |
| 1495 | const idAllocator = yield* IdAllocator.IdAllocatorV2; | 1489 | const idAllocator = yield* IdAllocator.IdAllocatorV2; |
| 1496 | const host = yield* ProviderHost.ProviderHost; | 1490 | const host = yield* ProviderHost.ProviderHost; |
| 1491 | const mcpSessions = yield* McpProviderSessions.McpProviderSessions; | ||
| 1497 | const { flavor, selfInvocation: self } = options; | 1492 | const { flavor, selfInvocation: self } = options; |
| 1493 | const readMcpContext = (threadId: ThreadId | null) => | ||
| 1494 | threadId === null | ||
| 1495 | ? Effect.succeed(acpMcpContext(undefined, self)) | ||
| 1496 | : mcpSessions.read(threadId).pipe(Effect.map((session) => acpMcpContext(session, self))); | ||
| 1498 | const driver = flavor.driver; | 1497 | const driver = flavor.driver; |
| 1499 | const continuationRequests = options.continuationRequests; | 1498 | const continuationRequests = options.continuationRequests; |
| 1500 | const postSettleContinuationEnabled = | 1499 | const postSettleContinuationEnabled = |
| ... | @@ -1550,32 +1549,32 @@ export const makeAcpAdapterV2 = Effect.fn("makeAcpAdapterV2")(function* ( | ... | @@ -1550,32 +1549,32 @@ export const makeAcpAdapterV2 = Effect.fn("makeAcpAdapterV2")(function* ( |
| 1550 | readonly sessionId: string | null; | 1549 | readonly sessionId: string | null; |
| 1551 | } | 1550 | } |
| 1552 | let pendingTerminalEnvironment: PendingTerminalEnvironment | null = { | 1551 | let pendingTerminalEnvironment: PendingTerminalEnvironment | null = { |
| 1553 | environment: acpMcpContext(input.threadId, self).processEnvironment, | 1552 | environment: (yield* readMcpContext(input.threadId)).processEnvironment, |
| 1554 | claimUnknownSession: input.initialNativeThreadId === undefined, | 1553 | claimUnknownSession: input.initialNativeThreadId === undefined, |
| 1555 | sessionId: input.initialNativeThreadId ?? null, | 1554 | sessionId: input.initialNativeThreadId ?? null, |
| 1556 | }; | 1555 | }; |
| 1557 | const prepareTerminalEnvironment = ( | 1556 | const prepareTerminalEnvironment = ( |
| 1558 | threadId: ThreadId | null, | 1557 | mcpContext: AcpMcpContext, |
| 1559 | sessionId?: string, | 1558 | sessionId?: string, |
| 1560 | ): void => { | 1559 | ): void => { |
| 1561 | pendingTerminalEnvironment = { | 1560 | pendingTerminalEnvironment = { |
| 1562 | environment: acpMcpContext(threadId, self).processEnvironment, | 1561 | environment: mcpContext.processEnvironment, |
| 1563 | claimUnknownSession: false, | 1562 | claimUnknownSession: false, |
| 1564 | sessionId: sessionId ?? null, | 1563 | sessionId: sessionId ?? null, |
| 1565 | }; | 1564 | }; |
| 1566 | }; | 1565 | }; |
| 1567 | const prepareClaimableTerminalEnvironment = (threadId: ThreadId | null): void => { | 1566 | const prepareClaimableTerminalEnvironment = (mcpContext: AcpMcpContext): void => { |
| 1568 | pendingTerminalEnvironment = { | 1567 | pendingTerminalEnvironment = { |
| 1569 | environment: acpMcpContext(threadId, self).processEnvironment, | 1568 | environment: mcpContext.processEnvironment, |
| 1570 | claimUnknownSession: true, | 1569 | claimUnknownSession: true, |
| 1571 | sessionId: null, | 1570 | sessionId: null, |
| 1572 | }; | 1571 | }; |
| 1573 | }; | 1572 | }; |
| 1574 | const rememberTerminalEnvironment = ( | 1573 | const rememberTerminalEnvironment = ( |
| 1575 | sessionId: string, | 1574 | sessionId: string, |
| 1576 | threadId: ThreadId | null, | 1575 | mcpContext: AcpMcpContext, |
| 1577 | ): void => { | 1576 | ): void => { |
| 1578 | const environment = acpMcpContext(threadId, self).processEnvironment; | 1577 | const environment = mcpContext.processEnvironment; |
| 1579 | pendingTerminalEnvironment = null; | 1578 | pendingTerminalEnvironment = null; |
| 1580 | if (environment === undefined) { | 1579 | if (environment === undefined) { |
| 1581 | terminalEnvironmentBySessionId.delete(sessionId); | 1580 | terminalEnvironmentBySessionId.delete(sessionId); |
| ... | @@ -2097,12 +2096,11 @@ export const makeAcpAdapterV2 = Effect.fn("makeAcpAdapterV2")(function* ( | ... | @@ -2097,12 +2096,11 @@ export const makeAcpAdapterV2 = Effect.fn("makeAcpAdapterV2")(function* ( |
| 2097 | ).pipe(Effect.asVoid); | 2096 | ).pipe(Effect.asVoid); |
| 2098 | const makeRuntimeInput = ( | 2097 | const makeRuntimeInput = ( |
| 2099 | runtimeGeneration: number, | 2098 | runtimeGeneration: number, |
| 2100 | threadId: ThreadId | null, | 2099 | mcpContext: AcpMcpContext, |
| 2101 | resumeSessionId?: string, | 2100 | resumeSessionId?: string, |
| 2102 | onTermination: AcpAdapterV2RuntimeInput["onTermination"] = () => | 2101 | onTermination: AcpAdapterV2RuntimeInput["onTermination"] = () => |
| 2103 | handleRuntimeTerminationAtGeneration(runtimeGeneration), | 2102 | handleRuntimeTerminationAtGeneration(runtimeGeneration), |
| 2104 | ): AcpAdapterV2RuntimeInput => { | 2103 | ): AcpAdapterV2RuntimeInput => { |
| 2105 | const mcpContext = acpMcpContext(threadId, self); | ||
| 2106 | return { | 2104 | return { |
| 2107 | cwd: input.runtimePolicy.cwd ?? process.cwd(), | 2105 | cwd: input.runtimePolicy.cwd ?? process.cwd(), |
| 2108 | runtimePolicy: input.runtimePolicy, | 2106 | runtimePolicy: input.runtimePolicy, |
| ... | @@ -5989,10 +5987,9 @@ export const makeAcpAdapterV2 = Effect.fn("makeAcpAdapterV2")(function* ( | ... | @@ -5989,10 +5987,9 @@ export const makeAcpAdapterV2 = Effect.fn("makeAcpAdapterV2")(function* ( |
| 5989 | 5987 | ||
| 5990 | /** Builds the MCP-over-ACP bridge for one runtime, disposed with that runtime's scope. */ | 5988 | /** Builds the MCP-over-ACP bridge for one runtime, disposed with that runtime's scope. */ |
| 5991 | const makeRuntimeMcpBridge = Effect.fnUntraced(function* ( | 5989 | const makeRuntimeMcpBridge = Effect.fnUntraced(function* ( |
| 5992 | threadId: ThreadId | null, | 5990 | mcpContext: AcpMcpContext, |
| 5993 | scope: Scope.Scope, | 5991 | scope: Scope.Scope, |
| 5994 | ) { | 5992 | ) { |
| 5995 | const mcpContext = acpMcpContext(threadId, self); | ||
| 5996 | if (mcpContext.endpoint === undefined || mcpContext.authorization === undefined) { | 5993 | if (mcpContext.endpoint === undefined || mcpContext.authorization === undefined) { |
| 5997 | return undefined; | 5994 | return undefined; |
| 5998 | } | 5995 | } |
| ... | @@ -6013,10 +6010,11 @@ export const makeAcpAdapterV2 = Effect.fn("makeAcpAdapterV2")(function* ( | ... | @@ -6013,10 +6010,11 @@ export const makeAcpAdapterV2 = Effect.fn("makeAcpAdapterV2")(function* ( |
| 6013 | yield* Scope.close(runtimeScope, Exit.void); | 6010 | yield* Scope.close(runtimeScope, Exit.void); |
| 6014 | } | 6011 | } |
| 6015 | runtimeScope = yield* Scope.make(); | 6012 | runtimeScope = yield* Scope.make(); |
| 6016 | runtimeMcpBridge = yield* makeRuntimeMcpBridge(threadId, runtimeScope); | 6013 | const mcpContext = yield* readMcpContext(threadId); |
| 6014 | runtimeMcpBridge = yield* makeRuntimeMcpBridge(mcpContext, runtimeScope); | ||
| 6017 | const runtimeGeneration = yield* Ref.get(runtimeCallbackGeneration); | 6015 | const runtimeGeneration = yield* Ref.get(runtimeCallbackGeneration); |
| 6018 | runtime = yield* flavor | 6016 | runtime = yield* flavor |
| 6019 | .makeRuntime(makeRuntimeInput(runtimeGeneration, threadId, resumeSessionId)) | 6017 | .makeRuntime(makeRuntimeInput(runtimeGeneration, mcpContext, resumeSessionId)) |
| 6020 | .pipe( | 6018 | .pipe( |
| 6021 | Effect.provideService(Scope.Scope, runtimeScope), | 6019 | Effect.provideService(Scope.Scope, runtimeScope), |
| 6022 | Effect.provideService(Crypto.Crypto, crypto), | 6020 | Effect.provideService(Crypto.Crypto, crypto), |
| ... | @@ -6084,12 +6082,13 @@ export const makeAcpAdapterV2 = Effect.fn("makeAcpAdapterV2")(function* ( | ... | @@ -6084,12 +6082,13 @@ export const makeAcpAdapterV2 = Effect.fn("makeAcpAdapterV2")(function* ( |
| 6084 | }); | 6082 | }); |
| 6085 | let replacementMcpBridge: AcpMcpOverAcpBridge | undefined; | 6083 | let replacementMcpBridge: AcpMcpOverAcpBridge | undefined; |
| 6086 | const startup = Effect.gen(function* () { | 6084 | const startup = Effect.gen(function* () { |
| 6087 | replacementMcpBridge = yield* makeRuntimeMcpBridge(threadId, replacementScope); | 6085 | const mcpContext = yield* readMcpContext(threadId); |
| 6086 | replacementMcpBridge = yield* makeRuntimeMcpBridge(mcpContext, replacementScope); | ||
| 6088 | const replacementRuntime = yield* flavor | 6087 | const replacementRuntime = yield* flavor |
| 6089 | .makeRuntime( | 6088 | .makeRuntime( |
| 6090 | makeRuntimeInput( | 6089 | makeRuntimeInput( |
| 6091 | replacementGeneration, | 6090 | replacementGeneration, |
| 6092 | threadId, | 6091 | mcpContext, |
| 6093 | undefined, | 6092 | undefined, |
| 6094 | handleCandidateTermination, | 6093 | handleCandidateTermination, |
| 6095 | ), | 6094 | ), |
| ... | @@ -6150,7 +6149,10 @@ export const makeAcpAdapterV2 = Effect.fn("makeAcpAdapterV2")(function* ( | ... | @@ -6150,7 +6149,10 @@ export const makeAcpAdapterV2 = Effect.fn("makeAcpAdapterV2")(function* ( |
| 6150 | runtimeScope = replacementScope; | 6149 | runtimeScope = replacementScope; |
| 6151 | runtimeMcpBridge = replacementMcpBridge; | 6150 | runtimeMcpBridge = replacementMcpBridge; |
| 6152 | yield* Ref.set(runtimeCallbackGeneration, replacementGeneration); | 6151 | yield* Ref.set(runtimeCallbackGeneration, replacementGeneration); |
| 6153 | prepareTerminalEnvironment(threadId, replacementExit.value.started.sessionId); | 6152 | prepareTerminalEnvironment( |
| 6153 | yield* readMcpContext(threadId), | ||
| 6154 | replacementExit.value.started.sessionId, | ||
| 6155 | ); | ||
| 6154 | yield* wireAcpRuntimeTerminalHandlers(replacementExit.value.replacementRuntime); | 6156 | yield* wireAcpRuntimeTerminalHandlers(replacementExit.value.replacementRuntime); |
| 6155 | yield* commitSessionState(replacementExit.value.started); | 6157 | yield* commitSessionState(replacementExit.value.started); |
| 6156 | while (true) { | 6158 | while (true) { |
| ... | @@ -6224,12 +6226,12 @@ export const makeAcpAdapterV2 = Effect.fn("makeAcpAdapterV2")(function* ( | ... | @@ -6224,12 +6226,12 @@ export const makeAcpAdapterV2 = Effect.fn("makeAcpAdapterV2")(function* ( |
| 6224 | }); | 6226 | }); |
| 6225 | itemIdentityVersion = 2; | 6227 | itemIdentityVersion = 2; |
| 6226 | yield* Ref.set(runtimeRestartRequired, false); | 6228 | yield* Ref.set(runtimeRestartRequired, false); |
| 6227 | prepareClaimableTerminalEnvironment(input.threadId); | 6229 | prepareClaimableTerminalEnvironment(yield* readMcpContext(input.threadId)); |
| 6228 | return yield* startAcpRuntime(input.threadId); | 6230 | return yield* startAcpRuntime(input.threadId); |
| 6229 | }); | 6231 | }); |
| 6230 | yield* Ref.set(activeSessionId, started.sessionId); | 6232 | yield* Ref.set(activeSessionId, started.sessionId); |
| 6231 | yield* Ref.set(activeSessionSetup, started); | 6233 | yield* Ref.set(activeSessionSetup, started); |
| 6232 | rememberTerminalEnvironment(started.sessionId, input.threadId); | 6234 | rememberTerminalEnvironment(started.sessionId, yield* readMcpContext(input.threadId)); |
| 6233 | const capabilities = negotiatedCapabilities(flavor.capabilities, started); | 6235 | const capabilities = negotiatedCapabilities(flavor.capabilities, started); |
| 6234 | const canLoadSession = started.initializeResult.agentCapabilities?.loadSession === true; | 6236 | const canLoadSession = started.initializeResult.agentCapabilities?.loadSession === true; |
| 6235 | const canResumeSession = | 6237 | const canResumeSession = |
| ... | @@ -6250,8 +6252,9 @@ export const makeAcpAdapterV2 = Effect.fn("makeAcpAdapterV2")(function* ( | ... | @@ -6250,8 +6252,9 @@ export const makeAcpAdapterV2 = Effect.fn("makeAcpAdapterV2")(function* ( |
| 6250 | if (initialFailure !== undefined) { | 6252 | if (initialFailure !== undefined) { |
| 6251 | return yield* initialFailure; | 6253 | return yield* initialFailure; |
| 6252 | } | 6254 | } |
| 6253 | const activationOptions = acpMcpActivation(threadId, self); | 6255 | const mcpContext = yield* readMcpContext(threadId); |
| 6254 | prepareTerminalEnvironment(threadId, sessionId); | 6256 | const activationOptions = acpMcpActivation(mcpContext); |
| 6257 | prepareTerminalEnvironment(mcpContext, sessionId); | ||
| 6255 | const activated = canLoadSession | 6258 | const activated = canLoadSession |
| 6256 | ? yield* runtime.loadSession(sessionId, activationOptions) | 6259 | ? yield* runtime.loadSession(sessionId, activationOptions) |
| 6257 | : canResumeSession | 6260 | : canResumeSession |
| ... | @@ -6260,7 +6263,7 @@ export const makeAcpAdapterV2 = Effect.fn("makeAcpAdapterV2")(function* ( | ... | @@ -6260,7 +6263,7 @@ export const makeAcpAdapterV2 = Effect.fn("makeAcpAdapterV2")(function* ( |
| 6260 | driver, | 6263 | driver, |
| 6261 | detail: `ACP driver cannot load or resume session ${sessionId}`, | 6264 | detail: `ACP driver cannot load or resume session ${sessionId}`, |
| 6262 | }); | 6265 | }); |
| 6263 | rememberTerminalEnvironment(activated.sessionId, threadId); | 6266 | rememberTerminalEnvironment(activated.sessionId, yield* readMcpContext(threadId)); |
| 6264 | return activated; | 6267 | return activated; |
| 6265 | }); | 6268 | }); |
| 6266 | 6269 | ||
| ... | @@ -6771,7 +6774,7 @@ export const makeAcpAdapterV2 = Effect.fn("makeAcpAdapterV2")(function* ( | ... | @@ -6771,7 +6774,7 @@ export const makeAcpAdapterV2 = Effect.fn("makeAcpAdapterV2")(function* ( |
| 6771 | const prompt: Array<EffectAcpSchema.ContentBlock> = []; | 6774 | const prompt: Array<EffectAcpSchema.ContentBlock> = []; |
| 6772 | const instructionState = { | 6775 | const instructionState = { |
| 6773 | interactionMode: turnInput.runtimePolicy.interactionMode, | 6776 | interactionMode: turnInput.runtimePolicy.interactionMode, |
| 6774 | hasT3Mcp: acpMcpServers(turnInput.threadId, self).length > 0, | 6777 | hasT3Mcp: (yield* readMcpContext(turnInput.threadId)).servers.length > 0, |
| 6775 | } satisfies T3AcpInstructionState; | 6778 | } satisfies T3AcpInstructionState; |
| 6776 | const previousInstructionState = (yield* Ref.get(promptInstructionStates)).get(sessionId); | 6779 | const previousInstructionState = (yield* Ref.get(promptInstructionStates)).get(sessionId); |
| 6777 | const messageText = providerMessageTextWithAttachmentPaths({ | 6780 | const messageText = providerMessageTextWithAttachmentPaths({ |
| ... | @@ -7820,14 +7823,17 @@ export const makeAcpAdapterV2 = Effect.fn("makeAcpAdapterV2")(function* ( | ... | @@ -7820,14 +7823,17 @@ export const makeAcpAdapterV2 = Effect.fn("makeAcpAdapterV2")(function* ( |
| 7820 | loadingMessageId: null, | 7823 | loadingMessageId: null, |
| 7821 | loadingIndex: 0, | 7824 | loadingIndex: 0, |
| 7822 | }); | 7825 | }); |
| 7823 | prepareTerminalEnvironment(snapshotInput.providerThread.appThreadId, sessionId); | 7826 | const mcpContext = yield* readMcpContext( |
| 7827 | snapshotInput.providerThread.appThreadId, | ||
| 7828 | ); | ||
| 7829 | prepareTerminalEnvironment(mcpContext, sessionId); | ||
| 7824 | const activated = yield* runtime.loadSession( | 7830 | const activated = yield* runtime.loadSession( |
| 7825 | sessionId, | 7831 | sessionId, |
| 7826 | acpMcpActivation(snapshotInput.providerThread.appThreadId, self), | 7832 | acpMcpActivation(mcpContext), |
| 7827 | ); | 7833 | ); |
| 7828 | rememberTerminalEnvironment( | 7834 | rememberTerminalEnvironment( |
| 7829 | activated.sessionId, | 7835 | activated.sessionId, |
| 7830 | snapshotInput.providerThread.appThreadId, | 7836 | yield* readMcpContext(snapshotInput.providerThread.appThreadId), |
| 7831 | ); | 7837 | ); |
| 7832 | yield* Ref.set(activeSessionId, activated.sessionId); | 7838 | yield* Ref.set(activeSessionId, activated.sessionId); |
| 7833 | yield* Ref.set(activeSessionSetup, activated); | 7839 | yield* Ref.set(activeSessionSetup, activated); |
| ... | @@ -7883,14 +7889,16 @@ export const makeAcpAdapterV2 = Effect.fn("makeAcpAdapterV2")(function* ( | ... | @@ -7883,14 +7889,16 @@ export const makeAcpAdapterV2 = Effect.fn("makeAcpAdapterV2")(function* ( |
| 7883 | // loading any of the rolled-back conversation. | 7889 | // loading any of the rolled-back conversation. |
| 7884 | yield* awaitRuntimeTeardown(); | 7890 | yield* awaitRuntimeTeardown(); |
| 7885 | itemIdentityVersion = 2; | 7891 | itemIdentityVersion = 2; |
| 7886 | prepareTerminalEnvironment(rollbackInput.providerThread.appThreadId); | 7892 | prepareTerminalEnvironment( |
| 7893 | yield* readMcpContext(rollbackInput.providerThread.appThreadId), | ||
| 7894 | ); | ||
| 7887 | const replacement = yield* startReplacementAcpRuntime( | 7895 | const replacement = yield* startReplacementAcpRuntime( |
| 7888 | rollbackInput.providerThread.appThreadId, | 7896 | rollbackInput.providerThread.appThreadId, |
| 7889 | (candidate) => | 7897 | (candidate) => |
| 7890 | Effect.gen(function* () { | 7898 | Effect.gen(function* () { |
| 7891 | rememberTerminalEnvironment( | 7899 | rememberTerminalEnvironment( |
| 7892 | candidate.sessionId, | 7900 | candidate.sessionId, |
| 7893 | rollbackInput.providerThread.appThreadId, | 7901 | yield* readMcpContext(rollbackInput.providerThread.appThreadId), |
| 7894 | ); | 7902 | ); |
| 7895 | yield* Ref.set(runtimeRestartRequired, false); | 7903 | yield* Ref.set(runtimeRestartRequired, false); |
| 7896 | yield* Ref.set(activeSessionId, candidate.sessionId); | 7904 | yield* Ref.set(activeSessionId, candidate.sessionId); |
| ... | @@ -7983,12 +7991,16 @@ export const makeAcpAdapterV2 = Effect.fn("makeAcpAdapterV2")(function* ( | ... | @@ -7983,12 +7991,16 @@ export const makeAcpAdapterV2 = Effect.fn("makeAcpAdapterV2")(function* ( |
| 7983 | driver, | 7991 | driver, |
| 7984 | forkInput.sourceProviderThread, | 7992 | forkInput.sourceProviderThread, |
| 7985 | ); | 7993 | ); |
| 7986 | prepareTerminalEnvironment(forkInput.targetThreadId); | 7994 | const mcpContext = yield* readMcpContext(forkInput.targetThreadId); |
| 7995 | prepareTerminalEnvironment(mcpContext); | ||
| 7987 | const forked = yield* runtime.forkSession( | 7996 | const forked = yield* runtime.forkSession( |
| 7988 | sourceSessionId, | 7997 | sourceSessionId, |
| 7989 | acpMcpActivation(forkInput.targetThreadId, self), | 7998 | acpMcpActivation(mcpContext), |
| 7999 | ); | ||
| 8000 | rememberTerminalEnvironment( | ||
| 8001 | forked.sessionId, | ||
| 8002 | yield* readMcpContext(forkInput.targetThreadId), | ||
| 7990 | ); | 8003 | ); |
| 7991 | rememberTerminalEnvironment(forked.sessionId, forkInput.targetThreadId); | ||
| 7992 | yield* Ref.set(activeSessionId, forked.sessionId); | 8004 | yield* Ref.set(activeSessionId, forked.sessionId); |
| 7993 | yield* Ref.set(activeSessionSetup, forked); | 8005 | yield* Ref.set(activeSessionSetup, forked); |
| 7994 | yield* Ref.set(activeSelection, null); | 8006 | yield* Ref.set(activeSelection, null); |
| ... | @@ -8051,4 +8063,7 @@ export const makeAcpAdapterV2 = Effect.fn("makeAcpAdapterV2")(function* ( | ... | @@ -8051,4 +8063,7 @@ export const makeAcpAdapterV2 = Effect.fn("makeAcpAdapterV2")(function* ( |
| 8051 | }); | 8063 | }); |
| 8052 | }); | 8064 | }); |
| 8053 | 8065 | ||
| 8054 | export type AcpAdapterV2Env = FileSystem.FileSystem | IdAllocator.IdAllocatorV2; | 8066 | export type AcpAdapterV2Env = |
| 8067 | | FileSystem.FileSystem | ||
| 8068 | | IdAllocator.IdAllocatorV2 | ||
| 8069 | | McpProviderSessions.McpProviderSessions; |
packages/provider-core/package.json+4| ... | @@ -63,6 +63,10 @@ | ... | @@ -63,6 +63,10 @@ |
| 63 | "types": "./src/server/managedProvider.ts", | 63 | "types": "./src/server/managedProvider.ts", |
| 64 | "import": "./src/server/managedProvider.ts" | 64 | "import": "./src/server/managedProvider.ts" |
| 65 | }, | 65 | }, |
| 66 | "./server/McpProviderSessions": { | ||
| 67 | "types": "./src/server/McpProviderSessions.ts", | ||
| 68 | "import": "./src/server/McpProviderSessions.ts" | ||
| 69 | }, | ||
| 66 | "./server/mcpSession": { | 70 | "./server/mcpSession": { |
| 67 | "types": "./src/server/mcpSession.ts", | 71 | "types": "./src/server/mcpSession.ts", |
| 68 | "import": "./src/server/mcpSession.ts" | 72 | "import": "./src/server/mcpSession.ts" |
packages/provider-core/src/server/McpProviderSessions.ts created+40| ... | @@ -0,0 +1,40 @@ | ||
| 1 | /** | ||
| 2 | * The T3 MCP credential each thread's provider session should expose to its | ||
| 3 | * agent. The session manager writes it before a session starts; adapters read | ||
| 4 | * it when they build provider launch options. | ||
| 5 | * | ||
| 6 | * @module provider-core/server/McpProviderSessions | ||
| 7 | */ | ||
| 8 | import type { ThreadId } from "@t3tools/contracts"; | ||
| 9 | import * as Context from "effect/Context"; | ||
| 10 | import * as Effect from "effect/Effect"; | ||
| 11 | import * as Layer from "effect/Layer"; | ||
| 12 | import * as Ref from "effect/Ref"; | ||
| 13 | |||
| 14 | import type { McpProviderSessionConfig } from "./mcpSession.ts"; | ||
| 15 | |||
| 16 | export class McpProviderSessions extends Context.Service< | ||
| 17 | McpProviderSessions, | ||
| 18 | { | ||
| 19 | readonly set: (config: McpProviderSessionConfig) => Effect.Effect<void>; | ||
| 20 | readonly read: (threadId: ThreadId) => Effect.Effect<McpProviderSessionConfig | undefined>; | ||
| 21 | readonly clear: (threadId: ThreadId) => Effect.Effect<void>; | ||
| 22 | } | ||
| 23 | >()("@t3tools/provider-core/server/McpProviderSessions") {} | ||
| 24 | |||
| 25 | const make = Effect.gen(function* () { | ||
| 26 | const sessions = yield* Ref.make(new Map<ThreadId, McpProviderSessionConfig>()); | ||
| 27 | return McpProviderSessions.of({ | ||
| 28 | set: (config) => | ||
| 29 | Ref.update(sessions, (current) => new Map(current).set(config.threadId, config)), | ||
| 30 | read: (threadId) => Ref.get(sessions).pipe(Effect.map((current) => current.get(threadId))), | ||
| 31 | clear: (threadId) => | ||
| 32 | Ref.update(sessions, (current) => { | ||
| 33 | const next = new Map(current); | ||
| 34 | next.delete(threadId); | ||
| 35 | return next; | ||
| 36 | }), | ||
| 37 | }); | ||
| 38 | }); | ||
| 39 | |||
| 40 | export const layer = Layer.effect(McpProviderSessions, make); | ||
packages/provider-core/src/server/mcpSession.ts-18| ... | @@ -40,21 +40,3 @@ export function withAgentDeviceEnvironment( | ... | @@ -40,21 +40,3 @@ export function withAgentDeviceEnvironment( |
| 40 | ...(shimDir ? { PATH: basePath ? `${shimDir}${separator}${basePath}` : shimDir } : {}), | 40 | ...(shimDir ? { PATH: basePath ? `${shimDir}${separator}${basePath}` : shimDir } : {}), |
| 41 | }; | 41 | }; |
| 42 | } | 42 | } |
| 43 | |||
| 44 | const sessionsByThread = new Map<ThreadId, McpProviderSessionConfig>(); | ||
| 45 | |||
| 46 | export function setMcpProviderSession(config: McpProviderSessionConfig): void { | ||
| 47 | sessionsByThread.set(config.threadId, config); | ||
| 48 | } | ||
| 49 | |||
| 50 | export function readMcpProviderSession(threadId: ThreadId): McpProviderSessionConfig | undefined { | ||
| 51 | return sessionsByThread.get(threadId); | ||
| 52 | } | ||
| 53 | |||
| 54 | export function clearMcpProviderSession(threadId: ThreadId): void { | ||
| 55 | sessionsByThread.delete(threadId); | ||
| 56 | } | ||
| 57 | |||
| 58 | function clearAllMcpProviderSessions(): void { | ||
| 59 | sessionsByThread.clear(); | ||
| 60 | } |
packages/provider-cursor/src/server/adapter.test.ts+48-35| ... | @@ -23,8 +23,8 @@ import * as Schema from "effect/Schema"; | ... | @@ -23,8 +23,8 @@ import * as Schema from "effect/Schema"; |
| 23 | import * as Stream from "effect/Stream"; | 23 | import * as Stream from "effect/Stream"; |
| 24 | 24 | ||
| 25 | import { layerTestProviderHost } from "@t3tools/provider-testing/host"; | 25 | import { layerTestProviderHost } from "@t3tools/provider-testing/host"; |
| 26 | import * as McpProviderSession from "@t3tools/provider-core/server/mcpSession"; | ||
| 27 | import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator"; | 26 | import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator"; |
| 27 | import * as McpProviderSessions from "@t3tools/provider-core/server/McpProviderSessions"; | ||
| 28 | import * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter"; | 28 | import * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter"; |
| 29 | import { | 29 | import { |
| 30 | cursorMcpServers, | 30 | cursorMcpServers, |
| ... | @@ -199,7 +199,12 @@ describe("CursorAdapterV2", () => { | ... | @@ -199,7 +199,12 @@ describe("CursorAdapterV2", () => { |
| 199 | : "failed", | 199 | : "failed", |
| 200 | ); | 200 | ); |
| 201 | assert.isNotNull(rows.at(-1)?.subagent.completedAt); | 201 | assert.isNotNull(rows.at(-1)?.subagent.completedAt); |
| 202 | }).pipe(Effect.scoped, Effect.provide(Layer.merge(NodeServices.layer, IdAllocator.layer))), | 202 | }).pipe( |
| 203 | Effect.scoped, | ||
| 204 | Effect.provide( | ||
| 205 | Layer.mergeAll(NodeServices.layer, IdAllocator.layer, McpProviderSessions.layer), | ||
| 206 | ), | ||
| 207 | ), | ||
| 203 | ); | 208 | ); |
| 204 | 209 | ||
| 205 | it.effect("fails standalone SDK transport diagnostics and sends compaction as /compress", () => | 210 | it.effect("fails standalone SDK transport diagnostics and sends compaction as /compress", () => |
| ... | @@ -344,7 +349,12 @@ describe("CursorAdapterV2", () => { | ... | @@ -344,7 +349,12 @@ describe("CursorAdapterV2", () => { |
| 344 | Stream.runHead, | 349 | Stream.runHead, |
| 345 | ); | 350 | ); |
| 346 | assert.equal(sentMessages[1], "/compress"); | 351 | assert.equal(sentMessages[1], "/compress"); |
| 347 | }).pipe(Effect.scoped, Effect.provide(Layer.merge(NodeServices.layer, IdAllocator.layer))), | 352 | }).pipe( |
| 353 | Effect.scoped, | ||
| 354 | Effect.provide( | ||
| 355 | Layer.mergeAll(NodeServices.layer, IdAllocator.layer, McpProviderSessions.layer), | ||
| 356 | ), | ||
| 357 | ), | ||
| 348 | ); | 358 | ); |
| 349 | 359 | ||
| 350 | it.effect("projects Cursor directory trees and lint diagnostics as file search results", () => | 360 | it.effect("projects Cursor directory trees and lint diagnostics as file search results", () => |
| ... | @@ -746,7 +756,12 @@ describe("CursorAdapterV2", () => { | ... | @@ -746,7 +756,12 @@ describe("CursorAdapterV2", () => { |
| 746 | }, | 756 | }, |
| 747 | ], | 757 | ], |
| 748 | ); | 758 | ); |
| 749 | }).pipe(Effect.scoped, Effect.provide(Layer.merge(NodeServices.layer, IdAllocator.layer))), | 759 | }).pipe( |
| 760 | Effect.scoped, | ||
| 761 | Effect.provide( | ||
| 762 | Layer.mergeAll(NodeServices.layer, IdAllocator.layer, McpProviderSessions.layer), | ||
| 763 | ), | ||
| 764 | ), | ||
| 750 | ); | 765 | ); |
| 751 | 766 | ||
| 752 | it("maps Cursor auto and model parameters to SDK selections", () => { | 767 | it("maps Cursor auto and model parameters to SDK selections", () => { |
| ... | @@ -843,6 +858,7 @@ describe("CursorAdapterV2", () => { | ... | @@ -843,6 +858,7 @@ describe("CursorAdapterV2", () => { |
| 843 | }, | 858 | }, |
| 844 | runtimePolicy: { runtimeMode, interactionMode: "default", cwd: "/workspace" }, | 859 | runtimePolicy: { runtimeMode, interactionMode: "default", cwd: "/workspace" }, |
| 845 | threadId: ThreadId.make("thread-cursor-setting-sources"), | 860 | threadId: ThreadId.make("thread-cursor-setting-sources"), |
| 861 | mcpSession: undefined, | ||
| 846 | }); | 862 | }); |
| 847 | assert.deepEqual(options.local?.settingSources, [ | 863 | assert.deepEqual(options.local?.settingSources, [ |
| 848 | "project", | 864 | "project", |
| ... | @@ -856,7 +872,7 @@ describe("CursorAdapterV2", () => { | ... | @@ -856,7 +872,7 @@ describe("CursorAdapterV2", () => { |
| 856 | 872 | ||
| 857 | it("injects thread-scoped MCP credentials without logging them", () => { | 873 | it("injects thread-scoped MCP credentials without logging them", () => { |
| 858 | const threadId = ThreadId.make("thread-cursor-mcp"); | 874 | const threadId = ThreadId.make("thread-cursor-mcp"); |
| 859 | McpProviderSession.setMcpProviderSession({ | 875 | const mcpSession = { |
| 860 | environmentId: EnvironmentId.make("environment-cursor-mcp"), | 876 | environmentId: EnvironmentId.make("environment-cursor-mcp"), |
| 861 | threadId, | 877 | threadId, |
| 862 | providerSessionId: "mcp-session-cursor", | 878 | providerSessionId: "mcp-session-cursor", |
| ... | @@ -864,40 +880,37 @@ describe("CursorAdapterV2", () => { | ... | @@ -864,40 +880,37 @@ describe("CursorAdapterV2", () => { |
| 864 | endpoint: "http://127.0.0.1:43123/mcp", | 880 | endpoint: "http://127.0.0.1:43123/mcp", |
| 865 | authorizationHeader: "Bearer secret-cursor-mcp-token", | 881 | authorizationHeader: "Bearer secret-cursor-mcp-token", |
| 866 | browserToolsAvailable: true, | 882 | browserToolsAvailable: true, |
| 867 | }); | 883 | }; |
| 868 | 884 | ||
| 869 | try { | 885 | assert.deepEqual(cursorMcpServers(mcpSession), { |
| 870 | assert.deepEqual(cursorMcpServers(threadId), { | 886 | "t3-code": { |
| 871 | "t3-code": { | 887 | type: "http", |
| 872 | type: "http", | 888 | url: "http://127.0.0.1:43123/mcp", |
| 873 | url: "http://127.0.0.1:43123/mcp", | 889 | headers: { |
| 874 | headers: { | 890 | Authorization: "Bearer secret-cursor-mcp-token", |
| 875 | Authorization: "Bearer secret-cursor-mcp-token", | ||
| 876 | }, | ||
| 877 | }, | 891 | }, |
| 878 | }); | 892 | }, |
| 893 | }); | ||
| 879 | 894 | ||
| 880 | const options = makeCursorAgentOptions({ | 895 | const options = makeCursorAgentOptions({ |
| 881 | apiKey: "secret-cursor-api-key", | 896 | apiKey: "secret-cursor-api-key", |
| 882 | modelSelection: { | 897 | modelSelection: { |
| 883 | instanceId: ProviderInstanceId.make("cursor"), | 898 | instanceId: ProviderInstanceId.make("cursor"), |
| 884 | model: "composer-2.5", | 899 | model: "composer-2.5", |
| 885 | }, | 900 | }, |
| 886 | runtimePolicy: { | 901 | runtimePolicy: { |
| 887 | runtimeMode: "full-access", | 902 | runtimeMode: "full-access", |
| 888 | interactionMode: "default", | 903 | interactionMode: "default", |
| 889 | cwd: "/workspace", | 904 | cwd: "/workspace", |
| 890 | }, | 905 | }, |
| 891 | threadId, | 906 | threadId, |
| 892 | }); | 907 | mcpSession, |
| 893 | assert.deepEqual(options.mcpServers, cursorMcpServers(threadId)); | 908 | }); |
| 909 | assert.deepEqual(options.mcpServers, cursorMcpServers(mcpSession)); | ||
| 894 | 910 | ||
| 895 | const logged = JSON.stringify(CursorAgentSdk.loggedCursorAgentOptions(options)); | 911 | const logged = JSON.stringify(CursorAgentSdk.loggedCursorAgentOptions(options)); |
| 896 | assert.notInclude(logged, "secret-cursor-api-key"); | 912 | assert.notInclude(logged, "secret-cursor-api-key"); |
| 897 | assert.notInclude(logged, "secret-cursor-mcp-token"); | 913 | assert.notInclude(logged, "secret-cursor-mcp-token"); |
| 898 | } finally { | ||
| 899 | McpProviderSession.clearMcpProviderSession(threadId); | ||
| 900 | } | ||
| 901 | }); | 914 | }); |
| 902 | 915 | ||
| 903 | it("recognizes direct and SDK-wrapped abort failures as cancellation", () => { | 916 | it("recognizes direct and SDK-wrapped abort failures as cancellation", () => { |
packages/provider-cursor/src/server/adapter.ts+14-6| ... | @@ -44,6 +44,7 @@ import * as Stream from "effect/Stream"; | ... | @@ -44,6 +44,7 @@ import * as Stream from "effect/Stream"; |
| 44 | 44 | ||
| 45 | import * as ProviderHost from "@t3tools/provider-core/server/ProviderHost"; | 45 | import * as ProviderHost from "@t3tools/provider-core/server/ProviderHost"; |
| 46 | import { mcpToolPresentation } from "@t3tools/provider-core/server/mcpToolPresentation"; | 46 | import { mcpToolPresentation } from "@t3tools/provider-core/server/mcpToolPresentation"; |
| 47 | import * as McpProviderSessions from "@t3tools/provider-core/server/McpProviderSessions"; | ||
| 47 | import * as McpProviderSession from "@t3tools/provider-core/server/mcpSession"; | 48 | import * as McpProviderSession from "@t3tools/provider-core/server/mcpSession"; |
| 48 | import { CursorTransportFailure } from "./transportFailure.ts"; | 49 | import { CursorTransportFailure } from "./transportFailure.ts"; |
| 49 | import { cursorSdkModelSelection } from "./sdkModel.ts"; | 50 | import { cursorSdkModelSelection } from "./sdkModel.ts"; |
| ... | @@ -201,8 +202,9 @@ export function cursorRuntimeAgentPolicy( | ... | @@ -201,8 +202,9 @@ export function cursorRuntimeAgentPolicy( |
| 201 | }; | 202 | }; |
| 202 | } | 203 | } |
| 203 | 204 | ||
| 204 | export function cursorMcpServers(threadId: ThreadId): Record<string, McpServerConfig> | undefined { | 205 | export function cursorMcpServers( |
| 205 | const session = McpProviderSession.readMcpProviderSession(threadId); | 206 | session: McpProviderSession.McpProviderSessionConfig | undefined, |
| 207 | ): Record<string, McpServerConfig> | undefined { | ||
| 206 | if (session === undefined) { | 208 | if (session === undefined) { |
| 207 | return undefined; | 209 | return undefined; |
| 208 | } | 210 | } |
| ... | @@ -306,9 +308,10 @@ export function makeCursorAgentOptions(input: { | ... | @@ -306,9 +308,10 @@ export function makeCursorAgentOptions(input: { |
| 306 | readonly modelSelection: ModelSelection; | 308 | readonly modelSelection: ModelSelection; |
| 307 | readonly runtimePolicy: ProviderAdapter.ProviderAdapterV2RuntimePolicy; | 309 | readonly runtimePolicy: ProviderAdapter.ProviderAdapterV2RuntimePolicy; |
| 308 | readonly threadId: ThreadId; | 310 | readonly threadId: ThreadId; |
| 311 | readonly mcpSession: McpProviderSession.McpProviderSessionConfig | undefined; | ||
| 309 | }): AgentOptions { | 312 | }): AgentOptions { |
| 310 | const policy = cursorRuntimeAgentPolicy(input.runtimePolicy); | 313 | const policy = cursorRuntimeAgentPolicy(input.runtimePolicy); |
| 311 | const mcpServers = cursorMcpServers(input.threadId); | 314 | const mcpServers = cursorMcpServers(input.mcpSession); |
| 312 | return { | 315 | return { |
| 313 | model: cursorSdkModelSelection(input.modelSelection), | 316 | model: cursorSdkModelSelection(input.modelSelection), |
| 314 | name: `T3 Code ${input.threadId}`, | 317 | name: `T3 Code ${input.threadId}`, |
| ... | @@ -854,6 +857,7 @@ export const makeCursorAdapterV2 = Effect.fn("makeCursorAdapterV2")(function* ( | ... | @@ -854,6 +857,7 @@ export const makeCursorAdapterV2 = Effect.fn("makeCursorAdapterV2")(function* ( |
| 854 | const idAllocator = yield* IdAllocator.IdAllocatorV2; | 857 | const idAllocator = yield* IdAllocator.IdAllocatorV2; |
| 855 | const runner = yield* CursorAgentSdk.CursorAgentSdkRunner; | 858 | const runner = yield* CursorAgentSdk.CursorAgentSdkRunner; |
| 856 | const host = yield* ProviderHost.ProviderHost; | 859 | const host = yield* ProviderHost.ProviderHost; |
| 860 | const mcpSessions = yield* McpProviderSessions.McpProviderSessions; | ||
| 857 | const adapterOptions = options; | 861 | const adapterOptions = options; |
| 858 | const apiKey = adapterOptions.environment.CURSOR_API_KEY?.trim() || undefined; | 862 | const apiKey = adapterOptions.environment.CURSOR_API_KEY?.trim() || undefined; |
| 859 | 863 | ||
| ... | @@ -2091,6 +2095,7 @@ export const makeCursorAdapterV2 = Effect.fn("makeCursorAdapterV2")(function* ( | ... | @@ -2091,6 +2095,7 @@ export const makeCursorAdapterV2 = Effect.fn("makeCursorAdapterV2")(function* ( |
| 2091 | modelSelection: openInput.modelSelection, | 2095 | modelSelection: openInput.modelSelection, |
| 2092 | runtimePolicy: openInput.runtimePolicy, | 2096 | runtimePolicy: openInput.runtimePolicy, |
| 2093 | threadId: openInput.threadId, | 2097 | threadId: openInput.threadId, |
| 2098 | mcpSession: yield* mcpSessions.read(openInput.threadId), | ||
| 2094 | }), | 2099 | }), |
| 2095 | threadId: openInput.threadId, | 2100 | threadId: openInput.threadId, |
| 2096 | providerSessionId: input.providerSessionId, | 2101 | providerSessionId: input.providerSessionId, |
| ... | @@ -2106,6 +2111,7 @@ export const makeCursorAdapterV2 = Effect.fn("makeCursorAdapterV2")(function* ( | ... | @@ -2106,6 +2111,7 @@ export const makeCursorAdapterV2 = Effect.fn("makeCursorAdapterV2")(function* ( |
| 2106 | let cursorSkillNames: ReadonlySet<string> | undefined; | 2111 | let cursorSkillNames: ReadonlySet<string> | undefined; |
| 2107 | const resolveUserMessage = Effect.fnUntraced(function* ( | 2112 | const resolveUserMessage = Effect.fnUntraced(function* ( |
| 2108 | turnInput: ProviderAdapter.ProviderAdapterV2TurnInput, | 2113 | turnInput: ProviderAdapter.ProviderAdapterV2TurnInput, |
| 2114 | mcpServers: Record<string, McpServerConfig> | undefined, | ||
| 2109 | ) { | 2115 | ) { |
| 2110 | const rawText = turnInput.message.text; | 2116 | const rawText = turnInput.message.text; |
| 2111 | if (rawText.trim() === "/compress" && turnInput.message.attachments.length === 0) { | 2117 | if (rawText.trim() === "/compress" && turnInput.message.attachments.length === 0) { |
| ... | @@ -2135,7 +2141,7 @@ export const makeCursorAdapterV2 = Effect.fn("makeCursorAdapterV2")(function* ( | ... | @@ -2135,7 +2141,7 @@ export const makeCursorAdapterV2 = Effect.fn("makeCursorAdapterV2")(function* ( |
| 2135 | resolveAttachmentPath: host.resolveAttachmentPath, | 2141 | resolveAttachmentPath: host.resolveAttachmentPath, |
| 2136 | }), | 2142 | }), |
| 2137 | runOrdinal: turnInput.runOrdinal, | 2143 | runOrdinal: turnInput.runOrdinal, |
| 2138 | hasT3Mcp: cursorMcpServers(turnInput.threadId) !== undefined, | 2144 | hasT3Mcp: mcpServers !== undefined, |
| 2139 | }); | 2145 | }); |
| 2140 | const images = yield* Effect.forEach( | 2146 | const images = yield* Effect.forEach( |
| 2141 | turnInput.message.attachments.filter(isProviderNativeImageAttachment), | 2147 | turnInput.message.attachments.filter(isProviderNativeImageAttachment), |
| ... | @@ -2197,8 +2203,8 @@ export const makeCursorAdapterV2 = Effect.fn("makeCursorAdapterV2")(function* ( | ... | @@ -2197,8 +2203,8 @@ export const makeCursorAdapterV2 = Effect.fn("makeCursorAdapterV2")(function* ( |
| 2197 | modelSelection: turnInput.modelSelection, | 2203 | modelSelection: turnInput.modelSelection, |
| 2198 | runtimePolicy: turnInput.runtimePolicy, | 2204 | runtimePolicy: turnInput.runtimePolicy, |
| 2199 | }); | 2205 | }); |
| 2200 | const message = yield* resolveUserMessage(turnInput); | 2206 | const mcpServers = cursorMcpServers(yield* mcpSessions.read(turnInput.threadId)); |
| 2201 | const mcpServers = cursorMcpServers(turnInput.threadId); | 2207 | const message = yield* resolveUserMessage(turnInput, mcpServers); |
| 2202 | const pendingUpdates: Array<InteractionUpdate> = []; | 2208 | const pendingUpdates: Array<InteractionUpdate> = []; |
| 2203 | let context: ActiveCursorTurn | null = null; | 2209 | let context: ActiveCursorTurn | null = null; |
| 2204 | const sdkRun = yield* agent.session.send({ | 2210 | const sdkRun = yield* agent.session.send({ |
| ... | @@ -2603,6 +2609,7 @@ export type CursorAdapterV2DriverEnv = | ... | @@ -2603,6 +2609,7 @@ export type CursorAdapterV2DriverEnv = |
| 2603 | | FileSystem.FileSystem | 2609 | | FileSystem.FileSystem |
| 2604 | | Path.Path | 2610 | | Path.Path |
| 2605 | | IdAllocator.IdAllocatorV2 | 2611 | | IdAllocator.IdAllocatorV2 |
| 2612 | | McpProviderSessions.McpProviderSessions | ||
| 2606 | | ProviderHost.ProviderHost; | 2613 | | ProviderHost.ProviderHost; |
| 2607 | 2614 | ||
| 2608 | export const CursorAdapterV2Driver: ProviderAdapterDriver< | 2615 | export const CursorAdapterV2Driver: ProviderAdapterDriver< |
| ... | @@ -2646,6 +2653,7 @@ const layer: Layer.Layer< | ... | @@ -2646,6 +2653,7 @@ const layer: Layer.Layer< |
| 2646 | | FileSystem.FileSystem | 2653 | | FileSystem.FileSystem |
| 2647 | | Path.Path | 2654 | | Path.Path |
| 2648 | | IdAllocator.IdAllocatorV2 | 2655 | | IdAllocator.IdAllocatorV2 |
| 2656 | | McpProviderSessions.McpProviderSessions | ||
| 2649 | | ProviderHost.ProviderHost | 2657 | | ProviderHost.ProviderHost |
| 2650 | > = Layer.effect( | 2658 | > = Layer.effect( |
| 2651 | ProviderAdapter.ProviderAdapterV2, | 2659 | ProviderAdapter.ProviderAdapterV2, |
packages/provider-cursor/src/server/driver.test.ts+2| ... | @@ -15,12 +15,14 @@ import { layerTestProviderHost } from "@t3tools/provider-testing/host"; | ... | @@ -15,12 +15,14 @@ import { layerTestProviderHost } from "@t3tools/provider-testing/host"; |
| 15 | import { CursorDriver } from "./driver.ts"; | 15 | import { CursorDriver } from "./driver.ts"; |
| 16 | import * as CursorAgentSdk from "./CursorAgentSdk.ts"; | 16 | import * as CursorAgentSdk from "./CursorAgentSdk.ts"; |
| 17 | import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator"; | 17 | import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator"; |
| 18 | import * as McpProviderSessions from "@t3tools/provider-core/server/McpProviderSessions"; | ||
| 18 | import * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter"; | 19 | import * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter"; |
| 19 | import { Cursor } from "./sdk.ts"; | 20 | import { Cursor } from "./sdk.ts"; |
| 20 | 21 | ||
| 21 | const layerTest = layerTestProviderHost({ runBackgroundWork: false }).pipe( | 22 | const layerTest = layerTestProviderHost({ runBackgroundWork: false }).pipe( |
| 22 | Layer.provideMerge(NodeServices.layer), | 23 | Layer.provideMerge(NodeServices.layer), |
| 23 | Layer.provideMerge(IdAllocator.layer), | 24 | Layer.provideMerge(IdAllocator.layer), |
| 25 | Layer.provideMerge(McpProviderSessions.layer), | ||
| 24 | Layer.provideMerge( | 26 | Layer.provideMerge( |
| 25 | Layer.mock(CursorAgentSdk.CursorAgentSdkRunner)({ | 27 | Layer.mock(CursorAgentSdk.CursorAgentSdkRunner)({ |
| 26 | open: () => Effect.die("Maintenance resolution must not open a Cursor session"), | 28 | open: () => Effect.die("Maintenance resolution must not open a Cursor session"), |
packages/provider-grok/src/server/adapter.ts+3| ... | @@ -23,6 +23,7 @@ import type * as Scope from "effect/Scope"; | ... | @@ -23,6 +23,7 @@ import type * as Scope from "effect/Scope"; |
| 23 | import { ChildProcessSpawner } from "effect/process"; | 23 | import { ChildProcessSpawner } from "effect/process"; |
| 24 | import * as EffectAcpErrors from "effect-acp/errors"; | 24 | import * as EffectAcpErrors from "effect-acp/errors"; |
| 25 | 25 | ||
| 26 | import * as McpProviderSessions from "@t3tools/provider-core/server/McpProviderSessions"; | ||
| 26 | import * as ProviderHost from "@t3tools/provider-core/server/ProviderHost"; | 27 | import * as ProviderHost from "@t3tools/provider-core/server/ProviderHost"; |
| 27 | import { makeAcpNativeLoggerFactory } from "@t3tools/provider-acp/server/nativeLogging"; | 28 | import { makeAcpNativeLoggerFactory } from "@t3tools/provider-acp/server/nativeLogging"; |
| 28 | import { | 29 | import { |
| ... | @@ -352,6 +353,7 @@ export type GrokAdapterV2DriverEnv = | ... | @@ -352,6 +353,7 @@ export type GrokAdapterV2DriverEnv = |
| 352 | | FileSystem.FileSystem | 353 | | FileSystem.FileSystem |
| 353 | | IdAllocator.IdAllocatorV2 | 354 | | IdAllocator.IdAllocatorV2 |
| 354 | | Path.Path | 355 | | Path.Path |
| 356 | | McpProviderSessions.McpProviderSessions | ||
| 355 | | ProviderEventLoggers.ProviderEventLoggers | 357 | | ProviderEventLoggers.ProviderEventLoggers |
| 356 | | ProviderHost.ProviderHost; | 358 | | ProviderHost.ProviderHost; |
| 357 | 359 | ||
| ... | @@ -405,6 +407,7 @@ const layer: Layer.Layer< | ... | @@ -405,6 +407,7 @@ const layer: Layer.Layer< |
| 405 | | Crypto.Crypto | 407 | | Crypto.Crypto |
| 406 | | FileSystem.FileSystem | 408 | | FileSystem.FileSystem |
| 407 | | IdAllocator.IdAllocatorV2 | 409 | | IdAllocator.IdAllocatorV2 |
| 410 | | McpProviderSessions.McpProviderSessions | ||
| 408 | | ProviderEventLoggers.ProviderEventLoggers | 411 | | ProviderEventLoggers.ProviderEventLoggers |
| 409 | | ProviderHost.ProviderHost | 412 | | ProviderHost.ProviderHost |
| 410 | > = Layer.effect( | 413 | > = Layer.effect( |
packages/provider-grok/src/server/driver.test.ts+2| ... | @@ -15,10 +15,12 @@ import { layerTestProviderHost } from "@t3tools/provider-testing/host"; | ... | @@ -15,10 +15,12 @@ import { layerTestProviderHost } from "@t3tools/provider-testing/host"; |
| 15 | import { GrokDriver } from "./driver.ts"; | 15 | import { GrokDriver } from "./driver.ts"; |
| 16 | 16 | ||
| 17 | import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator"; | 17 | import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator"; |
| 18 | import * as McpProviderSessions from "@t3tools/provider-core/server/McpProviderSessions"; | ||
| 18 | 19 | ||
| 19 | const layerTest = layerTestProviderHost({ runBackgroundWork: false }).pipe( | 20 | const layerTest = layerTestProviderHost({ runBackgroundWork: false }).pipe( |
| 20 | Layer.provideMerge(NodeServices.layer), | 21 | Layer.provideMerge(NodeServices.layer), |
| 21 | Layer.provideMerge(IdAllocator.layer), | 22 | Layer.provideMerge(IdAllocator.layer), |
| 23 | Layer.provideMerge(McpProviderSessions.layer), | ||
| 22 | Layer.provideMerge(ProviderLatestVersions.layer), | 24 | Layer.provideMerge(ProviderLatestVersions.layer), |
| 23 | Layer.provideMerge( | 25 | Layer.provideMerge( |
| 24 | Layer.succeed( | 26 | Layer.succeed( |
packages/provider-muse/src/server/adapter.test.ts+4-9| ... | @@ -31,7 +31,7 @@ import * as Schema from "effect/Schema"; | ... | @@ -31,7 +31,7 @@ import * as Schema from "effect/Schema"; |
| 31 | import * as Stream from "effect/Stream"; | 31 | import * as Stream from "effect/Stream"; |
| 32 | 32 | ||
| 33 | import { layerTestProviderHost } from "@t3tools/provider-testing/host"; | 33 | import { layerTestProviderHost } from "@t3tools/provider-testing/host"; |
| 34 | import * as McpProviderSession from "@t3tools/provider-core/server/mcpSession"; | 34 | import * as McpProviderSessions from "@t3tools/provider-core/server/McpProviderSessions"; |
| 35 | import type { MuseItem } from "./protocol.ts"; | 35 | import type { MuseItem } from "./protocol.ts"; |
| 36 | import type { MuseSdkHost } from "./sdk.ts"; | 36 | import type { MuseSdkHost } from "./sdk.ts"; |
| 37 | import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator"; | 37 | import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator"; |
| ... | @@ -42,6 +42,7 @@ import { makeMuseAdapterV2, type MuseAdapterV2Options } from "./adapter.ts"; | ... | @@ -42,6 +42,7 @@ import { makeMuseAdapterV2, type MuseAdapterV2Options } from "./adapter.ts"; |
| 42 | const testLayer = Layer.mergeAll( | 42 | const testLayer = Layer.mergeAll( |
| 43 | NodeServices.layer, | 43 | NodeServices.layer, |
| 44 | IdAllocator.layer, | 44 | IdAllocator.layer, |
| 45 | McpProviderSessions.layer, | ||
| 45 | layerTestProviderHost().pipe(Layer.provide(NodeServices.layer)), | 46 | layerTestProviderHost().pipe(Layer.provide(NodeServices.layer)), |
| 46 | ); | 47 | ); |
| 47 | const MUSE_PROVIDER = ProviderDriverKind.make("muse"); | 48 | const MUSE_PROVIDER = ProviderDriverKind.make("muse"); |
| ... | @@ -351,10 +352,7 @@ const approval = (turnId: string) => ({ | ... | @@ -351,10 +352,7 @@ const approval = (turnId: string) => ({ |
| 351 | describe("MuseAdapterV2", () => { | 352 | describe("MuseAdapterV2", () => { |
| 352 | it.effect("connects the thread's MCP credential on native start and resume", () => | 353 | it.effect("connects the thread's MCP credential on native start and resume", () => |
| 353 | Effect.gen(function* () { | 354 | Effect.gen(function* () { |
| 354 | yield* Effect.addFinalizer(() => | 355 | yield* (yield* McpProviderSessions.McpProviderSessions).set({ |
| 355 | Effect.sync(() => McpProviderSession.clearMcpProviderSession(THREAD_ID)), | ||
| 356 | ); | ||
| 357 | McpProviderSession.setMcpProviderSession({ | ||
| 358 | environmentId: EnvironmentId.make("test-environment"), | 356 | environmentId: EnvironmentId.make("test-environment"), |
| 359 | threadId: THREAD_ID, | 357 | threadId: THREAD_ID, |
| 360 | providerSessionId: "test-session", | 358 | providerSessionId: "test-session", |
| ... | @@ -384,10 +382,7 @@ describe("MuseAdapterV2", () => { | ... | @@ -384,10 +382,7 @@ describe("MuseAdapterV2", () => { |
| 384 | 382 | ||
| 385 | it.effect("rejects a host without session MCP support before opening a native session", () => | 383 | it.effect("rejects a host without session MCP support before opening a native session", () => |
| 386 | Effect.gen(function* () { | 384 | Effect.gen(function* () { |
| 387 | yield* Effect.addFinalizer(() => | 385 | yield* (yield* McpProviderSessions.McpProviderSessions).set({ |
| 388 | Effect.sync(() => McpProviderSession.clearMcpProviderSession(THREAD_ID)), | ||
| 389 | ); | ||
| 390 | McpProviderSession.setMcpProviderSession({ | ||
| 391 | environmentId: EnvironmentId.make("test-environment"), | 386 | environmentId: EnvironmentId.make("test-environment"), |
| 392 | threadId: THREAD_ID, | 387 | threadId: THREAD_ID, |
| 393 | providerSessionId: "test-session", | 388 | providerSessionId: "test-session", |
packages/provider-muse/src/server/adapter.ts+4-2| ... | @@ -37,6 +37,7 @@ import * as Stream from "effect/Stream"; | ... | @@ -37,6 +37,7 @@ import * as Stream from "effect/Stream"; |
| 37 | 37 | ||
| 38 | import * as ProviderHost from "@t3tools/provider-core/server/ProviderHost"; | 38 | import * as ProviderHost from "@t3tools/provider-core/server/ProviderHost"; |
| 39 | import * as McpProviderSession from "@t3tools/provider-core/server/mcpSession"; | 39 | import * as McpProviderSession from "@t3tools/provider-core/server/mcpSession"; |
| 40 | import * as McpProviderSessions from "@t3tools/provider-core/server/McpProviderSessions"; | ||
| 40 | import { buildRuntimeInstructions } from "@t3tools/provider-core/server/runtimeInstructions"; | 41 | import { buildRuntimeInstructions } from "@t3tools/provider-core/server/runtimeInstructions"; |
| 41 | import { museModelCapabilities, resolveMuseReasoningEffort } from "./modelCatalog.ts"; | 42 | import { museModelCapabilities, resolveMuseReasoningEffort } from "./modelCatalog.ts"; |
| 42 | import { | 43 | import { |
| ... | @@ -248,6 +249,7 @@ export const makeMuseAdapterV2 = Effect.fn("makeMuseAdapterV2")(function* ( | ... | @@ -248,6 +249,7 @@ export const makeMuseAdapterV2 = Effect.fn("makeMuseAdapterV2")(function* ( |
| 248 | const idAllocator = yield* IdAllocator.IdAllocatorV2; | 249 | const idAllocator = yield* IdAllocator.IdAllocatorV2; |
| 249 | const providerHost = yield* ProviderHost.ProviderHost; | 250 | const providerHost = yield* ProviderHost.ProviderHost; |
| 250 | const fileSystem = yield* FileSystem.FileSystem; | 251 | const fileSystem = yield* FileSystem.FileSystem; |
| 252 | const mcpSessions = yield* McpProviderSessions.McpProviderSessions; | ||
| 251 | 253 | ||
| 252 | const protocolError = (detail: string, payload?: unknown) => | 254 | const protocolError = (detail: string, payload?: unknown) => |
| 253 | new ProviderAdapter.ProviderAdapterProtocolError({ | 255 | new ProviderAdapter.ProviderAdapterProtocolError({ |
| ... | @@ -1364,7 +1366,7 @@ export const makeMuseAdapterV2 = Effect.fn("makeMuseAdapterV2")(function* ( | ... | @@ -1364,7 +1366,7 @@ export const makeMuseAdapterV2 = Effect.fn("makeMuseAdapterV2")(function* ( |
| 1364 | ); | 1366 | ); |
| 1365 | const launchHost = Effect.fnUntraced(function* () { | 1367 | const launchHost = Effect.fnUntraced(function* () { |
| 1366 | const epoch = ++hostEpoch; | 1368 | const epoch = ++hostEpoch; |
| 1367 | const mcpSession = McpProviderSession.readMcpProviderSession(input.threadId); | 1369 | const mcpSession = yield* mcpSessions.read(input.threadId); |
| 1368 | const created = yield* Effect.acquireRelease( | 1370 | const created = yield* Effect.acquireRelease( |
| 1369 | createMuseSdkHostEffect( | 1371 | createMuseSdkHostEffect( |
| 1370 | { | 1372 | { |
| ... | @@ -1478,7 +1480,7 @@ export const makeMuseAdapterV2 = Effect.fn("makeMuseAdapterV2")(function* ( | ... | @@ -1478,7 +1480,7 @@ export const makeMuseAdapterV2 = Effect.fn("makeMuseAdapterV2")(function* ( |
| 1478 | let missingNativeSession = false; | 1480 | let missingNativeSession = false; |
| 1479 | return yield* Effect.gen(function* () { | 1481 | return yield* Effect.gen(function* () { |
| 1480 | nativeSessionId = requestedId ?? host.connection.mintCommandId(); | 1482 | nativeSessionId = requestedId ?? host.connection.mintCommandId(); |
| 1481 | const mcpSession = McpProviderSession.readMcpProviderSession(args.threadId); | 1483 | const mcpSession = yield* mcpSessions.read(args.threadId); |
| 1482 | if (mcpSession && !host.initializeResult.grantedCapabilities.includes("sessionMcp")) | 1484 | if (mcpSession && !host.initializeResult.grantedCapabilities.includes("sessionMcp")) |
| 1483 | return yield* protocolError( | 1485 | return yield* protocolError( |
| 1484 | "Update Muse Code to a version that supports session MCP servers", | 1486 | "Update Muse Code to a version that supports session MCP servers", |
packages/provider-muse/src/server/driver.ts+2| ... | @@ -10,6 +10,7 @@ import { ChildProcessSpawner } from "effect/process"; | ... | @@ -10,6 +10,7 @@ import { ChildProcessSpawner } from "effect/process"; |
| 10 | 10 | ||
| 11 | import * as ProviderLatestVersions from "@t3tools/provider-core/server/ProviderLatestVersions"; | 11 | import * as ProviderLatestVersions from "@t3tools/provider-core/server/ProviderLatestVersions"; |
| 12 | import * as ProviderHost from "@t3tools/provider-core/server/ProviderHost"; | 12 | import * as ProviderHost from "@t3tools/provider-core/server/ProviderHost"; |
| 13 | import * as McpProviderSessions from "@t3tools/provider-core/server/McpProviderSessions"; | ||
| 13 | import { expandHomePath } from "@t3tools/provider-core/server/pathExpansion"; | 14 | import { expandHomePath } from "@t3tools/provider-core/server/pathExpansion"; |
| 14 | import { makeMuseTextGeneration } from "./textGeneration.ts"; | 15 | import { makeMuseTextGeneration } from "./textGeneration.ts"; |
| 15 | import { ProviderDriverError } from "@t3tools/provider-core/server/errors"; | 16 | import { ProviderDriverError } from "@t3tools/provider-core/server/errors"; |
| ... | @@ -43,6 +44,7 @@ const decodeMuseSettings = Schema.decodeSync(MuseSettings); | ... | @@ -43,6 +44,7 @@ const decodeMuseSettings = Schema.decodeSync(MuseSettings); |
| 43 | 44 | ||
| 44 | export type MuseDriverEnv = | 45 | export type MuseDriverEnv = |
| 45 | | IdAllocator.IdAllocatorV2 | 46 | | IdAllocator.IdAllocatorV2 |
| 47 | | McpProviderSessions.McpProviderSessions | ||
| 46 | | ProviderHost.ProviderHost | 48 | | ProviderHost.ProviderHost |
| 47 | | ChildProcessSpawner.ChildProcessSpawner | 49 | | ChildProcessSpawner.ChildProcessSpawner |
| 48 | | FileSystem.FileSystem | 50 | | FileSystem.FileSystem |
packages/provider-opencode/src/server/adapter.test.ts+91-23| ... | @@ -36,6 +36,7 @@ import { layerTestProviderHost } from "@t3tools/provider-testing/host"; | ... | @@ -36,6 +36,7 @@ import { layerTestProviderHost } from "@t3tools/provider-testing/host"; |
| 36 | import type * as ProviderEventLoggers from "@t3tools/provider-core/server/ProviderEventLoggers"; | 36 | import type * as ProviderEventLoggers from "@t3tools/provider-core/server/ProviderEventLoggers"; |
| 37 | import * as OpenCodeRuntime from "./OpenCodeRuntime.ts"; | 37 | import * as OpenCodeRuntime from "./OpenCodeRuntime.ts"; |
| 38 | import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator"; | 38 | import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator"; |
| 39 | import * as McpProviderSessions from "@t3tools/provider-core/server/McpProviderSessions"; | ||
| 39 | 40 | ||
| 40 | import { | 41 | import { |
| 41 | advanceOpenCodePromptAdmission, | 42 | advanceOpenCodePromptAdmission, |
| ... | @@ -378,7 +379,10 @@ describe("OpenCodeAdapterV2", () => { | ... | @@ -378,7 +379,10 @@ describe("OpenCodeAdapterV2", () => { |
| 378 | hasSubagents: false, | 379 | hasSubagents: false, |
| 379 | }, | 380 | }, |
| 380 | ); | 381 | ); |
| 381 | }).pipe(Effect.provide(IdAllocator.layer), Effect.scoped), | 382 | }).pipe( |
| 383 | Effect.provide(Layer.merge(IdAllocator.layer, McpProviderSessions.layer)), | ||
| 384 | Effect.scoped, | ||
| 385 | ), | ||
| 382 | ); | 386 | ); |
| 383 | 387 | ||
| 384 | it.effect.each(["permission", "question"] as const)( | 388 | it.effect.each(["permission", "question"] as const)( |
| ... | @@ -456,7 +460,10 @@ describe("OpenCodeAdapterV2", () => { | ... | @@ -456,7 +460,10 @@ describe("OpenCodeAdapterV2", () => { |
| 456 | deliver = true; | 460 | deliver = true; |
| 457 | // Failed delivery leaves the request available for an explicit retry. | 461 | // Failed delivery leaves the request available for an explicit retry. |
| 458 | yield* harness.runtime.respondToRuntimeRequest(response); | 462 | yield* harness.runtime.respondToRuntimeRequest(response); |
| 459 | }).pipe(Effect.provide(IdAllocator.layer), Effect.scoped), | 463 | }).pipe( |
| 464 | Effect.provide(Layer.merge(IdAllocator.layer, McpProviderSessions.layer)), | ||
| 465 | Effect.scoped, | ||
| 466 | ), | ||
| 460 | ); | 467 | ); |
| 461 | 468 | ||
| 462 | it.effect("aborts external root and descendants before closing the event stream", () => | 469 | it.effect("aborts external root and descendants before closing the event stream", () => |
| ... | @@ -494,7 +501,10 @@ describe("OpenCodeAdapterV2", () => { | ... | @@ -494,7 +501,10 @@ describe("OpenCodeAdapterV2", () => { |
| 494 | "children:child", | 501 | "children:child", |
| 495 | "stream.close", | 502 | "stream.close", |
| 496 | ]); | 503 | ]); |
| 497 | }).pipe(Effect.provide(IdAllocator.layer), Effect.scoped), | 504 | }).pipe( |
| 505 | Effect.provide(Layer.merge(IdAllocator.layer, McpProviderSessions.layer)), | ||
| 506 | Effect.scoped, | ||
| 507 | ), | ||
| 498 | ); | 508 | ); |
| 499 | 509 | ||
| 500 | it.effect.each(["enumeration", "abort", "not-found", "timeout"] as const)( | 510 | it.effect.each(["enumeration", "abort", "not-found", "timeout"] as const)( |
| ... | @@ -548,7 +558,10 @@ describe("OpenCodeAdapterV2", () => { | ... | @@ -548,7 +558,10 @@ describe("OpenCodeAdapterV2", () => { |
| 548 | const result = yield* Fiber.join(stop); | 558 | const result = yield* Fiber.join(stop); |
| 549 | assert.equal(Exit.isSuccess(result), failure === "not-found"); | 559 | assert.equal(Exit.isSuccess(result), failure === "not-found"); |
| 550 | if (failure === "timeout") assert.isTrue(childSignal?.aborted); | 560 | if (failure === "timeout") assert.isTrue(childSignal?.aborted); |
| 551 | }).pipe(Effect.provide(IdAllocator.layer), Effect.scoped), | 561 | }).pipe( |
| 562 | Effect.provide(Layer.merge(IdAllocator.layer, McpProviderSessions.layer)), | ||
| 563 | Effect.scoped, | ||
| 564 | ), | ||
| 552 | ); | 565 | ); |
| 553 | 566 | ||
| 554 | it.effect( | 567 | it.effect( |
| ... | @@ -712,7 +725,10 @@ describe("OpenCodeAdapterV2", () => { | ... | @@ -712,7 +725,10 @@ describe("OpenCodeAdapterV2", () => { |
| 712 | const assistant = items.filter((item) => item.type === "assistant_message"); | 725 | const assistant = items.filter((item) => item.type === "assistant_message"); |
| 713 | assert.equal(assistant.at(-1)?.text, "Tool results received"); | 726 | assert.equal(assistant.at(-1)?.text, "Tool results received"); |
| 714 | assert.equal(assistant.at(-1)?.status, "completed"); | 727 | assert.equal(assistant.at(-1)?.status, "completed"); |
| 715 | }).pipe(Effect.provide(IdAllocator.layer), Effect.scoped), | 728 | }).pipe( |
| 729 | Effect.provide(Layer.merge(IdAllocator.layer, McpProviderSessions.layer)), | ||
| 730 | Effect.scoped, | ||
| 731 | ), | ||
| 716 | ); | 732 | ); |
| 717 | 733 | ||
| 718 | // Event order from a live OpenCode 1.18.32 run of a `task` call with | 734 | // Event order from a live OpenCode 1.18.32 run of a `task` call with |
| ... | @@ -807,7 +823,10 @@ describe("OpenCodeAdapterV2", () => { | ... | @@ -807,7 +823,10 @@ describe("OpenCodeAdapterV2", () => { |
| 807 | yield* push(status(child, "idle")); | 823 | yield* push(status(child, "idle")); |
| 808 | yield* push({ type: "session.idle", properties: { sessionID: child } }); | 824 | yield* push({ type: "session.idle", properties: { sessionID: child } }); |
| 809 | assert.isFalse(yield* hasPendingBackgroundWork, "an idle child must not pin idle release"); | 825 | assert.isFalse(yield* hasPendingBackgroundWork, "an idle child must not pin idle release"); |
| 810 | }).pipe(Effect.provide(IdAllocator.layer), Effect.scoped), | 826 | }).pipe( |
| 827 | Effect.provide(Layer.merge(IdAllocator.layer, McpProviderSessions.layer)), | ||
| 828 | Effect.scoped, | ||
| 829 | ), | ||
| 811 | ); | 830 | ); |
| 812 | 831 | ||
| 813 | it.effect("titles OpenCode reads and searches from their input", () => | 832 | it.effect("titles OpenCode reads and searches from their input", () => |
| ... | @@ -902,7 +921,10 @@ describe("OpenCodeAdapterV2", () => { | ... | @@ -902,7 +921,10 @@ describe("OpenCodeAdapterV2", () => { |
| 902 | ); | 921 | ); |
| 903 | assert.ok(emptyWebSearch?.type === "web_search"); | 922 | assert.ok(emptyWebSearch?.type === "web_search"); |
| 904 | assert.equal(emptyWebSearch.results, undefined); | 923 | assert.equal(emptyWebSearch.results, undefined); |
| 905 | }).pipe(Effect.provide(IdAllocator.layer), Effect.scoped), | 924 | }).pipe( |
| 925 | Effect.provide(Layer.merge(IdAllocator.layer, McpProviderSessions.layer)), | ||
| 926 | Effect.scoped, | ||
| 927 | ), | ||
| 906 | ); | 928 | ); |
| 907 | 929 | ||
| 908 | it.effect("presents OpenCode MCP calls without treating remote tools as local edits", () => | 930 | it.effect("presents OpenCode MCP calls without treating remote tools as local edits", () => |
| ... | @@ -1058,7 +1080,10 @@ describe("OpenCodeAdapterV2", () => { | ... | @@ -1058,7 +1080,10 @@ describe("OpenCodeAdapterV2", () => { |
| 1058 | assert.equal(items[3]?.toolName, "ambiguous_server_edit_document"); | 1080 | assert.equal(items[3]?.toolName, "ambiguous_server_edit_document"); |
| 1059 | assert.isUndefined(items[3]?.toolSource); | 1081 | assert.isUndefined(items[3]?.toolSource); |
| 1060 | assert.equal(statusReads, 1); | 1082 | assert.equal(statusReads, 1); |
| 1061 | }).pipe(Effect.provide(IdAllocator.layer), Effect.scoped), | 1083 | }).pipe( |
| 1084 | Effect.provide(Layer.merge(IdAllocator.layer, McpProviderSessions.layer)), | ||
| 1085 | Effect.scoped, | ||
| 1086 | ), | ||
| 1062 | ); | 1087 | ); |
| 1063 | 1088 | ||
| 1064 | it.effect.each(["failure", "timeout"] as const)("retries MCP status after %s", (failure) => | 1089 | it.effect.each(["failure", "timeout"] as const)("retries MCP status after %s", (failure) => |
| ... | @@ -1146,7 +1171,10 @@ describe("OpenCodeAdapterV2", () => { | ... | @@ -1146,7 +1171,10 @@ describe("OpenCodeAdapterV2", () => { |
| 1146 | kind: "integration", | 1171 | kind: "integration", |
| 1147 | }); | 1172 | }); |
| 1148 | if (failure === "timeout") assert.isTrue(signal?.aborted); | 1173 | if (failure === "timeout") assert.isTrue(signal?.aborted); |
| 1149 | }).pipe(Effect.provide(IdAllocator.layer), Effect.scoped), | 1174 | }).pipe( |
| 1175 | Effect.provide(Layer.merge(IdAllocator.layer, McpProviderSessions.layer)), | ||
| 1176 | Effect.scoped, | ||
| 1177 | ), | ||
| 1150 | ); | 1178 | ); |
| 1151 | 1179 | ||
| 1152 | it.effect("admits a native command on its user receipt before generation completes", () => | 1180 | it.effect("admits a native command on its user receipt before generation completes", () => |
| ... | @@ -1209,7 +1237,10 @@ describe("OpenCodeAdapterV2", () => { | ... | @@ -1209,7 +1237,10 @@ describe("OpenCodeAdapterV2", () => { |
| 1209 | yield* Fiber.join(start); | 1237 | yield* Fiber.join(start); |
| 1210 | assert.equal(promptCalls, 0); | 1238 | assert.equal(promptCalls, 0); |
| 1211 | release.resolve(); | 1239 | release.resolve(); |
| 1212 | }).pipe(Effect.provide(IdAllocator.layer), Effect.scoped), | 1240 | }).pipe( |
| 1241 | Effect.provide(Layer.merge(IdAllocator.layer, McpProviderSessions.layer)), | ||
| 1242 | Effect.scoped, | ||
| 1243 | ), | ||
| 1213 | ); | 1244 | ); |
| 1214 | 1245 | ||
| 1215 | it.effect("sends unadvertised slash commands as ordinary prompts", () => | 1246 | it.effect("sends unadvertised slash commands as ordinary prompts", () => |
| ... | @@ -1236,7 +1267,10 @@ describe("OpenCodeAdapterV2", () => { | ... | @@ -1236,7 +1267,10 @@ describe("OpenCodeAdapterV2", () => { |
| 1236 | }); | 1267 | }); |
| 1237 | yield* harness.startTurn("/unknown words"); | 1268 | yield* harness.startTurn("/unknown words"); |
| 1238 | assert.deepEqual(prompts, [[{ type: "text", text: "/unknown words" }]]); | 1269 | assert.deepEqual(prompts, [[{ type: "text", text: "/unknown words" }]]); |
| 1239 | }).pipe(Effect.provide(IdAllocator.layer), Effect.scoped), | 1270 | }).pipe( |
| 1271 | Effect.provide(Layer.merge(IdAllocator.layer, McpProviderSessions.layer)), | ||
| 1272 | Effect.scoped, | ||
| 1273 | ), | ||
| 1240 | ); | 1274 | ); |
| 1241 | 1275 | ||
| 1242 | it.effect("compacts with the native summarize API and emits a completed compaction", () => | 1276 | it.effect("compacts with the native summarize API and emits a completed compaction", () => |
| ... | @@ -1291,7 +1325,10 @@ describe("OpenCodeAdapterV2", () => { | ... | @@ -1291,7 +1325,10 @@ describe("OpenCodeAdapterV2", () => { |
| 1291 | assert.isTrue( | 1325 | assert.isTrue( |
| 1292 | events.some((event) => event.type === "turn.terminal" && event.status === "completed"), | 1326 | events.some((event) => event.type === "turn.terminal" && event.status === "completed"), |
| 1293 | ); | 1327 | ); |
| 1294 | }).pipe(Effect.provide(IdAllocator.layer), Effect.scoped), | 1328 | }).pipe( |
| 1329 | Effect.provide(Layer.merge(IdAllocator.layer, McpProviderSessions.layer)), | ||
| 1330 | Effect.scoped, | ||
| 1331 | ), | ||
| 1295 | ); | 1332 | ); |
| 1296 | 1333 | ||
| 1297 | it.effect("ends a turn on the provider thread it started on", () => | 1334 | it.effect("ends a turn on the provider thread it started on", () => |
| ... | @@ -1330,7 +1367,10 @@ describe("OpenCodeAdapterV2", () => { | ... | @@ -1330,7 +1367,10 @@ describe("OpenCodeAdapterV2", () => { |
| 1330 | Stream.runHead, | 1367 | Stream.runHead, |
| 1331 | ); | 1368 | ); |
| 1332 | assert.equal(Option.getOrUndefined(terminal)?.providerThreadId, forkedRow.id); | 1369 | assert.equal(Option.getOrUndefined(terminal)?.providerThreadId, forkedRow.id); |
| 1333 | }).pipe(Effect.provide(IdAllocator.layer), Effect.scoped), | 1370 | }).pipe( |
| 1371 | Effect.provide(Layer.merge(IdAllocator.layer, McpProviderSessions.layer)), | ||
| 1372 | Effect.scoped, | ||
| 1373 | ), | ||
| 1334 | ); | 1374 | ); |
| 1335 | 1375 | ||
| 1336 | it.effect("does not restore messages beyond OpenCode's persisted revert boundary", () => | 1376 | it.effect("does not restore messages beyond OpenCode's persisted revert boundary", () => |
| ... | @@ -1415,7 +1455,10 @@ describe("OpenCodeAdapterV2", () => { | ... | @@ -1415,7 +1455,10 @@ describe("OpenCodeAdapterV2", () => { |
| 1415 | ["user-kept", "assistant-kept"], | 1455 | ["user-kept", "assistant-kept"], |
| 1416 | ); | 1456 | ); |
| 1417 | assert.equal(snapshot.providerThread.nativeConversationHeadRef?.nativeId, "user-kept"); | 1457 | assert.equal(snapshot.providerThread.nativeConversationHeadRef?.nativeId, "user-kept"); |
| 1418 | }).pipe(Effect.provide(IdAllocator.layer), Effect.scoped), | 1458 | }).pipe( |
| 1459 | Effect.provide(Layer.merge(IdAllocator.layer, McpProviderSessions.layer)), | ||
| 1460 | Effect.scoped, | ||
| 1461 | ), | ||
| 1419 | ); | 1462 | ); |
| 1420 | 1463 | ||
| 1421 | it.effect("keeps a newly admitted prompt alive across stale idle and delayed busy evidence", () => | 1464 | it.effect("keeps a newly admitted prompt alive across stale idle and delayed busy evidence", () => |
| ... | @@ -1732,7 +1775,10 @@ describe("OpenCodeAdapterV2", () => { | ... | @@ -1732,7 +1775,10 @@ describe("OpenCodeAdapterV2", () => { |
| 1732 | ); | 1775 | ); |
| 1733 | const interrupted = yield* runtime.readThreadSnapshot({ providerThread }); | 1776 | const interrupted = yield* runtime.readThreadSnapshot({ providerThread }); |
| 1734 | assert.equal(interrupted.providerTurns.at(-1)?.status, "interrupted"); | 1777 | assert.equal(interrupted.providerTurns.at(-1)?.status, "interrupted"); |
| 1735 | }).pipe(Effect.provide(IdAllocator.layer), Effect.scoped), | 1778 | }).pipe( |
| 1779 | Effect.provide(Layer.merge(IdAllocator.layer, McpProviderSessions.layer)), | ||
| 1780 | Effect.scoped, | ||
| 1781 | ), | ||
| 1736 | ); | 1782 | ); |
| 1737 | 1783 | ||
| 1738 | it.effect("interrupts an initial prompt while its SDK request is pending", () => | 1784 | it.effect("interrupts an initial prompt while its SDK request is pending", () => |
| ... | @@ -1897,7 +1943,10 @@ describe("OpenCodeAdapterV2", () => { | ... | @@ -1897,7 +1943,10 @@ describe("OpenCodeAdapterV2", () => { |
| 1897 | (event.type === "provider_session.updated" && event.providerSession.status === "error"), | 1943 | (event.type === "provider_session.updated" && event.providerSession.status === "error"), |
| 1898 | ), | 1944 | ), |
| 1899 | ); | 1945 | ); |
| 1900 | }).pipe(Effect.provide(IdAllocator.layer), Effect.scoped), | 1946 | }).pipe( |
| 1947 | Effect.provide(Layer.merge(IdAllocator.layer, McpProviderSessions.layer)), | ||
| 1948 | Effect.scoped, | ||
| 1949 | ), | ||
| 1901 | ); | 1950 | ); |
| 1902 | 1951 | ||
| 1903 | it.effect("fails an active turn when the OpenCode event stream ends cleanly", () => | 1952 | it.effect("fails an active turn when the OpenCode event stream ends cleanly", () => |
| ... | @@ -1940,7 +1989,10 @@ describe("OpenCodeAdapterV2", () => { | ... | @@ -1940,7 +1989,10 @@ describe("OpenCodeAdapterV2", () => { |
| 1940 | assert.equal(terminal?.failure?.class, "transport_error"); | 1989 | assert.equal(terminal?.failure?.class, "transport_error"); |
| 1941 | assert.equal(terminal?.threadDisposition, "broken"); | 1990 | assert.equal(terminal?.threadDisposition, "broken"); |
| 1942 | assert.equal((yield* Effect.exit(harness.startTurn()))._tag, "Failure"); | 1991 | assert.equal((yield* Effect.exit(harness.startTurn()))._tag, "Failure"); |
| 1943 | }).pipe(Effect.provide(IdAllocator.layer), Effect.scoped), | 1992 | }).pipe( |
| 1993 | Effect.provide(Layer.merge(IdAllocator.layer, McpProviderSessions.layer)), | ||
| 1994 | Effect.scoped, | ||
| 1995 | ), | ||
| 1944 | ); | 1996 | ); |
| 1945 | 1997 | ||
| 1946 | it.effect("fails compaction when its response races stream termination", () => | 1998 | it.effect("fails compaction when its response races stream termination", () => |
| ... | @@ -2001,7 +2053,10 @@ describe("OpenCodeAdapterV2", () => { | ... | @@ -2001,7 +2053,10 @@ describe("OpenCodeAdapterV2", () => { |
| 2001 | event.turnItem.status === "completed", | 2053 | event.turnItem.status === "completed", |
| 2002 | ), | 2054 | ), |
| 2003 | ); | 2055 | ); |
| 2004 | }).pipe(Effect.provide(IdAllocator.layer), Effect.scoped), | 2056 | }).pipe( |
| 2057 | Effect.provide(Layer.merge(IdAllocator.layer, McpProviderSessions.layer)), | ||
| 2058 | Effect.scoped, | ||
| 2059 | ), | ||
| 2005 | ); | 2060 | ); |
| 2006 | 2061 | ||
| 2007 | it.effect("does not register a turn after the OpenCode event stream ends", () => | 2062 | it.effect("does not register a turn after the OpenCode event stream ends", () => |
| ... | @@ -2059,7 +2114,10 @@ describe("OpenCodeAdapterV2", () => { | ... | @@ -2059,7 +2114,10 @@ describe("OpenCodeAdapterV2", () => { |
| 2059 | 2114 | ||
| 2060 | assert.isTrue(Exit.isFailure(yield* Fiber.join(start))); | 2115 | assert.isTrue(Exit.isFailure(yield* Fiber.join(start))); |
| 2061 | assert.equal(promptCalls, 0); | 2116 | assert.equal(promptCalls, 0); |
| 2062 | }).pipe(Effect.provide(IdAllocator.layer), Effect.scoped), | 2117 | }).pipe( |
| 2118 | Effect.provide(Layer.merge(IdAllocator.layer, McpProviderSessions.layer)), | ||
| 2119 | Effect.scoped, | ||
| 2120 | ), | ||
| 2063 | ); | 2121 | ); |
| 2064 | 2122 | ||
| 2065 | it("holds stale idle through prompt admission until the new user message is observed", () => { | 2123 | it("holds stale idle through prompt admission until the new user message is observed", () => { |
| ... | @@ -2237,7 +2295,10 @@ describe("OpenCodeAdapterV2", () => { | ... | @@ -2237,7 +2295,10 @@ describe("OpenCodeAdapterV2", () => { |
| 2237 | assert.lengthOf(terminals, 1); | 2295 | assert.lengthOf(terminals, 1); |
| 2238 | assert.equal(terminals[0]?.status, "completed"); | 2296 | assert.equal(terminals[0]?.status, "completed"); |
| 2239 | assert.isNull(terminals[0]?.failure ?? null); | 2297 | assert.isNull(terminals[0]?.failure ?? null); |
| 2240 | }).pipe(Effect.provide(IdAllocator.layer), Effect.scoped), | 2298 | }).pipe( |
| 2299 | Effect.provide(Layer.merge(IdAllocator.layer, McpProviderSessions.layer)), | ||
| 2300 | Effect.scoped, | ||
| 2301 | ), | ||
| 2241 | ); | 2302 | ); |
| 2242 | 2303 | ||
| 2243 | it.effect("does not let a queued status retry adopt a newer steer generation", () => | 2304 | it.effect("does not let a queued status retry adopt a newer steer generation", () => |
| ... | @@ -2340,7 +2401,10 @@ describe("OpenCodeAdapterV2", () => { | ... | @@ -2340,7 +2401,10 @@ describe("OpenCodeAdapterV2", () => { |
| 2340 | }); | 2401 | }); |
| 2341 | assert.equal(statusCallCount, 1); | 2402 | assert.equal(statusCallCount, 1); |
| 2342 | assert.equal(afterRetry.providerTurns.at(-1)?.status, "running"); | 2403 | assert.equal(afterRetry.providerTurns.at(-1)?.status, "running"); |
| 2343 | }).pipe(Effect.provide(IdAllocator.layer), Effect.scoped), | 2404 | }).pipe( |
| 2405 | Effect.provide(Layer.merge(IdAllocator.layer, McpProviderSessions.layer)), | ||
| 2406 | Effect.scoped, | ||
| 2407 | ), | ||
| 2344 | ); | 2408 | ); |
| 2345 | 2409 | ||
| 2346 | it.effect("ignores a delayed status reply after steering starts a newer admission", () => | 2410 | it.effect("ignores a delayed status reply after steering starts a newer admission", () => |
| ... | @@ -2411,7 +2475,7 @@ describe("OpenCodeAdapterV2", () => { | ... | @@ -2411,7 +2475,7 @@ describe("OpenCodeAdapterV2", () => { |
| 2411 | assert.include(serialized, '"protocol":"opencode-sdk.sse"'); | 2475 | assert.include(serialized, '"protocol":"opencode-sdk.sse"'); |
| 2412 | assert.include(serialized, '"method":"session.prompt"'); | 2476 | assert.include(serialized, '"method":"session.prompt"'); |
| 2413 | assert.include(serialized, '"fieldCount":2'); | 2477 | assert.include(serialized, '"fieldCount":2'); |
| 2414 | }).pipe(Effect.provide(IdAllocator.layer)), | 2478 | }).pipe(Effect.provide(Layer.merge(IdAllocator.layer, McpProviderSessions.layer))), |
| 2415 | ); | 2479 | ); |
| 2416 | 2480 | ||
| 2417 | it.effect("adopts the handed-over provider thread identity on session create", () => | 2481 | it.effect("adopts the handed-over provider thread identity on session create", () => |
| ... | @@ -2520,6 +2584,7 @@ describe("OpenCodeAdapterV2", () => { | ... | @@ -2520,6 +2584,7 @@ describe("OpenCodeAdapterV2", () => { |
| 2520 | Effect.provide( | 2584 | Effect.provide( |
| 2521 | Layer.mergeAll( | 2585 | Layer.mergeAll( |
| 2522 | IdAllocator.layer, | 2586 | IdAllocator.layer, |
| 2587 | McpProviderSessions.layer, | ||
| 2523 | layerTestProviderHost().pipe(Layer.provide(NodeServices.layer)), | 2588 | layerTestProviderHost().pipe(Layer.provide(NodeServices.layer)), |
| 2524 | ), | 2589 | ), |
| 2525 | ), | 2590 | ), |
| ... | @@ -2718,5 +2783,8 @@ it.effect.each([false, true])( | ... | @@ -2718,5 +2783,8 @@ it.effect.each([false, true])( |
| 2718 | assert.equal(result.messages.length, 0); | 2783 | assert.equal(result.messages.length, 0); |
| 2719 | assert.deepEqual(calls, ["fork", "permissions"]); | 2784 | assert.deepEqual(calls, ["fork", "permissions"]); |
| 2720 | } | 2785 | } |
| 2721 | }).pipe(Effect.provide(IdAllocator.layer), Effect.scoped), | 2786 | }).pipe( |
| 2787 | Effect.provide(Layer.merge(IdAllocator.layer, McpProviderSessions.layer)), | ||
| 2788 | Effect.scoped, | ||
| 2789 | ), | ||
| 2722 | ); | 2790 | ); |
packages/provider-opencode/src/server/adapter.ts+4-2| ... | @@ -55,7 +55,7 @@ import * as Stream from "effect/Stream"; | ... | @@ -55,7 +55,7 @@ import * as Stream from "effect/Stream"; |
| 55 | 55 | ||
| 56 | import * as ProviderHost from "@t3tools/provider-core/server/ProviderHost"; | 56 | import * as ProviderHost from "@t3tools/provider-core/server/ProviderHost"; |
| 57 | import { mcpToolPresentation } from "@t3tools/provider-core/server/mcpToolPresentation"; | 57 | import { mcpToolPresentation } from "@t3tools/provider-core/server/mcpToolPresentation"; |
| 58 | import * as McpProviderSession from "@t3tools/provider-core/server/mcpSession"; | 58 | import * as McpProviderSessions from "@t3tools/provider-core/server/McpProviderSessions"; |
| 59 | import * as ProviderEventLoggers from "@t3tools/provider-core/server/ProviderEventLoggers"; | 59 | import * as ProviderEventLoggers from "@t3tools/provider-core/server/ProviderEventLoggers"; |
| 60 | import { | 60 | import { |
| 61 | structuralProtocolMethod, | 61 | structuralProtocolMethod, |
| ... | @@ -947,6 +947,7 @@ export const makeOpenCodeAdapterV2 = Effect.fn("makeOpenCodeAdapterV2")(function | ... | @@ -947,6 +947,7 @@ export const makeOpenCodeAdapterV2 = Effect.fn("makeOpenCodeAdapterV2")(function |
| 947 | const runtime = yield* OpenCodeRuntime.OpenCodeRuntime; | 947 | const runtime = yield* OpenCodeRuntime.OpenCodeRuntime; |
| 948 | const idAllocator = yield* IdAllocator.IdAllocatorV2; | 948 | const idAllocator = yield* IdAllocator.IdAllocatorV2; |
| 949 | const host = yield* ProviderHost.ProviderHost; | 949 | const host = yield* ProviderHost.ProviderHost; |
| 950 | const mcpSessions = yield* McpProviderSessions.McpProviderSessions; | ||
| 950 | 951 | ||
| 951 | return ProviderAdapter.ProviderAdapterV2.of({ | 952 | return ProviderAdapter.ProviderAdapterV2.of({ |
| 952 | instanceId: options.instanceId, | 953 | instanceId: options.instanceId, |
| ... | @@ -971,7 +972,7 @@ export const makeOpenCodeAdapterV2 = Effect.fn("makeOpenCodeAdapterV2")(function | ... | @@ -971,7 +972,7 @@ export const makeOpenCodeAdapterV2 = Effect.fn("makeOpenCodeAdapterV2")(function |
| 971 | : {}), | 972 | : {}), |
| 972 | }); | 973 | }); |
| 973 | 974 | ||
| 974 | const mcpSession = McpProviderSession.readMcpProviderSession(input.threadId); | 975 | const mcpSession = yield* mcpSessions.read(input.threadId); |
| 975 | const hasT3Mcp = mcpSession !== undefined && !connection.external; | 976 | const hasT3Mcp = mcpSession !== undefined && !connection.external; |
| 976 | const orchestrationSystemPrompt = t3OrchestrationSystemPrompt(hasT3Mcp); | 977 | const orchestrationSystemPrompt = t3OrchestrationSystemPrompt(hasT3Mcp); |
| 977 | if (hasT3Mcp) { | 978 | if (hasT3Mcp) { |
| ... | @@ -3728,6 +3729,7 @@ export const makeOpenCodeAdapterV2 = Effect.fn("makeOpenCodeAdapterV2")(function | ... | @@ -3728,6 +3729,7 @@ export const makeOpenCodeAdapterV2 = Effect.fn("makeOpenCodeAdapterV2")(function |
| 3728 | export type OpenCodeAdapterV2DriverEnv = | 3729 | export type OpenCodeAdapterV2DriverEnv = |
| 3729 | | OpenCodeRuntime.OpenCodeRuntime | 3730 | | OpenCodeRuntime.OpenCodeRuntime |
| 3730 | | IdAllocator.IdAllocatorV2 | 3731 | | IdAllocator.IdAllocatorV2 |
| 3732 | | McpProviderSessions.McpProviderSessions | ||
| 3731 | | ProviderEventLoggers.ProviderEventLoggers | 3733 | | ProviderEventLoggers.ProviderEventLoggers |
| 3732 | | ProviderHost.ProviderHost; | 3734 | | ProviderHost.ProviderHost; |
| 3733 | 3735 |
packages/provider-opencode/src/server/driver.test.ts+3| ... | @@ -17,6 +17,7 @@ import { ChildProcessSpawner } from "effect/process"; | ... | @@ -17,6 +17,7 @@ import { ChildProcessSpawner } from "effect/process"; |
| 17 | 17 | ||
| 18 | import * as ProviderLatestVersions from "@t3tools/provider-core/server/ProviderLatestVersions"; | 18 | import * as ProviderLatestVersions from "@t3tools/provider-core/server/ProviderLatestVersions"; |
| 19 | import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator"; | 19 | import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator"; |
| 20 | import * as McpProviderSessions from "@t3tools/provider-core/server/McpProviderSessions"; | ||
| 20 | import * as ProviderEventLoggers from "@t3tools/provider-core/server/ProviderEventLoggers"; | 21 | import * as ProviderEventLoggers from "@t3tools/provider-core/server/ProviderEventLoggers"; |
| 21 | import * as ProviderMaintenance from "@t3tools/provider-core/server/maintenanceResolver"; | 22 | import * as ProviderMaintenance from "@t3tools/provider-core/server/maintenanceResolver"; |
| 22 | import * as OpenCodeRuntime from "./OpenCodeRuntime.ts"; | 23 | import * as OpenCodeRuntime from "./OpenCodeRuntime.ts"; |
| ... | @@ -49,6 +50,7 @@ const openCode2Runtime = { | ... | @@ -49,6 +50,7 @@ const openCode2Runtime = { |
| 49 | 50 | ||
| 50 | const layer = Layer.mergeAll( | 51 | const layer = Layer.mergeAll( |
| 51 | IdAllocator.layer, | 52 | IdAllocator.layer, |
| 53 | McpProviderSessions.layer, | ||
| 52 | ProviderLatestVersions.layer, | 54 | ProviderLatestVersions.layer, |
| 53 | layerTestProviderHost(), | 55 | layerTestProviderHost(), |
| 54 | Layer.succeed( | 56 | Layer.succeed( |
| ... | @@ -212,6 +214,7 @@ const changingRuntime = { | ... | @@ -212,6 +214,7 @@ const changingRuntime = { |
| 212 | } as unknown as OpenCodeRuntime.OpenCodeRuntimeShape; | 214 | } as unknown as OpenCodeRuntime.OpenCodeRuntimeShape; |
| 213 | const layerUpdate = Layer.mergeAll( | 215 | const layerUpdate = Layer.mergeAll( |
| 214 | IdAllocator.layer, | 216 | IdAllocator.layer, |
| 217 | McpProviderSessions.layer, | ||
| 215 | ProviderLatestVersions.layer, | 218 | ProviderLatestVersions.layer, |
| 216 | layerTestProviderHost(), | 219 | layerTestProviderHost(), |
| 217 | Layer.succeed( | 220 | Layer.succeed( |
packages/provider-opencode/src/server/v2/adapter.ts+3-2| ... | @@ -74,7 +74,7 @@ import * as ProviderHost from "@t3tools/provider-core/server/ProviderHost"; | ... | @@ -74,7 +74,7 @@ import * as ProviderHost from "@t3tools/provider-core/server/ProviderHost"; |
| 74 | import * as OpenCode2Client from "./OpenCode2Client.ts"; | 74 | import * as OpenCode2Client from "./OpenCode2Client.ts"; |
| 75 | import * as OpenCode2Server from "./OpenCode2Server.ts"; | 75 | import * as OpenCode2Server from "./OpenCode2Server.ts"; |
| 76 | import * as OpenCodeRuntime from "../OpenCodeRuntime.ts"; | 76 | import * as OpenCodeRuntime from "../OpenCodeRuntime.ts"; |
| 77 | import * as McpProviderSession from "@t3tools/provider-core/server/mcpSession"; | 77 | import * as McpProviderSessions from "@t3tools/provider-core/server/McpProviderSessions"; |
| 78 | import { buildRuntimeInstructions } from "@t3tools/provider-core/server/runtimeInstructions"; | 78 | import { buildRuntimeInstructions } from "@t3tools/provider-core/server/runtimeInstructions"; |
| 79 | import { t3OrchestrationSystemPrompt } from "@t3tools/provider-core/server/orchestrationInstructions"; | 79 | import { t3OrchestrationSystemPrompt } from "@t3tools/provider-core/server/orchestrationInstructions"; |
| 80 | import { SKILL_MENTION_PATTERN } from "@t3tools/shared/composerInlineTokens"; | 80 | import { SKILL_MENTION_PATTERN } from "@t3tools/shared/composerInlineTokens"; |
| ... | @@ -831,6 +831,7 @@ export const make = Effect.fn("OpenCode2Adapter.make")(function* (instanceId: Pr | ... | @@ -831,6 +831,7 @@ export const make = Effect.fn("OpenCode2Adapter.make")(function* (instanceId: Pr |
| 831 | const server = yield* OpenCode2Server.OpenCode2Server; | 831 | const server = yield* OpenCode2Server.OpenCode2Server; |
| 832 | const idAllocator = yield* IdAllocator.IdAllocatorV2; | 832 | const idAllocator = yield* IdAllocator.IdAllocatorV2; |
| 833 | const host = yield* ProviderHost.ProviderHost; | 833 | const host = yield* ProviderHost.ProviderHost; |
| 834 | const mcpSessions = yield* McpProviderSessions.McpProviderSessions; | ||
| 834 | const continuationRequests = yield* ProviderContinuationRequests.ProviderContinuationRequests; | 835 | const continuationRequests = yield* ProviderContinuationRequests.ProviderContinuationRequests; |
| 835 | const crypto = yield* Crypto.Crypto; | 836 | const crypto = yield* Crypto.Crypto; |
| 836 | const driver = OPENCODE_PROVIDER; | 837 | const driver = OPENCODE_PROVIDER; |
| ... | @@ -3251,7 +3252,7 @@ export const make = Effect.fn("OpenCode2Adapter.make")(function* (instanceId: Pr | ... | @@ -3251,7 +3252,7 @@ export const make = Effect.fn("OpenCode2Adapter.make")(function* (instanceId: Pr |
| 3251 | state: ThreadState, | 3252 | state: ThreadState, |
| 3252 | turnInput: ProviderAdapter.ProviderAdapterV2TurnInput, | 3253 | turnInput: ProviderAdapter.ProviderAdapterV2TurnInput, |
| 3253 | ) { | 3254 | ) { |
| 3254 | const mcpSession = McpProviderSession.readMcpProviderSession(turnInput.threadId); | 3255 | const mcpSession = yield* mcpSessions.read(turnInput.threadId); |
| 3255 | const directory = turnInput.runtimePolicy.cwd ?? host.paths.cwd; | 3256 | const directory = turnInput.runtimePolicy.cwd ?? host.paths.cwd; |
| 3256 | const name = yield* mcpServerNameFor(turnInput.threadId); | 3257 | const name = yield* mcpServerNameFor(turnInput.threadId); |
| 3257 | // An external server may not reach T3's MCP endpoint, as with 1.x. | 3258 | // An external server may not reach T3's MCP endpoint, as with 1.x. |
packages/provider-pi/src/server/adapter.test.ts+4-7| ... | @@ -33,7 +33,7 @@ import { ChildProcess, ChildProcessSpawner } from "effect/process"; | ... | @@ -33,7 +33,7 @@ import { ChildProcess, ChildProcessSpawner } from "effect/process"; |
| 33 | 33 | ||
| 34 | import { layerTestProviderHost } from "@t3tools/provider-testing/host"; | 34 | import { layerTestProviderHost } from "@t3tools/provider-testing/host"; |
| 35 | import * as ProviderContinuationRequests from "@t3tools/provider-core/server/ProviderContinuationRequests"; | 35 | import * as ProviderContinuationRequests from "@t3tools/provider-core/server/ProviderContinuationRequests"; |
| 36 | import * as McpProviderSession from "@t3tools/provider-core/server/mcpSession"; | 36 | import * as McpProviderSessions from "@t3tools/provider-core/server/McpProviderSessions"; |
| 37 | import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator"; | 37 | import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator"; |
| 38 | import * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter"; | 38 | import * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter"; |
| 39 | import { handoffBudget } from "@t3tools/provider-core/server/handoffBudget"; | 39 | import { handoffBudget } from "@t3tools/provider-core/server/handoffBudget"; |
| ... | @@ -49,6 +49,7 @@ import { makePiRpcConnection, type PiRpcRecord } from "./rpc.ts"; | ... | @@ -49,6 +49,7 @@ import { makePiRpcConnection, type PiRpcRecord } from "./rpc.ts"; |
| 49 | const layerTest = Layer.mergeAll( | 49 | const layerTest = Layer.mergeAll( |
| 50 | NodeServices.layer, | 50 | NodeServices.layer, |
| 51 | IdAllocator.layer, | 51 | IdAllocator.layer, |
| 52 | McpProviderSessions.layer, | ||
| 52 | layerTestProviderHost().pipe(Layer.provide(NodeServices.layer)), | 53 | layerTestProviderHost().pipe(Layer.provide(NodeServices.layer)), |
| 53 | ); | 54 | ); |
| 54 | 55 | ||
| ... | @@ -1285,7 +1286,7 @@ describe("PiAdapterV2", () => { | ... | @@ -1285,7 +1286,7 @@ describe("PiAdapterV2", () => { |
| 1285 | 1286 | ||
| 1286 | it.effect("injects the T3 MCP extension and bearer when a session exists", () => | 1287 | it.effect("injects the T3 MCP extension and bearer when a session exists", () => |
| 1287 | Effect.gen(function* () { | 1288 | Effect.gen(function* () { |
| 1288 | McpProviderSession.setMcpProviderSession({ | 1289 | yield* (yield* McpProviderSessions.McpProviderSessions).set({ |
| 1289 | environmentId: EnvironmentId.make("environment-pi-mcp"), | 1290 | environmentId: EnvironmentId.make("environment-pi-mcp"), |
| 1290 | threadId: THREAD_ID, | 1291 | threadId: THREAD_ID, |
| 1291 | providerSessionId: "mcp-session-pi", | 1292 | providerSessionId: "mcp-session-pi", |
| ... | @@ -1306,11 +1307,7 @@ describe("PiAdapterV2", () => { | ... | @@ -1306,11 +1307,7 @@ describe("PiAdapterV2", () => { |
| 1306 | assert.equal(spawn.env.T3_MCP_URL, "http://127.0.0.1:43123/mcp"); | 1307 | assert.equal(spawn.env.T3_MCP_URL, "http://127.0.0.1:43123/mcp"); |
| 1307 | assert.equal(spawn.env.T3_MCP_BEARER_TOKEN, "secret-pi-token"); | 1308 | assert.equal(spawn.env.T3_MCP_BEARER_TOKEN, "secret-pi-token"); |
| 1308 | assert.equal(spawn.env.T3_PI_RUNTIME_MODE, "full-access"); | 1309 | assert.equal(spawn.env.T3_PI_RUNTIME_MODE, "full-access"); |
| 1309 | }).pipe( | 1310 | }).pipe(Effect.scoped, Effect.provide(layerTest)), |
| 1310 | Effect.ensuring(Effect.sync(() => McpProviderSession.clearMcpProviderSession(THREAD_ID))), | ||
| 1311 | Effect.scoped, | ||
| 1312 | Effect.provide(layerTest), | ||
| 1313 | ), | ||
| 1314 | ); | 1311 | ); |
| 1315 | 1312 | ||
| 1316 | it.effect("rejects a resume while a turn is active", () => | 1313 | it.effect("rejects a resume while a turn is active", () => |
packages/provider-pi/src/server/adapter.ts+4-2| ... | @@ -62,7 +62,7 @@ import { ChildProcessSpawner } from "effect/process"; | ... | @@ -62,7 +62,7 @@ import { ChildProcessSpawner } from "effect/process"; |
| 62 | 62 | ||
| 63 | import * as ProviderHost from "@t3tools/provider-core/server/ProviderHost"; | 63 | import * as ProviderHost from "@t3tools/provider-core/server/ProviderHost"; |
| 64 | import { mcpToolPresentation } from "@t3tools/provider-core/server/mcpToolPresentation"; | 64 | import { mcpToolPresentation } from "@t3tools/provider-core/server/mcpToolPresentation"; |
| 65 | import * as McpProviderSession from "@t3tools/provider-core/server/mcpSession"; | 65 | import * as McpProviderSessions from "@t3tools/provider-core/server/McpProviderSessions"; |
| 66 | import { parsePiCompactCommand, type PiCompactCommand } from "./commands.ts"; | 66 | import { parsePiCompactCommand, type PiCompactCommand } from "./commands.ts"; |
| 67 | import { mergeProviderInstanceEnvironment } from "@t3tools/provider-core/server/instanceEnvironment"; | 67 | import { mergeProviderInstanceEnvironment } from "@t3tools/provider-core/server/instanceEnvironment"; |
| 68 | import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator"; | 68 | import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator"; |
| ... | @@ -390,6 +390,7 @@ export const makePiAdapterV2 = Effect.fn("makePiAdapterV2")(function* ( | ... | @@ -390,6 +390,7 @@ export const makePiAdapterV2 = Effect.fn("makePiAdapterV2")(function* ( |
| 390 | const fileSystem = yield* FileSystem.FileSystem; | 390 | const fileSystem = yield* FileSystem.FileSystem; |
| 391 | const idAllocator = yield* IdAllocator.IdAllocatorV2; | 391 | const idAllocator = yield* IdAllocator.IdAllocatorV2; |
| 392 | const host = yield* ProviderHost.ProviderHost; | 392 | const host = yield* ProviderHost.ProviderHost; |
| 393 | const mcpSessions = yield* McpProviderSessions.McpProviderSessions; | ||
| 393 | const { continuationRequests } = options; | 394 | const { continuationRequests } = options; |
| 394 | 395 | ||
| 395 | const protocolError = (detail: string, payload?: unknown) => | 396 | const protocolError = (detail: string, payload?: unknown) => |
| ... | @@ -409,7 +410,7 @@ export const makePiAdapterV2 = Effect.fn("makePiAdapterV2")(function* ( | ... | @@ -409,7 +410,7 @@ export const makePiAdapterV2 = Effect.fn("makePiAdapterV2")(function* ( |
| 409 | ) { | 410 | ) { |
| 410 | const scope = yield* Effect.scope; | 411 | const scope = yield* Effect.scope; |
| 411 | const cwd = input.runtimePolicy.cwd ?? host.paths.cwd; | 412 | const cwd = input.runtimePolicy.cwd ?? host.paths.cwd; |
| 412 | const mcpSession = McpProviderSession.readMcpProviderSession(input.threadId); | 413 | const mcpSession = yield* mcpSessions.read(input.threadId); |
| 413 | const provideCacheFs = <A, E>(effect: Effect.Effect<A, E, FileSystem.FileSystem>) => | 414 | const provideCacheFs = <A, E>(effect: Effect.Effect<A, E, FileSystem.FileSystem>) => |
| 414 | effect.pipe( | 415 | effect.pipe( |
| 415 | Effect.provideService(FileSystem.FileSystem, fileSystem), | 416 | Effect.provideService(FileSystem.FileSystem, fileSystem), |
| ... | @@ -3261,6 +3262,7 @@ export type PiAdapterV2DriverEnv = | ... | @@ -3261,6 +3262,7 @@ export type PiAdapterV2DriverEnv = |
| 3261 | | ChildProcessSpawner.ChildProcessSpawner | 3262 | | ChildProcessSpawner.ChildProcessSpawner |
| 3262 | | FileSystem.FileSystem | 3263 | | FileSystem.FileSystem |
| 3263 | | IdAllocator.IdAllocatorV2 | 3264 | | IdAllocator.IdAllocatorV2 |
| 3265 | | McpProviderSessions.McpProviderSessions | ||
| 3264 | | ProviderHost.ProviderHost; | 3266 | | ProviderHost.ProviderHost; |
| 3265 | 3267 | ||
| 3266 | export const PiAdapterV2Driver: ProviderAdapterDriver<PiSettings, PiAdapterV2DriverEnv> = { | 3268 | export const PiAdapterV2Driver: ProviderAdapterDriver<PiSettings, PiAdapterV2DriverEnv> = { |
packages/provider-pi/src/server/driver.test.ts+2| ... | @@ -15,6 +15,7 @@ import { ChildProcess, ChildProcessSpawner } from "effect/process"; | ... | @@ -15,6 +15,7 @@ import { ChildProcess, ChildProcessSpawner } from "effect/process"; |
| 15 | 15 | ||
| 16 | import * as ProviderLatestVersions from "@t3tools/provider-core/server/ProviderLatestVersions"; | 16 | import * as ProviderLatestVersions from "@t3tools/provider-core/server/ProviderLatestVersions"; |
| 17 | import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator"; | 17 | import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator"; |
| 18 | import * as McpProviderSessions from "@t3tools/provider-core/server/McpProviderSessions"; | ||
| 18 | import { layerTestProviderHost } from "@t3tools/provider-testing/host"; | 19 | import { layerTestProviderHost } from "@t3tools/provider-testing/host"; |
| 19 | import type { PiSettings } from "../settings.ts"; | 20 | import type { PiSettings } from "../settings.ts"; |
| 20 | import { PiDriver } from "./driver.ts"; | 21 | import { PiDriver } from "./driver.ts"; |
| ... | @@ -26,6 +27,7 @@ const layerTest = Layer.mergeAll( | ... | @@ -26,6 +27,7 @@ const layerTest = Layer.mergeAll( |
| 26 | runBackgroundWork: false, | 27 | runBackgroundWork: false, |
| 27 | }), | 28 | }), |
| 28 | IdAllocator.layer, | 29 | IdAllocator.layer, |
| 30 | McpProviderSessions.layer, | ||
| 29 | ProviderLatestVersions.layer, | 31 | ProviderLatestVersions.layer, |
| 30 | Layer.succeed( | 32 | Layer.succeed( |
| 31 | HttpClient.HttpClient, | 33 | HttpClient.HttpClient, |