| author | |
| committer | |
| log | b66dd402e86899c3fb4341d46c32d0d69df1eefc |
| tree | 932594fff9ca5cd68ebd5cc0d7bad7ea5fb58677 |
| parent | 2c1915b3193f16e2493709b480e6622645d0ca4e |
| signature | Signed by PGP key B5690EEEBB952194 |
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>110 files changed, 2281 insertions(+), 2257 deletions(-)
apps/server/scripts/record-claude-agent-sdk-replay-fixture.ts+3-6| ... | ... | @@ -10,10 +10,7 @@ import { |
| 10 | 10 | CLAUDE_AGENT_SDK_REPLAY_PROTOCOL, |
| 11 | 11 | } from "../src/orchestration-v2/Adapters/ClaudeAdapterV2.testkit.ts"; |
| 12 | 12 | import { claudeRuntimeQueryPolicyForRuntimePolicy } from "../src/orchestration-v2/Adapters/ClaudeAdapterV2.ts"; |
| 13 | import { | |
| 14 | ProviderAdapterV2RuntimePolicy, | |
| 15 | type ProviderAdapterV2RuntimePolicy as ProviderAdapterV2RuntimePolicyType, | |
| 16 | } from "@t3tools/provider-core/server/ProviderAdapter"; | |
| 13 | import * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter"; | |
| 17 | 14 | import type { RuntimePolicyV2Override } from "../src/orchestration-v2/RuntimePolicy.ts"; |
| 18 | 15 | import { makeCheckpointWorkspace } from "@t3tools/provider-testing/replayWorkspace"; |
| 19 | 16 | import { CLAUDE_MODEL_SELECTION } from "../src/orchestration-v2/testkit/fixtures/shared.ts"; |
| ... | ... | @@ -440,8 +437,8 @@ validateClaudeReplayRecordingSelection({ |
| 440 | 437 | function runtimePolicyForRecording(input: { |
| 441 | 438 | readonly cwd: string; |
| 442 | 439 | readonly override?: RuntimePolicyV2Override; |
| 443 | }): ProviderAdapterV2RuntimePolicyType { | |
| 444 | return ProviderAdapterV2RuntimePolicy.make({ | |
| 440 | }): ProviderAdapter.ProviderAdapterV2RuntimePolicy { | |
| 441 | return ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({ | |
| 445 | 442 | runtimeMode: "full-access", |
| 446 | 443 | interactionMode: "default", |
| 447 | 444 | cwd: input.override?.cwd ?? input.cwd, |
apps/server/scripts/record-grok-acp-replay-fixture.ts+3-3| ... | ... | @@ -33,8 +33,8 @@ import { |
| 33 | 33 | } from "@t3tools/provider-grok/testing"; |
| 34 | 34 | import { ACP_PROTOCOL } from "@t3tools/provider-acp/server/adapter"; |
| 35 | 35 | import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator"; |
| 36 | import type { ProviderAdapterV2SessionRuntime } from "@t3tools/provider-core/server/ProviderAdapter"; | |
| 37 | import * as ProviderContinuationRequests from "@t3tools/provider-core/server/continuationRequests"; | |
| 36 | import type * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter"; | |
| 37 | import * as ProviderContinuationRequests from "@t3tools/provider-core/server/ProviderContinuationRequests"; | |
| 38 | 38 | import * as ProviderAdapterRegistry from "../src/orchestration-v2/ProviderAdapterRegistry.ts"; |
| 39 | 39 | import { provideDeterministicTestRuntime } from "../src/orchestration-v2/testkit/DeterministicRuntime.ts"; |
| 40 | 40 | import { ORCHESTRATOR_REPLAY_FIXTURES } from "../src/orchestration-v2/testkit/fixtures/index.ts"; |
| ... | ... | @@ -474,7 +474,7 @@ const recordScenario = Effect.fn("recordGrokScenario")(function* (fixtureName: s |
| 474 | 474 | ...adapter, |
| 475 | 475 | openSession: (input) => |
| 476 | 476 | adapter.openSession(input).pipe( |
| 477 | Effect.map((session): ProviderAdapterV2SessionRuntime => ({ | |
| 477 | Effect.map((session): ProviderAdapter.ProviderAdapterV2SessionRuntime => ({ | |
| 478 | 478 | ...session, |
| 479 | 479 | startTurn: (turnInput) => onWallClock(session.startTurn(turnInput)), |
| 480 | 480 | steerTurn: (turnInput) => onWallClock(session.steerTurn(turnInput)), |
apps/server/src/mcp/McpToolAccess.race.test.ts+2-2| ... | ... | @@ -19,13 +19,13 @@ import { DispatchModeLimit } from "../orchestration-v2/DispatchModeLimit.ts"; |
| 19 | 19 | import { CodexProviderCapabilitiesV2 } from "../orchestration-v2/Adapters/CodexAdapterV2.ts"; |
| 20 | 20 | import * as Orchestrator from "../orchestration-v2/Orchestrator.ts"; |
| 21 | 21 | import * as ProjectionStore from "../orchestration-v2/ProjectionStore.ts"; |
| 22 | import type { ProviderAdapterV2Shape } from "@t3tools/provider-core/server/ProviderAdapter"; | |
| 23 | 22 | import * as ProviderAdapterRegistry from "../orchestration-v2/ProviderAdapterRegistry.ts"; |
| 24 | 23 | import * as ThreadManagement from "../orchestration-v2/ThreadManagementService.ts"; |
| 25 | 24 | import * as ProviderReplayHarness from "../orchestration-v2/testkit/ProviderReplayHarness.ts"; |
| 26 | 25 | import * as McpHttpServer from "./McpHttpServer.ts"; |
| 27 | 26 | import * as McpInvocationContext from "./McpInvocationContext.ts"; |
| 28 | 27 | import * as McpToolAccess from "./McpToolAccess.ts"; |
| 28 | import type * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter"; | |
| 29 | 29 | |
| 30 | 30 | // A Supervised outside agent renames a thread through a `writesThreads` tool. |
| 31 | 31 | // The thread's user raises it to full access after the tool's check but |
| ... | ... | @@ -38,7 +38,7 @@ const adapter = { |
| 38 | 38 | getCapabilities: () => Effect.succeed(CodexProviderCapabilitiesV2), |
| 39 | 39 | planSelectionTransition: () => Effect.succeed({ type: "apply_on_next_turn" as const }), |
| 40 | 40 | openSession: () => Effect.die("No provider process needed for metadata commands"), |
| 41 | } as ProviderAdapterV2Shape; | |
| 41 | } as ProviderAdapter.ProviderAdapterV2["Service"]; | |
| 42 | 42 | const layerDatabase = SqlitePersistence.layerMemory; |
| 43 | 43 | const layerOrchestrator = Layer.mergeAll( |
| 44 | 44 | layerDatabase, |
apps/server/src/mcp/OrchestratorMcpService.test.ts+4-2| ... | ... | @@ -28,7 +28,6 @@ import { |
| 28 | 28 | OrchestratorProjectionError, |
| 29 | 29 | OrchestratorThreadAboveModeLimitError, |
| 30 | 30 | } from "../orchestration-v2/Orchestrator.ts"; |
| 31 | import type { ProviderAdapterV2Shape } from "@t3tools/provider-core/server/ProviderAdapter"; | |
| 32 | 31 | import * as ProviderAdapterRegistry from "../orchestration-v2/ProviderAdapterRegistry.ts"; |
| 33 | 32 | import * as ThreadManagementService from "../orchestration-v2/ThreadManagementService.ts"; |
| 34 | 33 | import * as ProviderRegistry from "../provider/ProviderRegistry.ts"; |
| ... | ... | @@ -39,6 +38,7 @@ import * as SecretRequests from "../secrets/SecretRequests.ts"; |
| 39 | 38 | import type { McpInvocationScope } from "./McpInvocationContext.ts"; |
| 40 | 39 | import { idleThreadProjection, liveThreadShell } from "./McpToolAccess.testkit.ts"; |
| 41 | 40 | import * as OrchestratorMcpService from "./OrchestratorMcpService.ts"; |
| 41 | import type * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter"; | |
| 42 | 42 | |
| 43 | 43 | describe("OrchestratorMcpService", () => { |
| 44 | 44 | it.effect("retries terminal acknowledgement with a fresh command id", () => |
| ... | ... | @@ -810,7 +810,9 @@ describe("OrchestratorMcpService provider resolution", () => { |
| 810 | 810 | list: () => Effect.succeed(instanceIds), |
| 811 | 811 | get: (instanceId) => |
| 812 | 812 | instanceIds.includes(instanceId) |
| 813 | ? Effect.succeed({ instanceId } as unknown as ProviderAdapterV2Shape) | |
| 813 | ? Effect.succeed({ | |
| 814 | instanceId, | |
| 815 | } as unknown as ProviderAdapter.ProviderAdapterV2["Service"]) | |
| 814 | 816 | : Effect.fail( |
| 815 | 817 | new ProviderAdapterRegistry.ProviderAdapterRegistryLookupError({ instanceId }), |
| 816 | 818 | ), |
apps/server/src/mcp/OrchestratorMcpToolkit.integration.test.ts+12-15| ... | ... | @@ -57,14 +57,8 @@ import { threadShellFromProjection } from "../orchestration-v2/ProjectionStore.t |
| 57 | 57 | import * as EventSink from "../orchestration-v2/EventSink.ts"; |
| 58 | 58 | import * as Orchestrator from "../orchestration-v2/Orchestrator.ts"; |
| 59 | 59 | import * as ThreadManagementService from "../orchestration-v2/ThreadManagementService.ts"; |
| 60 | import { | |
| 61 | type ProviderAdapterV2Event, | |
| 62 | ProviderAdapterProtocolError, | |
| 63 | type ProviderAdapterV2Shape, | |
| 64 | type ProviderAdapterV2TurnInput, | |
| 65 | } from "@t3tools/provider-core/server/ProviderAdapter"; | |
| 66 | 60 | import * as ProviderAdapterRegistry from "../orchestration-v2/ProviderAdapterRegistry.ts"; |
| 67 | import * as ProviderContinuationRequests from "@t3tools/provider-core/server/continuationRequests"; | |
| 61 | import * as ProviderContinuationRequests from "@t3tools/provider-core/server/ProviderContinuationRequests"; | |
| 68 | 62 | import { checkpointWorkspace } from "@t3tools/provider-testing/replayWorkspace"; |
| 69 | 63 | import * as ProviderReplayHarness from "../orchestration-v2/testkit/ProviderReplayHarness.ts"; |
| 70 | 64 | import { |
| ... | ... | @@ -79,6 +73,7 @@ import * as ServerSecretStore from "../auth/ServerSecretStore.ts"; |
| 79 | 73 | import * as McpHttpServer from "./McpHttpServer.ts"; |
| 80 | 74 | import * as McpInvocationContext from "./McpInvocationContext.ts"; |
| 81 | 75 | import { delegatedTaskRun, hasPendingChildRuns } from "./OrchestratorMcpService.ts"; |
| 76 | import * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter"; | |
| 82 | 77 | |
| 83 | 78 | // Effect returns a declared tool failure as `isError` with its encoded payload |
| 84 | 79 | // as JSON text, never as `structuredContent`. |
| ... | ... | @@ -146,7 +141,7 @@ interface CapturedTurn { |
| 146 | 141 | } |
| 147 | 142 | |
| 148 | 143 | function unsupported(driver: ProviderDriverKind, detail: string) { |
| 149 | return Effect.fail(new ProviderAdapterProtocolError({ driver, detail })); | |
| 144 | return Effect.fail(new ProviderAdapter.ProviderAdapterProtocolError({ driver, detail })); | |
| 150 | 145 | } |
| 151 | 146 | |
| 152 | 147 | function makeProviderSnapshot(input: { |
| ... | ... | @@ -185,10 +180,12 @@ function makeDeterministicAdapter(input: { |
| 185 | 180 | readonly driver: ProviderDriverKind; |
| 186 | 181 | readonly capabilities: OrchestrationV2ProviderCapabilities; |
| 187 | 182 | readonly capturedTurns: Ref.Ref<ReadonlyArray<CapturedTurn>>; |
| 188 | readonly shouldComplete: (turn: ProviderAdapterV2TurnInput) => boolean; | |
| 189 | readonly terminalGate?: (turn: ProviderAdapterV2TurnInput) => Deferred.Deferred<void> | undefined; | |
| 190 | readonly response: (turn: ProviderAdapterV2TurnInput) => string; | |
| 191 | }): ProviderAdapterV2Shape { | |
| 183 | readonly shouldComplete: (turn: ProviderAdapter.ProviderAdapterV2TurnInput) => boolean; | |
| 184 | readonly terminalGate?: ( | |
| 185 | turn: ProviderAdapter.ProviderAdapterV2TurnInput, | |
| 186 | ) => Deferred.Deferred<void> | undefined; | |
| 187 | readonly response: (turn: ProviderAdapter.ProviderAdapterV2TurnInput) => string; | |
| 188 | }): ProviderAdapter.ProviderAdapterV2["Service"] { | |
| 192 | 189 | return { |
| 193 | 190 | instanceId: input.instanceId, |
| 194 | 191 | driver: input.driver, |
| ... | ... | @@ -196,7 +193,7 @@ function makeDeterministicAdapter(input: { |
| 196 | 193 | planSelectionTransition: () => Effect.succeed({ type: "apply_on_next_turn" }), |
| 197 | 194 | openSession: (sessionInput) => |
| 198 | 195 | Effect.gen(function* () { |
| 199 | const events = yield* PubSub.unbounded<ProviderAdapterV2Event>(); | |
| 196 | const events = yield* PubSub.unbounded<ProviderAdapter.ProviderAdapterV2Event>(); | |
| 200 | 197 | const now = yield* DateTime.now; |
| 201 | 198 | const providerSession: OrchestrationV2ProviderSession = { |
| 202 | 199 | id: sessionInput.providerSessionId, |
| ... | ... | @@ -211,12 +208,12 @@ function makeDeterministicAdapter(input: { |
| 211 | 208 | lastError: null, |
| 212 | 209 | }; |
| 213 | 210 | |
| 214 | const publish = (providerEvents: ReadonlyArray<ProviderAdapterV2Event>) => | |
| 211 | const publish = (providerEvents: ReadonlyArray<ProviderAdapter.ProviderAdapterV2Event>) => | |
| 215 | 212 | Effect.forEach(providerEvents, (event) => PubSub.publish(events, event), { |
| 216 | 213 | discard: true, |
| 217 | 214 | }); |
| 218 | 215 | const runOrdinals = new Map<ProviderTurnId, number>(); |
| 219 | const turnInputs = new Map<ProviderTurnId, ProviderAdapterV2TurnInput>(); | |
| 216 | const turnInputs = new Map<ProviderTurnId, ProviderAdapter.ProviderAdapterV2TurnInput>(); | |
| 220 | 217 | |
| 221 | 218 | return { |
| 222 | 219 | instanceId: input.instanceId, |
apps/server/src/mcpApps/McpAppRequests.test.ts+6-7| ... | ... | @@ -14,10 +14,7 @@ import * as Layer from "effect/Layer"; |
| 14 | 14 | import * as Option from "effect/Option"; |
| 15 | 15 | |
| 16 | 16 | import * as Orchestrator from "../orchestration-v2/Orchestrator.ts"; |
| 17 | import type { | |
| 18 | ProviderAdapterV2McpApps, | |
| 19 | ProviderAdapterV2SessionRuntime, | |
| 20 | } from "@t3tools/provider-core/server/ProviderAdapter"; | |
| 17 | import type * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter"; | |
| 21 | 18 | import * as ProviderSessionManager from "../orchestration-v2/ProviderSessionManager.ts"; |
| 22 | 19 | import * as ThreadManagementService from "../orchestration-v2/ThreadManagementService.ts"; |
| 23 | 20 | import * as McpAppModelContext from "./McpAppModelContext.ts"; |
| ... | ... | @@ -71,7 +68,7 @@ const storedContext = new Map<string, string>(); |
| 71 | 68 | |
| 72 | 69 | function makeLayer(input: { |
| 73 | 70 | readonly item: OrchestrationV2TurnItem | null; |
| 74 | readonly mcpApps?: ProviderAdapterV2McpApps; | |
| 71 | readonly mcpApps?: ProviderAdapter.ProviderAdapterV2McpApps; | |
| 75 | 72 | readonly live?: boolean; |
| 76 | 73 | }) { |
| 77 | 74 | return McpAppRequests.layer.pipe( |
| ... | ... | @@ -121,7 +118,9 @@ function makeLayer(input: { |
| 121 | 118 | : Option.some( |
| 122 | 119 | (input.mcpApps === undefined |
| 123 | 120 | ? {} |
| 124 | : { mcpApps: input.mcpApps }) as ProviderAdapterV2SessionRuntime, | |
| 121 | : { | |
| 122 | mcpApps: input.mcpApps, | |
| 123 | }) as ProviderAdapter.ProviderAdapterV2SessionRuntime, | |
| 125 | 124 | ), |
| 126 | 125 | ), |
| 127 | 126 | }), |
| ... | ... | @@ -138,7 +137,7 @@ const reason = <A, R>(effect: Effect.Effect<A, McpAppRequestError, R>) => |
| 138 | 137 | |
| 139 | 138 | describe("McpAppRequests", () => { |
| 140 | 139 | const calls: Array<{ server: string; tool: string }> = []; |
| 141 | const mcpApps: ProviderAdapterV2McpApps = { | |
| 140 | const mcpApps: ProviderAdapter.ProviderAdapterV2McpApps = { | |
| 142 | 141 | listTools: () => |
| 143 | 142 | Effect.succeed([ |
| 144 | 143 | { name: "refresh", annotations: { readOnlyHint: true } }, |
apps/server/src/mcpApps/McpAppRequests.ts+4-7| ... | ... | @@ -21,10 +21,7 @@ import * as Predicate from "effect/Predicate"; |
| 21 | 21 | import * as Schema from "effect/Schema"; |
| 22 | 22 | |
| 23 | 23 | import * as Orchestrator from "../orchestration-v2/Orchestrator.ts"; |
| 24 | import type { | |
| 25 | ProviderAdapterV2McpApps, | |
| 26 | ProviderAdapterV2McpTool, | |
| 27 | } from "@t3tools/provider-core/server/ProviderAdapter"; | |
| 24 | import type * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter"; | |
| 28 | 25 | import * as ProviderSessionManager from "../orchestration-v2/ProviderSessionManager.ts"; |
| 29 | 26 | import * as ThreadManagementService from "../orchestration-v2/ThreadManagementService.ts"; |
| 30 | 27 | import * as McpAppModelContext from "./McpAppModelContext.ts"; |
| ... | ... | @@ -57,10 +54,10 @@ export class McpAppRequests extends Context.Service< |
| 57 | 54 | const encodeJson = Schema.encodeSync(Schema.fromJsonString(Schema.Unknown)); |
| 58 | 55 | const utf8 = new TextEncoder(); |
| 59 | 56 | |
| 60 | const readOnlyHint = (tool: ProviderAdapterV2McpTool) => | |
| 57 | const readOnlyHint = (tool: ProviderAdapter.ProviderAdapterV2McpTool) => | |
| 61 | 58 | Predicate.isObject(tool.annotations) && tool.annotations.readOnlyHint === true; |
| 62 | 59 | |
| 63 | const toolTitle = (tool: ProviderAdapterV2McpTool) => | |
| 60 | const toolTitle = (tool: ProviderAdapter.ProviderAdapterV2McpTool) => | |
| 64 | 61 | Predicate.isObject(tool.annotations) && typeof tool.annotations.title === "string" |
| 65 | 62 | ? tool.annotations.title |
| 66 | 63 | : undefined; |
| ... | ... | @@ -112,7 +109,7 @@ const make = Effect.gen(function* () { |
| 112 | 109 | .pipe(Effect.mapError((cause) => fail(input.threadId, "request-failed", cause))), |
| 113 | 110 | ); |
| 114 | 111 | if (runtime === undefined) return yield* fail(input.threadId, "session-stopped"); |
| 115 | const mcpApps: ProviderAdapterV2McpApps | undefined = runtime.mcpApps; | |
| 112 | const mcpApps: ProviderAdapter.ProviderAdapterV2McpApps | undefined = runtime.mcpApps; | |
| 116 | 113 | if (mcpApps === undefined) return yield* fail(input.threadId, "provider-unsupported"); |
| 117 | 114 | return { app, providerThread, mcpApps }; |
| 118 | 115 | }); |
apps/server/src/orchestration-v2/AcpRegistryOrchestratorV2.live.test.ts+1-1| ... | ... | @@ -29,7 +29,7 @@ import * as CodexInstallation from "../provider/CodexInstallation.ts"; |
| 29 | 29 | import * as ServerEnvironment from "../environment/ServerEnvironment.ts"; |
| 30 | 30 | import * as ModelManifest from "../provider/ModelManifest.ts"; |
| 31 | 31 | import * as ProviderInstanceRegistryHydration from "../provider/ProviderInstanceRegistryHydration.ts"; |
| 32 | import * as ProviderEventLoggers from "../provider/ProviderEventLoggers.ts"; | |
| 32 | import * as ProviderEventLoggers from "@t3tools/provider-core/server/ProviderEventLoggers"; | |
| 33 | 33 | import * as OpenCodeRuntime from "@t3tools/provider-opencode/server/OpenCodeRuntime"; |
| 34 | 34 | import * as OpenCodeServerLedger from "@t3tools/provider-opencode/server/OpenCodeServerLedger"; |
| 35 | 35 | import * as ServerSettings from "../serverSettings.ts"; |
apps/server/src/orchestration-v2/Adapters/AcpAdapterV2.test.ts+189-171| ... | ... | @@ -64,13 +64,8 @@ import { |
| 64 | 64 | registerXAiBackgroundTaskTracking, |
| 65 | 65 | } from "@t3tools/provider-grok/testing"; |
| 66 | 66 | import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator"; |
| 67 | import { | |
| 68 | ProviderAdapterProtocolError, | |
| 69 | ProviderAdapterV2RuntimePolicy, | |
| 70 | type ProviderAdapterV2Event, | |
| 71 | type ProviderAdapterV2TurnInput, | |
| 72 | } from "@t3tools/provider-core/server/ProviderAdapter"; | |
| 73 | import type { ProviderContinuationRequest } from "@t3tools/provider-core/server/continuationRequests"; | |
| 67 | import * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter"; | |
| 68 | import type * as ProviderContinuationRequests from "@t3tools/provider-core/server/ProviderContinuationRequests"; | |
| 74 | 69 | import { |
| 75 | 70 | AcpProviderCapabilitiesV2, |
| 76 | 71 | acpProviderItemNativeId, |
| ... | ... | @@ -540,7 +535,7 @@ function makeTurnInput(input: { |
| 540 | 535 | readonly threadId: ThreadId; |
| 541 | 536 | readonly providerThread: OrchestrationV2ProviderThread; |
| 542 | 537 | readonly instanceId: ProviderInstanceId; |
| 543 | readonly runtimePolicy: ProviderAdapterV2RuntimePolicy; | |
| 538 | readonly runtimePolicy: ProviderAdapter.ProviderAdapterV2RuntimePolicy; | |
| 544 | 539 | readonly now: DateTime.Utc; |
| 545 | 540 | readonly ordinal?: number; |
| 546 | 541 | readonly modelSelection?: ModelSelection; |
| ... | ... | @@ -548,7 +543,7 @@ function makeTurnInput(input: { |
| 548 | 543 | readonly messageCreatedBy?: "user" | "agent"; |
| 549 | 544 | readonly messageCreationSource?: "web" | "mobile" | "mcp" | "provider" | "server"; |
| 550 | 545 | readonly messageText?: string; |
| 551 | }): ProviderAdapterV2TurnInput { | |
| 546 | }): ProviderAdapter.ProviderAdapterV2TurnInput { | |
| 552 | 547 | const ordinal = input.ordinal ?? 1; |
| 553 | 548 | const suffix = `${input.threadId}:${ordinal}`; |
| 554 | 549 | const modelSelection = |
| ... | ... | @@ -625,7 +620,7 @@ describe("AcpAdapterV2", () => { |
| 625 | 620 | }), |
| 626 | 621 | }, |
| 627 | 622 | }); |
| 628 | const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({ | |
| 623 | const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({ | |
| 629 | 624 | runtimeMode: "full-access", |
| 630 | 625 | interactionMode: "default", |
| 631 | 626 | cwd: process.cwd(), |
| ... | ... | @@ -742,7 +737,7 @@ describe("AcpAdapterV2", () => { |
| 742 | 737 | }, |
| 743 | 738 | selfInvocation, |
| 744 | 739 | }); |
| 745 | const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({ | |
| 740 | const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({ | |
| 746 | 741 | runtimeMode: "full-access", |
| 747 | 742 | interactionMode: "default", |
| 748 | 743 | cwd: process.cwd(), |
| ... | ... | @@ -799,7 +794,7 @@ describe("AcpAdapterV2", () => { |
| 799 | 794 | selfInvocation, |
| 800 | 795 | }); |
| 801 | 796 | const policy = (interactionMode: "default" | "plan") => |
| 802 | ProviderAdapterV2RuntimePolicy.make({ | |
| 797 | ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({ | |
| 803 | 798 | runtimeMode: "full-access", |
| 804 | 799 | interactionMode, |
| 805 | 800 | cwd: process.cwd(), |
| ... | ... | @@ -820,7 +815,7 @@ describe("AcpAdapterV2", () => { |
| 820 | 815 | const now = yield* DateTime.now; |
| 821 | 816 | const runTurn = Effect.fnUntraced(function* ( |
| 822 | 817 | ordinal: number, |
| 823 | runtimePolicy: ProviderAdapterV2RuntimePolicy, | |
| 818 | runtimePolicy: ProviderAdapter.ProviderAdapterV2RuntimePolicy, | |
| 824 | 819 | messageText: string, |
| 825 | 820 | ) { |
| 826 | 821 | yield* runtime.startTurn( |
| ... | ... | @@ -917,7 +912,7 @@ describe("AcpAdapterV2", () => { |
| 917 | 912 | selfInvocation, |
| 918 | 913 | }); |
| 919 | 914 | const threadId = ThreadId.make("thread-acp-v2-plan-replay-boundary"); |
| 920 | const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({ | |
| 915 | const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({ | |
| 921 | 916 | runtimeMode: "full-access", |
| 922 | 917 | interactionMode: "default", |
| 923 | 918 | cwd: process.cwd(), |
| ... | ... | @@ -1091,7 +1086,7 @@ describe("AcpAdapterV2", () => { |
| 1091 | 1086 | }); |
| 1092 | 1087 | const threadId = ThreadId.make("devin-streamed-write"); |
| 1093 | 1088 | const modelSelection = { instanceId, model: "default" }; |
| 1094 | const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({ | |
| 1089 | const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({ | |
| 1095 | 1090 | runtimeMode: "full-access", |
| 1096 | 1091 | interactionMode: "default", |
| 1097 | 1092 | cwd: process.cwd(), |
| ... | ... | @@ -1342,7 +1337,7 @@ describe("AcpAdapterV2", () => { |
| 1342 | 1337 | }); |
| 1343 | 1338 | const threadId = ThreadId.make("devin-replay-parent"); |
| 1344 | 1339 | const modelSelection = { instanceId, model: "default" }; |
| 1345 | const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({ | |
| 1340 | const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({ | |
| 1346 | 1341 | runtimeMode: "full-access", |
| 1347 | 1342 | interactionMode: "default", |
| 1348 | 1343 | cwd: process.cwd(), |
| ... | ... | @@ -1504,7 +1499,7 @@ describe("AcpAdapterV2", () => { |
| 1504 | 1499 | selfInvocation, |
| 1505 | 1500 | }); |
| 1506 | 1501 | const threadId = ThreadId.make("thread-acp-v2-fidelity"); |
| 1507 | const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({ | |
| 1502 | const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({ | |
| 1508 | 1503 | runtimeMode: "full-access", |
| 1509 | 1504 | interactionMode: "default", |
| 1510 | 1505 | cwd: process.cwd(), |
| ... | ... | @@ -1684,7 +1679,7 @@ describe("AcpAdapterV2", () => { |
| 1684 | 1679 | selfInvocation, |
| 1685 | 1680 | }); |
| 1686 | 1681 | const threadId = ThreadId.make("thread-acp-eager-resume"); |
| 1687 | const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({ | |
| 1682 | const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({ | |
| 1688 | 1683 | runtimeMode: "full-access", |
| 1689 | 1684 | interactionMode: "default", |
| 1690 | 1685 | cwd: process.cwd(), |
| ... | ... | @@ -1765,7 +1760,7 @@ describe("AcpAdapterV2", () => { |
| 1765 | 1760 | selfInvocation, |
| 1766 | 1761 | }); |
| 1767 | 1762 | const threadId = ThreadId.make("thread-acp-stale-eager-resume"); |
| 1768 | const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({ | |
| 1763 | const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({ | |
| 1769 | 1764 | runtimeMode: "full-access", |
| 1770 | 1765 | interactionMode: "default", |
| 1771 | 1766 | cwd: process.cwd(), |
| ... | ... | @@ -1910,7 +1905,7 @@ describe("AcpAdapterV2", () => { |
| 1910 | 1905 | selfInvocation, |
| 1911 | 1906 | }); |
| 1912 | 1907 | const threadId = ThreadId.make("thread-acp-unexpected-termination"); |
| 1913 | const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({ | |
| 1908 | const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({ | |
| 1914 | 1909 | runtimeMode: "full-access", |
| 1915 | 1910 | interactionMode: "default", |
| 1916 | 1911 | cwd: process.cwd(), |
| ... | ... | @@ -1993,7 +1988,7 @@ describe("AcpAdapterV2", () => { |
| 1993 | 1988 | selfInvocation, |
| 1994 | 1989 | }); |
| 1995 | 1990 | const threadId = ThreadId.make("thread-acp-provider-exit-running-command"); |
| 1996 | const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({ | |
| 1991 | const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({ | |
| 1997 | 1992 | runtimeMode: "full-access", |
| 1998 | 1993 | interactionMode: "default", |
| 1999 | 1994 | cwd: process.cwd(), |
| ... | ... | @@ -2065,7 +2060,7 @@ describe("AcpAdapterV2", () => { |
| 2065 | 2060 | selfInvocation, |
| 2066 | 2061 | }); |
| 2067 | 2062 | const threadId = ThreadId.make("thread-acp-cgroup-unavailable"); |
| 2068 | const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({ | |
| 2063 | const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({ | |
| 2069 | 2064 | runtimeMode: "full-access", |
| 2070 | 2065 | interactionMode: "default", |
| 2071 | 2066 | cwd: process.cwd(), |
| ... | ... | @@ -2130,7 +2125,7 @@ describe("AcpAdapterV2", () => { |
| 2130 | 2125 | selfInvocation, |
| 2131 | 2126 | }); |
| 2132 | 2127 | const threadId = ThreadId.make("thread-acp-cgroup-join-failure"); |
| 2133 | const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({ | |
| 2128 | const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({ | |
| 2134 | 2129 | runtimeMode: "full-access", |
| 2135 | 2130 | interactionMode: "default", |
| 2136 | 2131 | cwd: process.cwd(), |
| ... | ... | @@ -2226,7 +2221,7 @@ describe("AcpAdapterV2", () => { |
| 2226 | 2221 | McpProviderSession.clearMcpProviderSession(targetThreadId); |
| 2227 | 2222 | }), |
| 2228 | 2223 | ); |
| 2229 | const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({ | |
| 2224 | const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({ | |
| 2230 | 2225 | runtimeMode: "full-access", |
| 2231 | 2226 | interactionMode: "default", |
| 2232 | 2227 | cwd: process.cwd(), |
| ... | ... | @@ -2392,7 +2387,7 @@ describe("AcpAdapterV2", () => { |
| 2392 | 2387 | selfInvocation, |
| 2393 | 2388 | }); |
| 2394 | 2389 | const threadId = ThreadId.make("thread-acp-no-client-fs"); |
| 2395 | const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({ | |
| 2390 | const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({ | |
| 2396 | 2391 | runtimeMode: "full-access", |
| 2397 | 2392 | interactionMode: "default", |
| 2398 | 2393 | cwd: workspace, |
| ... | ... | @@ -2493,7 +2488,7 @@ describe("AcpAdapterV2", () => { |
| 2493 | 2488 | clientTerminals: { childProcessSpawner }, |
| 2494 | 2489 | }); |
| 2495 | 2490 | const threadId = ThreadId.make("thread-acp-unknown-permission-grant"); |
| 2496 | const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({ | |
| 2491 | const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({ | |
| 2497 | 2492 | runtimeMode: "approval-required", |
| 2498 | 2493 | interactionMode: "default", |
| 2499 | 2494 | cwd: process.cwd(), |
| ... | ... | @@ -2598,7 +2593,7 @@ describe("AcpAdapterV2", () => { |
| 2598 | 2593 | selfInvocation, |
| 2599 | 2594 | }); |
| 2600 | 2595 | const threadId = ThreadId.make("thread-acp-missing-native-thread"); |
| 2601 | const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({ | |
| 2596 | const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({ | |
| 2602 | 2597 | runtimeMode: "full-access", |
| 2603 | 2598 | interactionMode: "default", |
| 2604 | 2599 | cwd: process.cwd(), |
| ... | ... | @@ -2629,7 +2624,7 @@ describe("AcpAdapterV2", () => { |
| 2629 | 2624 | .pipe(Effect.flip); |
| 2630 | 2625 | |
| 2631 | 2626 | assert.equal(error._tag, "ProviderAdapterTurnStartError"); |
| 2632 | assert.instanceOf(error.cause, ProviderAdapterProtocolError); | |
| 2627 | assert.instanceOf(error.cause, ProviderAdapter.ProviderAdapterProtocolError); | |
| 2633 | 2628 | assert.include(String(error.cause), "missing its ACP session id"); |
| 2634 | 2629 | }).pipe(Effect.provide(layerTest), Effect.scoped), |
| 2635 | 2630 | ); |
| ... | ... | @@ -2676,7 +2671,7 @@ describe("AcpAdapterV2", () => { |
| 2676 | 2671 | selfInvocation, |
| 2677 | 2672 | }); |
| 2678 | 2673 | const threadId = ThreadId.make("thread-acp-rollback-session"); |
| 2679 | const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({ | |
| 2674 | const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({ | |
| 2680 | 2675 | runtimeMode: "full-access", |
| 2681 | 2676 | interactionMode: "default", |
| 2682 | 2677 | cwd: process.cwd(), |
| ... | ... | @@ -2689,7 +2684,7 @@ describe("AcpAdapterV2", () => { |
| 2689 | 2684 | runtimePolicy, |
| 2690 | 2685 | }); |
| 2691 | 2686 | assert.isTrue(runtime.providerSession.capabilities.threads.canRollbackThread); |
| 2692 | const events = yield* Queue.unbounded<ProviderAdapterV2Event>(); | |
| 2687 | const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>(); | |
| 2693 | 2688 | yield* runtime.events.pipe( |
| 2694 | 2689 | Stream.runForEach((event) => Queue.offer(events, event)), |
| 2695 | 2690 | Effect.forkScoped, |
| ... | ... | @@ -2883,7 +2878,7 @@ describe("AcpAdapterV2", () => { |
| 2883 | 2878 | continuationRequests: { offer: () => Effect.void }, |
| 2884 | 2879 | }); |
| 2885 | 2880 | const threadId = ThreadId.make("thread-acp-rollback-retry-generation"); |
| 2886 | const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({ | |
| 2881 | const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({ | |
| 2887 | 2882 | runtimeMode: "full-access", |
| 2888 | 2883 | interactionMode: "default", |
| 2889 | 2884 | cwd: process.cwd(), |
| ... | ... | @@ -2899,7 +2894,7 @@ describe("AcpAdapterV2", () => { |
| 2899 | 2894 | return yield* Effect.die("ACP runtime must expose background work state"); |
| 2900 | 2895 | } |
| 2901 | 2896 | const hasPendingBackgroundWork = runtime.hasPendingBackgroundWork; |
| 2902 | const events = yield* Queue.unbounded<ProviderAdapterV2Event>(); | |
| 2897 | const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>(); | |
| 2903 | 2898 | yield* runtime.events.pipe( |
| 2904 | 2899 | Stream.runForEach((event) => Queue.offer(events, event)), |
| 2905 | 2900 | Effect.forkScoped, |
| ... | ... | @@ -2995,7 +2990,7 @@ describe("AcpAdapterV2", () => { |
| 2995 | 2990 | selfInvocation, |
| 2996 | 2991 | }); |
| 2997 | 2992 | const threadId = ThreadId.make("thread-acp-rollback-failure-compensation"); |
| 2998 | const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({ | |
| 2993 | const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({ | |
| 2999 | 2994 | runtimeMode: "full-access", |
| 3000 | 2995 | interactionMode: "default", |
| 3001 | 2996 | cwd: process.cwd(), |
| ... | ... | @@ -3009,7 +3004,7 @@ describe("AcpAdapterV2", () => { |
| 3009 | 3004 | modelSelection, |
| 3010 | 3005 | runtimePolicy, |
| 3011 | 3006 | }); |
| 3012 | const events = yield* Queue.unbounded<ProviderAdapterV2Event>(); | |
| 3007 | const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>(); | |
| 3013 | 3008 | yield* runtime.events.pipe( |
| 3014 | 3009 | Stream.runForEach((event) => Queue.offer(events, event)), |
| 3015 | 3010 | Effect.forkScoped, |
| ... | ... | @@ -3079,7 +3074,7 @@ describe("AcpAdapterV2", () => { |
| 3079 | 3074 | selfInvocation, |
| 3080 | 3075 | }); |
| 3081 | 3076 | const threadId = ThreadId.make("thread-acp-idle-finalizer"); |
| 3082 | const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({ | |
| 3077 | const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({ | |
| 3083 | 3078 | runtimeMode: "full-access", |
| 3084 | 3079 | interactionMode: "default", |
| 3085 | 3080 | cwd: process.cwd(), |
| ... | ... | @@ -3129,7 +3124,7 @@ describe("AcpAdapterV2", () => { |
| 3129 | 3124 | threadId: ThreadId.make(`grok-model-${model}`), |
| 3130 | 3125 | providerSessionId: ProviderSessionId.make(`grok-model-${model}`), |
| 3131 | 3126 | modelSelection: { instanceId, model }, |
| 3132 | runtimePolicy: ProviderAdapterV2RuntimePolicy.make({ | |
| 3127 | runtimePolicy: ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({ | |
| 3133 | 3128 | runtimeMode: "full-access", |
| 3134 | 3129 | interactionMode: "default", |
| 3135 | 3130 | cwd: process.cwd(), |
| ... | ... | @@ -3173,7 +3168,7 @@ describe("AcpAdapterV2", () => { |
| 3173 | 3168 | ), |
| 3174 | 3169 | }); |
| 3175 | 3170 | const threadId = ThreadId.make("grok-model-switch-back"); |
| 3176 | const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({ | |
| 3171 | const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({ | |
| 3177 | 3172 | runtimeMode: "full-access", |
| 3178 | 3173 | interactionMode: "default", |
| 3179 | 3174 | cwd: process.cwd(), |
| ... | ... | @@ -3242,7 +3237,7 @@ describe("AcpAdapterV2", () => { |
| 3242 | 3237 | selfInvocation, |
| 3243 | 3238 | }); |
| 3244 | 3239 | const threadId = ThreadId.make("thread-acp-unsupported-option"); |
| 3245 | const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({ | |
| 3240 | const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({ | |
| 3246 | 3241 | runtimeMode: "full-access", |
| 3247 | 3242 | interactionMode: "default", |
| 3248 | 3243 | cwd: process.cwd(), |
| ... | ... | @@ -3286,7 +3281,7 @@ describe("AcpAdapterV2", () => { |
| 3286 | 3281 | }); |
| 3287 | 3282 | const firstThreadId = ThreadId.make("thread-acp-active-setup:first"); |
| 3288 | 3283 | const secondThreadId = ThreadId.make("thread-acp-active-setup:second"); |
| 3289 | const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({ | |
| 3284 | const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({ | |
| 3290 | 3285 | runtimeMode: "full-access", |
| 3291 | 3286 | interactionMode: "default", |
| 3292 | 3287 | cwd: process.cwd(), |
| ... | ... | @@ -3400,7 +3395,7 @@ describe("AcpAdapterV2", () => { |
| 3400 | 3395 | selfInvocation, |
| 3401 | 3396 | }); |
| 3402 | 3397 | const threadId = ThreadId.make("thread-acp-empty-successful-bash"); |
| 3403 | const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({ | |
| 3398 | const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({ | |
| 3404 | 3399 | runtimeMode: "full-access", |
| 3405 | 3400 | interactionMode: "default", |
| 3406 | 3401 | cwd: process.cwd(), |
| ... | ... | @@ -3412,7 +3407,7 @@ describe("AcpAdapterV2", () => { |
| 3412 | 3407 | modelSelection, |
| 3413 | 3408 | runtimePolicy, |
| 3414 | 3409 | }); |
| 3415 | const events = yield* Queue.unbounded<ProviderAdapterV2Event>(); | |
| 3410 | const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>(); | |
| 3416 | 3411 | yield* runtime.events.pipe( |
| 3417 | 3412 | Stream.runForEach((event) => Queue.offer(events, event)), |
| 3418 | 3413 | Effect.forkScoped, |
| ... | ... | @@ -3540,7 +3535,7 @@ describe("AcpAdapterV2", () => { |
| 3540 | 3535 | selfInvocation, |
| 3541 | 3536 | }); |
| 3542 | 3537 | const threadId = ThreadId.make("thread-acp-native-cancel"); |
| 3543 | const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({ | |
| 3538 | const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({ | |
| 3544 | 3539 | runtimeMode: "full-access", |
| 3545 | 3540 | interactionMode: "default", |
| 3546 | 3541 | cwd: process.cwd(), |
| ... | ... | @@ -3642,7 +3637,7 @@ describe("AcpAdapterV2", () => { |
| 3642 | 3637 | selfInvocation, |
| 3643 | 3638 | }); |
| 3644 | 3639 | const threadId = ThreadId.make("thread-acp-cancel-permission"); |
| 3645 | const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({ | |
| 3640 | const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({ | |
| 3646 | 3641 | runtimeMode: "approval-required", |
| 3647 | 3642 | interactionMode: "default", |
| 3648 | 3643 | cwd: process.cwd(), |
| ... | ... | @@ -3741,7 +3736,7 @@ describe("AcpAdapterV2", () => { |
| 3741 | 3736 | selfInvocation, |
| 3742 | 3737 | }); |
| 3743 | 3738 | const threadId = ThreadId.make("thread-acp-response-wins-permission"); |
| 3744 | const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({ | |
| 3739 | const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({ | |
| 3745 | 3740 | runtimeMode: "approval-required", |
| 3746 | 3741 | interactionMode: "default", |
| 3747 | 3742 | cwd: process.cwd(), |
| ... | ... | @@ -3833,7 +3828,7 @@ describe("AcpAdapterV2", () => { |
| 3833 | 3828 | selfInvocation, |
| 3834 | 3829 | }); |
| 3835 | 3830 | const threadId = ThreadId.make("thread-acp-reordered-elicitation"); |
| 3836 | const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({ | |
| 3831 | const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({ | |
| 3837 | 3832 | runtimeMode: "approval-required", |
| 3838 | 3833 | interactionMode: "default", |
| 3839 | 3834 | cwd: process.cwd(), |
| ... | ... | @@ -3908,7 +3903,7 @@ describe("AcpAdapterV2", () => { |
| 3908 | 3903 | selfInvocation, |
| 3909 | 3904 | }); |
| 3910 | 3905 | const threadId = ThreadId.make("thread-acp-mcp-approval-elicitation"); |
| 3911 | const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({ | |
| 3906 | const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({ | |
| 3912 | 3907 | runtimeMode: "full-access", |
| 3913 | 3908 | interactionMode: "default", |
| 3914 | 3909 | cwd: process.cwd(), |
| ... | ... | @@ -3983,7 +3978,7 @@ describe("AcpAdapterV2", () => { |
| 3983 | 3978 | }, |
| 3984 | 3979 | }); |
| 3985 | 3980 | const threadId = ThreadId.make("thread-acp-normal-close-held-response"); |
| 3986 | const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({ | |
| 3981 | const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({ | |
| 3987 | 3982 | runtimeMode: "approval-required", |
| 3988 | 3983 | interactionMode: "default", |
| 3989 | 3984 | cwd: process.cwd(), |
| ... | ... | @@ -4089,7 +4084,7 @@ describe("AcpAdapterV2", () => { |
| 4089 | 4084 | }, |
| 4090 | 4085 | }); |
| 4091 | 4086 | const threadId = ThreadId.make("thread-acp-close-wins-registration"); |
| 4092 | const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({ | |
| 4087 | const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({ | |
| 4093 | 4088 | runtimeMode: "approval-required", |
| 4094 | 4089 | interactionMode: "default", |
| 4095 | 4090 | cwd: process.cwd(), |
| ... | ... | @@ -4208,7 +4203,7 @@ describe("AcpAdapterV2", () => { |
| 4208 | 4203 | }, |
| 4209 | 4204 | }); |
| 4210 | 4205 | const threadId = ThreadId.make("thread-acp-pending-response-timeout"); |
| 4211 | const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({ | |
| 4206 | const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({ | |
| 4212 | 4207 | runtimeMode: "approval-required", |
| 4213 | 4208 | interactionMode: "default", |
| 4214 | 4209 | cwd: process.cwd(), |
| ... | ... | @@ -4333,7 +4328,7 @@ describe("AcpAdapterV2", () => { |
| 4333 | 4328 | selfInvocation, |
| 4334 | 4329 | }); |
| 4335 | 4330 | const threadId = ThreadId.make("thread-acp-pending-response-cancel"); |
| 4336 | const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({ | |
| 4331 | const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({ | |
| 4337 | 4332 | runtimeMode: "approval-required", |
| 4338 | 4333 | interactionMode: "default", |
| 4339 | 4334 | cwd: process.cwd(), |
| ... | ... | @@ -4442,7 +4437,7 @@ describe("AcpAdapterV2", () => { |
| 4442 | 4437 | selfInvocation, |
| 4443 | 4438 | }); |
| 4444 | 4439 | const threadId = ThreadId.make(`thread-acp-immediate-permission-${name}`); |
| 4445 | const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({ | |
| 4440 | const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({ | |
| 4446 | 4441 | runtimeMode: "full-access", |
| 4447 | 4442 | interactionMode: "default", |
| 4448 | 4443 | approvalPolicy: "never", |
| ... | ... | @@ -4535,7 +4530,7 @@ describe("AcpAdapterV2", () => { |
| 4535 | 4530 | selfInvocation, |
| 4536 | 4531 | }); |
| 4537 | 4532 | const threadId = ThreadId.make("thread-acp-immediate-url-elicitation"); |
| 4538 | const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({ | |
| 4533 | const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({ | |
| 4539 | 4534 | runtimeMode: "full-access", |
| 4540 | 4535 | interactionMode: "default", |
| 4541 | 4536 | cwd: process.cwd(), |
| ... | ... | @@ -4626,7 +4621,7 @@ describe("AcpAdapterV2", () => { |
| 4626 | 4621 | selfInvocation, |
| 4627 | 4622 | }); |
| 4628 | 4623 | const threadId = ThreadId.make("thread-acp-missing-response-ack"); |
| 4629 | const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({ | |
| 4624 | const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({ | |
| 4630 | 4625 | runtimeMode: "full-access", |
| 4631 | 4626 | interactionMode: "default", |
| 4632 | 4627 | approvalPolicy: "never", |
| ... | ... | @@ -4708,7 +4703,7 @@ describe("AcpAdapterV2", () => { |
| 4708 | 4703 | selfInvocation, |
| 4709 | 4704 | }); |
| 4710 | 4705 | const threadId = ThreadId.make("thread-acp-teardown-wins-elicitation"); |
| 4711 | const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({ | |
| 4706 | const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({ | |
| 4712 | 4707 | runtimeMode: "approval-required", |
| 4713 | 4708 | interactionMode: "default", |
| 4714 | 4709 | cwd: process.cwd(), |
| ... | ... | @@ -4802,7 +4797,7 @@ describe("AcpAdapterV2", () => { |
| 4802 | 4797 | selfInvocation, |
| 4803 | 4798 | }); |
| 4804 | 4799 | const threadId = ThreadId.make("thread-acp-cancel-timeout"); |
| 4805 | const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({ | |
| 4800 | const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({ | |
| 4806 | 4801 | runtimeMode: "full-access", |
| 4807 | 4802 | interactionMode: "default", |
| 4808 | 4803 | cwd: process.cwd(), |
| ... | ... | @@ -4903,7 +4898,7 @@ describe("AcpAdapterV2", () => { |
| 4903 | 4898 | selfInvocation, |
| 4904 | 4899 | }); |
| 4905 | 4900 | const threadId = ThreadId.make("thread-acp-double-stop"); |
| 4906 | const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({ | |
| 4901 | const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({ | |
| 4907 | 4902 | runtimeMode: "full-access", |
| 4908 | 4903 | interactionMode: "default", |
| 4909 | 4904 | cwd: process.cwd(), |
| ... | ... | @@ -4915,7 +4910,7 @@ describe("AcpAdapterV2", () => { |
| 4915 | 4910 | modelSelection, |
| 4916 | 4911 | runtimePolicy, |
| 4917 | 4912 | }); |
| 4918 | const events = yield* Queue.unbounded<ProviderAdapterV2Event>(); | |
| 4913 | const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>(); | |
| 4919 | 4914 | yield* runtime.events.pipe( |
| 4920 | 4915 | Stream.runForEach((event) => Queue.offer(events, event)), |
| 4921 | 4916 | Effect.forkScoped, |
| ... | ... | @@ -5007,7 +5002,7 @@ describe("AcpAdapterV2", () => { |
| 5007 | 5002 | selfInvocation, |
| 5008 | 5003 | }); |
| 5009 | 5004 | const threadId = ThreadId.make("thread-acp-interrupt-background-hold"); |
| 5010 | const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({ | |
| 5005 | const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({ | |
| 5011 | 5006 | runtimeMode: "full-access", |
| 5012 | 5007 | interactionMode: "default", |
| 5013 | 5008 | cwd: process.cwd(), |
| ... | ... | @@ -5019,7 +5014,7 @@ describe("AcpAdapterV2", () => { |
| 5019 | 5014 | modelSelection, |
| 5020 | 5015 | runtimePolicy, |
| 5021 | 5016 | }); |
| 5022 | const events = yield* Queue.unbounded<ProviderAdapterV2Event>(); | |
| 5017 | const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>(); | |
| 5023 | 5018 | yield* runtime.events.pipe( |
| 5024 | 5019 | Stream.runForEach((event) => Queue.offer(events, event)), |
| 5025 | 5020 | Effect.forkScoped, |
| ... | ... | @@ -5111,7 +5106,7 @@ describe("AcpAdapterV2", () => { |
| 5111 | 5106 | selfInvocation, |
| 5112 | 5107 | }); |
| 5113 | 5108 | const threadId = ThreadId.make("thread-acp-subagent-carryover"); |
| 5114 | const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({ | |
| 5109 | const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({ | |
| 5115 | 5110 | runtimeMode: "full-access", |
| 5116 | 5111 | interactionMode: "default", |
| 5117 | 5112 | cwd: process.cwd(), |
| ... | ... | @@ -5123,7 +5118,7 @@ describe("AcpAdapterV2", () => { |
| 5123 | 5118 | modelSelection, |
| 5124 | 5119 | runtimePolicy, |
| 5125 | 5120 | }); |
| 5126 | const events = yield* Queue.unbounded<ProviderAdapterV2Event>(); | |
| 5121 | const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>(); | |
| 5127 | 5122 | yield* runtime.events.pipe( |
| 5128 | 5123 | Stream.runForEach((event) => Queue.offer(events, event)), |
| 5129 | 5124 | Effect.forkScoped, |
| ... | ... | @@ -5252,7 +5247,7 @@ describe("AcpAdapterV2", () => { |
| 5252 | 5247 | continuationRequests: { offer: () => Effect.void }, |
| 5253 | 5248 | }); |
| 5254 | 5249 | const threadId = ThreadId.make("thread-acp-carryover-pending-pin"); |
| 5255 | const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({ | |
| 5250 | const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({ | |
| 5256 | 5251 | runtimeMode: "full-access", |
| 5257 | 5252 | interactionMode: "default", |
| 5258 | 5253 | cwd: process.cwd(), |
| ... | ... | @@ -5270,7 +5265,7 @@ describe("AcpAdapterV2", () => { |
| 5270 | 5265 | ); |
| 5271 | 5266 | } |
| 5272 | 5267 | const hasPendingBackgroundWork = runtime.hasPendingBackgroundWork; |
| 5273 | const events = yield* Queue.unbounded<ProviderAdapterV2Event>(); | |
| 5268 | const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>(); | |
| 5274 | 5269 | yield* runtime.events.pipe( |
| 5275 | 5270 | Stream.runForEach((event) => Queue.offer(events, event)), |
| 5276 | 5271 | Effect.forkScoped, |
| ... | ... | @@ -5416,7 +5411,7 @@ describe("AcpAdapterV2", () => { |
| 5416 | 5411 | continuationRequests: { offer: () => Effect.void }, |
| 5417 | 5412 | }); |
| 5418 | 5413 | const threadId = ThreadId.make("thread-acp-active-carryover-pending-pin"); |
| 5419 | const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({ | |
| 5414 | const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({ | |
| 5420 | 5415 | runtimeMode: "full-access", |
| 5421 | 5416 | interactionMode: "default", |
| 5422 | 5417 | cwd: process.cwd(), |
| ... | ... | @@ -5436,7 +5431,7 @@ describe("AcpAdapterV2", () => { |
| 5436 | 5431 | ); |
| 5437 | 5432 | } |
| 5438 | 5433 | const hasPendingBackgroundWork = runtime.hasPendingBackgroundWork; |
| 5439 | const events = yield* Queue.unbounded<ProviderAdapterV2Event>(); | |
| 5434 | const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>(); | |
| 5440 | 5435 | yield* runtime.events.pipe( |
| 5441 | 5436 | Stream.runForEach((event) => Queue.offer(events, event)), |
| 5442 | 5437 | Effect.forkScoped, |
| ... | ... | @@ -5661,7 +5656,7 @@ describe("AcpAdapterV2", () => { |
| 5661 | 5656 | continuationRequests: { offer: () => Effect.void }, |
| 5662 | 5657 | }); |
| 5663 | 5658 | const threadId = ThreadId.make("thread-acp-subagent-finalize-window"); |
| 5664 | const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({ | |
| 5659 | const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({ | |
| 5665 | 5660 | runtimeMode: "full-access", |
| 5666 | 5661 | interactionMode: "default", |
| 5667 | 5662 | cwd: process.cwd(), |
| ... | ... | @@ -5681,7 +5676,7 @@ describe("AcpAdapterV2", () => { |
| 5681 | 5676 | ); |
| 5682 | 5677 | } |
| 5683 | 5678 | const hasPendingBackgroundWork = runtime.hasPendingBackgroundWork; |
| 5684 | const events = yield* Queue.unbounded<ProviderAdapterV2Event>(); | |
| 5679 | const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>(); | |
| 5685 | 5680 | yield* runtime.events.pipe( |
| 5686 | 5681 | Stream.runForEach((event) => Queue.offer(events, event)), |
| 5687 | 5682 | Effect.forkScoped, |
| ... | ... | @@ -5796,7 +5791,8 @@ describe("AcpAdapterV2", () => { |
| 5796 | 5791 | new URL("../../../scripts/acp-mock-agent.ts", import.meta.url), |
| 5797 | 5792 | ); |
| 5798 | 5793 | const protocolEvents = yield* Queue.bounded<EffectAcpProtocol.AcpProtocolLogEvent>(256); |
| 5799 | const continuationRequests: Array<ProviderContinuationRequest> = []; | |
| 5794 | const continuationRequests: Array<ProviderContinuationRequests.ProviderContinuationRequest> = | |
| 5795 | []; | |
| 5800 | 5796 | const promptSettled = yield* Deferred.make<void>(); |
| 5801 | 5797 | const instanceId = ProviderInstanceId.make("acp-test"); |
| 5802 | 5798 | const childSessionId = "mock-child-session-post-settle"; |
| ... | ... | @@ -5859,7 +5855,7 @@ describe("AcpAdapterV2", () => { |
| 5859 | 5855 | }, |
| 5860 | 5856 | }); |
| 5861 | 5857 | const threadId = ThreadId.make("thread-acp-carryover-child-session-post-settle"); |
| 5862 | const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({ | |
| 5858 | const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({ | |
| 5863 | 5859 | runtimeMode: "full-access", |
| 5864 | 5860 | interactionMode: "default", |
| 5865 | 5861 | cwd: process.cwd(), |
| ... | ... | @@ -5879,7 +5875,7 @@ describe("AcpAdapterV2", () => { |
| 5879 | 5875 | ); |
| 5880 | 5876 | } |
| 5881 | 5877 | const hasPendingBackgroundWork = runtime.hasPendingBackgroundWork; |
| 5882 | const events = yield* Queue.unbounded<ProviderAdapterV2Event>(); | |
| 5878 | const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>(); | |
| 5883 | 5879 | yield* runtime.events.pipe( |
| 5884 | 5880 | Stream.runForEach((event) => Queue.offer(events, event)), |
| 5885 | 5881 | Effect.forkScoped, |
| ... | ... | @@ -6046,7 +6042,7 @@ describe("AcpAdapterV2", () => { |
| 6046 | 6042 | continuationRequests: { offer: () => Effect.void }, |
| 6047 | 6043 | }); |
| 6048 | 6044 | const threadId = ThreadId.make("thread-acp-carryover-subagent-finished"); |
| 6049 | const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({ | |
| 6045 | const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({ | |
| 6050 | 6046 | runtimeMode: "full-access", |
| 6051 | 6047 | interactionMode: "default", |
| 6052 | 6048 | cwd: process.cwd(), |
| ... | ... | @@ -6062,7 +6058,7 @@ describe("AcpAdapterV2", () => { |
| 6062 | 6058 | return yield* Effect.die("post-settle continuation must expose hasPendingBackgroundWork"); |
| 6063 | 6059 | } |
| 6064 | 6060 | const hasPendingBackgroundWork = runtime.hasPendingBackgroundWork; |
| 6065 | const events = yield* Queue.unbounded<ProviderAdapterV2Event>(); | |
| 6061 | const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>(); | |
| 6066 | 6062 | yield* runtime.events.pipe( |
| 6067 | 6063 | Stream.runForEach((event) => Queue.offer(events, event)), |
| 6068 | 6064 | Effect.forkScoped, |
| ... | ... | @@ -6151,7 +6147,8 @@ describe("AcpAdapterV2", () => { |
| 6151 | 6147 | new URL("../../../scripts/acp-mock-agent.ts", import.meta.url), |
| 6152 | 6148 | ); |
| 6153 | 6149 | const protocolEvents = yield* Queue.bounded<EffectAcpProtocol.AcpProtocolLogEvent>(256); |
| 6154 | const continuationRequests: Array<ProviderContinuationRequest> = []; | |
| 6150 | const continuationRequests: Array<ProviderContinuationRequests.ProviderContinuationRequest> = | |
| 6151 | []; | |
| 6155 | 6152 | const instanceId = ProviderInstanceId.make("acp-test"); |
| 6156 | 6153 | const childSessionId = "019f44a6-4820-7402-925d-bc862ee711dd"; |
| 6157 | 6154 | let subagentPhase: "spawn" | "complete" = "spawn"; |
| ... | ... | @@ -6210,7 +6207,7 @@ describe("AcpAdapterV2", () => { |
| 6210 | 6207 | }, |
| 6211 | 6208 | }); |
| 6212 | 6209 | const threadId = ThreadId.make("thread-acp-completed-root-eager-carryover"); |
| 6213 | const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({ | |
| 6210 | const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({ | |
| 6214 | 6211 | runtimeMode: "full-access", |
| 6215 | 6212 | interactionMode: "default", |
| 6216 | 6213 | cwd: process.cwd(), |
| ... | ... | @@ -6230,7 +6227,7 @@ describe("AcpAdapterV2", () => { |
| 6230 | 6227 | ); |
| 6231 | 6228 | } |
| 6232 | 6229 | const hasPendingBackgroundWork = runtime.hasPendingBackgroundWork; |
| 6233 | const events = yield* Queue.unbounded<ProviderAdapterV2Event>(); | |
| 6230 | const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>(); | |
| 6234 | 6231 | yield* runtime.events.pipe( |
| 6235 | 6232 | Stream.runForEach((event) => Queue.offer(events, event)), |
| 6236 | 6233 | Effect.forkScoped, |
| ... | ... | @@ -6396,7 +6393,8 @@ describe("AcpAdapterV2", () => { |
| 6396 | 6393 | const mockAgentPath = yield* path.fromFileUrl( |
| 6397 | 6394 | new URL("../../../scripts/acp-mock-agent.ts", import.meta.url), |
| 6398 | 6395 | ); |
| 6399 | const continuationRequests: Array<ProviderContinuationRequest> = []; | |
| 6396 | const continuationRequests: Array<ProviderContinuationRequests.ProviderContinuationRequest> = | |
| 6397 | []; | |
| 6400 | 6398 | const bufferedAssistantText = "BUFFERED_WAKE_AFTER_USER_ATTACH"; |
| 6401 | 6399 | const instanceId = ProviderInstanceId.make("acp-test"); |
| 6402 | 6400 | let subagentPhase: "spawn" | "complete" = "spawn"; |
| ... | ... | @@ -6458,7 +6456,7 @@ describe("AcpAdapterV2", () => { |
| 6458 | 6456 | }, |
| 6459 | 6457 | }); |
| 6460 | 6458 | const threadId = ThreadId.make("thread-acp-carryover-root-session-continuation"); |
| 6461 | const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({ | |
| 6459 | const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({ | |
| 6462 | 6460 | runtimeMode: "full-access", |
| 6463 | 6461 | interactionMode: "default", |
| 6464 | 6462 | cwd: process.cwd(), |
| ... | ... | @@ -6478,7 +6476,7 @@ describe("AcpAdapterV2", () => { |
| 6478 | 6476 | ); |
| 6479 | 6477 | } |
| 6480 | 6478 | const hasPendingBackgroundWork = runtime.hasPendingBackgroundWork; |
| 6481 | const events = yield* Queue.unbounded<ProviderAdapterV2Event>(); | |
| 6479 | const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>(); | |
| 6482 | 6480 | yield* runtime.events.pipe( |
| 6483 | 6481 | Stream.runForEach((event) => Queue.offer(events, event)), |
| 6484 | 6482 | Effect.forkScoped, |
| ... | ... | @@ -6678,7 +6676,8 @@ describe("AcpAdapterV2", () => { |
| 6678 | 6676 | const mockAgentPath = yield* path.fromFileUrl( |
| 6679 | 6677 | new URL("../../../scripts/acp-mock-agent.ts", import.meta.url), |
| 6680 | 6678 | ); |
| 6681 | const continuationRequests: Array<ProviderContinuationRequest> = []; | |
| 6679 | const continuationRequests: Array<ProviderContinuationRequests.ProviderContinuationRequest> = | |
| 6680 | []; | |
| 6682 | 6681 | const promptWireReturned = yield* Deferred.make<void>(); |
| 6683 | 6682 | const releasePromptCompletion = yield* Deferred.make<void>(); |
| 6684 | 6683 | const instanceId = ProviderInstanceId.make("acp-test"); |
| ... | ... | @@ -6750,7 +6749,7 @@ describe("AcpAdapterV2", () => { |
| 6750 | 6749 | }, |
| 6751 | 6750 | }); |
| 6752 | 6751 | const threadId = ThreadId.make("thread-acp-carryover-already-handled-re-report"); |
| 6753 | const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({ | |
| 6752 | const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({ | |
| 6754 | 6753 | runtimeMode: "full-access", |
| 6755 | 6754 | interactionMode: "default", |
| 6756 | 6755 | cwd: process.cwd(), |
| ... | ... | @@ -6770,7 +6769,7 @@ describe("AcpAdapterV2", () => { |
| 6770 | 6769 | ); |
| 6771 | 6770 | } |
| 6772 | 6771 | const hasPendingBackgroundWork = runtime.hasPendingBackgroundWork; |
| 6773 | const events = yield* Queue.unbounded<ProviderAdapterV2Event>(); | |
| 6772 | const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>(); | |
| 6774 | 6773 | yield* runtime.events.pipe( |
| 6775 | 6774 | Stream.runForEach((event) => Queue.offer(events, event)), |
| 6776 | 6775 | Effect.forkScoped, |
| ... | ... | @@ -6914,7 +6913,8 @@ describe("AcpAdapterV2", () => { |
| 6914 | 6913 | new URL("../../../scripts/acp-mock-agent.ts", import.meta.url), |
| 6915 | 6914 | ); |
| 6916 | 6915 | const protocolEvents = yield* Queue.bounded<EffectAcpProtocol.AcpProtocolLogEvent>(256); |
| 6917 | const continuationRequests: Array<ProviderContinuationRequest> = []; | |
| 6916 | const continuationRequests: Array<ProviderContinuationRequests.ProviderContinuationRequest> = | |
| 6917 | []; | |
| 6918 | 6918 | const promptSettled = yield* Deferred.make<void>(); |
| 6919 | 6919 | const instanceId = ProviderInstanceId.make("acp-test"); |
| 6920 | 6920 | const childSessionId = "019f5470-bf92-7a90-afb3-5a6cea5b34a3"; |
| ... | ... | @@ -6988,7 +6988,7 @@ describe("AcpAdapterV2", () => { |
| 6988 | 6988 | }, |
| 6989 | 6989 | }); |
| 6990 | 6990 | const threadId = ThreadId.make("thread-acp-carryover-root-end-notice"); |
| 6991 | const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({ | |
| 6991 | const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({ | |
| 6992 | 6992 | runtimeMode: "full-access", |
| 6993 | 6993 | interactionMode: "default", |
| 6994 | 6994 | cwd: process.cwd(), |
| ... | ... | @@ -7006,7 +7006,7 @@ describe("AcpAdapterV2", () => { |
| 7006 | 7006 | ); |
| 7007 | 7007 | } |
| 7008 | 7008 | const hasPendingBackgroundWork = runtime.hasPendingBackgroundWork; |
| 7009 | const events = yield* Queue.unbounded<ProviderAdapterV2Event>(); | |
| 7009 | const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>(); | |
| 7010 | 7010 | yield* runtime.events.pipe( |
| 7011 | 7011 | Stream.runForEach((event) => Queue.offer(events, event)), |
| 7012 | 7012 | Effect.forkScoped, |
| ... | ... | @@ -7136,7 +7136,8 @@ describe("AcpAdapterV2", () => { |
| 7136 | 7136 | const mockAgentPath = yield* path.fromFileUrl( |
| 7137 | 7137 | new URL("../../../scripts/acp-mock-agent.ts", import.meta.url), |
| 7138 | 7138 | ); |
| 7139 | const continuationRequests: Array<ProviderContinuationRequest> = []; | |
| 7139 | const continuationRequests: Array<ProviderContinuationRequests.ProviderContinuationRequest> = | |
| 7140 | []; | |
| 7140 | 7141 | const instanceId = ProviderInstanceId.make("acp-test"); |
| 7141 | 7142 | const childSessionId = "mock-child-session-pending-continuation"; |
| 7142 | 7143 | const bufferedAssistantText = "POST_SETTLE_BUFFERED_ASSISTANT_TEXT"; |
| ... | ... | @@ -7199,7 +7200,7 @@ describe("AcpAdapterV2", () => { |
| 7199 | 7200 | }, |
| 7200 | 7201 | }); |
| 7201 | 7202 | const threadId = ThreadId.make("thread-acp-carryover-child-pending-continuation"); |
| 7202 | const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({ | |
| 7203 | const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({ | |
| 7203 | 7204 | runtimeMode: "full-access", |
| 7204 | 7205 | interactionMode: "default", |
| 7205 | 7206 | cwd: process.cwd(), |
| ... | ... | @@ -7219,7 +7220,7 @@ describe("AcpAdapterV2", () => { |
| 7219 | 7220 | ); |
| 7220 | 7221 | } |
| 7221 | 7222 | const hasPendingBackgroundWork = runtime.hasPendingBackgroundWork; |
| 7222 | const events = yield* Queue.unbounded<ProviderAdapterV2Event>(); | |
| 7223 | const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>(); | |
| 7223 | 7224 | yield* runtime.events.pipe( |
| 7224 | 7225 | Stream.runForEach((event) => Queue.offer(events, event)), |
| 7225 | 7226 | Effect.forkScoped, |
| ... | ... | @@ -7401,7 +7402,8 @@ describe("AcpAdapterV2", () => { |
| 7401 | 7402 | const mockAgentPath = yield* path.fromFileUrl( |
| 7402 | 7403 | new URL("../../../scripts/acp-mock-agent.ts", import.meta.url), |
| 7403 | 7404 | ); |
| 7404 | const continuationRequests: Array<ProviderContinuationRequest> = []; | |
| 7405 | const continuationRequests: Array<ProviderContinuationRequests.ProviderContinuationRequest> = | |
| 7406 | []; | |
| 7405 | 7407 | const instanceId = ProviderInstanceId.make("acp-test"); |
| 7406 | 7408 | const childSessionId = "mock-child-session-root-then-child"; |
| 7407 | 7409 | let subagentPhase: "spawn" | "complete" = "spawn"; |
| ... | ... | @@ -7463,7 +7465,7 @@ describe("AcpAdapterV2", () => { |
| 7463 | 7465 | }, |
| 7464 | 7466 | }); |
| 7465 | 7467 | const threadId = ThreadId.make("thread-acp-carryover-root-then-child"); |
| 7466 | const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({ | |
| 7468 | const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({ | |
| 7467 | 7469 | runtimeMode: "full-access", |
| 7468 | 7470 | interactionMode: "default", |
| 7469 | 7471 | cwd: process.cwd(), |
| ... | ... | @@ -7483,7 +7485,7 @@ describe("AcpAdapterV2", () => { |
| 7483 | 7485 | ); |
| 7484 | 7486 | } |
| 7485 | 7487 | const hasPendingBackgroundWork = runtime.hasPendingBackgroundWork; |
| 7486 | const events = yield* Queue.unbounded<ProviderAdapterV2Event>(); | |
| 7488 | const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>(); | |
| 7487 | 7489 | yield* runtime.events.pipe( |
| 7488 | 7490 | Stream.runForEach((event) => Queue.offer(events, event)), |
| 7489 | 7491 | Effect.forkScoped, |
| ... | ... | @@ -7701,7 +7703,7 @@ describe("AcpAdapterV2", () => { |
| 7701 | 7703 | selfInvocation, |
| 7702 | 7704 | }); |
| 7703 | 7705 | const threadId = ThreadId.make("thread-acp-settled-soft-steer"); |
| 7704 | const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({ | |
| 7706 | const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({ | |
| 7705 | 7707 | runtimeMode: "full-access", |
| 7706 | 7708 | interactionMode: "default", |
| 7707 | 7709 | cwd: process.cwd(), |
| ... | ... | @@ -7713,7 +7715,7 @@ describe("AcpAdapterV2", () => { |
| 7713 | 7715 | modelSelection, |
| 7714 | 7716 | runtimePolicy, |
| 7715 | 7717 | }); |
| 7716 | const events = yield* Queue.unbounded<ProviderAdapterV2Event>(); | |
| 7718 | const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>(); | |
| 7717 | 7719 | yield* runtime.events.pipe( |
| 7718 | 7720 | Stream.runForEach((event) => Queue.offer(events, event)), |
| 7719 | 7721 | Effect.forkScoped, |
| ... | ... | @@ -7834,7 +7836,7 @@ describe("AcpAdapterV2", () => { |
| 7834 | 7836 | selfInvocation, |
| 7835 | 7837 | }); |
| 7836 | 7838 | const threadId = ThreadId.make("thread-acp-unsettled-steer-stays-hard"); |
| 7837 | const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({ | |
| 7839 | const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({ | |
| 7838 | 7840 | runtimeMode: "full-access", |
| 7839 | 7841 | interactionMode: "default", |
| 7840 | 7842 | cwd: process.cwd(), |
| ... | ... | @@ -7892,7 +7894,8 @@ describe("AcpAdapterV2", () => { |
| 7892 | 7894 | new URL("../../../scripts/acp-mock-agent.ts", import.meta.url), |
| 7893 | 7895 | ); |
| 7894 | 7896 | const protocolEvents = yield* Queue.bounded<EffectAcpProtocol.AcpProtocolLogEvent>(256); |
| 7895 | const continuationRequests: Array<ProviderContinuationRequest> = []; | |
| 7897 | const continuationRequests: Array<ProviderContinuationRequests.ProviderContinuationRequest> = | |
| 7898 | []; | |
| 7896 | 7899 | const instanceId = ProviderInstanceId.make("acp-test"); |
| 7897 | 7900 | let cancelCalled = false; |
| 7898 | 7901 | let runtimeOrdinalSeen = 0; |
| ... | ... | @@ -7940,7 +7943,7 @@ describe("AcpAdapterV2", () => { |
| 7940 | 7943 | }, |
| 7941 | 7944 | }); |
| 7942 | 7945 | const threadId = ThreadId.make("thread-acp-soft-mid-prompt-steer"); |
| 7943 | const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({ | |
| 7946 | const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({ | |
| 7944 | 7947 | runtimeMode: "full-access", |
| 7945 | 7948 | interactionMode: "default", |
| 7946 | 7949 | cwd: process.cwd(), |
| ... | ... | @@ -7958,7 +7961,7 @@ describe("AcpAdapterV2", () => { |
| 7958 | 7961 | ); |
| 7959 | 7962 | } |
| 7960 | 7963 | const hasPendingBackgroundWork = runtime.hasPendingBackgroundWork; |
| 7961 | const events = yield* Queue.unbounded<ProviderAdapterV2Event>(); | |
| 7964 | const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>(); | |
| 7962 | 7965 | yield* runtime.events.pipe( |
| 7963 | 7966 | Stream.runForEach((event) => Queue.offer(events, event)), |
| 7964 | 7967 | Effect.forkScoped, |
| ... | ... | @@ -8115,7 +8118,7 @@ describe("AcpAdapterV2", () => { |
| 8115 | 8118 | continuationRequests: { offer: () => Effect.void }, |
| 8116 | 8119 | }); |
| 8117 | 8120 | const threadId = ThreadId.make("thread-acp-stop-quarantine-late-task"); |
| 8118 | const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({ | |
| 8121 | const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({ | |
| 8119 | 8122 | runtimeMode: "full-access", |
| 8120 | 8123 | interactionMode: "default", |
| 8121 | 8124 | cwd: process.cwd(), |
| ... | ... | @@ -8133,7 +8136,7 @@ describe("AcpAdapterV2", () => { |
| 8133 | 8136 | ); |
| 8134 | 8137 | } |
| 8135 | 8138 | const hasPendingBackgroundWork = runtime.hasPendingBackgroundWork; |
| 8136 | const events = yield* Queue.unbounded<ProviderAdapterV2Event>(); | |
| 8139 | const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>(); | |
| 8137 | 8140 | yield* runtime.events.pipe( |
| 8138 | 8141 | Stream.runForEach((event) => Queue.offer(events, event)), |
| 8139 | 8142 | Effect.forkScoped, |
| ... | ... | @@ -8255,7 +8258,7 @@ describe("AcpAdapterV2", () => { |
| 8255 | 8258 | continuationRequests: { offer: () => Effect.void }, |
| 8256 | 8259 | }); |
| 8257 | 8260 | const threadId = ThreadId.make("thread-acp-production-stop-hard-kill"); |
| 8258 | const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({ | |
| 8261 | const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({ | |
| 8259 | 8262 | runtimeMode: "full-access", |
| 8260 | 8263 | interactionMode: "default", |
| 8261 | 8264 | cwd: process.cwd(), |
| ... | ... | @@ -8267,7 +8270,7 @@ describe("AcpAdapterV2", () => { |
| 8267 | 8270 | modelSelection, |
| 8268 | 8271 | runtimePolicy, |
| 8269 | 8272 | }); |
| 8270 | const events = yield* Queue.unbounded<ProviderAdapterV2Event>(); | |
| 8273 | const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>(); | |
| 8271 | 8274 | yield* runtime.events.pipe( |
| 8272 | 8275 | Stream.runForEach((event) => Queue.offer(events, event)), |
| 8273 | 8276 | Effect.forkScoped, |
| ... | ... | @@ -8417,7 +8420,7 @@ describe("AcpAdapterV2", () => { |
| 8417 | 8420 | selfInvocation, |
| 8418 | 8421 | }); |
| 8419 | 8422 | const threadId = ThreadId.make("thread-acp-stop-after-soft-steer-orphan"); |
| 8420 | const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({ | |
| 8423 | const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({ | |
| 8421 | 8424 | runtimeMode: "full-access", |
| 8422 | 8425 | interactionMode: "default", |
| 8423 | 8426 | cwd: process.cwd(), |
| ... | ... | @@ -8431,7 +8434,7 @@ describe("AcpAdapterV2", () => { |
| 8431 | 8434 | modelSelection, |
| 8432 | 8435 | runtimePolicy, |
| 8433 | 8436 | }); |
| 8434 | const events = yield* Queue.unbounded<ProviderAdapterV2Event>(); | |
| 8437 | const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>(); | |
| 8435 | 8438 | yield* runtime.events.pipe( |
| 8436 | 8439 | Stream.runForEach((event) => Queue.offer(events, event)), |
| 8437 | 8440 | Effect.forkScoped, |
| ... | ... | @@ -8700,7 +8703,7 @@ describe("AcpAdapterV2", () => { |
| 8700 | 8703 | continuationRequests: { offer: () => Effect.void }, |
| 8701 | 8704 | }); |
| 8702 | 8705 | const threadId = ThreadId.make("thread-acp-direct-stop-deferred-terminal"); |
| 8703 | const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({ | |
| 8706 | const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({ | |
| 8704 | 8707 | runtimeMode: "full-access", |
| 8705 | 8708 | interactionMode: "default", |
| 8706 | 8709 | cwd: process.cwd(), |
| ... | ... | @@ -8720,7 +8723,7 @@ describe("AcpAdapterV2", () => { |
| 8720 | 8723 | ); |
| 8721 | 8724 | } |
| 8722 | 8725 | const hasPendingBackgroundWork = runtime.hasPendingBackgroundWork; |
| 8723 | const events = yield* Queue.unbounded<ProviderAdapterV2Event>(); | |
| 8726 | const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>(); | |
| 8724 | 8727 | yield* runtime.events.pipe( |
| 8725 | 8728 | Stream.runForEach((event) => Queue.offer(events, event)), |
| 8726 | 8729 | Effect.forkScoped, |
| ... | ... | @@ -8904,7 +8907,7 @@ describe("AcpAdapterV2", () => { |
| 8904 | 8907 | selfInvocation, |
| 8905 | 8908 | }); |
| 8906 | 8909 | const threadId = ThreadId.make("thread-acp-settled-soft-admission-race"); |
| 8907 | const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({ | |
| 8910 | const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({ | |
| 8908 | 8911 | runtimeMode: "full-access", |
| 8909 | 8912 | interactionMode: "default", |
| 8910 | 8913 | cwd: process.cwd(), |
| ... | ... | @@ -8918,7 +8921,7 @@ describe("AcpAdapterV2", () => { |
| 8918 | 8921 | modelSelection, |
| 8919 | 8922 | runtimePolicy, |
| 8920 | 8923 | }); |
| 8921 | const events = yield* Queue.unbounded<ProviderAdapterV2Event>(); | |
| 8924 | const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>(); | |
| 8922 | 8925 | yield* runtime.events.pipe( |
| 8923 | 8926 | Stream.runForEach((event) => Queue.offer(events, event)), |
| 8924 | 8927 | Effect.forkScoped, |
| ... | ... | @@ -8999,7 +9002,8 @@ describe("AcpAdapterV2", () => { |
| 8999 | 9002 | new URL("../../../scripts/acp-mock-agent.ts", import.meta.url), |
| 9000 | 9003 | ); |
| 9001 | 9004 | const protocolEvents = yield* Queue.bounded<EffectAcpProtocol.AcpProtocolLogEvent>(256); |
| 9002 | const continuationRequests: Array<ProviderContinuationRequest> = []; | |
| 9005 | const continuationRequests: Array<ProviderContinuationRequests.ProviderContinuationRequest> = | |
| 9006 | []; | |
| 9003 | 9007 | const instanceId = ProviderInstanceId.make("acp-test"); |
| 9004 | 9008 | const adapter = yield* makeAcpAdapterV2({ |
| 9005 | 9009 | instanceId, |
| ... | ... | @@ -9037,7 +9041,7 @@ describe("AcpAdapterV2", () => { |
| 9037 | 9041 | }, |
| 9038 | 9042 | }); |
| 9039 | 9043 | const threadId = ThreadId.make("thread-acp-already-handled-wake-pin"); |
| 9040 | const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({ | |
| 9044 | const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({ | |
| 9041 | 9045 | runtimeMode: "full-access", |
| 9042 | 9046 | interactionMode: "default", |
| 9043 | 9047 | cwd: process.cwd(), |
| ... | ... | @@ -9057,7 +9061,7 @@ describe("AcpAdapterV2", () => { |
| 9057 | 9061 | ); |
| 9058 | 9062 | } |
| 9059 | 9063 | const hasPendingBackgroundWork = runtime.hasPendingBackgroundWork; |
| 9060 | const events = yield* Queue.unbounded<ProviderAdapterV2Event>(); | |
| 9064 | const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>(); | |
| 9061 | 9065 | yield* runtime.events.pipe( |
| 9062 | 9066 | Stream.runForEach((event) => Queue.offer(events, event)), |
| 9063 | 9067 | Effect.forkScoped, |
| ... | ... | @@ -9123,7 +9127,8 @@ describe("AcpAdapterV2", () => { |
| 9123 | 9127 | const mockAgentPath = yield* path.fromFileUrl( |
| 9124 | 9128 | new URL("../../../scripts/acp-mock-agent.ts", import.meta.url), |
| 9125 | 9129 | ); |
| 9126 | const continuationRequests: Array<ProviderContinuationRequest> = []; | |
| 9130 | const continuationRequests: Array<ProviderContinuationRequests.ProviderContinuationRequest> = | |
| 9131 | []; | |
| 9127 | 9132 | type RuntimeService = AcpSessionRuntime.AcpSessionRuntime["Service"]; |
| 9128 | 9133 | let sessionUpdateHandler: Parameters<RuntimeService["handleSessionUpdate"]>[0] | undefined; |
| 9129 | 9134 | const instanceId = ProviderInstanceId.make("acp-test"); |
| ... | ... | @@ -9154,7 +9159,7 @@ describe("AcpAdapterV2", () => { |
| 9154 | 9159 | }, |
| 9155 | 9160 | }); |
| 9156 | 9161 | const threadId = ThreadId.make("thread-acp-sticky-continuation-dispatch"); |
| 9157 | const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({ | |
| 9162 | const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({ | |
| 9158 | 9163 | runtimeMode: "full-access", |
| 9159 | 9164 | interactionMode: "default", |
| 9160 | 9165 | cwd: process.cwd(), |
| ... | ... | @@ -9168,7 +9173,7 @@ describe("AcpAdapterV2", () => { |
| 9168 | 9173 | modelSelection, |
| 9169 | 9174 | runtimePolicy, |
| 9170 | 9175 | }); |
| 9171 | const events = yield* Queue.unbounded<ProviderAdapterV2Event>(); | |
| 9176 | const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>(); | |
| 9172 | 9177 | yield* runtime.events.pipe( |
| 9173 | 9178 | Stream.runForEach((event) => Queue.offer(events, event)), |
| 9174 | 9179 | Effect.forkScoped, |
| ... | ... | @@ -9237,7 +9242,8 @@ describe("AcpAdapterV2", () => { |
| 9237 | 9242 | const mockAgentPath = yield* path.fromFileUrl( |
| 9238 | 9243 | new URL("../../../scripts/acp-mock-agent.ts", import.meta.url), |
| 9239 | 9244 | ); |
| 9240 | const continuationRequests: Array<ProviderContinuationRequest> = []; | |
| 9245 | const continuationRequests: Array<ProviderContinuationRequests.ProviderContinuationRequest> = | |
| 9246 | []; | |
| 9241 | 9247 | const userPromptStarted = yield* Deferred.make<void>(); |
| 9242 | 9248 | const releaseUserPrompt = yield* Deferred.make<void>(); |
| 9243 | 9249 | const bufferedAssistantText = "BUFFERED_WAKE_QUEUED_AFTER_USER"; |
| ... | ... | @@ -9284,7 +9290,7 @@ describe("AcpAdapterV2", () => { |
| 9284 | 9290 | }, |
| 9285 | 9291 | }); |
| 9286 | 9292 | const threadId = ThreadId.make("thread-acp-buffered-continuation-user-race"); |
| 9287 | const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({ | |
| 9293 | const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({ | |
| 9288 | 9294 | runtimeMode: "full-access", |
| 9289 | 9295 | interactionMode: "default", |
| 9290 | 9296 | cwd: process.cwd(), |
| ... | ... | @@ -9304,7 +9310,7 @@ describe("AcpAdapterV2", () => { |
| 9304 | 9310 | ); |
| 9305 | 9311 | } |
| 9306 | 9312 | const hasPendingBackgroundWork = runtime.hasPendingBackgroundWork; |
| 9307 | const events = yield* Queue.unbounded<ProviderAdapterV2Event>(); | |
| 9313 | const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>(); | |
| 9308 | 9314 | yield* runtime.events.pipe( |
| 9309 | 9315 | Stream.runForEach((event) => Queue.offer(events, event)), |
| 9310 | 9316 | Effect.forkScoped, |
| ... | ... | @@ -9480,7 +9486,7 @@ describe("AcpAdapterV2", () => { |
| 9480 | 9486 | selfInvocation, |
| 9481 | 9487 | }); |
| 9482 | 9488 | const threadId = ThreadId.make("thread-acp-injected-report-hold"); |
| 9483 | const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({ | |
| 9489 | const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({ | |
| 9484 | 9490 | runtimeMode: "full-access", |
| 9485 | 9491 | interactionMode: "default", |
| 9486 | 9492 | cwd: process.cwd(), |
| ... | ... | @@ -9492,7 +9498,7 @@ describe("AcpAdapterV2", () => { |
| 9492 | 9498 | modelSelection, |
| 9493 | 9499 | runtimePolicy, |
| 9494 | 9500 | }); |
| 9495 | const events = yield* Queue.unbounded<ProviderAdapterV2Event>(); | |
| 9501 | const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>(); | |
| 9496 | 9502 | yield* runtime.events.pipe( |
| 9497 | 9503 | Stream.runForEach((event) => Queue.offer(events, event)), |
| 9498 | 9504 | Effect.forkScoped, |
| ... | ... | @@ -9515,7 +9521,7 @@ describe("AcpAdapterV2", () => { |
| 9515 | 9521 | // hydration are ingested: the hydrated monitor card carries the |
| 9516 | 9522 | // fetched listing. |
| 9517 | 9523 | let reportSeen = false; |
| 9518 | const trackReport = (event: ProviderAdapterV2Event): void => { | |
| 9524 | const trackReport = (event: ProviderAdapter.ProviderAdapterV2Event): void => { | |
| 9519 | 9525 | if ( |
| 9520 | 9526 | event.type === "turn_item.updated" && |
| 9521 | 9527 | event.turnItem.type === "assistant_message" && |
| ... | ... | @@ -9605,7 +9611,8 @@ describe("AcpAdapterV2", () => { |
| 9605 | 9611 | const triggerDir = yield* fileSystem.makeTempDirectoryScoped(); |
| 9606 | 9612 | const triggerPath = path.join(triggerDir, "report-trigger"); |
| 9607 | 9613 | const protocolEvents = yield* Queue.bounded<EffectAcpProtocol.AcpProtocolLogEvent>(256); |
| 9608 | const continuationRequests: Array<ProviderContinuationRequest> = []; | |
| 9614 | const continuationRequests: Array<ProviderContinuationRequests.ProviderContinuationRequest> = | |
| 9615 | []; | |
| 9609 | 9616 | const capturedMutation: { |
| 9610 | 9617 | current: |
| 9611 | 9618 | | ((mutation: { |
| ... | ... | @@ -9662,7 +9669,7 @@ describe("AcpAdapterV2", () => { |
| 9662 | 9669 | }, |
| 9663 | 9670 | }); |
| 9664 | 9671 | const threadId = ThreadId.make("thread-acp-settle-hold-ext-complete-no-wake"); |
| 9665 | const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({ | |
| 9672 | const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({ | |
| 9666 | 9673 | runtimeMode: "full-access", |
| 9667 | 9674 | interactionMode: "default", |
| 9668 | 9675 | cwd: process.cwd(), |
| ... | ... | @@ -9676,7 +9683,7 @@ describe("AcpAdapterV2", () => { |
| 9676 | 9683 | modelSelection, |
| 9677 | 9684 | runtimePolicy, |
| 9678 | 9685 | }); |
| 9679 | const events = yield* Queue.unbounded<ProviderAdapterV2Event>(); | |
| 9686 | const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>(); | |
| 9680 | 9687 | yield* runtime.events.pipe( |
| 9681 | 9688 | Stream.runForEach((event) => Queue.offer(events, event)), |
| 9682 | 9689 | Effect.forkScoped, |
| ... | ... | @@ -9696,7 +9703,7 @@ describe("AcpAdapterV2", () => { |
| 9696 | 9703 | }); |
| 9697 | 9704 | |
| 9698 | 9705 | let reportSeen = false; |
| 9699 | const trackReport = (event: ProviderAdapterV2Event): void => { | |
| 9706 | const trackReport = (event: ProviderAdapter.ProviderAdapterV2Event): void => { | |
| 9700 | 9707 | if ( |
| 9701 | 9708 | event.type === "turn_item.updated" && |
| 9702 | 9709 | event.turnItem.type === "assistant_message" && |
| ... | ... | @@ -9799,7 +9806,8 @@ describe("AcpAdapterV2", () => { |
| 9799 | 9806 | new URL("../../../scripts/acp-mock-agent.ts", import.meta.url), |
| 9800 | 9807 | ); |
| 9801 | 9808 | const protocolEvents = yield* Queue.bounded<EffectAcpProtocol.AcpProtocolLogEvent>(256); |
| 9802 | const continuationRequests: Array<ProviderContinuationRequest> = []; | |
| 9809 | const continuationRequests: Array<ProviderContinuationRequests.ProviderContinuationRequest> = | |
| 9810 | []; | |
| 9803 | 9811 | type RuntimeService = AcpSessionRuntime.AcpSessionRuntime["Service"]; |
| 9804 | 9812 | let sessionUpdateHandler: Parameters<RuntimeService["handleSessionUpdate"]>[0] | undefined; |
| 9805 | 9813 | const promptGate = yield* Deferred.make<EffectAcpSchema.PromptResponse>(); |
| ... | ... | @@ -9854,7 +9862,7 @@ describe("AcpAdapterV2", () => { |
| 9854 | 9862 | }, |
| 9855 | 9863 | }); |
| 9856 | 9864 | const threadId = ThreadId.make("thread-acp-pre-settle-arm-cleared-by-report"); |
| 9857 | const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({ | |
| 9865 | const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({ | |
| 9858 | 9866 | runtimeMode: "full-access", |
| 9859 | 9867 | interactionMode: "default", |
| 9860 | 9868 | cwd: process.cwd(), |
| ... | ... | @@ -9868,7 +9876,7 @@ describe("AcpAdapterV2", () => { |
| 9868 | 9876 | modelSelection, |
| 9869 | 9877 | runtimePolicy, |
| 9870 | 9878 | }); |
| 9871 | const events = yield* Queue.unbounded<ProviderAdapterV2Event>(); | |
| 9879 | const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>(); | |
| 9872 | 9880 | yield* runtime.events.pipe( |
| 9873 | 9881 | Stream.runForEach((event) => Queue.offer(events, event)), |
| 9874 | 9882 | Effect.forkScoped, |
| ... | ... | @@ -9965,7 +9973,7 @@ describe("AcpAdapterV2", () => { |
| 9965 | 9973 | yield* Effect.yieldNow; |
| 9966 | 9974 | |
| 9967 | 9975 | let reportSeen = false; |
| 9968 | const trackReport = (event: ProviderAdapterV2Event): void => { | |
| 9976 | const trackReport = (event: ProviderAdapter.ProviderAdapterV2Event): void => { | |
| 9969 | 9977 | if ( |
| 9970 | 9978 | event.type === "turn_item.updated" && |
| 9971 | 9979 | event.turnItem.type === "assistant_message" && |
| ... | ... | @@ -10033,7 +10041,8 @@ describe("AcpAdapterV2", () => { |
| 10033 | 10041 | new URL("../../../scripts/acp-mock-agent.ts", import.meta.url), |
| 10034 | 10042 | ); |
| 10035 | 10043 | const protocolEvents = yield* Queue.bounded<EffectAcpProtocol.AcpProtocolLogEvent>(256); |
| 10036 | const continuationRequests: Array<ProviderContinuationRequest> = []; | |
| 10044 | const continuationRequests: Array<ProviderContinuationRequests.ProviderContinuationRequest> = | |
| 10045 | []; | |
| 10037 | 10046 | type RuntimeService = AcpSessionRuntime.AcpSessionRuntime["Service"]; |
| 10038 | 10047 | let sessionUpdateHandler: Parameters<RuntimeService["handleSessionUpdate"]>[0] | undefined; |
| 10039 | 10048 | const promptGate = yield* Deferred.make<EffectAcpSchema.PromptResponse>(); |
| ... | ... | @@ -10095,7 +10104,7 @@ describe("AcpAdapterV2", () => { |
| 10095 | 10104 | }, |
| 10096 | 10105 | }); |
| 10097 | 10106 | const threadId = ThreadId.make("thread-acp-staggered-midturn-keep-until-last"); |
| 10098 | const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({ | |
| 10107 | const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({ | |
| 10099 | 10108 | runtimeMode: "full-access", |
| 10100 | 10109 | interactionMode: "default", |
| 10101 | 10110 | cwd: process.cwd(), |
| ... | ... | @@ -10109,7 +10118,7 @@ describe("AcpAdapterV2", () => { |
| 10109 | 10118 | modelSelection, |
| 10110 | 10119 | runtimePolicy, |
| 10111 | 10120 | }); |
| 10112 | const events = yield* Queue.unbounded<ProviderAdapterV2Event>(); | |
| 10121 | const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>(); | |
| 10113 | 10122 | yield* runtime.events.pipe( |
| 10114 | 10123 | Stream.runForEach((event) => Queue.offer(events, event)), |
| 10115 | 10124 | Effect.forkScoped, |
| ... | ... | @@ -10246,7 +10255,8 @@ describe("AcpAdapterV2", () => { |
| 10246 | 10255 | new URL("../../../scripts/acp-mock-agent.ts", import.meta.url), |
| 10247 | 10256 | ); |
| 10248 | 10257 | const protocolEvents = yield* Queue.bounded<EffectAcpProtocol.AcpProtocolLogEvent>(256); |
| 10249 | const continuationRequests: Array<ProviderContinuationRequest> = []; | |
| 10258 | const continuationRequests: Array<ProviderContinuationRequests.ProviderContinuationRequest> = | |
| 10259 | []; | |
| 10250 | 10260 | type RuntimeService = AcpSessionRuntime.AcpSessionRuntime["Service"]; |
| 10251 | 10261 | let sessionUpdateHandler: Parameters<RuntimeService["handleSessionUpdate"]>[0] | undefined; |
| 10252 | 10262 | const promptGate = yield* Deferred.make<EffectAcpSchema.PromptResponse>(); |
| ... | ... | @@ -10312,7 +10322,7 @@ describe("AcpAdapterV2", () => { |
| 10312 | 10322 | }, |
| 10313 | 10323 | }); |
| 10314 | 10324 | const threadId = ThreadId.make("thread-acp-staggered-midturn-clear-on-interrupt"); |
| 10315 | const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({ | |
| 10325 | const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({ | |
| 10316 | 10326 | runtimeMode: "full-access", |
| 10317 | 10327 | interactionMode: "default", |
| 10318 | 10328 | cwd: process.cwd(), |
| ... | ... | @@ -10326,7 +10336,7 @@ describe("AcpAdapterV2", () => { |
| 10326 | 10336 | modelSelection, |
| 10327 | 10337 | runtimePolicy, |
| 10328 | 10338 | }); |
| 10329 | const events = yield* Queue.unbounded<ProviderAdapterV2Event>(); | |
| 10339 | const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>(); | |
| 10330 | 10340 | yield* runtime.events.pipe( |
| 10331 | 10341 | Stream.runForEach((event) => Queue.offer(events, event)), |
| 10332 | 10342 | Effect.forkScoped, |
| ... | ... | @@ -10455,7 +10465,8 @@ describe("AcpAdapterV2", () => { |
| 10455 | 10465 | new URL("../../../scripts/acp-mock-agent.ts", import.meta.url), |
| 10456 | 10466 | ); |
| 10457 | 10467 | const protocolEvents = yield* Queue.bounded<EffectAcpProtocol.AcpProtocolLogEvent>(256); |
| 10458 | const continuationRequests: Array<ProviderContinuationRequest> = []; | |
| 10468 | const continuationRequests: Array<ProviderContinuationRequests.ProviderContinuationRequest> = | |
| 10469 | []; | |
| 10459 | 10470 | type RuntimeService = AcpSessionRuntime.AcpSessionRuntime["Service"]; |
| 10460 | 10471 | let sessionUpdateHandler: Parameters<RuntimeService["handleSessionUpdate"]>[0] | undefined; |
| 10461 | 10472 | const promptGates: Array<Deferred.Deferred<EffectAcpSchema.PromptResponse, never>> = []; |
| ... | ... | @@ -10543,7 +10554,7 @@ describe("AcpAdapterV2", () => { |
| 10543 | 10554 | }, |
| 10544 | 10555 | }); |
| 10545 | 10556 | const threadId = ThreadId.make("thread-acp-multiturn-in-turn-monitor-no-wake"); |
| 10546 | const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({ | |
| 10557 | const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({ | |
| 10547 | 10558 | runtimeMode: "full-access", |
| 10548 | 10559 | interactionMode: "default", |
| 10549 | 10560 | cwd: process.cwd(), |
| ... | ... | @@ -10563,7 +10574,7 @@ describe("AcpAdapterV2", () => { |
| 10563 | 10574 | ); |
| 10564 | 10575 | } |
| 10565 | 10576 | const hasPendingBackgroundWork = runtime.hasPendingBackgroundWork; |
| 10566 | const events = yield* Queue.unbounded<ProviderAdapterV2Event>(); | |
| 10577 | const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>(); | |
| 10567 | 10578 | yield* runtime.events.pipe( |
| 10568 | 10579 | Stream.runForEach((event) => Queue.offer(events, event)), |
| 10569 | 10580 | Effect.forkScoped, |
| ... | ... | @@ -10820,7 +10831,8 @@ describe("AcpAdapterV2", () => { |
| 10820 | 10831 | const mockAgentPath = yield* path.fromFileUrl( |
| 10821 | 10832 | new URL("../../../scripts/acp-mock-agent.ts", import.meta.url), |
| 10822 | 10833 | ); |
| 10823 | const continuationRequests: Array<ProviderContinuationRequest> = []; | |
| 10834 | const continuationRequests: Array<ProviderContinuationRequests.ProviderContinuationRequest> = | |
| 10835 | []; | |
| 10824 | 10836 | type RuntimeService = AcpSessionRuntime.AcpSessionRuntime["Service"]; |
| 10825 | 10837 | let sessionUpdateHandler: Parameters<RuntimeService["handleSessionUpdate"]>[0] | undefined; |
| 10826 | 10838 | let applyMutation: AcpAdapterV2ExtensionContext["applyBackgroundTaskMutation"] | undefined; |
| ... | ... | @@ -10860,7 +10872,7 @@ describe("AcpAdapterV2", () => { |
| 10860 | 10872 | }, |
| 10861 | 10873 | }); |
| 10862 | 10874 | const threadId = ThreadId.make("thread-acp-wake-report-while-queued"); |
| 10863 | const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({ | |
| 10875 | const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({ | |
| 10864 | 10876 | runtimeMode: "full-access", |
| 10865 | 10877 | interactionMode: "default", |
| 10866 | 10878 | cwd: process.cwd(), |
| ... | ... | @@ -10872,7 +10884,7 @@ describe("AcpAdapterV2", () => { |
| 10872 | 10884 | modelSelection, |
| 10873 | 10885 | runtimePolicy, |
| 10874 | 10886 | }); |
| 10875 | const events = yield* Queue.unbounded<ProviderAdapterV2Event>(); | |
| 10887 | const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>(); | |
| 10876 | 10888 | yield* runtime.events.pipe( |
| 10877 | 10889 | Stream.runForEach((event) => Queue.offer(events, event)), |
| 10878 | 10890 | Effect.forkScoped, |
| ... | ... | @@ -10969,7 +10981,8 @@ describe("AcpAdapterV2", () => { |
| 10969 | 10981 | new URL("../../../scripts/acp-mock-agent.ts", import.meta.url), |
| 10970 | 10982 | ); |
| 10971 | 10983 | const protocolEvents = yield* Queue.bounded<EffectAcpProtocol.AcpProtocolLogEvent>(256); |
| 10972 | const continuationRequests: Array<ProviderContinuationRequest> = []; | |
| 10984 | const continuationRequests: Array<ProviderContinuationRequests.ProviderContinuationRequest> = | |
| 10985 | []; | |
| 10973 | 10986 | type RuntimeService = AcpSessionRuntime.AcpSessionRuntime["Service"]; |
| 10974 | 10987 | let sessionUpdateHandler: Parameters<RuntimeService["handleSessionUpdate"]>[0] | undefined; |
| 10975 | 10988 | const promptGates: Array<Deferred.Deferred<EffectAcpSchema.PromptResponse, never>> = []; |
| ... | ... | @@ -11038,7 +11051,7 @@ describe("AcpAdapterV2", () => { |
| 11038 | 11051 | }, |
| 11039 | 11052 | }); |
| 11040 | 11053 | const threadId = ThreadId.make("thread-acp-mid-turn-dirty-wake-buffer"); |
| 11041 | const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({ | |
| 11054 | const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({ | |
| 11042 | 11055 | runtimeMode: "full-access", |
| 11043 | 11056 | interactionMode: "default", |
| 11044 | 11057 | cwd: process.cwd(), |
| ... | ... | @@ -11058,7 +11071,7 @@ describe("AcpAdapterV2", () => { |
| 11058 | 11071 | ); |
| 11059 | 11072 | } |
| 11060 | 11073 | const hasPendingBackgroundWork = runtime.hasPendingBackgroundWork; |
| 11061 | const events = yield* Queue.unbounded<ProviderAdapterV2Event>(); | |
| 11074 | const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>(); | |
| 11062 | 11075 | yield* runtime.events.pipe( |
| 11063 | 11076 | Stream.runForEach((event) => Queue.offer(events, event)), |
| 11064 | 11077 | Effect.forkScoped, |
| ... | ... | @@ -11294,7 +11307,8 @@ describe("AcpAdapterV2", () => { |
| 11294 | 11307 | new URL("../../../scripts/acp-mock-agent.ts", import.meta.url), |
| 11295 | 11308 | ); |
| 11296 | 11309 | const protocolEvents = yield* Queue.bounded<EffectAcpProtocol.AcpProtocolLogEvent>(256); |
| 11297 | const continuationRequests: Array<ProviderContinuationRequest> = []; | |
| 11310 | const continuationRequests: Array<ProviderContinuationRequests.ProviderContinuationRequest> = | |
| 11311 | []; | |
| 11298 | 11312 | type RuntimeService = AcpSessionRuntime.AcpSessionRuntime["Service"]; |
| 11299 | 11313 | let sessionUpdateHandler: Parameters<RuntimeService["handleSessionUpdate"]>[0] | undefined; |
| 11300 | 11314 | const promptGate = yield* Deferred.make<EffectAcpSchema.PromptResponse>(); |
| ... | ... | @@ -11344,7 +11358,7 @@ describe("AcpAdapterV2", () => { |
| 11344 | 11358 | }, |
| 11345 | 11359 | }); |
| 11346 | 11360 | const threadId = ThreadId.make("thread-acp-mid-turn-unhandled-offers-after-finalize"); |
| 11347 | const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({ | |
| 11361 | const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({ | |
| 11348 | 11362 | runtimeMode: "full-access", |
| 11349 | 11363 | interactionMode: "default", |
| 11350 | 11364 | cwd: process.cwd(), |
| ... | ... | @@ -11358,7 +11372,7 @@ describe("AcpAdapterV2", () => { |
| 11358 | 11372 | modelSelection, |
| 11359 | 11373 | runtimePolicy, |
| 11360 | 11374 | }); |
| 11361 | const events = yield* Queue.unbounded<ProviderAdapterV2Event>(); | |
| 11375 | const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>(); | |
| 11362 | 11376 | yield* runtime.events.pipe( |
| 11363 | 11377 | Stream.runForEach((event) => Queue.offer(events, event)), |
| 11364 | 11378 | Effect.forkScoped, |
| ... | ... | @@ -11446,7 +11460,7 @@ describe("AcpAdapterV2", () => { |
| 11446 | 11460 | content: { type: "text", text: " \n\t " }, |
| 11447 | 11461 | }, |
| 11448 | 11462 | }); |
| 11449 | const lateEvents: Array<ProviderAdapterV2Event> = []; | |
| 11463 | const lateEvents: Array<ProviderAdapter.ProviderAdapterV2Event> = []; | |
| 11450 | 11464 | let lateEvent = yield* Queue.poll(events); |
| 11451 | 11465 | while (Option.isSome(lateEvent)) { |
| 11452 | 11466 | lateEvents.push(lateEvent.value); |
| ... | ... | @@ -11475,7 +11489,8 @@ describe("AcpAdapterV2", () => { |
| 11475 | 11489 | new URL("../../../scripts/acp-mock-agent.ts", import.meta.url), |
| 11476 | 11490 | ); |
| 11477 | 11491 | const protocolEvents = yield* Queue.bounded<EffectAcpProtocol.AcpProtocolLogEvent>(256); |
| 11478 | const continuationRequests: Array<ProviderContinuationRequest> = []; | |
| 11492 | const continuationRequests: Array<ProviderContinuationRequests.ProviderContinuationRequest> = | |
| 11493 | []; | |
| 11479 | 11494 | type RuntimeService = AcpSessionRuntime.AcpSessionRuntime["Service"]; |
| 11480 | 11495 | let sessionUpdateHandler: Parameters<RuntimeService["handleSessionUpdate"]>[0] | undefined; |
| 11481 | 11496 | const promptGate = yield* Deferred.make<EffectAcpSchema.PromptResponse>(); |
| ... | ... | @@ -11529,7 +11544,7 @@ describe("AcpAdapterV2", () => { |
| 11529 | 11544 | }, |
| 11530 | 11545 | }); |
| 11531 | 11546 | const threadId = ThreadId.make("thread-acp-empty-drain-continuation-quiet"); |
| 11532 | const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({ | |
| 11547 | const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({ | |
| 11533 | 11548 | runtimeMode: "full-access", |
| 11534 | 11549 | interactionMode: "default", |
| 11535 | 11550 | cwd: process.cwd(), |
| ... | ... | @@ -11543,7 +11558,7 @@ describe("AcpAdapterV2", () => { |
| 11543 | 11558 | modelSelection, |
| 11544 | 11559 | runtimePolicy, |
| 11545 | 11560 | }); |
| 11546 | const events = yield* Queue.unbounded<ProviderAdapterV2Event>(); | |
| 11561 | const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>(); | |
| 11547 | 11562 | yield* runtime.events.pipe( |
| 11548 | 11563 | Stream.runForEach((event) => Queue.offer(events, event)), |
| 11549 | 11564 | Effect.forkScoped, |
| ... | ... | @@ -11713,7 +11728,8 @@ describe("AcpAdapterV2", () => { |
| 11713 | 11728 | new URL("../../../scripts/acp-mock-agent.ts", import.meta.url), |
| 11714 | 11729 | ); |
| 11715 | 11730 | const protocolEvents = yield* Queue.bounded<EffectAcpProtocol.AcpProtocolLogEvent>(256); |
| 11716 | const continuationRequests: Array<ProviderContinuationRequest> = []; | |
| 11731 | const continuationRequests: Array<ProviderContinuationRequests.ProviderContinuationRequest> = | |
| 11732 | []; | |
| 11717 | 11733 | type RuntimeService = AcpSessionRuntime.AcpSessionRuntime["Service"]; |
| 11718 | 11734 | let sessionUpdateHandler: Parameters<RuntimeService["handleSessionUpdate"]>[0] | undefined; |
| 11719 | 11735 | const promptGate = yield* Deferred.make<EffectAcpSchema.PromptResponse>(); |
| ... | ... | @@ -11765,7 +11781,7 @@ describe("AcpAdapterV2", () => { |
| 11765 | 11781 | }, |
| 11766 | 11782 | }); |
| 11767 | 11783 | const threadId = ThreadId.make("thread-acp-empty-drain-continuation-no-wedge"); |
| 11768 | const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({ | |
| 11784 | const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({ | |
| 11769 | 11785 | runtimeMode: "full-access", |
| 11770 | 11786 | interactionMode: "default", |
| 11771 | 11787 | cwd: process.cwd(), |
| ... | ... | @@ -11779,7 +11795,7 @@ describe("AcpAdapterV2", () => { |
| 11779 | 11795 | modelSelection, |
| 11780 | 11796 | runtimePolicy, |
| 11781 | 11797 | }); |
| 11782 | const events = yield* Queue.unbounded<ProviderAdapterV2Event>(); | |
| 11798 | const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>(); | |
| 11783 | 11799 | yield* runtime.events.pipe( |
| 11784 | 11800 | Stream.runForEach((event) => Queue.offer(events, event)), |
| 11785 | 11801 | Effect.forkScoped, |
| ... | ... | @@ -11913,7 +11929,7 @@ describe("AcpAdapterV2", () => { |
| 11913 | 11929 | selfInvocation, |
| 11914 | 11930 | }); |
| 11915 | 11931 | const threadId = ThreadId.make("thread-acp-restart-after-interrupt"); |
| 11916 | const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({ | |
| 11932 | const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({ | |
| 11917 | 11933 | runtimeMode: "full-access", |
| 11918 | 11934 | interactionMode: "default", |
| 11919 | 11935 | cwd: process.cwd(), |
| ... | ... | @@ -12144,7 +12160,8 @@ describe("AcpAdapterV2", () => { |
| 12144 | 12160 | const residualCallbackResponseLogPath = path.join(residualCallbackDir, "responses.log"); |
| 12145 | 12161 | const residualCallbackTriggerPath = path.join(residualCallbackDir, "trigger"); |
| 12146 | 12162 | const protocolEvents = yield* Queue.bounded<EffectAcpProtocol.AcpProtocolLogEvent>(256); |
| 12147 | const continuationRequests: Array<ProviderContinuationRequest> = []; | |
| 12163 | const continuationRequests: Array<ProviderContinuationRequests.ProviderContinuationRequest> = | |
| 12164 | []; | |
| 12148 | 12165 | const teardownStarted = yield* Deferred.make<void>(); |
| 12149 | 12166 | const releaseTeardown = yield* Deferred.make<void>(); |
| 12150 | 12167 | let terminatorCallCount = 0; |
| ... | ... | @@ -12187,7 +12204,7 @@ describe("AcpAdapterV2", () => { |
| 12187 | 12204 | }, |
| 12188 | 12205 | }); |
| 12189 | 12206 | const threadId = ThreadId.make("thread-acp-failed-hard-teardown"); |
| 12190 | const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({ | |
| 12207 | const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({ | |
| 12191 | 12208 | runtimeMode: "full-access", |
| 12192 | 12209 | interactionMode: "default", |
| 12193 | 12210 | cwd: process.cwd(), |
| ... | ... | @@ -12202,7 +12219,7 @@ describe("AcpAdapterV2", () => { |
| 12202 | 12219 | runtimePolicy, |
| 12203 | 12220 | }) |
| 12204 | 12221 | .pipe(Effect.provideService(Scope.Scope, sessionScope)); |
| 12205 | const adapterEvents = yield* Queue.unbounded<ProviderAdapterV2Event>(); | |
| 12222 | const adapterEvents = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>(); | |
| 12206 | 12223 | yield* runtime.events.pipe( |
| 12207 | 12224 | Stream.runForEach((event) => Queue.offer(adapterEvents, event)), |
| 12208 | 12225 | Effect.forkIn(sessionScope), |
| ... | ... | @@ -12368,7 +12385,7 @@ describe("AcpAdapterV2", () => { |
| 12368 | 12385 | selfInvocation, |
| 12369 | 12386 | }); |
| 12370 | 12387 | const threadId = ThreadId.make("thread-acp-missing-hard-teardown"); |
| 12371 | const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({ | |
| 12388 | const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({ | |
| 12372 | 12389 | runtimeMode: "full-access", |
| 12373 | 12390 | interactionMode: "default", |
| 12374 | 12391 | cwd: process.cwd(), |
| ... | ... | @@ -12464,7 +12481,7 @@ describe("AcpAdapterV2", () => { |
| 12464 | 12481 | selfInvocation, |
| 12465 | 12482 | }); |
| 12466 | 12483 | const threadId = ThreadId.make("thread-acp-concurrent-hard-teardown"); |
| 12467 | const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({ | |
| 12484 | const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({ | |
| 12468 | 12485 | runtimeMode: "full-access", |
| 12469 | 12486 | interactionMode: "default", |
| 12470 | 12487 | cwd: process.cwd(), |
| ... | ... | @@ -12540,7 +12557,8 @@ describe("AcpAdapterV2", () => { |
| 12540 | 12557 | new URL("../../../scripts/acp-mock-agent.ts", import.meta.url), |
| 12541 | 12558 | ); |
| 12542 | 12559 | const protocolEvents = yield* Queue.bounded<EffectAcpProtocol.AcpProtocolLogEvent>(256); |
| 12543 | const continuationRequests: Array<ProviderContinuationRequest> = []; | |
| 12560 | const continuationRequests: Array<ProviderContinuationRequests.ProviderContinuationRequest> = | |
| 12561 | []; | |
| 12544 | 12562 | const responseLifecycle: Array<string> = []; |
| 12545 | 12563 | type RuntimeService = AcpSessionRuntime.AcpSessionRuntime["Service"]; |
| 12546 | 12564 | type HandlerRecord = { |
| ... | ... | @@ -12633,7 +12651,7 @@ describe("AcpAdapterV2", () => { |
| 12633 | 12651 | }, |
| 12634 | 12652 | }); |
| 12635 | 12653 | const threadId = ThreadId.make("thread-acp-successful-teardown-callback-quarantine"); |
| 12636 | const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({ | |
| 12654 | const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({ | |
| 12637 | 12655 | runtimeMode: "full-access", |
| 12638 | 12656 | interactionMode: "default", |
| 12639 | 12657 | cwd: process.cwd(), |
| ... | ... | @@ -12647,7 +12665,7 @@ describe("AcpAdapterV2", () => { |
| 12647 | 12665 | modelSelection, |
| 12648 | 12666 | runtimePolicy, |
| 12649 | 12667 | }); |
| 12650 | const adapterEvents = yield* Queue.unbounded<ProviderAdapterV2Event>(); | |
| 12668 | const adapterEvents = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>(); | |
| 12651 | 12669 | yield* runtime.events.pipe( |
| 12652 | 12670 | Stream.runForEach((event) => Queue.offer(adapterEvents, event)), |
| 12653 | 12671 | Effect.forkScoped, |
| ... | ... | @@ -12849,7 +12867,7 @@ describe("AcpAdapterV2", () => { |
| 12849 | 12867 | method: "_x.ai/ask_user_question", |
| 12850 | 12868 | }, |
| 12851 | 12869 | ).pipe(Effect.forkScoped); |
| 12852 | let replacementRequest: ProviderAdapterV2Event | undefined; | |
| 12870 | let replacementRequest: ProviderAdapter.ProviderAdapterV2Event | undefined; | |
| 12853 | 12871 | while (replacementRequest === undefined) { |
| 12854 | 12872 | const event = yield* Queue.take(adapterEvents); |
| 12855 | 12873 | if (event.type === "runtime_request.updated" && event.runtimeRequest.status === "pending") { |
| ... | ... | @@ -12916,7 +12934,7 @@ describe("AcpAdapterV2", () => { |
| 12916 | 12934 | selfInvocation, |
| 12917 | 12935 | }); |
| 12918 | 12936 | const threadId = ThreadId.make("thread-acp-stale-deferred-cleanup"); |
| 12919 | const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({ | |
| 12937 | const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({ | |
| 12920 | 12938 | runtimeMode: "approval-required", |
| 12921 | 12939 | interactionMode: "default", |
| 12922 | 12940 | cwd: process.cwd(), |
| ... | ... | @@ -12928,7 +12946,7 @@ describe("AcpAdapterV2", () => { |
| 12928 | 12946 | modelSelection, |
| 12929 | 12947 | runtimePolicy, |
| 12930 | 12948 | }); |
| 12931 | const events = yield* Queue.unbounded<ProviderAdapterV2Event>(); | |
| 12949 | const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>(); | |
| 12932 | 12950 | yield* runtime.events.pipe( |
| 12933 | 12951 | Stream.runForEach((event) => Queue.offer(events, event)), |
| 12934 | 12952 | Effect.forkScoped, |
| ... | ... | @@ -12975,7 +12993,7 @@ describe("AcpAdapterV2", () => { |
| 12975 | 12993 | yield* runtime.startTurn( |
| 12976 | 12994 | makeTurnInput({ threadId, providerThread, instanceId, runtimePolicy, now, ordinal: 2 }), |
| 12977 | 12995 | ); |
| 12978 | let replacementPending: ProviderAdapterV2Event | undefined; | |
| 12996 | let replacementPending: ProviderAdapter.ProviderAdapterV2Event | undefined; | |
| 12979 | 12997 | while (replacementPending === undefined) { |
| 12980 | 12998 | const event = yield* Queue.take(events); |
| 12981 | 12999 | if (event.type === "runtime_request.updated" && event.runtimeRequest.status === "pending") { |
| ... | ... | @@ -13045,7 +13063,7 @@ describe("AcpAdapterV2", () => { |
| 13045 | 13063 | selfInvocation, |
| 13046 | 13064 | }); |
| 13047 | 13065 | const threadId = ThreadId.make("thread-acp-concurrent-resume-teardown"); |
| 13048 | const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({ | |
| 13066 | const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({ | |
| 13049 | 13067 | runtimeMode: "full-access", |
| 13050 | 13068 | interactionMode: "default", |
| 13051 | 13069 | cwd: process.cwd(), |
| ... | ... | @@ -13192,7 +13210,7 @@ describe("AcpAdapterV2", () => { |
| 13192 | 13210 | continuationRequests: { offer: () => Effect.void }, |
| 13193 | 13211 | }); |
| 13194 | 13212 | const threadId = ThreadId.make("thread-acp-direct-stop-running-command"); |
| 13195 | const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({ | |
| 13213 | const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({ | |
| 13196 | 13214 | runtimeMode: "full-access", |
| 13197 | 13215 | interactionMode: "default", |
| 13198 | 13216 | cwd: process.cwd(), |
| ... | ... | @@ -13206,7 +13224,7 @@ describe("AcpAdapterV2", () => { |
| 13206 | 13224 | modelSelection, |
| 13207 | 13225 | runtimePolicy, |
| 13208 | 13226 | }); |
| 13209 | const events = yield* Queue.unbounded<ProviderAdapterV2Event>(); | |
| 13227 | const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>(); | |
| 13210 | 13228 | yield* runtime.events.pipe( |
| 13211 | 13229 | Stream.runForEach((event) => Queue.offer(events, event)), |
| 13212 | 13230 | Effect.forkScoped, |
| ... | ... | @@ -13458,7 +13476,7 @@ describe("AcpAdapterV2", () => { |
| 13458 | 13476 | selfInvocation, |
| 13459 | 13477 | }); |
| 13460 | 13478 | const threadId = ThreadId.make("thread-acp-direct-stop-subagent-hold"); |
| 13461 | const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({ | |
| 13479 | const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({ | |
| 13462 | 13480 | runtimeMode: "full-access", |
| 13463 | 13481 | interactionMode: "default", |
| 13464 | 13482 | cwd: process.cwd(), |
| ... | ... | @@ -13472,7 +13490,7 @@ describe("AcpAdapterV2", () => { |
| 13472 | 13490 | modelSelection, |
| 13473 | 13491 | runtimePolicy, |
| 13474 | 13492 | }); |
| 13475 | const events = yield* Queue.unbounded<ProviderAdapterV2Event>(); | |
| 13493 | const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>(); | |
| 13476 | 13494 | yield* runtime.events.pipe( |
| 13477 | 13495 | Stream.runForEach((event) => Queue.offer(events, event)), |
| 13478 | 13496 | Effect.forkScoped, |
| ... | ... | @@ -13602,7 +13620,7 @@ describe("AcpAdapterV2", () => { |
| 13602 | 13620 | selfInvocation, |
| 13603 | 13621 | }); |
| 13604 | 13622 | const threadId = ThreadId.make("thread-acp-restart-active-in-process"); |
| 13605 | const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({ | |
| 13623 | const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({ | |
| 13606 | 13624 | runtimeMode: "full-access", |
| 13607 | 13625 | interactionMode: "default", |
| 13608 | 13626 | cwd: process.cwd(), |
| ... | ... | @@ -13614,7 +13632,7 @@ describe("AcpAdapterV2", () => { |
| 13614 | 13632 | modelSelection, |
| 13615 | 13633 | runtimePolicy, |
| 13616 | 13634 | }); |
| 13617 | const events = yield* Queue.unbounded<ProviderAdapterV2Event>(); | |
| 13635 | const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>(); | |
| 13618 | 13636 | yield* runtime.events.pipe( |
| 13619 | 13637 | Stream.runForEach((event) => Queue.offer(events, event)), |
| 13620 | 13638 | Effect.forkScoped, |
apps/server/src/orchestration-v2/Adapters/AcpRegistryAdapterV2.test.ts+4-4| ... | ... | @@ -29,7 +29,7 @@ import { |
| 29 | 29 | makeAcpReplayCompletenessAssertion, |
| 30 | 30 | makeAcpReplayRuntime, |
| 31 | 31 | } from "./AcpAdapterV2.testkit.ts"; |
| 32 | import { ProviderAdapterV2RuntimePolicy } from "@t3tools/provider-core/server/ProviderAdapter"; | |
| 32 | import * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter"; | |
| 33 | 33 | import { BUILT_IN_PROVIDER_ADAPTER_DRIVER_KINDS_V2 } from "../builtInProviderAdapterDrivers.ts"; |
| 34 | 34 | import { AcpRegistryAdapterV2Driver } from "@t3tools/provider-acp-registry/server"; |
| 35 | 35 | import { |
| ... | ... | @@ -216,7 +216,7 @@ describe("AcpRegistryAdapterV2", () => { |
| 216 | 216 | model: "default", |
| 217 | 217 | options: [{ id: ACP_SESSION_MODE_OPTION_ID, value: input.storedModePick }], |
| 218 | 218 | }, |
| 219 | runtimePolicy: ProviderAdapterV2RuntimePolicy.make({ | |
| 219 | runtimePolicy: ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({ | |
| 220 | 220 | runtimeMode: "approval-required", |
| 221 | 221 | interactionMode: "default", |
| 222 | 222 | cwd: replayDir, |
| ... | ... | @@ -304,7 +304,7 @@ describe("AcpRegistryAdapterV2", () => { |
| 304 | 304 | ); |
| 305 | 305 | }), |
| 306 | 306 | }); |
| 307 | const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({ | |
| 307 | const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({ | |
| 308 | 308 | runtimeMode: "full-access", |
| 309 | 309 | interactionMode: "default", |
| 310 | 310 | cwd: process.cwd(), |
| ... | ... | @@ -413,7 +413,7 @@ describe("AcpRegistryAdapterV2", () => { |
| 413 | 413 | }, |
| 414 | 414 | }); |
| 415 | 415 | const threadId = ThreadId.make("thread-acp-registry-fixture"); |
| 416 | const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({ | |
| 416 | const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({ | |
| 417 | 417 | runtimeMode: "full-access", |
| 418 | 418 | interactionMode: "default", |
| 419 | 419 | cwd: process.cwd(), |
apps/server/src/orchestration-v2/Adapters/AntigravityAdapterV2.test.ts+5-5| ... | ... | @@ -27,7 +27,7 @@ import { layerTestProviderHost } from "@t3tools/provider-testing/host"; |
| 27 | 27 | import type * as AcpSessionRuntime from "@t3tools/provider-acp/server/AcpSessionRuntime"; |
| 28 | 28 | import { makeAntigravityAcpRuntime } from "../../provider/acp/AntigravityAcpSupport.ts"; |
| 29 | 29 | import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator"; |
| 30 | import { ProviderAdapterV2RuntimePolicy } from "@t3tools/provider-core/server/ProviderAdapter"; | |
| 30 | import * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter"; | |
| 31 | 31 | import { |
| 32 | 32 | makeAntigravityAcpAdapterFlavor, |
| 33 | 33 | makeAntigravityAdapterV2, |
| ... | ... | @@ -74,7 +74,7 @@ describe("AntigravityAdapterV2 flavor", () => { |
| 74 | 74 | it("maps runtime modes to the agent's native permission modes", () => { |
| 75 | 75 | const mode = (runtimeMode: "approval-required" | "auto-accept-edits" | "full-access") => |
| 76 | 76 | flavor.sessionModeForPolicy?.( |
| 77 | ProviderAdapterV2RuntimePolicy.make({ | |
| 77 | ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({ | |
| 78 | 78 | runtimeMode, |
| 79 | 79 | interactionMode: "default", |
| 80 | 80 | cwd: "/workspace", |
| ... | ... | @@ -190,7 +190,7 @@ describe("AntigravityAdapterV2 client file system", () => { |
| 190 | 190 | yield* fileSystem.writeFileString(attachment, "pasted"); |
| 191 | 191 | |
| 192 | 192 | const threadId = ThreadId.make("thread-antigravity-containment"); |
| 193 | const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({ | |
| 193 | const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({ | |
| 194 | 194 | runtimeMode: "full-access", |
| 195 | 195 | interactionMode: "default", |
| 196 | 196 | cwd: workspace, |
| ... | ... | @@ -325,7 +325,7 @@ describe("AntigravityAdapterV2 workspace changes", () => { |
| 325 | 325 | yield* fileSystem.writeFileString(path.join(workspaceA, "a.txt"), "from a"); |
| 326 | 326 | yield* fileSystem.writeFileString(path.join(workspaceB, "b.txt"), "from b"); |
| 327 | 327 | const policyFor = (cwd: string) => |
| 328 | ProviderAdapterV2RuntimePolicy.make({ | |
| 328 | ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({ | |
| 329 | 329 | runtimeMode: "full-access", |
| 330 | 330 | interactionMode: "default", |
| 331 | 331 | cwd, |
| ... | ... | @@ -476,7 +476,7 @@ describe("AntigravityAdapterV2 client file system under restrictive policies", ( |
| 476 | 476 | }); |
| 477 | 477 | yield* fileSystem.writeFileString(path.join(workspace, "existing.ts"), "existing"); |
| 478 | 478 | const threadId = ThreadId.make(`thread-antigravity-restrictive-${policy.runtimeMode}`); |
| 479 | const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({ | |
| 479 | const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({ | |
| 480 | 480 | ...policy, |
| 481 | 481 | interactionMode: "default", |
| 482 | 482 | cwd: workspace, |
apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.test.ts+101-67| ... | ... | @@ -61,13 +61,9 @@ import { WorktreeToolkit } from "../../mcp/toolkits/worktree/tools.ts"; |
| 61 | 61 | import { ThreadToolkit } from "../../mcp/toolkits/thread/tools.ts"; |
| 62 | 62 | import { OrchestratorToolkit } from "../../mcp/toolkits/orchestrator/tools.ts"; |
| 63 | 63 | import { ClaudeExecutableFileCheck } from "../../provider/Drivers/ClaudeExecutable.ts"; |
| 64 | import type { EventNdjsonLogger } from "../../provider/EventNdjsonLogger.ts"; | |
| 65 | import { | |
| 66 | ProviderAdapterV2RuntimePolicy, | |
| 67 | type ProviderAdapterV2Event, | |
| 68 | type ProviderAdapterV2TurnInput, | |
| 69 | } from "@t3tools/provider-core/server/ProviderAdapter"; | |
| 70 | import type { ProviderContinuationRequest } from "@t3tools/provider-core/server/continuationRequests"; | |
| 64 | import type * as ProviderEventLoggers from "@t3tools/provider-core/server/ProviderEventLoggers"; | |
| 65 | import * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter"; | |
| 66 | import type * as ProviderContinuationRequests from "@t3tools/provider-core/server/ProviderContinuationRequests"; | |
| 71 | 67 | import { makeProviderFailure } from "@t3tools/provider-core/server/failure"; |
| 72 | 68 | import * as ClaudeAdapterV2 from "./ClaudeAdapterV2.ts"; |
| 73 | 69 | import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator"; |
| ... | ... | @@ -81,7 +77,7 @@ const CLAUDE_TEST_MODEL_SELECTION = { |
| 81 | 77 | model: "claude-sonnet-4-6", |
| 82 | 78 | options: [{ id: "effort", value: "ultrathink" }], |
| 83 | 79 | } satisfies ModelSelection; |
| 84 | const CLAUDE_TEST_RUNTIME_POLICY = ProviderAdapterV2RuntimePolicy.make({ | |
| 80 | const CLAUDE_TEST_RUNTIME_POLICY = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({ | |
| 85 | 81 | runtimeMode: "full-access", |
| 86 | 82 | interactionMode: "default", |
| 87 | 83 | cwd: "/workspace", |
| ... | ... | @@ -127,14 +123,14 @@ function makeClaudeTestTurnInput(input: { |
| 127 | 123 | readonly now: DateTime.Utc; |
| 128 | 124 | readonly attemptId: RunAttemptId; |
| 129 | 125 | readonly text: string; |
| 130 | readonly attachments: ProviderAdapterV2TurnInput["message"]["attachments"]; | |
| 126 | readonly attachments: ProviderAdapter.ProviderAdapterV2TurnInput["message"]["attachments"]; | |
| 131 | 127 | readonly providerTurnOrdinal?: number; |
| 132 | 128 | readonly nativeThreadHasTurns?: boolean; |
| 133 | readonly messageCreatedBy?: ProviderAdapterV2TurnInput["message"]["createdBy"]; | |
| 134 | readonly messageCreationSource?: ProviderAdapterV2TurnInput["message"]["creationSource"]; | |
| 129 | readonly messageCreatedBy?: ProviderAdapter.ProviderAdapterV2TurnInput["message"]["createdBy"]; | |
| 130 | readonly messageCreationSource?: ProviderAdapter.ProviderAdapterV2TurnInput["message"]["creationSource"]; | |
| 135 | 131 | readonly modelSelection?: ModelSelection; |
| 136 | readonly runtimePolicy?: ProviderAdapterV2RuntimePolicy; | |
| 137 | }): ProviderAdapterV2TurnInput { | |
| 132 | readonly runtimePolicy?: ProviderAdapter.ProviderAdapterV2RuntimePolicy; | |
| 133 | }): ProviderAdapter.ProviderAdapterV2TurnInput { | |
| 138 | 134 | return { |
| 139 | 135 | appThread: makeClaudeTestAppThread(input), |
| 140 | 136 | threadId: input.threadId, |
| ... | ... | @@ -298,7 +294,7 @@ describe("ClaudeAdapterV2 runtime query policy", () => { |
| 298 | 294 | |
| 299 | 295 | it("maps canonical read-only never policy to Claude dontAsk with read-only tools", () => { |
| 300 | 296 | const queryPolicy = ClaudeAdapterV2.claudeRuntimeQueryPolicyForRuntimePolicy( |
| 301 | ProviderAdapterV2RuntimePolicy.make({ | |
| 297 | ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({ | |
| 302 | 298 | runtimeMode: "full-access", |
| 303 | 299 | interactionMode: "default", |
| 304 | 300 | cwd: "/workspace", |
| ... | ... | @@ -321,7 +317,7 @@ describe("ClaudeAdapterV2 runtime query policy", () => { |
| 321 | 317 | |
| 322 | 318 | it("maps canonical read-only on-request policy to Claude default with callbacks", () => { |
| 323 | 319 | const queryPolicy = ClaudeAdapterV2.claudeRuntimeQueryPolicyForRuntimePolicy( |
| 324 | ProviderAdapterV2RuntimePolicy.make({ | |
| 320 | ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({ | |
| 325 | 321 | runtimeMode: "full-access", |
| 326 | 322 | interactionMode: "default", |
| 327 | 323 | cwd: "/workspace", |
| ... | ... | @@ -344,7 +340,7 @@ describe("ClaudeAdapterV2 runtime query policy", () => { |
| 344 | 340 | |
| 345 | 341 | it("does not auto-allow reads for canonical restricted read-only never policy", () => { |
| 346 | 342 | const queryPolicy = ClaudeAdapterV2.claudeRuntimeQueryPolicyForRuntimePolicy( |
| 347 | ProviderAdapterV2RuntimePolicy.make({ | |
| 343 | ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({ | |
| 348 | 344 | runtimeMode: "full-access", |
| 349 | 345 | interactionMode: "default", |
| 350 | 346 | cwd: "/workspace", |
| ... | ... | @@ -370,7 +366,7 @@ describe("ClaudeAdapterV2 runtime query policy", () => { |
| 370 | 366 | |
| 371 | 367 | it("maps default full-access policy to Claude bypass permissions", () => { |
| 372 | 368 | const queryPolicy = ClaudeAdapterV2.claudeRuntimeQueryPolicyForRuntimePolicy( |
| 373 | ProviderAdapterV2RuntimePolicy.make({ | |
| 369 | ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({ | |
| 374 | 370 | runtimeMode: "full-access", |
| 375 | 371 | interactionMode: "default", |
| 376 | 372 | cwd: "/workspace", |
| ... | ... | @@ -386,7 +382,7 @@ describe("ClaudeAdapterV2 runtime query policy", () => { |
| 386 | 382 | |
| 387 | 383 | it("maps Auto runtime mode to Claude's AI-reviewed permission mode", () => { |
| 388 | 384 | const queryPolicy = ClaudeAdapterV2.claudeRuntimeQueryPolicyForRuntimePolicy( |
| 389 | ProviderAdapterV2RuntimePolicy.make({ | |
| 385 | ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({ | |
| 390 | 386 | runtimeMode: "auto", |
| 391 | 387 | interactionMode: "default", |
| 392 | 388 | cwd: "/workspace", |
| ... | ... | @@ -401,7 +397,7 @@ describe("ClaudeAdapterV2 runtime query policy", () => { |
| 401 | 397 | |
| 402 | 398 | it("keeps approval-required mode interactive with danger-full-access sandboxing", () => { |
| 403 | 399 | const queryPolicy = ClaudeAdapterV2.claudeRuntimeQueryPolicyForRuntimePolicy( |
| 404 | ProviderAdapterV2RuntimePolicy.make({ | |
| 400 | ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({ | |
| 405 | 401 | runtimeMode: "approval-required", |
| 406 | 402 | interactionMode: "default", |
| 407 | 403 | cwd: "/workspace", |
| ... | ... | @@ -419,7 +415,7 @@ describe("ClaudeAdapterV2 runtime query policy", () => { |
| 419 | 415 | |
| 420 | 416 | it("installs the permission callback for approval-required plan mode", () => { |
| 421 | 417 | const queryPolicy = ClaudeAdapterV2.claudeRuntimeQueryPolicyForRuntimePolicy( |
| 422 | ProviderAdapterV2RuntimePolicy.make({ | |
| 418 | ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({ | |
| 423 | 419 | runtimeMode: "approval-required", |
| 424 | 420 | interactionMode: "plan", |
| 425 | 421 | cwd: "/workspace", |
| ... | ... | @@ -434,7 +430,7 @@ describe("ClaudeAdapterV2 runtime query policy", () => { |
| 434 | 430 | |
| 435 | 431 | it("honors never approvals for approval-required workspace-write policy", () => { |
| 436 | 432 | const queryPolicy = ClaudeAdapterV2.claudeRuntimeQueryPolicyForRuntimePolicy( |
| 437 | ProviderAdapterV2RuntimePolicy.make({ | |
| 433 | ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({ | |
| 438 | 434 | runtimeMode: "approval-required", |
| 439 | 435 | interactionMode: "default", |
| 440 | 436 | cwd: "/workspace", |
| ... | ... | @@ -453,7 +449,7 @@ describe("ClaudeAdapterV2 runtime query policy", () => { |
| 453 | 449 | |
| 454 | 450 | it("honors never approvals for externally sandboxed policy", () => { |
| 455 | 451 | const queryPolicy = ClaudeAdapterV2.claudeRuntimeQueryPolicyForRuntimePolicy( |
| 456 | ProviderAdapterV2RuntimePolicy.make({ | |
| 452 | ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({ | |
| 457 | 453 | runtimeMode: "approval-required", |
| 458 | 454 | interactionMode: "default", |
| 459 | 455 | cwd: "/workspace", |
| ... | ... | @@ -595,7 +591,7 @@ describe("ClaudeAdapterV2 MCP query overrides", () => { |
| 595 | 591 | const threadId = ThreadId.make("thread-claude-mcp-query-key"); |
| 596 | 592 | withMcpSession(threadId, () => { |
| 597 | 593 | const queryPolicy = ClaudeAdapterV2.claudeRuntimeQueryPolicyForRuntimePolicy( |
| 598 | ProviderAdapterV2RuntimePolicy.make({ | |
| 594 | ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({ | |
| 599 | 595 | runtimeMode: "full-access", |
| 600 | 596 | interactionMode: "default", |
| 601 | 597 | cwd: "/workspace", |
| ... | ... | @@ -627,7 +623,7 @@ describe("ClaudeAdapterV2 MCP query overrides", () => { |
| 627 | 623 | const threadId = ThreadId.make("thread-claude-mcp-credential-rotation"); |
| 628 | 624 | withMcpSession(threadId, () => { |
| 629 | 625 | const queryPolicy = ClaudeAdapterV2.claudeRuntimeQueryPolicyForRuntimePolicy( |
| 630 | ProviderAdapterV2RuntimePolicy.make({ | |
| 626 | ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({ | |
| 631 | 627 | runtimeMode: "approval-required", |
| 632 | 628 | interactionMode: "default", |
| 633 | 629 | cwd: "/workspace", |
| ... | ... | @@ -750,7 +746,7 @@ describe("ClaudeAdapterV2 native protocol logging", () => { |
| 750 | 746 | readonly event: unknown; |
| 751 | 747 | readonly threadId: ThreadId | null; |
| 752 | 748 | }> = []; |
| 753 | const logger: EventNdjsonLogger = { | |
| 749 | const logger: ProviderEventLoggers.EventNdjsonLogger = { | |
| 754 | 750 | filePath: "/tmp/events.log", |
| 755 | 751 | write: (event, threadId) => |
| 756 | 752 | Effect.sync(() => { |
| ... | ... | @@ -993,7 +989,7 @@ describe("ClaudeAdapterV2 Auto-accept edits", () => { |
| 993 | 989 | assertComplete: Effect.void, |
| 994 | 990 | }, |
| 995 | 991 | }); |
| 996 | const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({ | |
| 992 | const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({ | |
| 997 | 993 | runtimeMode: "auto-accept-edits", |
| 998 | 994 | interactionMode: "default", |
| 999 | 995 | cwd: "/workspace", |
| ... | ... | @@ -1701,7 +1697,7 @@ describe("ClaudeAdapterV2 native fork", () => { |
| 1701 | 1697 | instanceId: ProviderInstanceId.make(ClaudeAdapterV2.CLAUDE_PROVIDER), |
| 1702 | 1698 | model: "claude-sonnet-4-6", |
| 1703 | 1699 | }, |
| 1704 | runtimePolicy: ProviderAdapterV2RuntimePolicy.make({ | |
| 1700 | runtimePolicy: ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({ | |
| 1705 | 1701 | runtimeMode: "full-access", |
| 1706 | 1702 | interactionMode: "default", |
| 1707 | 1703 | cwd: "/workspace", |
| ... | ... | @@ -1713,7 +1709,7 @@ describe("ClaudeAdapterV2 native fork", () => { |
| 1713 | 1709 | instanceId: ProviderInstanceId.make(ClaudeAdapterV2.CLAUDE_PROVIDER), |
| 1714 | 1710 | model: "claude-sonnet-4-6", |
| 1715 | 1711 | }, |
| 1716 | runtimePolicy: ProviderAdapterV2RuntimePolicy.make({ | |
| 1712 | runtimePolicy: ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({ | |
| 1717 | 1713 | runtimeMode: "full-access", |
| 1718 | 1714 | interactionMode: "default", |
| 1719 | 1715 | cwd: "/workspace", |
| ... | ... | @@ -1808,7 +1804,7 @@ describe("ClaudeAdapterV2 native fork", () => { |
| 1808 | 1804 | instanceId: ProviderInstanceId.make(ClaudeAdapterV2.CLAUDE_PROVIDER), |
| 1809 | 1805 | model: "claude-sonnet-4-6", |
| 1810 | 1806 | }, |
| 1811 | runtimePolicy: ProviderAdapterV2RuntimePolicy.make({ | |
| 1807 | runtimePolicy: ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({ | |
| 1812 | 1808 | runtimeMode: "full-access", |
| 1813 | 1809 | interactionMode: "default", |
| 1814 | 1810 | cwd: "/workspace", |
| ... | ... | @@ -2120,11 +2116,16 @@ describe("ClaudeAdapterV2 background wake turns", () => { |
| 2120 | 2116 | }); |
| 2121 | 2117 | const offeredMessages: Array<SDKUserMessage> = []; |
| 2122 | 2118 | const permissionModeChanges: Array<string> = []; |
| 2123 | const continuationRequests: Array<ProviderContinuationRequest> = []; | |
| 2119 | const continuationRequests: Array<ProviderContinuationRequests.ProviderContinuationRequest> = | |
| 2120 | []; | |
| 2124 | 2121 | const terminalReceipts = |
| 2125 | yield* Queue.unbounded<Extract<ProviderAdapterV2Event, { type: "turn.terminal" }>>(); | |
| 2122 | yield* Queue.unbounded< | |
| 2123 | Extract<ProviderAdapter.ProviderAdapterV2Event, { type: "turn.terminal" }> | |
| 2124 | >(); | |
| 2126 | 2125 | const systemNoticeReceipts = |
| 2127 | yield* Queue.unbounded<Extract<ProviderAdapterV2Event, { type: "turn_item.updated" }>>(); | |
| 2126 | yield* Queue.unbounded< | |
| 2127 | Extract<ProviderAdapter.ProviderAdapterV2Event, { type: "turn_item.updated" }> | |
| 2128 | >(); | |
| 2128 | 2129 | let openedOptions: ClaudeAdapterV2.ClaudeAgentSdkQueryOptions | undefined; |
| 2129 | 2130 | const adapter = ClaudeAdapterV2.makeClaudeAdapterV2({ |
| 2130 | 2131 | instanceId: ClaudeAdapterV2.CLAUDE_DEFAULT_INSTANCE_ID, |
| ... | ... | @@ -2210,7 +2211,7 @@ describe("ClaudeAdapterV2 background wake turns", () => { |
| 2210 | 2211 | modelSelection: CLAUDE_TEST_MODEL_SELECTION, |
| 2211 | 2212 | runtimePolicy: CLAUDE_TEST_RUNTIME_POLICY, |
| 2212 | 2213 | }); |
| 2213 | const events: Array<ProviderAdapterV2Event> = []; | |
| 2214 | const events: Array<ProviderAdapter.ProviderAdapterV2Event> = []; | |
| 2214 | 2215 | yield* runtime.events.pipe( |
| 2215 | 2216 | Stream.runForEach((event) => |
| 2216 | 2217 | Effect.gen(function* () { |
| ... | ... | @@ -2231,7 +2232,9 @@ describe("ClaudeAdapterV2 background wake turns", () => { |
| 2231 | 2232 | const hasPendingBackgroundWork = runtime.hasPendingBackgroundWork; |
| 2232 | 2233 | const terminalEvents = () => |
| 2233 | 2234 | events.filter( |
| 2234 | (event): event is Extract<ProviderAdapterV2Event, { type: "turn.terminal" }> => | |
| 2235 | ( | |
| 2236 | event, | |
| 2237 | ): event is Extract<ProviderAdapter.ProviderAdapterV2Event, { type: "turn.terminal" }> => | |
| 2235 | 2238 | event.type === "turn.terminal", |
| 2236 | 2239 | ); |
| 2237 | 2240 | return { |
| ... | ... | @@ -2817,7 +2820,7 @@ describe("ClaudeAdapterV2 background wake turns", () => { |
| 2817 | 2820 | attemptId: RunAttemptId.make("attempt-claude-auth-failure"), |
| 2818 | 2821 | text: "Continue.", |
| 2819 | 2822 | attachments: [], |
| 2820 | runtimePolicy: ProviderAdapterV2RuntimePolicy.make({ | |
| 2823 | runtimePolicy: ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({ | |
| 2821 | 2824 | runtimeMode: "full-access", |
| 2822 | 2825 | interactionMode: "default", |
| 2823 | 2826 | cwd, |
| ... | ... | @@ -3633,10 +3636,16 @@ describe("ClaudeAdapterV2 background wake turns", () => { |
| 3633 | 3636 | ), |
| 3634 | 3637 | ); |
| 3635 | 3638 | |
| 3636 | const providerThreadRosterEvents = (events: ReadonlyArray<ProviderAdapterV2Event>) => | |
| 3639 | const providerThreadRosterEvents = ( | |
| 3640 | events: ReadonlyArray<ProviderAdapter.ProviderAdapterV2Event>, | |
| 3641 | ) => | |
| 3637 | 3642 | events.filter( |
| 3638 | (event): event is Extract<ProviderAdapterV2Event, { type: "provider_thread.updated" }> => | |
| 3639 | event.type === "provider_thread.updated", | |
| 3643 | ( | |
| 3644 | event, | |
| 3645 | ): event is Extract< | |
| 3646 | ProviderAdapter.ProviderAdapterV2Event, | |
| 3647 | { type: "provider_thread.updated" } | |
| 3648 | > => event.type === "provider_thread.updated", | |
| 3640 | 3649 | ); |
| 3641 | 3650 | |
| 3642 | 3651 | it.effect( |
| ... | ... | @@ -3932,8 +3941,9 @@ describe("ClaudeAdapterV2 background wake turns", () => { |
| 3932 | 3941 | }); |
| 3933 | 3942 | const processQueues: Array<Queue.Queue<SDKMessage>> = []; |
| 3934 | 3943 | const firstCloseRequested = yield* Deferred.make<void>(); |
| 3935 | const events: Array<ProviderAdapterV2Event> = []; | |
| 3936 | const continuationRequests: Array<ProviderContinuationRequest> = []; | |
| 3944 | const events: Array<ProviderAdapter.ProviderAdapterV2Event> = []; | |
| 3945 | const continuationRequests: Array<ProviderContinuationRequests.ProviderContinuationRequest> = | |
| 3946 | []; | |
| 3937 | 3947 | const adapter = ClaudeAdapterV2.makeClaudeAdapterV2({ |
| 3938 | 3948 | instanceId: ClaudeAdapterV2.CLAUDE_DEFAULT_INSTANCE_ID, |
| 3939 | 3949 | settings: DEFAULT_CLAUDE_SETTINGS, |
| ... | ... | @@ -4179,7 +4189,7 @@ describe("ClaudeAdapterV2 background wake turns", () => { |
| 4179 | 4189 | readonly nativeThreadId: string; |
| 4180 | 4190 | readonly queue: Queue.Queue<SDKMessage>; |
| 4181 | 4191 | }> = []; |
| 4182 | const events: Array<ProviderAdapterV2Event> = []; | |
| 4192 | const events: Array<ProviderAdapter.ProviderAdapterV2Event> = []; | |
| 4183 | 4193 | const adapter = ClaudeAdapterV2.makeClaudeAdapterV2({ |
| 4184 | 4194 | instanceId: ClaudeAdapterV2.CLAUDE_DEFAULT_INSTANCE_ID, |
| 4185 | 4195 | settings: DEFAULT_CLAUDE_SETTINGS, |
| ... | ... | @@ -4430,7 +4440,7 @@ describe("ClaudeAdapterV2 background wake turns", () => { |
| 4430 | 4440 | Effect.gen(function* () { |
| 4431 | 4441 | const harness = yield* makeWakeHarness; |
| 4432 | 4442 | const now = yield* DateTime.now; |
| 4433 | const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({ | |
| 4443 | const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({ | |
| 4434 | 4444 | runtimeMode, |
| 4435 | 4445 | interactionMode: "default", |
| 4436 | 4446 | cwd: "/workspace", |
| ... | ... | @@ -4731,7 +4741,7 @@ describe("ClaudeAdapterV2 background wake turns", () => { |
| 4731 | 4741 | Effect.gen(function* () { |
| 4732 | 4742 | const harness = yield* makeWakeHarness; |
| 4733 | 4743 | const now = yield* DateTime.now; |
| 4734 | const approvalPolicy = ProviderAdapterV2RuntimePolicy.make({ | |
| 4744 | const approvalPolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({ | |
| 4735 | 4745 | runtimeMode: "approval-required", |
| 4736 | 4746 | interactionMode: "default", |
| 4737 | 4747 | cwd: "/workspace", |
| ... | ... | @@ -5972,7 +5982,7 @@ describe("ClaudeAdapterV2 background wake turns", () => { |
| 5972 | 5982 | session_id: WAKE_NATIVE_SESSION, |
| 5973 | 5983 | }); |
| 5974 | 5984 | const subagentRouting = ( |
| 5975 | events: ReadonlyArray<ProviderAdapterV2Event>, | |
| 5985 | events: ReadonlyArray<ProviderAdapter.ProviderAdapterV2Event>, | |
| 5976 | 5986 | nativeToolIds: ReadonlyArray<string>, |
| 5977 | 5987 | ) => { |
| 5978 | 5988 | const childThreadId = |
| ... | ... | @@ -6090,7 +6100,7 @@ describe("ClaudeAdapterV2 background wake turns", () => { |
| 6090 | 6100 | const userTurn = ( |
| 6091 | 6101 | attempt: string, |
| 6092 | 6102 | providerTurnOrdinal: number, |
| 6093 | attachments: ProviderAdapterV2TurnInput["message"]["attachments"] = [], | |
| 6103 | attachments: ProviderAdapter.ProviderAdapterV2TurnInput["message"]["attachments"] = [], | |
| 6094 | 6104 | ) => |
| 6095 | 6105 | harness.runtime.startTurn( |
| 6096 | 6106 | makeClaudeTestTurnInput({ |
| ... | ... | @@ -7838,8 +7848,12 @@ describe("ClaudeAdapterV2 background wake turns", () => { |
| 7838 | 7848 | const now = yield* DateTime.now; |
| 7839 | 7849 | const subagentEvents = () => |
| 7840 | 7850 | harness.events.filter( |
| 7841 | (event): event is Extract<ProviderAdapterV2Event, { type: "subagent.updated" }> => | |
| 7842 | event.type === "subagent.updated", | |
| 7851 | ( | |
| 7852 | event, | |
| 7853 | ): event is Extract< | |
| 7854 | ProviderAdapter.ProviderAdapterV2Event, | |
| 7855 | { type: "subagent.updated" } | |
| 7856 | > => event.type === "subagent.updated", | |
| 7843 | 7857 | ); |
| 7844 | 7858 | |
| 7845 | 7859 | yield* harness.runtime.startTurn( |
| ... | ... | @@ -7942,8 +7956,12 @@ describe("ClaudeAdapterV2 background wake turns", () => { |
| 7942 | 7956 | const now = yield* DateTime.now; |
| 7943 | 7957 | const subagentEvents = () => |
| 7944 | 7958 | harness.events.filter( |
| 7945 | (event): event is Extract<ProviderAdapterV2Event, { type: "subagent.updated" }> => | |
| 7946 | event.type === "subagent.updated", | |
| 7959 | ( | |
| 7960 | event, | |
| 7961 | ): event is Extract< | |
| 7962 | ProviderAdapter.ProviderAdapterV2Event, | |
| 7963 | { type: "subagent.updated" } | |
| 7964 | > => event.type === "subagent.updated", | |
| 7947 | 7965 | ); |
| 7948 | 7966 | |
| 7949 | 7967 | yield* harness.runtime.startTurn( |
| ... | ... | @@ -8059,8 +8077,12 @@ describe("ClaudeAdapterV2 background wake turns", () => { |
| 8059 | 8077 | const now = yield* DateTime.now; |
| 8060 | 8078 | const subagentEvents = () => |
| 8061 | 8079 | harness.events.filter( |
| 8062 | (event): event is Extract<ProviderAdapterV2Event, { type: "subagent.updated" }> => | |
| 8063 | event.type === "subagent.updated", | |
| 8080 | ( | |
| 8081 | event, | |
| 8082 | ): event is Extract< | |
| 8083 | ProviderAdapter.ProviderAdapterV2Event, | |
| 8084 | { type: "subagent.updated" } | |
| 8085 | > => event.type === "subagent.updated", | |
| 8064 | 8086 | ); |
| 8065 | 8087 | |
| 8066 | 8088 | yield* harness.runtime.startTurn( |
| ... | ... | @@ -8249,7 +8271,9 @@ describe("ClaudeAdapterV2 background wake turns", () => { |
| 8249 | 8271 | // already pre-opened by the wake buffer before the drain replay. |
| 8250 | 8272 | const nodeStatuses = harness.events |
| 8251 | 8273 | .filter( |
| 8252 | (event): event is Extract<ProviderAdapterV2Event, { type: "node.updated" }> => | |
| 8274 | ( | |
| 8275 | event, | |
| 8276 | ): event is Extract<ProviderAdapter.ProviderAdapterV2Event, { type: "node.updated" }> => | |
| 8253 | 8277 | event.type === "node.updated" && |
| 8254 | 8278 | event.node.kind === "subagent" && |
| 8255 | 8279 | event.node.nativeItemRef?.nativeId === SUBAGENT_TASK_ID, |
| ... | ... | @@ -8288,8 +8312,9 @@ describe("ClaudeAdapterV2 background wake turns", () => { |
| 8288 | 8312 | prefix: "t3-claude-v2-process-reset-", |
| 8289 | 8313 | }); |
| 8290 | 8314 | const processQueues: Array<Queue.Queue<SDKMessage>> = []; |
| 8291 | const events: Array<ProviderAdapterV2Event> = []; | |
| 8292 | const continuationRequests: Array<ProviderContinuationRequest> = []; | |
| 8315 | const events: Array<ProviderAdapter.ProviderAdapterV2Event> = []; | |
| 8316 | const continuationRequests: Array<ProviderContinuationRequests.ProviderContinuationRequest> = | |
| 8317 | []; | |
| 8293 | 8318 | const adapter = ClaudeAdapterV2.makeClaudeAdapterV2({ |
| 8294 | 8319 | instanceId: ClaudeAdapterV2.CLAUDE_DEFAULT_INSTANCE_ID, |
| 8295 | 8320 | settings: DEFAULT_CLAUDE_SETTINGS, |
| ... | ... | @@ -8472,8 +8497,9 @@ describe("ClaudeAdapterV2 background wake turns", () => { |
| 8472 | 8497 | prefix: "t3-claude-v2-buffer-replace-", |
| 8473 | 8498 | }); |
| 8474 | 8499 | const processQueues: Array<Queue.Queue<SDKMessage>> = []; |
| 8475 | const events: Array<ProviderAdapterV2Event> = []; | |
| 8476 | const continuationRequests: Array<ProviderContinuationRequest> = []; | |
| 8500 | const events: Array<ProviderAdapter.ProviderAdapterV2Event> = []; | |
| 8501 | const continuationRequests: Array<ProviderContinuationRequests.ProviderContinuationRequest> = | |
| 8502 | []; | |
| 8477 | 8503 | const adapter = ClaudeAdapterV2.makeClaudeAdapterV2({ |
| 8478 | 8504 | instanceId: ClaudeAdapterV2.CLAUDE_DEFAULT_INSTANCE_ID, |
| 8479 | 8505 | settings: DEFAULT_CLAUDE_SETTINGS, |
| ... | ... | @@ -8708,8 +8734,9 @@ describe("ClaudeAdapterV2 background wake turns", () => { |
| 8708 | 8734 | prefix: "t3-claude-v2-subagent-buffer-replace-", |
| 8709 | 8735 | }); |
| 8710 | 8736 | const processQueues: Array<Queue.Queue<SDKMessage>> = []; |
| 8711 | const events: Array<ProviderAdapterV2Event> = []; | |
| 8712 | const continuationRequests: Array<ProviderContinuationRequest> = []; | |
| 8737 | const events: Array<ProviderAdapter.ProviderAdapterV2Event> = []; | |
| 8738 | const continuationRequests: Array<ProviderContinuationRequests.ProviderContinuationRequest> = | |
| 8739 | []; | |
| 8713 | 8740 | const adapter = ClaudeAdapterV2.makeClaudeAdapterV2({ |
| 8714 | 8741 | instanceId: ClaudeAdapterV2.CLAUDE_DEFAULT_INSTANCE_ID, |
| 8715 | 8742 | settings: DEFAULT_CLAUDE_SETTINGS, |
| ... | ... | @@ -8775,8 +8802,12 @@ describe("ClaudeAdapterV2 background wake turns", () => { |
| 8775 | 8802 | const hasPendingBackgroundWork = runtime.hasPendingBackgroundWork; |
| 8776 | 8803 | const subagentEvents = () => |
| 8777 | 8804 | events.filter( |
| 8778 | (event): event is Extract<ProviderAdapterV2Event, { type: "subagent.updated" }> => | |
| 8779 | event.type === "subagent.updated", | |
| 8805 | ( | |
| 8806 | event, | |
| 8807 | ): event is Extract< | |
| 8808 | ProviderAdapter.ProviderAdapterV2Event, | |
| 8809 | { type: "subagent.updated" } | |
| 8810 | > => event.type === "subagent.updated", | |
| 8780 | 8811 | ); |
| 8781 | 8812 | const now = yield* DateTime.now; |
| 8782 | 8813 | |
| ... | ... | @@ -8880,7 +8911,9 @@ describe("ClaudeAdapterV2 background wake turns", () => { |
| 8880 | 8911 | assert.equal(finalSubagent?.result, SUBAGENT_SUMMARY); |
| 8881 | 8912 | assert.equal(finalSubagent?.runId, subagentEvents()[0]?.subagent.runId); |
| 8882 | 8913 | const subagentNodeEvents = events.filter( |
| 8883 | (event): event is Extract<ProviderAdapterV2Event, { type: "node.updated" }> => | |
| 8914 | ( | |
| 8915 | event, | |
| 8916 | ): event is Extract<ProviderAdapter.ProviderAdapterV2Event, { type: "node.updated" }> => | |
| 8884 | 8917 | event.type === "node.updated" && |
| 8885 | 8918 | event.node.kind === "subagent" && |
| 8886 | 8919 | event.node.nativeItemRef?.nativeId === SUBAGENT_TASK_ID, |
| ... | ... | @@ -8902,7 +8935,7 @@ describe("ClaudeAdapterV2 background wake turns", () => { |
| 8902 | 8935 | prefix: "t3-claude-v2-model-change-running-subagent-", |
| 8903 | 8936 | }); |
| 8904 | 8937 | const processQueues: Array<Queue.Queue<SDKMessage>> = []; |
| 8905 | const events: Array<ProviderAdapterV2Event> = []; | |
| 8938 | const events: Array<ProviderAdapter.ProviderAdapterV2Event> = []; | |
| 8906 | 8939 | const adapter = ClaudeAdapterV2.makeClaudeAdapterV2({ |
| 8907 | 8940 | instanceId: ClaudeAdapterV2.CLAUDE_DEFAULT_INSTANCE_ID, |
| 8908 | 8941 | settings: DEFAULT_CLAUDE_SETTINGS, |
| ... | ... | @@ -9069,7 +9102,7 @@ describe("ClaudeAdapterV2 background wake turns", () => { |
| 9069 | 9102 | }); |
| 9070 | 9103 | let openCount = 0; |
| 9071 | 9104 | const processQueues: Array<Queue.Queue<SDKMessage>> = []; |
| 9072 | const events: Array<ProviderAdapterV2Event> = []; | |
| 9105 | const events: Array<ProviderAdapter.ProviderAdapterV2Event> = []; | |
| 9073 | 9106 | const adapter = ClaudeAdapterV2.makeClaudeAdapterV2({ |
| 9074 | 9107 | instanceId: ClaudeAdapterV2.CLAUDE_DEFAULT_INSTANCE_ID, |
| 9075 | 9108 | settings: DEFAULT_CLAUDE_SETTINGS, |
| ... | ... | @@ -9198,8 +9231,9 @@ describe("ClaudeAdapterV2 background wake turns", () => { |
| 9198 | 9231 | }); |
| 9199 | 9232 | let openCount = 0; |
| 9200 | 9233 | const processQueues: Array<Queue.Queue<SDKMessage>> = []; |
| 9201 | const events: Array<ProviderAdapterV2Event> = []; | |
| 9202 | const continuationRequests: Array<ProviderContinuationRequest> = []; | |
| 9234 | const events: Array<ProviderAdapter.ProviderAdapterV2Event> = []; | |
| 9235 | const continuationRequests: Array<ProviderContinuationRequests.ProviderContinuationRequest> = | |
| 9236 | []; | |
| 9203 | 9237 | const adapter = ClaudeAdapterV2.makeClaudeAdapterV2({ |
| 9204 | 9238 | instanceId: ClaudeAdapterV2.CLAUDE_DEFAULT_INSTANCE_ID, |
| 9205 | 9239 | settings: DEFAULT_CLAUDE_SETTINGS, |
| ... | ... | @@ -9394,7 +9428,7 @@ describe("ClaudeAdapterV2 background wake turns", () => { |
| 9394 | 9428 | const attachmentsDir = yield* fileSystem.makeTempDirectoryScoped({ |
| 9395 | 9429 | prefix: "t3-claude-v2-first-open-fail-", |
| 9396 | 9430 | }); |
| 9397 | const events: Array<ProviderAdapterV2Event> = []; | |
| 9431 | const events: Array<ProviderAdapter.ProviderAdapterV2Event> = []; | |
| 9398 | 9432 | const adapter = ClaudeAdapterV2.makeClaudeAdapterV2({ |
| 9399 | 9433 | instanceId: ClaudeAdapterV2.CLAUDE_DEFAULT_INSTANCE_ID, |
| 9400 | 9434 | settings: DEFAULT_CLAUDE_SETTINGS, |
| ... | ... | @@ -9496,7 +9530,7 @@ describe("ClaudeAdapterV2 background wake turns", () => { |
| 9496 | 9530 | uuid, |
| 9497 | 9531 | session_id: WAKE_NATIVE_SESSION, |
| 9498 | 9532 | }); |
| 9499 | const goalStatuses = (events: ReadonlyArray<ProviderAdapterV2Event>) => | |
| 9533 | const goalStatuses = (events: ReadonlyArray<ProviderAdapter.ProviderAdapterV2Event>) => | |
| 9500 | 9534 | events.flatMap((event) => |
| 9501 | 9535 | event.type === "provider_thread.updated" && event.providerThread.goal != null |
| 9502 | 9536 | ? [event.providerThread.goal] |
apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts+7-11| ... | ... | @@ -101,10 +101,9 @@ import { |
| 101 | 101 | } from "../../provider/ClaudeModelCatalog.ts"; |
| 102 | 102 | import { |
| 103 | 103 | boundProviderEventForLogging, |
| 104 | type EventNdjsonLogger, | |
| 105 | 104 | shouldPersistProviderEvent, |
| 106 | 105 | } from "../../provider/EventNdjsonLogger.ts"; |
| 107 | import * as ProviderEventLoggers from "../../provider/ProviderEventLoggers.ts"; | |
| 106 | import * as ProviderEventLoggers from "@t3tools/provider-core/server/ProviderEventLoggers"; | |
| 108 | 107 | import { |
| 109 | 108 | claudeRateLimitEventToUpdate, |
| 110 | 109 | type ClaudeScopedLimitNames, |
| ... | ... | @@ -135,7 +134,7 @@ import { |
| 135 | 134 | type BackgroundWorkReport, |
| 136 | 135 | backgroundWorkNotification, |
| 137 | 136 | } from "@t3tools/provider-core/server/notification"; |
| 138 | import * as ProviderContinuationRequests from "@t3tools/provider-core/server/continuationRequests"; | |
| 137 | import * as ProviderContinuationRequests from "@t3tools/provider-core/server/ProviderContinuationRequests"; | |
| 139 | 138 | import { |
| 140 | 139 | makeSubagentChildThread, |
| 141 | 140 | makeSubagentConversationArtifacts, |
| ... | ... | @@ -570,7 +569,7 @@ export function loggedClaudeQueryOptions( |
| 570 | 569 | } |
| 571 | 570 | |
| 572 | 571 | export function makeClaudeAgentSdkProtocolLogger(input: { |
| 573 | readonly nativeEventLogger: EventNdjsonLogger | undefined; | |
| 572 | readonly nativeEventLogger: ProviderEventLoggers.EventNdjsonLogger | undefined; | |
| 574 | 573 | readonly threadId: ThreadId; |
| 575 | 574 | readonly providerSessionId: OrchestrationV2ProviderSession["id"]; |
| 576 | 575 | }): ClaudeAgentSdkProtocolLogger | undefined { |
| ... | ... | @@ -1118,7 +1117,7 @@ function resultTextFromSdkMessage( |
| 1118 | 1117 | } |
| 1119 | 1118 | |
| 1120 | 1119 | function makeProviderThread(input: { |
| 1121 | readonly idAllocator: IdAllocator.IdAllocatorV2Shape; | |
| 1120 | readonly idAllocator: IdAllocator.IdAllocatorV2["Service"]; | |
| 1122 | 1121 | readonly providerInstanceId: ProviderInstanceId; |
| 1123 | 1122 | readonly appThreadId: OrchestrationV2ProviderThread["appThreadId"]; |
| 1124 | 1123 | readonly ownerNodeId?: OrchestrationV2ProviderThread["ownerNodeId"]; |
| ... | ... | @@ -2581,7 +2580,7 @@ function providerFailureFromApiRetry(message: SDKAPIRetryMessage): Orchestration |
| 2581 | 2580 | } |
| 2582 | 2581 | |
| 2583 | 2582 | function buildAssistantArtifacts(input: { |
| 2584 | readonly idAllocator: IdAllocator.IdAllocatorV2Shape; | |
| 2583 | readonly idAllocator: IdAllocator.IdAllocatorV2["Service"]; | |
| 2585 | 2584 | readonly turnInput: ProviderAdapter.ProviderAdapterV2TurnInput; |
| 2586 | 2585 | readonly providerTurnId: OrchestrationV2ProviderTurn["id"]; |
| 2587 | 2586 | readonly nativeItemId: string; |
| ... | ... | @@ -3015,7 +3014,7 @@ export interface ClaudeAdapterV2Options { |
| 3015 | 3014 | readonly fileSystem: FileSystem.FileSystem; |
| 3016 | 3015 | readonly path: Path.Path; |
| 3017 | 3016 | readonly crypto: Crypto.Crypto; |
| 3018 | readonly idAllocator: IdAllocator.IdAllocatorV2Shape; | |
| 3017 | readonly idAllocator: IdAllocator.IdAllocatorV2["Service"]; | |
| 3019 | 3018 | readonly queryRunner: ClaudeAgentSdkQueryRunnerShape; |
| 3020 | 3019 | readonly scopedLimitNames?: Ref.Ref<ClaudeScopedLimitNames>; |
| 3021 | 3020 | readonly onUsageLimits?: ServerProviderShape["applyUsageLimits"]; |
| ... | ... | @@ -3029,7 +3028,7 @@ export interface ClaudeAdapterV2Options { |
| 3029 | 3028 | |
| 3030 | 3029 | export function makeClaudeAdapterV2( |
| 3031 | 3030 | adapterOptions: ClaudeAdapterV2Options, |
| 3032 | ): ProviderAdapter.ProviderAdapterV2Shape { | |
| 3031 | ): ProviderAdapter.ProviderAdapterV2["Service"] { | |
| 3033 | 3032 | const { attachmentsDir, fileSystem, path, crypto, idAllocator, queryRunner } = adapterOptions; |
| 3034 | 3033 | const continuationRequests = adapterOptions.continuationRequests ?? { |
| 3035 | 3034 | offer: () => Effect.void, |
| ... | ... | @@ -3356,7 +3355,6 @@ export function makeClaudeAdapterV2( |
| 3356 | 3355 | type: "turn_item.updated", |
| 3357 | 3356 | driver: CLAUDE_PROVIDER, |
| 3358 | 3357 | turnItem: makeProviderRetryTurnItem({ |
| 3359 | idAllocator, | |
| 3360 | 3358 | driver: CLAUDE_PROVIDER, |
| 3361 | 3359 | threadId: context.input.threadId, |
| 3362 | 3360 | runId: context.input.runId, |
| ... | ... | @@ -5083,7 +5081,6 @@ export function makeClaudeAdapterV2( |
| 5083 | 5081 | type: "turn_item.updated", |
| 5084 | 5082 | driver: CLAUDE_PROVIDER, |
| 5085 | 5083 | turnItem: makeProviderRetryTurnItem({ |
| 5086 | idAllocator, | |
| 5087 | 5084 | driver: CLAUDE_PROVIDER, |
| 5088 | 5085 | threadId: input.context.input.threadId, |
| 5089 | 5086 | runId: input.context.input.runId, |
| ... | ... | @@ -6089,7 +6086,6 @@ export function makeClaudeAdapterV2( |
| 6089 | 6086 | type: "turn_item.updated", |
| 6090 | 6087 | driver: CLAUDE_PROVIDER, |
| 6091 | 6088 | turnItem: makeProviderRetryTurnItem({ |
| 6092 | idAllocator, | |
| 6093 | 6089 | driver: CLAUDE_PROVIDER, |
| 6094 | 6090 | threadId: context.input.threadId, |
| 6095 | 6091 | runId: context.input.runId, |
apps/server/src/orchestration-v2/Adapters/CodexAdapterV2.test.ts+108-57| ... | ... | @@ -1,7 +1,6 @@ |
| 1 | 1 | import * as NodeOS from "node:os"; |
| 2 | 2 | |
| 3 | 3 | import { historyResponseItems } from "@t3tools/provider-core/server/handoffBudget"; |
| 4 | import type { ProviderAdapterV2HistoricalContext } from "@t3tools/provider-core/server/ProviderAdapter"; | |
| 5 | 4 | import { |
| 6 | 5 | makeProviderTextDeltaCoalescer, |
| 7 | 6 | type ProviderTextDeltaUpdate, |
| ... | ... | @@ -56,21 +55,13 @@ import { ChildProcess, ChildProcessSpawner } from "effect/process"; |
| 56 | 55 | import packageJson from "../../../package.json" with { type: "json" }; |
| 57 | 56 | import * as ServerConfig from "../../config.ts"; |
| 58 | 57 | import * as McpProviderSession from "@t3tools/provider-core/server/mcpSession"; |
| 59 | import type { EventNdjsonLogger } from "../../provider/EventNdjsonLogger.ts"; | |
| 60 | import * as ProviderEventLoggers from "../../provider/ProviderEventLoggers.ts"; | |
| 58 | import * as ProviderEventLoggers from "@t3tools/provider-core/server/ProviderEventLoggers"; | |
| 61 | 59 | import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator"; |
| 62 | 60 | import * as EffectWorker from "../EffectWorker.ts"; |
| 63 | 61 | import * as Orchestrator from "../Orchestrator.ts"; |
| 64 | 62 | import * as ProviderReplayHarness from "../testkit/ProviderReplayHarness.ts"; |
| 65 | import { | |
| 66 | ProviderAdapterForkThreadError, | |
| 67 | ProviderAdapterOpenSessionError, | |
| 68 | ProviderAdapterRollbackThreadError, | |
| 69 | ProviderAdapterV2RuntimePolicy, | |
| 70 | type ProviderAdapterV2Event, | |
| 71 | type ProviderAdapterV2TurnInput, | |
| 72 | } from "@t3tools/provider-core/server/ProviderAdapter"; | |
| 73 | import type { ProviderContinuationRequest } from "@t3tools/provider-core/server/continuationRequests"; | |
| 63 | import * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter"; | |
| 64 | import type * as ProviderContinuationRequests from "@t3tools/provider-core/server/ProviderContinuationRequests"; | |
| 74 | 65 | import * as CodexAdapterV2 from "./CodexAdapterV2.ts"; |
| 75 | 66 | import { makeReplayServerConfig, withCodexReplayChildMetadata } from "./CodexAdapterV2.testkit.ts"; |
| 76 | 67 | import * as CodexAdapterV2Testkit from "./CodexAdapterV2.testkit.ts"; |
| ... | ... | @@ -767,7 +758,7 @@ describe("CodexAdapterV2 process spawning", () => { |
| 767 | 758 | instanceId: CodexAdapterV2.CODEX_DEFAULT_INSTANCE_ID, |
| 768 | 759 | threadId: ThreadId.make("thread-launch-args"), |
| 769 | 760 | providerSessionId: ProviderSessionId.make("provider-session-launch-args"), |
| 770 | runtimePolicy: ProviderAdapterV2RuntimePolicy.make({ | |
| 761 | runtimePolicy: ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({ | |
| 771 | 762 | runtimeMode: "full-access", |
| 772 | 763 | interactionMode: "default", |
| 773 | 764 | cwd: "/workspace", |
| ... | ... | @@ -1036,7 +1027,7 @@ describe("CodexAdapterV2 native protocol logging", () => { |
| 1036 | 1027 | readonly event: unknown; |
| 1037 | 1028 | readonly threadId: ThreadId | null; |
| 1038 | 1029 | }> = []; |
| 1039 | const logger: EventNdjsonLogger = { | |
| 1030 | const logger: ProviderEventLoggers.EventNdjsonLogger = { | |
| 1040 | 1031 | filePath: "/tmp/events.log", |
| 1041 | 1032 | write: (event, threadId) => |
| 1042 | 1033 | Effect.sync(() => { |
| ... | ... | @@ -1410,7 +1401,7 @@ describe("CodexAdapterV2 fork boundary", () => { |
| 1410 | 1401 | }), |
| 1411 | 1402 | ); |
| 1412 | 1403 | |
| 1413 | assert.instanceOf(error, ProviderAdapterForkThreadError); | |
| 1404 | assert.instanceOf(error, ProviderAdapter.ProviderAdapterForkThreadError); | |
| 1414 | 1405 | assert.include(String(error.cause), "provider-turn-missing"); |
| 1415 | 1406 | }), |
| 1416 | 1407 | ); |
| ... | ... | @@ -1478,7 +1469,7 @@ const CODEX_TEST_MODEL_SELECTION = { |
| 1478 | 1469 | instanceId: CodexAdapterV2.CODEX_DEFAULT_INSTANCE_ID, |
| 1479 | 1470 | model: "gpt-5.4", |
| 1480 | 1471 | } satisfies ModelSelection; |
| 1481 | const CODEX_TEST_RUNTIME_POLICY = ProviderAdapterV2RuntimePolicy.make({ | |
| 1472 | const CODEX_TEST_RUNTIME_POLICY = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({ | |
| 1482 | 1473 | runtimeMode: "full-access", |
| 1483 | 1474 | interactionMode: "default", |
| 1484 | 1475 | cwd: "/workspace", |
| ... | ... | @@ -1524,7 +1515,7 @@ function makeCodexTestTurnInput(input: { |
| 1524 | 1515 | readonly now: DateTime.Utc; |
| 1525 | 1516 | readonly attemptId: RunAttemptId; |
| 1526 | 1517 | readonly text: string; |
| 1527 | }): ProviderAdapterV2TurnInput { | |
| 1518 | }): ProviderAdapter.ProviderAdapterV2TurnInput { | |
| 1528 | 1519 | return { |
| 1529 | 1520 | appThread: makeCodexTestAppThread(input), |
| 1530 | 1521 | threadId: input.threadId, |
| ... | ... | @@ -1747,7 +1738,7 @@ describe("CodexAdapterV2 session initialize", () => { |
| 1747 | 1738 | ), |
| 1748 | 1739 | Effect.mapError( |
| 1749 | 1740 | (cause) => |
| 1750 | new ProviderAdapterOpenSessionError({ | |
| 1741 | new ProviderAdapter.ProviderAdapterOpenSessionError({ | |
| 1751 | 1742 | driver: CodexAdapterV2.CODEX_DRIVER_KIND, |
| 1752 | 1743 | providerSessionId: openInput.providerSessionId, |
| 1753 | 1744 | cause, |
| ... | ... | @@ -1907,7 +1898,8 @@ describe("CodexAdapterV2 post-settle continuation", () => { |
| 1907 | 1898 | |
| 1908 | 1899 | const makeCodexReplayHarness = ( |
| 1909 | 1900 | transcript: CodexReplay.CodexAppServerReplayTranscript, |
| 1910 | onEvent: (event: ProviderAdapterV2Event) => Effect.Effect<unknown> = () => Effect.void, | |
| 1901 | onEvent: (event: ProviderAdapter.ProviderAdapterV2Event) => Effect.Effect<unknown> = () => | |
| 1902 | Effect.void, | |
| 1911 | 1903 | onRequest: (method: string, params: unknown) => Effect.Effect<void> = () => Effect.void, |
| 1912 | 1904 | readChildMetadata?: Parameters<typeof withCodexReplayChildMetadata>[2], |
| 1913 | 1905 | ) => |
| ... | ... | @@ -1915,13 +1907,14 @@ describe("CodexAdapterV2 post-settle continuation", () => { |
| 1915 | 1907 | const fileSystem = yield* FileSystem.FileSystem; |
| 1916 | 1908 | const idAllocator = yield* IdAllocator.IdAllocatorV2; |
| 1917 | 1909 | const serverConfig = yield* makeReplayServerConfig(transcript.scenario).pipe(Effect.orDie); |
| 1918 | const continuationRequests: Array<ProviderContinuationRequest> = []; | |
| 1910 | const continuationRequests: Array<ProviderContinuationRequests.ProviderContinuationRequest> = | |
| 1911 | []; | |
| 1919 | 1912 | const clientFactory: CodexAdapterV2.CodexAppServerClientFactoryShape = { |
| 1920 | 1913 | open: (openInput) => |
| 1921 | 1914 | Layer.build(CodexReplay.layerReplay(transcript)).pipe( |
| 1922 | 1915 | Effect.mapError( |
| 1923 | 1916 | (cause) => |
| 1924 | new ProviderAdapterOpenSessionError({ | |
| 1917 | new ProviderAdapter.ProviderAdapterOpenSessionError({ | |
| 1925 | 1918 | driver: CodexAdapterV2.CODEX_DRIVER_KIND, |
| 1926 | 1919 | providerSessionId: openInput.providerSessionId, |
| 1927 | 1920 | cause, |
| ... | ... | @@ -1975,7 +1968,7 @@ describe("CodexAdapterV2 post-settle continuation", () => { |
| 1975 | 1968 | modelSelection: CODEX_TEST_MODEL_SELECTION, |
| 1976 | 1969 | runtimePolicy: CODEX_TEST_RUNTIME_POLICY, |
| 1977 | 1970 | }); |
| 1978 | const events: Array<ProviderAdapterV2Event> = []; | |
| 1971 | const events: Array<ProviderAdapter.ProviderAdapterV2Event> = []; | |
| 1979 | 1972 | const firstTerminal = yield* Deferred.make<void>(); |
| 1980 | 1973 | yield* runtime.events.pipe( |
| 1981 | 1974 | Stream.runForEach((event) => |
| ... | ... | @@ -1998,13 +1991,19 @@ describe("CodexAdapterV2 post-settle continuation", () => { |
| 1998 | 1991 | const hasPendingBackgroundWork = runtime.hasPendingBackgroundWork; |
| 1999 | 1992 | const terminalEvents = () => |
| 2000 | 1993 | events.filter( |
| 2001 | (event): event is Extract<ProviderAdapterV2Event, { type: "turn.terminal" }> => | |
| 1994 | ( | |
| 1995 | event, | |
| 1996 | ): event is Extract<ProviderAdapter.ProviderAdapterV2Event, { type: "turn.terminal" }> => | |
| 2002 | 1997 | event.type === "turn.terminal", |
| 2003 | 1998 | ); |
| 2004 | 1999 | const subagentUpdates = () => |
| 2005 | 2000 | events.filter( |
| 2006 | (event): event is Extract<ProviderAdapterV2Event, { type: "subagent.updated" }> => | |
| 2007 | event.type === "subagent.updated", | |
| 2001 | ( | |
| 2002 | event, | |
| 2003 | ): event is Extract< | |
| 2004 | ProviderAdapter.ProviderAdapterV2Event, | |
| 2005 | { type: "subagent.updated" } | |
| 2006 | > => event.type === "subagent.updated", | |
| 2008 | 2007 | ); |
| 2009 | 2008 | return { |
| 2010 | 2009 | runtime, |
| ... | ... | @@ -2026,7 +2025,7 @@ describe("CodexAdapterV2 post-settle continuation", () => { |
| 2026 | 2025 | Effect.gen(function* () { |
| 2027 | 2026 | const nativeThreadId = `inject-${response}`; |
| 2028 | 2027 | const prompt = "Only the current request"; |
| 2029 | const history: ProviderAdapterV2HistoricalContext = { | |
| 2028 | const history: ProviderAdapter.ProviderAdapterV2HistoricalContext = { | |
| 2030 | 2029 | context: "Historical conversation", |
| 2031 | 2030 | messages: (["user", "assistant"] as const).map((role) => ({ |
| 2032 | 2031 | role, |
| ... | ... | @@ -2528,12 +2527,14 @@ describe("CodexAdapterV2 post-settle continuation", () => { |
| 2528 | 2527 | }).pipe(Effect.scoped, Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), |
| 2529 | 2528 | ); |
| 2530 | 2529 | |
| 2531 | const assistantMessages = (events: ReadonlyArray<ProviderAdapterV2Event>) => | |
| 2530 | const assistantMessages = (events: ReadonlyArray<ProviderAdapter.ProviderAdapterV2Event>) => | |
| 2532 | 2531 | events.filter( |
| 2533 | (event): event is Extract<ProviderAdapterV2Event, { type: "message.updated" }> => | |
| 2532 | ( | |
| 2533 | event, | |
| 2534 | ): event is Extract<ProviderAdapter.ProviderAdapterV2Event, { type: "message.updated" }> => | |
| 2534 | 2535 | event.type === "message.updated" && event.message.role === "assistant", |
| 2535 | 2536 | ); |
| 2536 | const assistantTurnItems = (events: ReadonlyArray<ProviderAdapterV2Event>) => | |
| 2537 | const assistantTurnItems = (events: ReadonlyArray<ProviderAdapter.ProviderAdapterV2Event>) => | |
| 2537 | 2538 | events.flatMap((event) => |
| 2538 | 2539 | event.type === "turn_item.updated" && event.turnItem.type === "assistant_message" |
| 2539 | 2540 | ? [event.turnItem] |
| ... | ... | @@ -4914,8 +4915,12 @@ describe("CodexAdapterV2 post-settle continuation", () => { |
| 4914 | 4915 | ); |
| 4915 | 4916 | |
| 4916 | 4917 | const providerTurnId = harness.events.find( |
| 4917 | (event): event is Extract<ProviderAdapterV2Event, { type: "provider_turn.updated" }> => | |
| 4918 | event.type === "provider_turn.updated", | |
| 4918 | ( | |
| 4919 | event, | |
| 4920 | ): event is Extract< | |
| 4921 | ProviderAdapter.ProviderAdapterV2Event, | |
| 4922 | { type: "provider_turn.updated" } | |
| 4923 | > => event.type === "provider_turn.updated", | |
| 4919 | 4924 | )?.providerTurn.id; |
| 4920 | 4925 | assert.isDefined(providerTurnId); |
| 4921 | 4926 | |
| ... | ... | @@ -4931,7 +4936,7 @@ describe("CodexAdapterV2 post-settle continuation", () => { |
| 4931 | 4936 | assert.isAtLeast(terminalIndex, 0); |
| 4932 | 4937 | |
| 4933 | 4938 | let lastCommandBeforeTerminal: |
| 4934 | | Extract<ProviderAdapterV2Event, { type: "turn_item.updated" }> | |
| 4939 | | Extract<ProviderAdapter.ProviderAdapterV2Event, { type: "turn_item.updated" }> | |
| 4935 | 4940 | | undefined; |
| 4936 | 4941 | for (let index = 0; index < terminalIndex; index++) { |
| 4937 | 4942 | const event = harness.events[index]; |
| ... | ... | @@ -4997,8 +5002,12 @@ describe("CodexAdapterV2 post-settle continuation", () => { |
| 4997 | 5002 | ); |
| 4998 | 5003 | |
| 4999 | 5004 | const commandUpdates = harness.events.filter( |
| 5000 | (event): event is Extract<ProviderAdapterV2Event, { type: "turn_item.updated" }> => | |
| 5001 | event.type === "turn_item.updated" && event.turnItem.type === "command_execution", | |
| 5005 | ( | |
| 5006 | event, | |
| 5007 | ): event is Extract< | |
| 5008 | ProviderAdapter.ProviderAdapterV2Event, | |
| 5009 | { type: "turn_item.updated" } | |
| 5010 | > => event.type === "turn_item.updated" && event.turnItem.type === "command_execution", | |
| 5002 | 5011 | ); |
| 5003 | 5012 | assert.isAtLeast(commandUpdates.length, 2, "start + interrupt terminalization"); |
| 5004 | 5013 | assert.equal(commandUpdates[commandUpdates.length - 1]?.turnItem.status, "interrupted"); |
| ... | ... | @@ -5142,7 +5151,7 @@ describe("CodexAdapterV2 post-settle continuation", () => { |
| 5142 | 5151 | }); |
| 5143 | 5152 | |
| 5144 | 5153 | const assertChildProviderTerminalBeforeRoot = ( |
| 5145 | events: ReadonlyArray<ProviderAdapterV2Event>, | |
| 5154 | events: ReadonlyArray<ProviderAdapter.ProviderAdapterV2Event>, | |
| 5146 | 5155 | rootThreadId: ThreadId, |
| 5147 | 5156 | ) => { |
| 5148 | 5157 | const terminalIndex = events.findIndex((event) => event.type === "turn.terminal"); |
| ... | ... | @@ -5199,8 +5208,12 @@ describe("CodexAdapterV2 post-settle continuation", () => { |
| 5199 | 5208 | "running child command item", |
| 5200 | 5209 | ); |
| 5201 | 5210 | const providerTurnId = harness.events.find( |
| 5202 | (event): event is Extract<ProviderAdapterV2Event, { type: "provider_turn.updated" }> => | |
| 5203 | event.type === "provider_turn.updated" && event.threadId === harness.threadId, | |
| 5211 | ( | |
| 5212 | event, | |
| 5213 | ): event is Extract< | |
| 5214 | ProviderAdapter.ProviderAdapterV2Event, | |
| 5215 | { type: "provider_turn.updated" } | |
| 5216 | > => event.type === "provider_turn.updated" && event.threadId === harness.threadId, | |
| 5204 | 5217 | )?.providerTurn.id; |
| 5205 | 5218 | assert.isDefined(providerTurnId); |
| 5206 | 5219 | |
| ... | ... | @@ -5212,7 +5225,12 @@ describe("CodexAdapterV2 post-settle continuation", () => { |
| 5212 | 5225 | yield* awaitUntil(() => harness.terminalEvents().length === 1, "interrupted root terminal"); |
| 5213 | 5226 | assert.equal(harness.terminalEvents()[0]?.status, "interrupted"); |
| 5214 | 5227 | const childCommandUpdates = harness.events.filter( |
| 5215 | (event): event is Extract<ProviderAdapterV2Event, { type: "turn_item.updated" }> => | |
| 5228 | ( | |
| 5229 | event, | |
| 5230 | ): event is Extract< | |
| 5231 | ProviderAdapter.ProviderAdapterV2Event, | |
| 5232 | { type: "turn_item.updated" } | |
| 5233 | > => | |
| 5216 | 5234 | event.type === "turn_item.updated" && |
| 5217 | 5235 | event.turnItem.type === "command_execution" && |
| 5218 | 5236 | event.turnItem.nativeItemRef?.nativeId === INTERRUPT_CHILD_COMMAND_ITEM, |
| ... | ... | @@ -5289,8 +5307,12 @@ describe("CodexAdapterV2 post-settle continuation", () => { |
| 5289 | 5307 | "running child command item", |
| 5290 | 5308 | ); |
| 5291 | 5309 | const providerTurnId = harness.events.find( |
| 5292 | (event): event is Extract<ProviderAdapterV2Event, { type: "provider_turn.updated" }> => | |
| 5293 | event.type === "provider_turn.updated" && event.threadId === harness.threadId, | |
| 5310 | ( | |
| 5311 | event, | |
| 5312 | ): event is Extract< | |
| 5313 | ProviderAdapter.ProviderAdapterV2Event, | |
| 5314 | { type: "provider_turn.updated" } | |
| 5315 | > => event.type === "provider_turn.updated" && event.threadId === harness.threadId, | |
| 5294 | 5316 | )?.providerTurn.id; |
| 5295 | 5317 | assert.isDefined(providerTurnId); |
| 5296 | 5318 | |
| ... | ... | @@ -5361,8 +5383,12 @@ describe("CodexAdapterV2 post-settle continuation", () => { |
| 5361 | 5383 | "running child command item", |
| 5362 | 5384 | ); |
| 5363 | 5385 | const providerTurnId = harness.events.find( |
| 5364 | (event): event is Extract<ProviderAdapterV2Event, { type: "provider_turn.updated" }> => | |
| 5365 | event.type === "provider_turn.updated" && event.threadId === harness.threadId, | |
| 5386 | ( | |
| 5387 | event, | |
| 5388 | ): event is Extract< | |
| 5389 | ProviderAdapter.ProviderAdapterV2Event, | |
| 5390 | { type: "provider_turn.updated" } | |
| 5391 | > => event.type === "provider_turn.updated" && event.threadId === harness.threadId, | |
| 5366 | 5392 | )?.providerTurn.id; |
| 5367 | 5393 | assert.isDefined(providerTurnId); |
| 5368 | 5394 | |
| ... | ... | @@ -5460,8 +5486,12 @@ describe("CodexAdapterV2 post-settle continuation", () => { |
| 5460 | 5486 | "running child command item", |
| 5461 | 5487 | ); |
| 5462 | 5488 | const providerTurnId = harness.events.find( |
| 5463 | (event): event is Extract<ProviderAdapterV2Event, { type: "provider_turn.updated" }> => | |
| 5464 | event.type === "provider_turn.updated" && event.threadId === harness.threadId, | |
| 5489 | ( | |
| 5490 | event, | |
| 5491 | ): event is Extract< | |
| 5492 | ProviderAdapter.ProviderAdapterV2Event, | |
| 5493 | { type: "provider_turn.updated" } | |
| 5494 | > => event.type === "provider_turn.updated" && event.threadId === harness.threadId, | |
| 5465 | 5495 | )?.providerTurn.id; |
| 5466 | 5496 | assert.isDefined(providerTurnId); |
| 5467 | 5497 | |
| ... | ... | @@ -5481,7 +5511,12 @@ describe("CodexAdapterV2 post-settle continuation", () => { |
| 5481 | 5511 | |
| 5482 | 5512 | yield* awaitUntil(() => harness.terminalEvents().length === 1, "interrupted root terminal"); |
| 5483 | 5513 | const lateChildUpdates = harness.events.filter( |
| 5484 | (event): event is Extract<ProviderAdapterV2Event, { type: "provider_turn.updated" }> => | |
| 5514 | ( | |
| 5515 | event, | |
| 5516 | ): event is Extract< | |
| 5517 | ProviderAdapter.ProviderAdapterV2Event, | |
| 5518 | { type: "provider_turn.updated" } | |
| 5519 | > => | |
| 5485 | 5520 | event.type === "provider_turn.updated" && |
| 5486 | 5521 | event.providerTurn.nativeTurnRef?.nativeId === INTERRUPT_LATE_CHILD_NATIVE_TURN, |
| 5487 | 5522 | ); |
| ... | ... | @@ -5589,8 +5624,12 @@ describe("CodexAdapterV2 post-settle continuation", () => { |
| 5589 | 5624 | "running child command item", |
| 5590 | 5625 | ); |
| 5591 | 5626 | const providerTurnId = harness.events.find( |
| 5592 | (event): event is Extract<ProviderAdapterV2Event, { type: "provider_turn.updated" }> => | |
| 5593 | event.type === "provider_turn.updated" && event.threadId === harness.threadId, | |
| 5627 | ( | |
| 5628 | event, | |
| 5629 | ): event is Extract< | |
| 5630 | ProviderAdapter.ProviderAdapterV2Event, | |
| 5631 | { type: "provider_turn.updated" } | |
| 5632 | > => event.type === "provider_turn.updated" && event.threadId === harness.threadId, | |
| 5594 | 5633 | )?.providerTurn.id; |
| 5595 | 5634 | assert.isDefined(providerTurnId); |
| 5596 | 5635 | |
| ... | ... | @@ -5835,8 +5874,12 @@ describe("CodexAdapterV2 post-settle continuation", () => { |
| 5835 | 5874 | "running command item", |
| 5836 | 5875 | ); |
| 5837 | 5876 | const providerTurnId = harness.events.find( |
| 5838 | (event): event is Extract<ProviderAdapterV2Event, { type: "provider_turn.updated" }> => | |
| 5839 | event.type === "provider_turn.updated", | |
| 5877 | ( | |
| 5878 | event, | |
| 5879 | ): event is Extract< | |
| 5880 | ProviderAdapter.ProviderAdapterV2Event, | |
| 5881 | { type: "provider_turn.updated" } | |
| 5882 | > => event.type === "provider_turn.updated", | |
| 5840 | 5883 | )?.providerTurn.id; |
| 5841 | 5884 | assert.isDefined(providerTurnId); |
| 5842 | 5885 | |
| ... | ... | @@ -5868,8 +5911,12 @@ describe("CodexAdapterV2 post-settle continuation", () => { |
| 5868 | 5911 | assert.lengthOf(terminalProviderTurnsBeforeLateEvents, 1); |
| 5869 | 5912 | |
| 5870 | 5913 | const commandUpdatesBeforeLateEvents = harness.events.filter( |
| 5871 | (event): event is Extract<ProviderAdapterV2Event, { type: "turn_item.updated" }> => | |
| 5872 | event.type === "turn_item.updated" && event.turnItem.type === "command_execution", | |
| 5914 | ( | |
| 5915 | event, | |
| 5916 | ): event is Extract< | |
| 5917 | ProviderAdapter.ProviderAdapterV2Event, | |
| 5918 | { type: "turn_item.updated" } | |
| 5919 | > => event.type === "turn_item.updated" && event.turnItem.type === "command_execution", | |
| 5873 | 5920 | ); |
| 5874 | 5921 | const terminalCommands = commandUpdatesBeforeLateEvents.filter( |
| 5875 | 5922 | (event) => |
| ... | ... | @@ -5993,8 +6040,12 @@ describe("CodexAdapterV2 post-settle continuation", () => { |
| 5993 | 6040 | "running command item", |
| 5994 | 6041 | ); |
| 5995 | 6042 | const providerTurnId = harness.events.find( |
| 5996 | (event): event is Extract<ProviderAdapterV2Event, { type: "provider_turn.updated" }> => | |
| 5997 | event.type === "provider_turn.updated", | |
| 6043 | ( | |
| 6044 | event, | |
| 6045 | ): event is Extract< | |
| 6046 | ProviderAdapter.ProviderAdapterV2Event, | |
| 6047 | { type: "provider_turn.updated" } | |
| 6048 | > => event.type === "provider_turn.updated", | |
| 5998 | 6049 | )?.providerTurn.id; |
| 5999 | 6050 | assert.isDefined(providerTurnId); |
| 6000 | 6051 | |
| ... | ... | @@ -7663,7 +7714,7 @@ describe("CodexAdapterV2 post-settle continuation", () => { |
| 7663 | 7714 | }), |
| 7664 | 7715 | ); |
| 7665 | 7716 | |
| 7666 | assert.instanceOf(error, ProviderAdapterRollbackThreadError); | |
| 7717 | assert.instanceOf(error, ProviderAdapter.ProviderAdapterRollbackThreadError); | |
| 7667 | 7718 | assert.include( |
| 7668 | 7719 | errorCauseChainText(error), |
| 7669 | 7720 | "legacy", |
| ... | ... | @@ -7904,7 +7955,7 @@ describe("CodexAdapterV2 post-settle continuation", () => { |
| 7904 | 7955 | }), |
| 7905 | 7956 | ); |
| 7906 | 7957 | |
| 7907 | assert.instanceOf(error, ProviderAdapterForkThreadError); | |
| 7958 | assert.instanceOf(error, ProviderAdapter.ProviderAdapterForkThreadError); | |
| 7908 | 7959 | assert.include( |
| 7909 | 7960 | errorCauseChainText(error), |
| 7910 | 7961 | "legacy", |
| ... | ... | @@ -7969,7 +8020,7 @@ describe("CodexAdapterV2 post-settle continuation", () => { |
| 7969 | 8020 | }), |
| 7970 | 8021 | ); |
| 7971 | 8022 | |
| 7972 | assert.instanceOf(error, ProviderAdapterForkThreadError); | |
| 8023 | assert.instanceOf(error, ProviderAdapter.ProviderAdapterForkThreadError); | |
| 7973 | 8024 | assert.include(errorCauseChainText(error), "fork exploded"); |
| 7974 | 8025 | }).pipe(Effect.scoped, Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))), |
| 7975 | 8026 | ); |
| ... | ... | @@ -8033,7 +8084,7 @@ describe("CodexAdapterV2 post-settle continuation", () => { |
| 8033 | 8084 | }), |
| 8034 | 8085 | ), |
| 8035 | 8086 | ); |
| 8036 | const providerGoals = (events: ReadonlyArray<ProviderAdapterV2Event>) => | |
| 8087 | const providerGoals = (events: ReadonlyArray<ProviderAdapter.ProviderAdapterV2Event>) => | |
| 8037 | 8088 | events.flatMap((event) => |
| 8038 | 8089 | event.type === "provider_thread.updated" ? [event.providerThread.goal?.status ?? null] : [], |
| 8039 | 8090 | ); |
apps/server/src/orchestration-v2/Adapters/CodexAdapterV2.testkit.ts+2-2| ... | ... | @@ -15,7 +15,7 @@ import * as Schema from "effect/Schema"; |
| 15 | 15 | |
| 16 | 16 | import * as ServerConfig from "../../config.ts"; |
| 17 | 17 | import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator"; |
| 18 | import { ProviderAdapterOpenSessionError } from "@t3tools/provider-core/server/ProviderAdapter"; | |
| 18 | import * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter"; | |
| 19 | 19 | import { ProviderAdapterDriverCreateError } from "@t3tools/provider-core/server/adapterDriver"; |
| 20 | 20 | import * as ProviderAdapterRegistry from "../ProviderAdapterRegistry.ts"; |
| 21 | 21 | import type { OrchestratorV2ProviderReplayHarness } from "../testkit/ProviderReplayHarness.ts"; |
| ... | ... | @@ -195,7 +195,7 @@ export function layer(input: { |
| 195 | 195 | const context = yield* Layer.build(layerReplay).pipe( |
| 196 | 196 | Effect.mapError( |
| 197 | 197 | (cause) => |
| 198 | new ProviderAdapterOpenSessionError({ | |
| 198 | new ProviderAdapter.ProviderAdapterOpenSessionError({ | |
| 199 | 199 | driver: CodexAdapterV2.CODEX_DRIVER_KIND, |
| 200 | 200 | providerSessionId: openInput.providerSessionId, |
| 201 | 201 | cause, |
apps/server/src/orchestration-v2/Adapters/CodexAdapterV2.ts+102-106| ... | ... | @@ -102,10 +102,9 @@ import { |
| 102 | 102 | } from "../../provider/Drivers/CodexHomeLayout.ts"; |
| 103 | 103 | import { |
| 104 | 104 | boundProviderEventForLogging, |
| 105 | type EventNdjsonLogger, | |
| 106 | 105 | shouldPersistProviderEvent, |
| 107 | 106 | } from "../../provider/EventNdjsonLogger.ts"; |
| 108 | import { ProviderEventLoggers } from "../../provider/ProviderEventLoggers.ts"; | |
| 107 | import * as ProviderEventLoggers from "@t3tools/provider-core/server/ProviderEventLoggers"; | |
| 109 | 108 | import { codexAppServerArgs, resolveCodexLaunchArgs } from "../../provider/codexLaunchArgs.ts"; |
| 110 | 109 | import { mergeProviderInstanceEnvironment } from "@t3tools/provider-core/server/instanceEnvironment"; |
| 111 | 110 | import * as McpProviderSession from "@t3tools/provider-core/server/mcpSession"; |
| ... | ... | @@ -121,11 +120,7 @@ import { |
| 121 | 120 | type ProviderAdapterDriver, |
| 122 | 121 | type ProviderAdapterDriverCreateInput, |
| 123 | 122 | } from "@t3tools/provider-core/server/adapterDriver"; |
| 124 | import { IdAllocatorV2, type IdAllocatorV2Shape } from "@t3tools/provider-core/server/IdAllocator"; | |
| 125 | import { | |
| 126 | type ProviderContinuationRequest, | |
| 127 | ProviderContinuationRequests, | |
| 128 | } from "@t3tools/provider-core/server/continuationRequests"; | |
| 123 | import * as ProviderContinuationRequests from "@t3tools/provider-core/server/ProviderContinuationRequests"; | |
| 129 | 124 | import { backgroundWorkNotification } from "@t3tools/provider-core/server/notification"; |
| 130 | 125 | import { |
| 131 | 126 | makeProviderFailure, |
| ... | ... | @@ -137,35 +132,13 @@ import { |
| 137 | 132 | isProviderNativeImageAttachment, |
| 138 | 133 | providerMessageTextWithAttachmentPaths, |
| 139 | 134 | } from "@t3tools/provider-core/server/attachmentPrompt"; |
| 140 | import { | |
| 141 | ProviderAdapterEnsureThreadError, | |
| 142 | ProviderAdapterForkThreadError, | |
| 143 | ProviderAdapterInterruptError, | |
| 144 | ProviderAdapterOpenSessionError, | |
| 145 | ProviderAdapterProtocolError, | |
| 146 | ProviderAdapterReadThreadSnapshotError, | |
| 147 | ProviderAdapterResumeThreadError, | |
| 148 | ProviderAdapterRollbackThreadError, | |
| 149 | ProviderAdapterRuntimeRequestResponseError, | |
| 150 | ProviderAdapterSteerRunError, | |
| 151 | ProviderAdapterTurnStartError, | |
| 152 | ProviderAdapterV2, | |
| 153 | type ProviderAdapterV2Shape, | |
| 154 | type ProviderAdapterV2Event, | |
| 155 | type ProviderAdapterV2ForkThreadInput, | |
| 156 | type ProviderAdapterV2RollbackThreadInput, | |
| 157 | type ProviderAdapterV2RuntimePolicy, | |
| 158 | type ProviderAdapterV2McpApps, | |
| 159 | type ProviderAdapterV2SessionRuntime, | |
| 160 | type ProviderAdapterV2InterruptInput, | |
| 161 | type ProviderAdapterV2SteerInput, | |
| 162 | type ProviderAdapterV2TurnInput, | |
| 163 | } from "@t3tools/provider-core/server/ProviderAdapter"; | |
| 164 | 135 | import { |
| 165 | 136 | makeSubagentChildThread, |
| 166 | 137 | makeSubagentConversationArtifacts, |
| 167 | 138 | subagentThreadTitle, |
| 168 | 139 | } from "@t3tools/provider-core/server/subagentProjection"; |
| 140 | import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator"; | |
| 141 | import * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter"; | |
| 169 | 142 | |
| 170 | 143 | const CODEX_PROVIDER = ProviderDriverKind.make("codex"); |
| 171 | 144 | export const CODEX_DRIVER_KIND = CODEX_PROVIDER; |
| ... | ... | @@ -341,8 +314,11 @@ export const CodexProviderCapabilitiesV2 = { |
| 341 | 314 | }, |
| 342 | 315 | } satisfies OrchestrationV2ProviderCapabilities; |
| 343 | 316 | |
| 344 | function toProtocolError(detail: string, payload?: unknown): ProviderAdapterProtocolError { | |
| 345 | return new ProviderAdapterProtocolError({ | |
| 317 | function toProtocolError( | |
| 318 | detail: string, | |
| 319 | payload?: unknown, | |
| 320 | ): ProviderAdapter.ProviderAdapterProtocolError { | |
| 321 | return new ProviderAdapter.ProviderAdapterProtocolError({ | |
| 346 | 322 | driver: CODEX_PROVIDER, |
| 347 | 323 | detail, |
| 348 | 324 | ...(payload === undefined ? {} : { payload }), |
| ... | ... | @@ -385,7 +361,7 @@ function mapCodexTurnStatus( |
| 385 | 361 | |
| 386 | 362 | function providerTurnStatusToTerminal( |
| 387 | 363 | status: OrchestrationV2ProviderTurn["status"], |
| 388 | ): Extract<ProviderAdapterV2Event, { type: "turn.terminal" }>["status"] { | |
| 364 | ): Extract<ProviderAdapter.ProviderAdapterV2Event, { type: "turn.terminal" }>["status"] { | |
| 389 | 365 | switch (status) { |
| 390 | 366 | case "completed": |
| 391 | 367 | return "completed"; |
| ... | ... | @@ -677,7 +653,7 @@ const decodeCodexTurnStartParamsWithCollaborationMode = Schema.decodeUnknownEffe |
| 677 | 653 | CodexTurnStartParamsWithCollaborationMode, |
| 678 | 654 | ); |
| 679 | 655 | const isProviderAdapterRuntimeRequestResponseError = Schema.is( |
| 680 | ProviderAdapterRuntimeRequestResponseError, | |
| 656 | ProviderAdapter.ProviderAdapterRuntimeRequestResponseError, | |
| 681 | 657 | ); |
| 682 | 658 | |
| 683 | 659 | function codexRuntimeModeTurnDefaults(runtimeMode: RuntimeMode): { |
| ... | ... | @@ -724,7 +700,7 @@ function codexRuntimeModeTurnDefaults(runtimeMode: RuntimeMode): { |
| 724 | 700 | export function buildCodexTurnStartParams(input: { |
| 725 | 701 | readonly nativeThreadId: string; |
| 726 | 702 | readonly codexInput: ReadonlyArray<CodexSchema.V2TurnStartParams__UserInput>; |
| 727 | readonly runtimePolicy: ProviderAdapterV2RuntimePolicy; | |
| 703 | readonly runtimePolicy: ProviderAdapter.ProviderAdapterV2RuntimePolicy; | |
| 728 | 704 | readonly modelSelection: ModelSelection; |
| 729 | 705 | readonly hasT3Mcp?: boolean; |
| 730 | 706 | readonly browserToolsAvailable?: boolean; |
| ... | ... | @@ -732,7 +708,7 @@ export function buildCodexTurnStartParams(input: { |
| 732 | 708 | /** ChatGPT token sharing does not accept service tiers. */ |
| 733 | 709 | readonly omitServiceTier?: boolean; |
| 734 | 710 | /** What the thread's MCP Apps want the agent to know (`ui/update-model-context`). */ |
| 735 | readonly appContext?: ProviderAdapterV2TurnInput["appContext"]; | |
| 711 | readonly appContext?: ProviderAdapter.ProviderAdapterV2TurnInput["appContext"]; | |
| 736 | 712 | }) { |
| 737 | 713 | return Effect.gen(function* () { |
| 738 | 714 | const runtimeModeDefaults = codexRuntimeModeTurnDefaults(input.runtimePolicy.runtimeMode); |
| ... | ... | @@ -851,7 +827,7 @@ function getNativeThreadId(providerThread: OrchestrationV2ProviderThread) { |
| 851 | 827 | |
| 852 | 828 | function providerThreadFromCodexThread(input: { |
| 853 | 829 | readonly appThreadId: ThreadId | null; |
| 854 | readonly idAllocator: IdAllocatorV2Shape; | |
| 830 | readonly idAllocator: IdAllocator.IdAllocatorV2["Service"]; | |
| 855 | 831 | readonly ownerNodeId: OrchestrationV2ProviderThread["ownerNodeId"]; |
| 856 | 832 | readonly providerSessionId: OrchestrationV2ProviderThread["providerSessionId"]; |
| 857 | 833 | readonly providerInstanceId: ProviderInstanceId; |
| ... | ... | @@ -983,7 +959,7 @@ const countTerminalTurnsAfterBoundary = ( |
| 983 | 959 | }; |
| 984 | 960 | |
| 985 | 961 | const resolveCodexForkRollbackTurnCount = Effect.fn("CodexAdapterV2.resolveForkRollbackTurnCount")( |
| 986 | function* (input: ProviderAdapterV2ForkThreadInput) { | |
| 962 | function* (input: ProviderAdapter.ProviderAdapterV2ForkThreadInput) { | |
| 987 | 963 | if (input.providerTurnId === undefined || input.sourceProviderTurns === undefined) { |
| 988 | 964 | return 0; |
| 989 | 965 | } |
| ... | ... | @@ -993,7 +969,7 @@ const resolveCodexForkRollbackTurnCount = Effect.fn("CodexAdapterV2.resolveForkR |
| 993 | 969 | input.providerTurnId, |
| 994 | 970 | ); |
| 995 | 971 | if (rollbackTurnCount === null) { |
| 996 | return yield* new ProviderAdapterForkThreadError({ | |
| 972 | return yield* new ProviderAdapter.ProviderAdapterForkThreadError({ | |
| 997 | 973 | driver: CODEX_PROVIDER, |
| 998 | 974 | providerThreadId: input.sourceProviderThread.id, |
| 999 | 975 | cause: `Cannot fork Codex thread from provider turn ${input.providerTurnId}: source turn was not found in provider thread ${input.sourceProviderThread.id}.`, |
| ... | ... | @@ -1012,7 +988,7 @@ const resolveCodexForkRollbackTurnCount = Effect.fn("CodexAdapterV2.resolveForkR |
| 1012 | 988 | * only for source turns that predate native turn references. |
| 1013 | 989 | */ |
| 1014 | 990 | export const resolveCodexForkBoundary = Effect.fn("CodexAdapterV2.resolveForkBoundary")(function* ( |
| 1015 | input: ProviderAdapterV2ForkThreadInput, | |
| 991 | input: ProviderAdapter.ProviderAdapterV2ForkThreadInput, | |
| 1016 | 992 | ) { |
| 1017 | 993 | const rollbackTurnCount = yield* resolveCodexForkRollbackTurnCount(input); |
| 1018 | 994 | if (input.providerTurnId === undefined || input.sourceProviderTurns === undefined) { |
| ... | ... | @@ -1066,14 +1042,14 @@ const readCodexThreadHistoryMetadata = Effect.fn("CodexAdapterV2.readThreadHisto |
| 1066 | 1042 | ); |
| 1067 | 1043 | |
| 1068 | 1044 | export const resolveCodexRollbackTurnCount = Effect.fn("CodexAdapterV2.resolveRollbackTurnCount")( |
| 1069 | function* (input: ProviderAdapterV2RollbackThreadInput) { | |
| 1045 | function* (input: ProviderAdapter.ProviderAdapterV2RollbackThreadInput) { | |
| 1070 | 1046 | const providerTurns = input.providerThreadTurns; |
| 1071 | 1047 | switch (input.target.type) { |
| 1072 | 1048 | case "thread_start": |
| 1073 | 1049 | return providerTurns.filter(isTerminalProviderTurn).length; |
| 1074 | 1050 | case "provider_turn": { |
| 1075 | 1051 | if (input.target.providerTurn.providerThreadId !== input.providerThread.id) { |
| 1076 | return yield* new ProviderAdapterRollbackThreadError({ | |
| 1052 | return yield* new ProviderAdapter.ProviderAdapterRollbackThreadError({ | |
| 1077 | 1053 | driver: CODEX_PROVIDER, |
| 1078 | 1054 | providerThreadId: input.providerThread.id, |
| 1079 | 1055 | cause: `Cannot roll back Codex thread ${input.providerThread.id} to provider turn ${input.target.providerTurn.id}: target turn belongs to provider thread ${input.target.providerTurn.providerThreadId}.`, |
| ... | ... | @@ -1085,7 +1061,7 @@ export const resolveCodexRollbackTurnCount = Effect.fn("CodexAdapterV2.resolveRo |
| 1085 | 1061 | input.target.providerTurn.id, |
| 1086 | 1062 | ); |
| 1087 | 1063 | if (rollbackTurnCount === null) { |
| 1088 | return yield* new ProviderAdapterRollbackThreadError({ | |
| 1064 | return yield* new ProviderAdapter.ProviderAdapterRollbackThreadError({ | |
| 1089 | 1065 | driver: CODEX_PROVIDER, |
| 1090 | 1066 | providerThreadId: input.providerThread.id, |
| 1091 | 1067 | cause: `Cannot roll back Codex thread ${input.providerThread.id} to provider turn ${input.target.providerTurn.id}: target turn was not found in durable provider turn history.`, |
| ... | ... | @@ -1129,10 +1105,10 @@ interface ActiveCodexTurnContext { |
| 1129 | 1105 | readonly failure: OrchestrationV2ProviderFailure; |
| 1130 | 1106 | }; |
| 1131 | 1107 | readonly nativeStartReady?: Deferred.Deferred<void>; |
| 1132 | readonly input: ProviderAdapterV2TurnInput; | |
| 1108 | readonly input: ProviderAdapter.ProviderAdapterV2TurnInput; | |
| 1133 | 1109 | readonly projectionAppThread: OrchestrationV2AppThread; |
| 1134 | 1110 | readonly projectionThreadId: ThreadId; |
| 1135 | readonly projectionRunId: ProviderAdapterV2TurnInput["runId"] | null; | |
| 1111 | readonly projectionRunId: ProviderAdapter.ProviderAdapterV2TurnInput["runId"] | null; | |
| 1136 | 1112 | readonly nativeTurnId: string; |
| 1137 | 1113 | readonly providerThread: OrchestrationV2ProviderThread; |
| 1138 | 1114 | readonly providerTurnId: ProviderTurnId; |
| ... | ... | @@ -1177,7 +1153,10 @@ function isPersistentCodexDynamicTool(item: CodexDynamicToolItem): boolean { |
| 1177 | 1153 | return Reflect.get(input, "persistent") === true; |
| 1178 | 1154 | } |
| 1179 | 1155 | |
| 1180 | type CodexRootTerminalEvent = Extract<ProviderAdapterV2Event, { readonly type: "turn.terminal" }>; | |
| 1156 | type CodexRootTerminalEvent = Extract< | |
| 1157 | ProviderAdapter.ProviderAdapterV2Event, | |
| 1158 | { readonly type: "turn.terminal" } | |
| 1159 | >; | |
| 1181 | 1160 | |
| 1182 | 1161 | /** A completed root turn whose run stays open for the goal turn Codex starts next. */ |
| 1183 | 1162 | interface CodexGoalHold { |
| ... | ... | @@ -1294,12 +1273,12 @@ export interface CodexAppServerClientFactoryShape { |
| 1294 | 1273 | readonly instanceId: ProviderInstanceId; |
| 1295 | 1274 | readonly threadId: ThreadId; |
| 1296 | 1275 | readonly providerSessionId: OrchestrationV2ProviderSession["id"]; |
| 1297 | readonly runtimePolicy: ProviderAdapterV2RuntimePolicy; | |
| 1276 | readonly runtimePolicy: ProviderAdapter.ProviderAdapterV2RuntimePolicy; | |
| 1298 | 1277 | readonly settings: CodexSettings; |
| 1299 | 1278 | readonly environment: NodeJS.ProcessEnv; |
| 1300 | 1279 | }) => Effect.Effect< |
| 1301 | 1280 | CodexClient.CodexAppServerClient["Service"], |
| 1302 | ProviderAdapterOpenSessionError, | |
| 1281 | ProviderAdapter.ProviderAdapterOpenSessionError, | |
| 1303 | 1282 | Scope.Scope |
| 1304 | 1283 | >; |
| 1305 | 1284 | } |
| ... | ... | @@ -1319,7 +1298,7 @@ export const CODEX_THREAD_CONFIG = { "tools.update_plan.enabled": true } as cons |
| 1319 | 1298 | export function codexThreadRuntimeParams(input: { |
| 1320 | 1299 | readonly threadId: ThreadId | null; |
| 1321 | 1300 | readonly modelSelection?: { readonly model: string }; |
| 1322 | readonly runtimePolicy?: ProviderAdapterV2RuntimePolicy; | |
| 1301 | readonly runtimePolicy?: ProviderAdapter.ProviderAdapterV2RuntimePolicy; | |
| 1323 | 1302 | }): { |
| 1324 | 1303 | readonly cwd?: string; |
| 1325 | 1304 | readonly model?: string; |
| ... | ... | @@ -1417,7 +1396,7 @@ const layerCodexAppServerClientFactoryCommand = ( |
| 1417 | 1396 | Effect.provideService(Scope.Scope, scope), |
| 1418 | 1397 | Effect.mapError( |
| 1419 | 1398 | (cause) => |
| 1420 | new ProviderAdapterOpenSessionError({ | |
| 1399 | new ProviderAdapter.ProviderAdapterOpenSessionError({ | |
| 1421 | 1400 | driver: CODEX_PROVIDER, |
| 1422 | 1401 | providerSessionId: input.providerSessionId, |
| 1423 | 1402 | cause, |
| ... | ... | @@ -1434,7 +1413,7 @@ const layerCodexAppServerClientFactoryCommand = ( |
| 1434 | 1413 | ); |
| 1435 | 1414 | |
| 1436 | 1415 | export function makeCodexAppServerProtocolLogger(input: { |
| 1437 | readonly nativeEventLogger: EventNdjsonLogger | undefined; | |
| 1416 | readonly nativeEventLogger: ProviderEventLoggers.EventNdjsonLogger | undefined; | |
| 1438 | 1417 | readonly threadId: ThreadId; |
| 1439 | 1418 | readonly providerSessionId: OrchestrationV2ProviderSession["id"]; |
| 1440 | 1419 | }): CodexClient.CodexAppServerClientOptions["logger"] | undefined { |
| ... | ... | @@ -1510,12 +1489,12 @@ function isSensitiveCodexProtocolKey(key: string): boolean { |
| 1510 | 1489 | export const layerAppServerClientFactory: Layer.Layer< |
| 1511 | 1490 | CodexAppServerClientFactory, |
| 1512 | 1491 | never, |
| 1513 | ChildProcessSpawner.ChildProcessSpawner | ProviderEventLoggers | |
| 1492 | ChildProcessSpawner.ChildProcessSpawner | ProviderEventLoggers.ProviderEventLoggers | |
| 1514 | 1493 | > = Layer.effect( |
| 1515 | 1494 | CodexAppServerClientFactory, |
| 1516 | 1495 | Effect.gen(function* () { |
| 1517 | 1496 | const spawner = yield* ChildProcessSpawner.ChildProcessSpawner; |
| 1518 | const { native: nativeEventLogger } = yield* ProviderEventLoggers; | |
| 1497 | const { native: nativeEventLogger } = yield* ProviderEventLoggers.ProviderEventLoggers; | |
| 1519 | 1498 | |
| 1520 | 1499 | return CodexAppServerClientFactory.of({ |
| 1521 | 1500 | open: (input) => |
| ... | ... | @@ -1536,7 +1515,7 @@ export const layerAppServerClientFactory: Layer.Layer< |
| 1536 | 1515 | Effect.provideService(Scope.Scope, scope), |
| 1537 | 1516 | Effect.mapError( |
| 1538 | 1517 | (cause) => |
| 1539 | new ProviderAdapterOpenSessionError({ | |
| 1518 | new ProviderAdapter.ProviderAdapterOpenSessionError({ | |
| 1540 | 1519 | driver: CODEX_PROVIDER, |
| 1541 | 1520 | providerSessionId: input.providerSessionId, |
| 1542 | 1521 | cause, |
| ... | ... | @@ -1569,7 +1548,7 @@ export type CodexAdapterV2DriverEnv = |
| 1569 | 1548 | | CodexAppServerClientFactory |
| 1570 | 1549 | | Crypto.Crypto |
| 1571 | 1550 | | FileSystem.FileSystem |
| 1572 | | IdAllocatorV2 | |
| 1551 | | IdAllocator.IdAllocatorV2 | |
| 1573 | 1552 | | Path.Path |
| 1574 | 1553 | | ServerConfig; |
| 1575 | 1554 | |
| ... | ... | @@ -1579,11 +1558,11 @@ export const createCodexAdapterV2 = ( |
| 1579 | 1558 | ) => |
| 1580 | 1559 | Effect.gen(function* () { |
| 1581 | 1560 | const clientFactory = yield* CodexAppServerClientFactory; |
| 1582 | const continuationRequests = yield* ProviderContinuationRequests; | |
| 1561 | const continuationRequests = yield* ProviderContinuationRequests.ProviderContinuationRequests; | |
| 1583 | 1562 | const crypto = yield* Crypto.Crypto; |
| 1584 | 1563 | const fileSystem = yield* FileSystem.FileSystem; |
| 1585 | 1564 | const hostEnvironment = yield* HostProcessEnvironment; |
| 1586 | const idAllocator = yield* IdAllocatorV2; | |
| 1565 | const idAllocator = yield* IdAllocator.IdAllocatorV2; | |
| 1587 | 1566 | const serverConfig = yield* ServerConfig; |
| 1588 | 1567 | const homeLayout = yield* resolveCodexHomeLayout(config); |
| 1589 | 1568 | |
| ... | ... | @@ -1628,18 +1607,22 @@ export const CodexAdapterV2Driver: ProviderAdapterDriver<CodexSettings, CodexAda |
| 1628 | 1607 | }; |
| 1629 | 1608 | |
| 1630 | 1609 | const layer: Layer.Layer< |
| 1631 | ProviderAdapterV2, | |
| 1610 | ProviderAdapter.ProviderAdapterV2, | |
| 1632 | 1611 | never, |
| 1633 | CodexAppServerClientFactory | Crypto.Crypto | FileSystem.FileSystem | IdAllocatorV2 | ServerConfig | |
| 1612 | | CodexAppServerClientFactory | |
| 1613 | | Crypto.Crypto | |
| 1614 | | FileSystem.FileSystem | |
| 1615 | | IdAllocator.IdAllocatorV2 | |
| 1616 | | ServerConfig | |
| 1634 | 1617 | > = Layer.effect( |
| 1635 | ProviderAdapterV2, | |
| 1618 | ProviderAdapter.ProviderAdapterV2, | |
| 1636 | 1619 | Effect.gen(function* () { |
| 1637 | 1620 | const clientFactory = yield* CodexAppServerClientFactory; |
| 1638 | const continuationRequests = yield* ProviderContinuationRequests; | |
| 1621 | const continuationRequests = yield* ProviderContinuationRequests.ProviderContinuationRequests; | |
| 1639 | 1622 | const crypto = yield* Crypto.Crypto; |
| 1640 | 1623 | const fileSystem = yield* FileSystem.FileSystem; |
| 1641 | 1624 | const hostEnvironment = yield* HostProcessEnvironment; |
| 1642 | const idAllocator = yield* IdAllocatorV2; | |
| 1625 | const idAllocator = yield* IdAllocator.IdAllocatorV2; | |
| 1643 | 1626 | const serverConfig = yield* ServerConfig; |
| 1644 | 1627 | |
| 1645 | 1628 | return makeCodexAdapterV2({ |
| ... | ... | @@ -1670,7 +1653,7 @@ export interface CodexAdapterV2Options { |
| 1670 | 1653 | readonly resolveRuntime?: Effect.Effect<CodexEffectiveRuntime, ProviderSetupError, Scope.Scope>; |
| 1671 | 1654 | readonly crypto: Crypto.Crypto; |
| 1672 | 1655 | readonly fileSystem: FileSystem.FileSystem; |
| 1673 | readonly idAllocator: IdAllocatorV2Shape; | |
| 1656 | readonly idAllocator: IdAllocator.IdAllocatorV2["Service"]; | |
| 1674 | 1657 | readonly serverConfig: ServerConfig["Service"]; |
| 1675 | 1658 | /** |
| 1676 | 1659 | * Sink for post-settle background command completions so the orchestrator |
| ... | ... | @@ -1678,15 +1661,19 @@ export interface CodexAdapterV2Options { |
| 1678 | 1661 | * projection-only handling for late item completions. |
| 1679 | 1662 | */ |
| 1680 | 1663 | readonly continuationRequests?: { |
| 1681 | readonly offer: (request: ProviderContinuationRequest) => Effect.Effect<void>; | |
| 1664 | readonly offer: ( | |
| 1665 | request: ProviderContinuationRequests.ProviderContinuationRequest, | |
| 1666 | ) => Effect.Effect<void>; | |
| 1682 | 1667 | }; |
| 1683 | 1668 | } |
| 1684 | 1669 | |
| 1685 | export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): ProviderAdapterV2Shape { | |
| 1670 | export function makeCodexAdapterV2( | |
| 1671 | adapterOptions: CodexAdapterV2Options, | |
| 1672 | ): ProviderAdapter.ProviderAdapterV2["Service"] { | |
| 1686 | 1673 | const { clientFactory, crypto, fileSystem, idAllocator, serverConfig } = adapterOptions; |
| 1687 | 1674 | const continuationRequests = adapterOptions.continuationRequests; |
| 1688 | 1675 | |
| 1689 | return ProviderAdapterV2.of({ | |
| 1676 | return ProviderAdapter.ProviderAdapterV2.of({ | |
| 1690 | 1677 | instanceId: adapterOptions.instanceId, |
| 1691 | 1678 | driver: CODEX_PROVIDER, |
| 1692 | 1679 | getCapabilities: () => Effect.succeed(CodexProviderCapabilitiesV2), |
| ... | ... | @@ -1700,7 +1687,7 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi |
| 1700 | 1687 | : yield* adapterOptions.resolveRuntime.pipe( |
| 1701 | 1688 | Effect.mapError( |
| 1702 | 1689 | (cause) => |
| 1703 | new ProviderAdapterOpenSessionError({ | |
| 1690 | new ProviderAdapter.ProviderAdapterOpenSessionError({ | |
| 1704 | 1691 | driver: CODEX_PROVIDER, |
| 1705 | 1692 | providerSessionId: input.providerSessionId, |
| 1706 | 1693 | cause, |
| ... | ... | @@ -1800,7 +1787,7 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi |
| 1800 | 1787 | model: input.modelSelection.model, |
| 1801 | 1788 | now, |
| 1802 | 1789 | }); |
| 1803 | const events = yield* Queue.unbounded<ProviderAdapterV2Event>(); | |
| 1790 | const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>(); | |
| 1804 | 1791 | const rateLimitSnapshot = yield* Ref.make<CodexRateLimitSnapshot | undefined>(undefined); |
| 1805 | 1792 | const limitedTurnItems = yield* Ref.make( |
| 1806 | 1793 | new Map<ProviderThreadId, Extract<OrchestrationV2TurnItem, { type: "error" }>>(), |
| ... | ... | @@ -1833,7 +1820,9 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi |
| 1833 | 1820 | context.nativeTurnId, |
| 1834 | 1821 | ).hasSubagents = true; |
| 1835 | 1822 | }; |
| 1836 | const pendingRootTurns = yield* Ref.make(new Map<string, ProviderAdapterV2TurnInput>()); | |
| 1823 | const pendingRootTurns = yield* Ref.make( | |
| 1824 | new Map<string, ProviderAdapter.ProviderAdapterV2TurnInput>(), | |
| 1825 | ); | |
| 1837 | 1826 | const turnWaiters = yield* Ref.make(new Map<string, Deferred.Deferred<void, never>>()); |
| 1838 | 1827 | const subagentThreads = yield* Ref.make(new Map<string, CodexSubagentThreadContext>()); |
| 1839 | 1828 | const subagentSelections = new Map<string, Omit<ModelSelection, "instanceId">>(); |
| ... | ... | @@ -1893,7 +1882,7 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi |
| 1893 | 1882 | const latestGoalTurnId = (providerTurnId: ProviderTurnId) => |
| 1894 | 1883 | goalRuns.get(providerTurnId)?.at(-1)?.providerTurnId ?? providerTurnId; |
| 1895 | 1884 | |
| 1896 | const emitProviderEvent = (event: ProviderAdapterV2Event) => | |
| 1885 | const emitProviderEvent = (event: ProviderAdapter.ProviderAdapterV2Event) => | |
| 1897 | 1886 | Queue.offer(events, event).pipe(Effect.asVoid); |
| 1898 | 1887 | |
| 1899 | 1888 | /** Writes the thread's current native goal onto its root provider thread. */ |
| ... | ... | @@ -1942,7 +1931,6 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi |
| 1942 | 1931 | type: "turn_item.updated", |
| 1943 | 1932 | driver: CODEX_PROVIDER, |
| 1944 | 1933 | turnItem: makeProviderRetryTurnItem({ |
| 1945 | idAllocator, | |
| 1946 | 1934 | driver: CODEX_PROVIDER, |
| 1947 | 1935 | threadId: context.projectionThreadId, |
| 1948 | 1936 | runId: context.projectionRunId, |
| ... | ... | @@ -1960,7 +1948,7 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi |
| 1960 | 1948 | }); |
| 1961 | 1949 | |
| 1962 | 1950 | const registerRootTurn = (input: { |
| 1963 | readonly turnInput: ProviderAdapterV2TurnInput; | |
| 1951 | readonly turnInput: ProviderAdapter.ProviderAdapterV2TurnInput; | |
| 1964 | 1952 | readonly nativeTurnId: string; |
| 1965 | 1953 | readonly startedAt: DateTime.Utc; |
| 1966 | 1954 | readonly waitForNativeStart?: boolean; |
| ... | ... | @@ -3143,7 +3131,11 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi |
| 3143 | 3131 | }); |
| 3144 | 3132 | |
| 3145 | 3133 | const toCodexInput = ( |
| 3146 | turnInput: Pick<ProviderAdapterV2TurnInput | ProviderAdapterV2SteerInput, "message">, | |
| 3134 | turnInput: Pick< | |
| 3135 | | ProviderAdapter.ProviderAdapterV2TurnInput | |
| 3136 | | ProviderAdapter.ProviderAdapterV2SteerInput, | |
| 3137 | "message" | |
| 3138 | >, | |
| 3147 | 3139 | ) => |
| 3148 | 3140 | Effect.gen(function* () { |
| 3149 | 3141 | const inputItems: Array<CodexSchema.V2TurnStartParams__UserInput> = []; |
| ... | ... | @@ -4539,7 +4531,6 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi |
| 4539 | 4531 | type: "turn_item.updated", |
| 4540 | 4532 | driver: CODEX_PROVIDER, |
| 4541 | 4533 | turnItem: makeProviderRetryTurnItem({ |
| 4542 | idAllocator, | |
| 4543 | 4534 | driver: CODEX_PROVIDER, |
| 4544 | 4535 | threadId: context.projectionThreadId, |
| 4545 | 4536 | runId: context.projectionRunId, |
| ... | ... | @@ -5584,7 +5575,6 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi |
| 5584 | 5575 | } |
| 5585 | 5576 | if (current.status === "failed" && current.failure.class === "usage_limit") { |
| 5586 | 5577 | const item = makeProviderFailureTurnItem({ |
| 5587 | idAllocator, | |
| 5588 | 5578 | driver: CODEX_PROVIDER, |
| 5589 | 5579 | threadId: context.input.threadId, |
| 5590 | 5580 | runId: context.input.runId, |
| ... | ... | @@ -5735,7 +5725,6 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi |
| 5735 | 5725 | type: "turn_item.updated", |
| 5736 | 5726 | driver: CODEX_PROVIDER, |
| 5737 | 5727 | turnItem: makeProviderRetryTurnItem({ |
| 5738 | idAllocator, | |
| 5739 | 5728 | driver: CODEX_PROVIDER, |
| 5740 | 5729 | threadId: input.context.projectionThreadId, |
| 5741 | 5730 | runId: input.context.projectionRunId, |
| ... | ... | @@ -6000,7 +5989,7 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi |
| 6000 | 5989 | * assistant text, as Claude's own `/goal` output is. |
| 6001 | 5990 | */ |
| 6002 | 5991 | const completeGoalCommandTurn = Effect.fnUntraced(function* ( |
| 6003 | turnInput: ProviderAdapterV2TurnInput, | |
| 5992 | turnInput: ProviderAdapter.ProviderAdapterV2TurnInput, | |
| 6004 | 5993 | reply: string, |
| 6005 | 5994 | ) { |
| 6006 | 5995 | const now = yield* DateTime.now; |
| ... | ... | @@ -6104,7 +6093,7 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi |
| 6104 | 6093 | * Stop also reaches. |
| 6105 | 6094 | */ |
| 6106 | 6095 | const resolveGoalStopTarget = Effect.fnUntraced(function* ( |
| 6107 | turnInput: ProviderAdapterV2InterruptInput, | |
| 6096 | turnInput: ProviderAdapter.ProviderAdapterV2InterruptInput, | |
| 6108 | 6097 | ) { |
| 6109 | 6098 | const goalThreadId = turnInput.providerThread.nativeThreadRef?.nativeId; |
| 6110 | 6099 | if (goalThreadId == null) { |
| ... | ... | @@ -6172,7 +6161,7 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi |
| 6172 | 6161 | |
| 6173 | 6162 | /** Starts a native turn with this run's full turn configuration. */ |
| 6174 | 6163 | const startNativeTurn = ( |
| 6175 | turnInput: ProviderAdapterV2TurnInput, | |
| 6164 | turnInput: ProviderAdapter.ProviderAdapterV2TurnInput, | |
| 6176 | 6165 | codexInput: ReadonlyArray<CodexSchema.V2TurnStartParams__UserInput>, |
| 6177 | 6166 | ) => |
| 6178 | 6167 | Effect.gen(function* () { |
| ... | ... | @@ -6227,7 +6216,10 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi |
| 6227 | 6216 | * turn Codex starts on its own reuses the last turn's settings. Codex |
| 6228 | 6217 | * continues later goal turns itself. Other commands settle with a reply. |
| 6229 | 6218 | */ |
| 6230 | const runGoalCommand = (turnInput: ProviderAdapterV2TurnInput, command: CodexGoalCommand) => | |
| 6219 | const runGoalCommand = ( | |
| 6220 | turnInput: ProviderAdapter.ProviderAdapterV2TurnInput, | |
| 6221 | command: CodexGoalCommand, | |
| 6222 | ) => | |
| 6231 | 6223 | Effect.gen(function* () { |
| 6232 | 6224 | const threadId = yield* getNativeThreadId(turnInput.providerThread); |
| 6233 | 6225 | // Goal notifications during the command belong to this run's snapshot. |
| ... | ... | @@ -6326,7 +6318,7 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi |
| 6326 | 6318 | }).pipe( |
| 6327 | 6319 | Effect.mapError( |
| 6328 | 6320 | (cause) => |
| 6329 | new ProviderAdapterTurnStartError({ | |
| 6321 | new ProviderAdapter.ProviderAdapterTurnStartError({ | |
| 6330 | 6322 | driver: CODEX_PROVIDER, |
| 6331 | 6323 | threadId: turnInput.threadId, |
| 6332 | 6324 | providerThreadId: turnInput.providerThread.id, |
| ... | ... | @@ -6339,8 +6331,12 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi |
| 6339 | 6331 | // MCP Apps reach their server through Codex's own MCP client, so any |
| 6340 | 6332 | // server the user configured for Codex works without T3 connecting to it. |
| 6341 | 6333 | const mcpAppsError = (detail: string) => (cause: unknown) => |
| 6342 | new ProviderAdapterProtocolError({ driver: CODEX_PROVIDER, detail, payload: cause }); | |
| 6343 | const mcpApps: ProviderAdapterV2McpApps = { | |
| 6334 | new ProviderAdapter.ProviderAdapterProtocolError({ | |
| 6335 | driver: CODEX_PROVIDER, | |
| 6336 | detail, | |
| 6337 | payload: cause, | |
| 6338 | }); | |
| 6339 | const mcpApps: ProviderAdapter.ProviderAdapterV2McpApps = { | |
| 6344 | 6340 | listTools: (toolsInput) => |
| 6345 | 6341 | Effect.gen(function* () { |
| 6346 | 6342 | const threadId = yield* getNativeThreadId(toolsInput.providerThread); |
| ... | ... | @@ -6394,7 +6390,7 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi |
| 6394 | 6390 | }).pipe(Effect.mapError(mcpAppsError("Codex MCP resource read failed."))), |
| 6395 | 6391 | }; |
| 6396 | 6392 | |
| 6397 | const runtime: ProviderAdapterV2SessionRuntime = { | |
| 6393 | const runtime: ProviderAdapter.ProviderAdapterV2SessionRuntime = { | |
| 6398 | 6394 | instanceId: adapterOptions.instanceId, |
| 6399 | 6395 | driver: CODEX_PROVIDER, |
| 6400 | 6396 | providerSessionId: input.providerSessionId, |
| ... | ... | @@ -6471,7 +6467,7 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi |
| 6471 | 6467 | ), |
| 6472 | 6468 | Effect.mapError( |
| 6473 | 6469 | (cause) => |
| 6474 | new ProviderAdapterEnsureThreadError({ | |
| 6470 | new ProviderAdapter.ProviderAdapterEnsureThreadError({ | |
| 6475 | 6471 | driver: CODEX_PROVIDER, |
| 6476 | 6472 | threadId: threadInput.threadId, |
| 6477 | 6473 | cause: normalizeCodexCause(cause), |
| ... | ... | @@ -6535,7 +6531,7 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi |
| 6535 | 6531 | }).pipe( |
| 6536 | 6532 | Effect.mapError( |
| 6537 | 6533 | (cause) => |
| 6538 | new ProviderAdapterResumeThreadError({ | |
| 6534 | new ProviderAdapter.ProviderAdapterResumeThreadError({ | |
| 6539 | 6535 | driver: CODEX_PROVIDER, |
| 6540 | 6536 | providerSessionId: input.providerSessionId, |
| 6541 | 6537 | providerThreadId: threadInput.providerThread.id, |
| ... | ... | @@ -6563,7 +6559,7 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi |
| 6563 | 6559 | }).pipe( |
| 6564 | 6560 | Effect.mapError( |
| 6565 | 6561 | (cause) => |
| 6566 | new ProviderAdapterTurnStartError({ | |
| 6562 | new ProviderAdapter.ProviderAdapterTurnStartError({ | |
| 6567 | 6563 | driver: CODEX_PROVIDER, |
| 6568 | 6564 | threadId: turnInput.threadId, |
| 6569 | 6565 | providerThreadId: turnInput.providerThread.id, |
| ... | ... | @@ -6593,7 +6589,7 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi |
| 6593 | 6589 | }).pipe( |
| 6594 | 6590 | Effect.mapError( |
| 6595 | 6591 | (cause) => |
| 6596 | new ProviderAdapterProtocolError({ | |
| 6592 | new ProviderAdapter.ProviderAdapterProtocolError({ | |
| 6597 | 6593 | driver: CODEX_PROVIDER, |
| 6598 | 6594 | detail: "Failed to inject historical context", |
| 6599 | 6595 | cause, |
| ... | ... | @@ -6617,7 +6613,7 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi |
| 6617 | 6613 | }).pipe( |
| 6618 | 6614 | Effect.mapError( |
| 6619 | 6615 | (cause) => |
| 6620 | new ProviderAdapterTurnStartError({ | |
| 6616 | new ProviderAdapter.ProviderAdapterTurnStartError({ | |
| 6621 | 6617 | driver: CODEX_PROVIDER, |
| 6622 | 6618 | threadId: turnInput.threadId, |
| 6623 | 6619 | providerThreadId: turnInput.providerThread.id, |
| ... | ... | @@ -6655,7 +6651,7 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi |
| 6655 | 6651 | }).pipe( |
| 6656 | 6652 | Effect.mapError( |
| 6657 | 6653 | (cause) => |
| 6658 | new ProviderAdapterSteerRunError({ | |
| 6654 | new ProviderAdapter.ProviderAdapterSteerRunError({ | |
| 6659 | 6655 | driver: CODEX_PROVIDER, |
| 6660 | 6656 | providerThreadId: turnInput.providerThread.id, |
| 6661 | 6657 | providerTurnId: turnInput.providerTurnId, |
| ... | ... | @@ -6674,7 +6670,7 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi |
| 6674 | 6670 | Effect.mapError((cause) => |
| 6675 | 6671 | cause._tag === "ProviderAdapterProtocolError" |
| 6676 | 6672 | ? cause |
| 6677 | : new ProviderAdapterProtocolError({ | |
| 6673 | : new ProviderAdapter.ProviderAdapterProtocolError({ | |
| 6678 | 6674 | driver: CODEX_PROVIDER, |
| 6679 | 6675 | detail: `Failed to unload Codex thread for provider thread ${unloadInput.providerThread.id}`, |
| 6680 | 6676 | cause: normalizeCodexCause(cause), |
| ... | ... | @@ -7061,7 +7057,7 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi |
| 7061 | 7057 | }).pipe( |
| 7062 | 7058 | Effect.mapError( |
| 7063 | 7059 | (cause) => |
| 7064 | new ProviderAdapterInterruptError({ | |
| 7060 | new ProviderAdapter.ProviderAdapterInterruptError({ | |
| 7065 | 7061 | driver: CODEX_PROVIDER, |
| 7066 | 7062 | providerThreadId: requestedInput.providerThread.id, |
| 7067 | 7063 | providerTurnId: requestedInput.providerTurnId, |
| ... | ... | @@ -7075,7 +7071,7 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi |
| 7075 | 7071 | String(requestInput.requestId), |
| 7076 | 7072 | ); |
| 7077 | 7073 | if (pending === undefined) { |
| 7078 | return yield* new ProviderAdapterRuntimeRequestResponseError({ | |
| 7074 | return yield* new ProviderAdapter.ProviderAdapterRuntimeRequestResponseError({ | |
| 7079 | 7075 | driver: CODEX_PROVIDER, |
| 7080 | 7076 | requestId: requestInput.requestId, |
| 7081 | 7077 | cause: toProtocolError( |
| ... | ... | @@ -7085,7 +7081,7 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi |
| 7085 | 7081 | } |
| 7086 | 7082 | if (pending.type === "user_input") { |
| 7087 | 7083 | if (requestInput.answers === undefined) { |
| 7088 | return yield* new ProviderAdapterRuntimeRequestResponseError({ | |
| 7084 | return yield* new ProviderAdapter.ProviderAdapterRuntimeRequestResponseError({ | |
| 7089 | 7085 | driver: CODEX_PROVIDER, |
| 7090 | 7086 | requestId: requestInput.requestId, |
| 7091 | 7087 | cause: toProtocolError( |
| ... | ... | @@ -7097,7 +7093,7 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi |
| 7097 | 7093 | return; |
| 7098 | 7094 | } |
| 7099 | 7095 | if (requestInput.decision === undefined) { |
| 7100 | return yield* new ProviderAdapterRuntimeRequestResponseError({ | |
| 7096 | return yield* new ProviderAdapter.ProviderAdapterRuntimeRequestResponseError({ | |
| 7101 | 7097 | driver: CODEX_PROVIDER, |
| 7102 | 7098 | requestId: requestInput.requestId, |
| 7103 | 7099 | cause: toProtocolError( |
| ... | ... | @@ -7110,7 +7106,7 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi |
| 7110 | 7106 | Effect.mapError((cause) => |
| 7111 | 7107 | isProviderAdapterRuntimeRequestResponseError(cause) |
| 7112 | 7108 | ? cause |
| 7113 | : new ProviderAdapterRuntimeRequestResponseError({ | |
| 7109 | : new ProviderAdapter.ProviderAdapterRuntimeRequestResponseError({ | |
| 7114 | 7110 | driver: CODEX_PROVIDER, |
| 7115 | 7111 | requestId: requestInput.requestId, |
| 7116 | 7112 | cause, |
| ... | ... | @@ -7134,7 +7130,7 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi |
| 7134 | 7130 | }).pipe( |
| 7135 | 7131 | Effect.mapError( |
| 7136 | 7132 | (cause) => |
| 7137 | new ProviderAdapterProtocolError({ | |
| 7133 | new ProviderAdapter.ProviderAdapterProtocolError({ | |
| 7138 | 7134 | driver: CODEX_PROVIDER, |
| 7139 | 7135 | detail: "Failed to upload Codex thread feedback.", |
| 7140 | 7136 | payload: cause, |
| ... | ... | @@ -7167,7 +7163,7 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi |
| 7167 | 7163 | }).pipe( |
| 7168 | 7164 | Effect.mapError( |
| 7169 | 7165 | (cause) => |
| 7170 | new ProviderAdapterReadThreadSnapshotError({ | |
| 7166 | new ProviderAdapter.ProviderAdapterReadThreadSnapshotError({ | |
| 7171 | 7167 | driver: CODEX_PROVIDER, |
| 7172 | 7168 | providerThreadId: threadInput.providerThread.id, |
| 7173 | 7169 | cause, |
| ... | ... | @@ -7200,7 +7196,7 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi |
| 7200 | 7196 | Effect.andThen(readCodexThreadHistoryMetadata(client.raw, threadId)), |
| 7201 | 7197 | ); |
| 7202 | 7198 | if (historyMode !== "paginated") { |
| 7203 | return yield* new ProviderAdapterRollbackThreadError({ | |
| 7199 | return yield* new ProviderAdapter.ProviderAdapterRollbackThreadError({ | |
| 7204 | 7200 | driver: CODEX_PROVIDER, |
| 7205 | 7201 | providerThreadId: threadInput.providerThread.id, |
| 7206 | 7202 | cause: `Cannot roll back Codex thread ${threadId}: the thread uses legacy history, which Codex 0.156 cannot revert.`, |
| ... | ... | @@ -7244,7 +7240,7 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi |
| 7244 | 7240 | }).pipe( |
| 7245 | 7241 | Effect.mapError( |
| 7246 | 7242 | (cause) => |
| 7247 | new ProviderAdapterRollbackThreadError({ | |
| 7243 | new ProviderAdapter.ProviderAdapterRollbackThreadError({ | |
| 7248 | 7244 | driver: CODEX_PROVIDER, |
| 7249 | 7245 | providerThreadId: threadInput.providerThread.id, |
| 7250 | 7246 | cause: normalizeCodexCause(cause), |
| ... | ... | @@ -7275,7 +7271,7 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi |
| 7275 | 7271 | ), |
| 7276 | 7272 | Effect.mapError( |
| 7277 | 7273 | (cause) => |
| 7278 | new ProviderAdapterForkThreadError({ | |
| 7274 | new ProviderAdapter.ProviderAdapterForkThreadError({ | |
| 7279 | 7275 | driver: CODEX_PROVIDER, |
| 7280 | 7276 | providerThreadId: threadInput.sourceProviderThread.id, |
| 7281 | 7277 | cause: normalizeCodexCause(cause), |
| ... | ... | @@ -7291,7 +7287,7 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi |
| 7291 | 7287 | Effect.andThen(readCodexThreadHistoryMetadata(client.raw, response.thread.id)), |
| 7292 | 7288 | ); |
| 7293 | 7289 | if (historyMode !== "paginated") { |
| 7294 | return yield* new ProviderAdapterForkThreadError({ | |
| 7290 | return yield* new ProviderAdapter.ProviderAdapterForkThreadError({ | |
| 7295 | 7291 | driver: CODEX_PROVIDER, |
| 7296 | 7292 | providerThreadId: threadInput.sourceProviderThread.id, |
| 7297 | 7293 | cause: `Cannot fork Codex thread ${threadId} at provider turn ${threadInput.providerTurnId}: the source turn has no native Codex turn reference, and the forked thread uses legacy history which Codex 0.156 cannot revert.`, |
| ... | ... | @@ -7303,7 +7299,7 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi |
| 7303 | 7299 | ), |
| 7304 | 7300 | Effect.mapError( |
| 7305 | 7301 | (cause) => |
| 7306 | new ProviderAdapterForkThreadError({ | |
| 7302 | new ProviderAdapter.ProviderAdapterForkThreadError({ | |
| 7307 | 7303 | driver: CODEX_PROVIDER, |
| 7308 | 7304 | providerThreadId: threadInput.sourceProviderThread.id, |
| 7309 | 7305 | cause: normalizeCodexCause(cause), |
| ... | ... | @@ -7328,7 +7324,7 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi |
| 7328 | 7324 | }).pipe( |
| 7329 | 7325 | Effect.mapError( |
| 7330 | 7326 | (cause) => |
| 7331 | new ProviderAdapterForkThreadError({ | |
| 7327 | new ProviderAdapter.ProviderAdapterForkThreadError({ | |
| 7332 | 7328 | driver: CODEX_PROVIDER, |
| 7333 | 7329 | providerThreadId: threadInput.sourceProviderThread.id, |
| 7334 | 7330 | cause: normalizeCodexCause(cause), |
| ... | ... | @@ -7340,7 +7336,7 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi |
| 7340 | 7336 | }).pipe( |
| 7341 | 7337 | Effect.mapError( |
| 7342 | 7338 | (cause) => |
| 7343 | new ProviderAdapterOpenSessionError({ | |
| 7339 | new ProviderAdapter.ProviderAdapterOpenSessionError({ | |
| 7344 | 7340 | driver: CODEX_PROVIDER, |
| 7345 | 7341 | providerSessionId: input.providerSessionId, |
| 7346 | 7342 | cause, |
apps/server/src/orchestration-v2/Adapters/CursorAdapterV2.testkit.ts+2-2| ... | ... | @@ -28,7 +28,7 @@ import { |
| 28 | 28 | cursorSdkModelSelection, |
| 29 | 29 | makeCursorAgentOptions, |
| 30 | 30 | } from "@t3tools/provider-cursor/testing"; |
| 31 | import type { ProviderAdapterV2RuntimePolicy } from "@t3tools/provider-core/server/ProviderAdapter"; | |
| 31 | import type * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter"; | |
| 32 | 32 | import type { RuntimePolicyV2Override } from "../RuntimePolicy.ts"; |
| 33 | 33 | |
| 34 | 34 | const CursorAgentSdkReplayTranscript = Schema.Struct({ |
| ... | ... | @@ -625,7 +625,7 @@ function recordingRuntimePolicy(input: { |
| 625 | 625 | readonly cwd: string; |
| 626 | 626 | readonly interactionMode: "default" | "plan"; |
| 627 | 627 | readonly override?: Pick<RuntimePolicyV2Override, "approvalPolicy" | "sandboxPolicy">; |
| 628 | }): ProviderAdapterV2RuntimePolicy { | |
| 628 | }): ProviderAdapter.ProviderAdapterV2RuntimePolicy { | |
| 629 | 629 | return { |
| 630 | 630 | runtimeMode: "full-access", |
| 631 | 631 | interactionMode: input.interactionMode, |
apps/server/src/orchestration-v2/Adapters/GrokAdapterV2.test.ts+5-5| ... | ... | @@ -26,7 +26,7 @@ import { buildInitialGrokProviderSnapshot } from "@t3tools/provider-grok/testing |
| 26 | 26 | import type { ProviderInstance } from "@t3tools/provider-core/server/driver"; |
| 27 | 27 | import * as ProviderInstanceRegistry from "../../provider/ProviderInstanceRegistry.ts"; |
| 28 | 28 | import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator"; |
| 29 | import { ProviderAdapterV2RuntimePolicy } from "@t3tools/provider-core/server/ProviderAdapter"; | |
| 29 | import * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter"; | |
| 30 | 30 | import * as RuntimePolicy from "../RuntimePolicy.ts"; |
| 31 | 31 | import { acpPermissionDisposition } from "@t3tools/provider-acp/server/clientPolicy"; |
| 32 | 32 | import { |
| ... | ... | @@ -68,7 +68,7 @@ function runtimePolicy(input: { |
| 68 | 68 | readonly approvalPolicy?: unknown; |
| 69 | 69 | readonly sandboxPolicy?: unknown; |
| 70 | 70 | }) { |
| 71 | return ProviderAdapterV2RuntimePolicy.make({ | |
| 71 | return ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({ | |
| 72 | 72 | runtimeMode: input.runtimeMode, |
| 73 | 73 | interactionMode: "default", |
| 74 | 74 | cwd: "/workspace", |
| ... | ... | @@ -314,7 +314,7 @@ describe("Grok launch permission mode", () => { |
| 314 | 314 | // Opens a session through the adapter's own Grok runtime factory and returns |
| 315 | 315 | // the argv it tried to launch. The spawn fails after recording, so no |
| 316 | 316 | // process starts. |
| 317 | const launchArgs = (runtimePolicy: ProviderAdapterV2RuntimePolicy) => | |
| 317 | const launchArgs = (runtimePolicy: ProviderAdapter.ProviderAdapterV2RuntimePolicy) => | |
| 318 | 318 | Effect.gen(function* () { |
| 319 | 319 | const launches: Array<ReadonlyArray<string>> = []; |
| 320 | 320 | const childProcessSpawner = ChildProcessSpawner.make((command) => { |
| ... | ... | @@ -352,9 +352,9 @@ describe("Grok launch permission mode", () => { |
| 352 | 352 | |
| 353 | 353 | const policy = ( |
| 354 | 354 | runtimeMode: RuntimeMode, |
| 355 | override: Partial<ProviderAdapterV2RuntimePolicy> = {}, | |
| 355 | override: Partial<ProviderAdapter.ProviderAdapterV2RuntimePolicy> = {}, | |
| 356 | 356 | ) => |
| 357 | ProviderAdapterV2RuntimePolicy.make({ | |
| 357 | ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({ | |
| 358 | 358 | runtimeMode, |
| 359 | 359 | interactionMode: "default", |
| 360 | 360 | cwd: process.cwd(), |
apps/server/src/orchestration-v2/Adapters/GrokAdapterV2.testkit.ts+1-1| ... | ... | @@ -13,7 +13,7 @@ import { layerTestProviderHost } from "@t3tools/provider-testing/host"; |
| 13 | 13 | import { GROK_ACP_CANCEL_META, GROK_ACP_INITIALIZE_META } from "@t3tools/provider-grok/testing"; |
| 14 | 14 | import { makeXAiPromptCompletionRuntime } from "@t3tools/provider-grok/testing"; |
| 15 | 15 | import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator"; |
| 16 | import * as ProviderContinuationRequests from "@t3tools/provider-core/server/continuationRequests"; | |
| 16 | import * as ProviderContinuationRequests from "@t3tools/provider-core/server/ProviderContinuationRequests"; | |
| 17 | 17 | import * as ProviderAdapterRegistry from "../ProviderAdapterRegistry.ts"; |
| 18 | 18 | import type { ProviderReplayGate } from "@t3tools/provider-testing/replayGate"; |
| 19 | 19 | import type { OrchestratorV2ProviderReplayHarness } from "../testkit/ProviderReplayHarness.ts"; |
apps/server/src/orchestration-v2/Adapters/MuseAdapterV2.testkit.ts+1-1| ... | ... | @@ -36,7 +36,7 @@ import { |
| 36 | 36 | } from "@t3tools/provider-muse/testing"; |
| 37 | 37 | import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator"; |
| 38 | 38 | import * as ProviderAdapterRegistry from "../ProviderAdapterRegistry.ts"; |
| 39 | import * as ProviderContinuationRequests from "@t3tools/provider-core/server/continuationRequests"; | |
| 39 | import * as ProviderContinuationRequests from "@t3tools/provider-core/server/ProviderContinuationRequests"; | |
| 40 | 40 | import type { OrchestratorV2ProviderReplayHarness } from "../testkit/ProviderReplayHarness.ts"; |
| 41 | 41 | import { makeMuseAdapterV2 } from "@t3tools/provider-muse/server"; |
| 42 | 42 |
apps/server/src/orchestration-v2/Adapters/OpenCode2AdapterV2.test.ts+36-24| ... | ... | @@ -38,14 +38,10 @@ import * as Exit from "effect/Exit"; |
| 38 | 38 | import { TestClock } from "effect/testing"; |
| 39 | 39 | import { describe } from "vite-plus/test"; |
| 40 | 40 | |
| 41 | import type { | |
| 42 | ProviderAdapterV2Event, | |
| 43 | ProviderAdapterV2SessionRuntime, | |
| 44 | } from "@t3tools/provider-core/server/ProviderAdapter"; | |
| 41 | import type * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter"; | |
| 45 | 42 | import * as McpProviderSession from "@t3tools/provider-core/server/mcpSession"; |
| 46 | 43 | import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator"; |
| 47 | import type { ProviderContinuationRequest } from "@t3tools/provider-core/server/continuationRequests"; | |
| 48 | import * as ProviderContinuationRequests from "@t3tools/provider-core/server/continuationRequests"; | |
| 44 | import * as ProviderContinuationRequests from "@t3tools/provider-core/server/ProviderContinuationRequests"; | |
| 49 | 45 | import { |
| 50 | 46 | OPENCODE_2_STILL_STOPPING, |
| 51 | 47 | OPENCODE_PROVIDER, |
| ... | ... | @@ -347,21 +343,27 @@ const resumed = ( |
| 347 | 343 | return { runtime, thread }; |
| 348 | 344 | }); |
| 349 | 345 | |
| 350 | const requestOf = (runtime: ProviderAdapterV2SessionRuntime) => | |
| 346 | const requestOf = (runtime: ProviderAdapter.ProviderAdapterV2SessionRuntime) => | |
| 351 | 347 | runtime.events.pipe( |
| 352 | 348 | Stream.filter( |
| 353 | (event): event is Extract<ProviderAdapterV2Event, { type: "runtime_request.updated" }> => | |
| 354 | event.type === "runtime_request.updated", | |
| 349 | ( | |
| 350 | event, | |
| 351 | ): event is Extract< | |
| 352 | ProviderAdapter.ProviderAdapterV2Event, | |
| 353 | { type: "runtime_request.updated" } | |
| 354 | > => event.type === "runtime_request.updated", | |
| 355 | 355 | ), |
| 356 | 356 | Stream.map((event) => event.runtimeRequest), |
| 357 | 357 | Stream.runHead, |
| 358 | 358 | Effect.map(Option.getOrUndefined), |
| 359 | 359 | ); |
| 360 | 360 | |
| 361 | const terminalOf = (runtime: ProviderAdapterV2SessionRuntime) => | |
| 361 | const terminalOf = (runtime: ProviderAdapter.ProviderAdapterV2SessionRuntime) => | |
| 362 | 362 | runtime.events.pipe( |
| 363 | 363 | Stream.filter( |
| 364 | (event): event is Extract<ProviderAdapterV2Event, { type: "turn.terminal" }> => | |
| 364 | ( | |
| 365 | event, | |
| 366 | ): event is Extract<ProviderAdapter.ProviderAdapterV2Event, { type: "turn.terminal" }> => | |
| 365 | 367 | event.type === "turn.terminal", |
| 366 | 368 | ), |
| 367 | 369 | Stream.runHead, |
| ... | ... | @@ -637,7 +639,7 @@ describe("OpenCode2 adapter", () => { |
| 637 | 639 | * The single reader of the runtime's events: resolves `attached` once the |
| 638 | 640 | * running background child has its thread, then returns the turn's terminal. |
| 639 | 641 | */ |
| 640 | const watchBackgroundTurn = (runtime: ProviderAdapterV2SessionRuntime) => | |
| 642 | const watchBackgroundTurn = (runtime: ProviderAdapter.ProviderAdapterV2SessionRuntime) => | |
| 641 | 643 | Effect.gen(function* () { |
| 642 | 644 | const attached = yield* Deferred.make<void>(); |
| 643 | 645 | const terminal = yield* runtime.events.pipe( |
| ... | ... | @@ -647,7 +649,9 @@ describe("OpenCode2 adapter", () => { |
| 647 | 649 | : Effect.void, |
| 648 | 650 | ), |
| 649 | 651 | Stream.filter( |
| 650 | (event): event is Extract<ProviderAdapterV2Event, { type: "turn.terminal" }> => | |
| 652 | ( | |
| 653 | event, | |
| 654 | ): event is Extract<ProviderAdapter.ProviderAdapterV2Event, { type: "turn.terminal" }> => | |
| 651 | 655 | event.type === "turn.terminal", |
| 652 | 656 | ), |
| 653 | 657 | Stream.runHead, |
| ... | ... | @@ -767,7 +771,9 @@ describe("OpenCode2 adapter", () => { |
| 767 | 771 | : Effect.void, |
| 768 | 772 | ), |
| 769 | 773 | Stream.filter( |
| 770 | (event): event is Extract<ProviderAdapterV2Event, { type: "turn.terminal" }> => | |
| 774 | ( | |
| 775 | event, | |
| 776 | ): event is Extract<ProviderAdapter.ProviderAdapterV2Event, { type: "turn.terminal" }> => | |
| 771 | 777 | event.type === "turn.terminal", |
| 772 | 778 | ), |
| 773 | 779 | Stream.runHead, |
| ... | ... | @@ -809,7 +815,9 @@ describe("OpenCode2 adapter", () => { |
| 809 | 815 | const firstEnded = yield* Deferred.make<void>(); |
| 810 | 816 | const ended = yield* runtime.events.pipe( |
| 811 | 817 | Stream.filter( |
| 812 | (event): event is Extract<ProviderAdapterV2Event, { type: "turn.terminal" }> => | |
| 818 | ( | |
| 819 | event, | |
| 820 | ): event is Extract<ProviderAdapter.ProviderAdapterV2Event, { type: "turn.terminal" }> => | |
| 813 | 821 | event.type === "turn.terminal", |
| 814 | 822 | ), |
| 815 | 823 | Stream.tap(() => Deferred.succeed(firstEnded, undefined)), |
| ... | ... | @@ -1110,7 +1118,7 @@ describe("OpenCode2 adapter", () => { |
| 1110 | 1118 | event("session.execution.succeeded", { sessionID: SESSION }), |
| 1111 | 1119 | ]; |
| 1112 | 1120 | /** The runtime's events, with the background subagent's statuses and the middle session's turns. */ |
| 1113 | const watchNested = (runtime: ProviderAdapterV2SessionRuntime) => | |
| 1121 | const watchNested = (runtime: ProviderAdapter.ProviderAdapterV2SessionRuntime) => | |
| 1114 | 1122 | Effect.gen(function* () { |
| 1115 | 1123 | const deep: Array<string> = []; |
| 1116 | 1124 | const middleTurns = new Map<string, string>(); |
| ... | ... | @@ -1325,7 +1333,7 @@ describe("OpenCode2 adapter", () => { |
| 1325 | 1333 | attemptId: RunAttemptId.make("attempt:opencode2-adapter:2"), |
| 1326 | 1334 | }); |
| 1327 | 1335 | const stopFirstTurn = ( |
| 1328 | runtime: ProviderAdapterV2SessionRuntime, | |
| 1336 | runtime: ProviderAdapter.ProviderAdapterV2SessionRuntime, | |
| 1329 | 1337 | thread: OrchestrationV2ProviderThread, |
| 1330 | 1338 | ) => |
| 1331 | 1339 | Effect.gen(function* () { |
| ... | ... | @@ -1336,10 +1344,12 @@ describe("OpenCode2 adapter", () => { |
| 1336 | 1344 | yield* TestClock.adjust("11 seconds"); |
| 1337 | 1345 | yield* Fiber.join(interrupt); |
| 1338 | 1346 | }); |
| 1339 | const terminals = (runtime: ProviderAdapterV2SessionRuntime, count: number) => | |
| 1347 | const terminals = (runtime: ProviderAdapter.ProviderAdapterV2SessionRuntime, count: number) => | |
| 1340 | 1348 | runtime.events.pipe( |
| 1341 | 1349 | Stream.filter( |
| 1342 | (event): event is Extract<ProviderAdapterV2Event, { type: "turn.terminal" }> => | |
| 1350 | ( | |
| 1351 | event, | |
| 1352 | ): event is Extract<ProviderAdapter.ProviderAdapterV2Event, { type: "turn.terminal" }> => | |
| 1343 | 1353 | event.type === "turn.terminal", |
| 1344 | 1354 | ), |
| 1345 | 1355 | Stream.take(count), |
| ... | ... | @@ -2113,7 +2123,9 @@ describe("OpenCode2 adapter", () => { |
| 2113 | 2123 | }), |
| 2114 | 2124 | ), |
| 2115 | 2125 | Stream.filter( |
| 2116 | (event): event is Extract<ProviderAdapterV2Event, { type: "turn.terminal" }> => | |
| 2126 | ( | |
| 2127 | event, | |
| 2128 | ): event is Extract<ProviderAdapter.ProviderAdapterV2Event, { type: "turn.terminal" }> => | |
| 2117 | 2129 | event.type === "turn.terminal", |
| 2118 | 2130 | ), |
| 2119 | 2131 | Stream.tap(() => Deferred.succeed(firstEnded, undefined)), |
| ... | ... | @@ -2331,7 +2343,7 @@ describe("OpenCode2 adapter", () => { |
| 2331 | 2343 | replyData("session.form.list", forms), |
| 2332 | 2344 | ]; |
| 2333 | 2345 | |
| 2334 | const turnItems = (collected: ReadonlyArray<ProviderAdapterV2Event>) => | |
| 2346 | const turnItems = (collected: ReadonlyArray<ProviderAdapter.ProviderAdapterV2Event>) => | |
| 2335 | 2347 | collected.flatMap((event) => |
| 2336 | 2348 | event.type === "turn_item.updated" ? [`${event.turnItem.type}:${event.turnItem.status}`] : [], |
| 2337 | 2349 | ); |
| ... | ... | @@ -3043,7 +3055,7 @@ describe("OpenCode2 adapter", () => { |
| 3043 | 3055 | Effect.gen(function* () { |
| 3044 | 3056 | const kept = "msg_t3_turn_run-attempt:kept:1"; |
| 3045 | 3057 | const dropped = "msg_t3_turn_run-attempt:dropped:1"; |
| 3046 | const offers: Array<ProviderContinuationRequest> = []; | |
| 3058 | const offers: Array<ProviderContinuationRequests.ProviderContinuationRequest> = []; | |
| 3047 | 3059 | const { runtime, thread } = yield* resumed([ |
| 3048 | 3060 | out("message.list", "<any>"), |
| 3049 | 3061 | reply("message.list", { |
| ... | ... | @@ -3727,7 +3739,7 @@ describe("OpenCode2 adapter", () => { |
| 3727 | 3739 | */ |
| 3728 | 3740 | const continued = (after: ReadonlyArray<ProviderReplayEntry>) => |
| 3729 | 3741 | Effect.gen(function* () { |
| 3730 | const offers: Array<ProviderContinuationRequest> = []; | |
| 3742 | const offers: Array<ProviderContinuationRequests.ProviderContinuationRequest> = []; | |
| 3731 | 3743 | const { runtime, thread } = yield* resumed([ |
| 3732 | 3744 | ...backgroundLaunch(CHILD), |
| 3733 | 3745 | event("session.execution.succeeded", { sessionID: SESSION }), |
| ... | ... | @@ -3867,7 +3879,7 @@ describe("OpenCode2 adapter", () => { |
| 3867 | 3879 | Effect.gen(function* () { |
| 3868 | 3880 | const reportText = `<subagent sessionID="${CHILD}" state="completed" description="Sleep">\nCHILD_OK\n</subagent>`; |
| 3869 | 3881 | const steerId = `msg_t3_steer_${SESSION}:message:opencode2-adapter:steer`; |
| 3870 | const offers: Array<ProviderContinuationRequest> = []; | |
| 3882 | const offers: Array<ProviderContinuationRequests.ProviderContinuationRequest> = []; | |
| 3871 | 3883 | const { runtime, thread } = yield* resumed([ |
| 3872 | 3884 | ...backgroundLaunch(CHILD), |
| 3873 | 3885 | event("session.execution.succeeded", { sessionID: SESSION }), |
apps/server/src/orchestration-v2/Adapters/OpenCodeAdapterV2.testkit.ts+1-1| ... | ... | @@ -9,7 +9,7 @@ import * as Schema from "effect/Schema"; |
| 9 | 9 | import { OpenCodeAdapterV2Driver } from "@t3tools/provider-opencode/server"; |
| 10 | 10 | import * as OpenCodeRuntime from "@t3tools/provider-opencode/server/OpenCodeRuntime"; |
| 11 | 11 | import { layerTestProviderHost } from "@t3tools/provider-testing/host"; |
| 12 | import * as ProviderEventLoggers from "../../provider/ProviderEventLoggers.ts"; | |
| 12 | import * as ProviderEventLoggers from "@t3tools/provider-core/server/ProviderEventLoggers"; | |
| 13 | 13 | import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator"; |
| 14 | 14 | import { ProviderAdapterDriverCreateError } from "@t3tools/provider-core/server/adapterDriver"; |
| 15 | 15 | import * as ProviderAdapterRegistry from "../ProviderAdapterRegistry.ts"; |
apps/server/src/orchestration-v2/BackgroundWorkStop.integration.test.ts+5-10| ... | ... | @@ -24,16 +24,11 @@ import { CodexProviderCapabilitiesV2 } from "./Adapters/CodexAdapterV2.ts"; |
| 24 | 24 | import * as EffectWorker from "./EffectWorker.ts"; |
| 25 | 25 | import * as EventSink from "./EventSink.ts"; |
| 26 | 26 | import * as Orchestrator from "./Orchestrator.ts"; |
| 27 | import type { | |
| 28 | ProviderAdapterV2Event, | |
| 29 | ProviderAdapterV2InterruptInput, | |
| 30 | ProviderAdapterV2Shape, | |
| 31 | ProviderAdapterV2TurnInput, | |
| 32 | } from "@t3tools/provider-core/server/ProviderAdapter"; | |
| 33 | 27 | import * as ProviderAdapterRegistry from "./ProviderAdapterRegistry.ts"; |
| 34 | 28 | import * as ProviderSessionManager from "./ProviderSessionManager.ts"; |
| 35 | 29 | import * as ProviderReplayHarness from "./testkit/ProviderReplayHarness.ts"; |
| 36 | 30 | import { checkpointWorkspace } from "@t3tools/provider-testing/replayWorkspace"; |
| 31 | import type * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter"; | |
| 37 | 32 | |
| 38 | 33 | const driver = ProviderDriverKind.make("codex"); |
| 39 | 34 | const instanceId = ProviderInstanceId.make("codex"); |
| ... | ... | @@ -61,10 +56,10 @@ const stopEarlierBackgroundWork = ({ |
| 61 | 56 | Effect.scoped( |
| 62 | 57 | Effect.gen(function* () { |
| 63 | 58 | const cwd = yield* checkpointWorkspace("background-work-stop"); |
| 64 | const events = yield* Queue.unbounded<ProviderAdapterV2Event>(); | |
| 65 | const started: ProviderAdapterV2TurnInput[] = []; | |
| 66 | const interrupts: ProviderAdapterV2InterruptInput[] = []; | |
| 67 | const adapter: ProviderAdapterV2Shape = { | |
| 59 | const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>(); | |
| 60 | const started: ProviderAdapter.ProviderAdapterV2TurnInput[] = []; | |
| 61 | const interrupts: ProviderAdapter.ProviderAdapterV2InterruptInput[] = []; | |
| 62 | const adapter: ProviderAdapter.ProviderAdapterV2["Service"] = { | |
| 68 | 63 | instanceId, |
| 69 | 64 | driver, |
| 70 | 65 | getCapabilities: () => Effect.succeed(CodexProviderCapabilitiesV2), |
apps/server/src/orchestration-v2/CheckpointCaptureService.ts+1-1| ... | ... | @@ -252,7 +252,7 @@ export const layer: Layer.Layer< |
| 252 | 252 | ); |
| 253 | 253 | |
| 254 | 254 | function makeCheckpointTurnItem(input: { |
| 255 | readonly idAllocator: IdAllocator.IdAllocatorV2Shape; | |
| 255 | readonly idAllocator: IdAllocator.IdAllocatorV2["Service"]; | |
| 256 | 256 | readonly run: OrchestrationV2Run; |
| 257 | 257 | readonly rootNode: OrchestrationV2ExecutionNode; |
| 258 | 258 | readonly providerThread: OrchestrationV2ProviderThread; |
apps/server/src/orchestration-v2/CheckpointRollbackService.test.ts+2-2| ... | ... | @@ -26,7 +26,7 @@ import * as EventSink from "./EventSink.ts"; |
| 26 | 26 | import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator"; |
| 27 | 27 | import * as ProjectionStore from "./ProjectionStore.ts"; |
| 28 | 28 | import * as ProjectStore from "./ProjectStore.ts"; |
| 29 | import type { ProviderAdapterV2RollbackThreadInput } from "@t3tools/provider-core/server/ProviderAdapter"; | |
| 29 | import type * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter"; | |
| 30 | 30 | import * as ProviderSessionManager from "./ProviderSessionManager.ts"; |
| 31 | 31 | import * as RuntimePolicy from "./RuntimePolicy.ts"; |
| 32 | 32 | |
| ... | ... | @@ -457,7 +457,7 @@ it.effect.each([ |
| 457 | 457 | Layer.mock(ProviderSessionManager.ProviderSessionManagerV2)({ |
| 458 | 458 | open: () => |
| 459 | 459 | Effect.succeed({ |
| 460 | rollbackThread: (input: ProviderAdapterV2RollbackThreadInput) => | |
| 460 | rollbackThread: (input: ProviderAdapter.ProviderAdapterV2RollbackThreadInput) => | |
| 461 | 461 | Effect.gen(function* () { |
| 462 | 462 | const count = yield* resolveCodexRollbackTurnCount(input); |
| 463 | 463 | assert.equal(count, 2 - targetOrdinal); |
apps/server/src/orchestration-v2/CheckpointRollbackService.ts+5-5| ... | ... | @@ -20,10 +20,10 @@ import { |
| 20 | 20 | } from "./CheckpointRestoreSafety.ts"; |
| 21 | 21 | import { CheckpointServiceV2 } from "./CheckpointService.ts"; |
| 22 | 22 | import { EventSinkV2 } from "./EventSink.ts"; |
| 23 | import { IdAllocatorV2 } from "@t3tools/provider-core/server/IdAllocator"; | |
| 23 | import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator"; | |
| 24 | 24 | import { ProjectionStoreV2 } from "./ProjectionStore.ts"; |
| 25 | 25 | import * as ProjectStore from "./ProjectStore.ts"; |
| 26 | import type { ProviderAdapterV2RollbackTarget } from "@t3tools/provider-core/server/ProviderAdapter"; | |
| 26 | import type * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter"; | |
| 27 | 27 | import { ProviderSessionManagerV2 } from "./ProviderSessionManager.ts"; |
| 28 | 28 | import { RuntimePolicyV2 } from "./RuntimePolicy.ts"; |
| 29 | 29 | import * as ThreadCommandExecutor from "./ThreadCommandExecutor.ts"; |
| ... | ... | @@ -85,7 +85,7 @@ export const layer: Layer.Layer< |
| 85 | 85 | never, |
| 86 | 86 | | CheckpointServiceV2 |
| 87 | 87 | | EventSinkV2 |
| 88 | | IdAllocatorV2 | |
| 88 | | IdAllocator.IdAllocatorV2 | |
| 89 | 89 | | ProjectionStoreV2 |
| 90 | 90 | | ProviderSessionManagerV2 |
| 91 | 91 | | RuntimePolicyV2 |
| ... | ... | @@ -98,7 +98,7 @@ export const layer: Layer.Layer< |
| 98 | 98 | Effect.gen(function* () { |
| 99 | 99 | const checkpoints = yield* CheckpointServiceV2; |
| 100 | 100 | const eventSink = yield* EventSinkV2; |
| 101 | const ids = yield* IdAllocatorV2; | |
| 101 | const ids = yield* IdAllocator.IdAllocatorV2; | |
| 102 | 102 | const projections = yield* ProjectionStoreV2; |
| 103 | 103 | const sessions = yield* ProviderSessionManagerV2; |
| 104 | 104 | const runtimePolicy = yield* RuntimePolicyV2; |
| ... | ... | @@ -225,7 +225,7 @@ export const layer: Layer.Layer< |
| 225 | 225 | turn.providerThreadId === providerThread.id && |
| 226 | 226 | (turn.runAttemptId === null || !rolledBackAttemptIds.has(turn.runAttemptId)), |
| 227 | 227 | ); |
| 228 | const rollbackTarget: ProviderAdapterV2RollbackTarget = | |
| 228 | const rollbackTarget: ProviderAdapter.ProviderAdapterV2RollbackTarget = | |
| 229 | 229 | targetOrdinal === 0 |
| 230 | 230 | ? { |
| 231 | 231 | type: "thread_start", |
apps/server/src/orchestration-v2/CheckpointService.ts+2-2| ... | ... | @@ -172,7 +172,7 @@ export const checkpointRefForScopeOrdinal = Effect.fn("checkpointRefForScopeOrdi |
| 172 | 172 | ); |
| 173 | 173 | |
| 174 | 174 | function checkpointIdForScopeOrdinal( |
| 175 | idAllocator: IdAllocator.IdAllocatorV2Shape, | |
| 175 | idAllocator: IdAllocator.IdAllocatorV2["Service"], | |
| 176 | 176 | input: { |
| 177 | 177 | readonly scopeId: CheckpointScopeId; |
| 178 | 178 | readonly ordinalWithinScope: number; |
| ... | ... | @@ -185,7 +185,7 @@ function checkpointIdForScopeOrdinal( |
| 185 | 185 | } |
| 186 | 186 | |
| 187 | 187 | function makeRootRunScope(input: { |
| 188 | readonly idAllocator: IdAllocator.IdAllocatorV2Shape; | |
| 188 | readonly idAllocator: IdAllocator.IdAllocatorV2["Service"]; | |
| 189 | 189 | readonly threadId: ThreadId; |
| 190 | 190 | readonly runId: RunId; |
| 191 | 191 | readonly rootNodeId: NodeId; |
apps/server/src/orchestration-v2/ContextHandoffBudget.test.ts+3-3| ... | ... | @@ -1,4 +1,4 @@ |
| 1 | import type { ProviderAdapterV2HistoricalContext } from "@t3tools/provider-core/server/ProviderAdapter"; | |
| 1 | import type * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter"; | |
| 2 | 2 | import { assert, describe, it } from "@effect/vitest"; |
| 3 | 3 | import { |
| 4 | 4 | ContextHandoffId, |
| ... | ... | @@ -501,7 +501,7 @@ describe("handoff delivery", () => { |
| 501 | 501 | providerThread, |
| 502 | 502 | budget: 16_000, |
| 503 | 503 | alreadyDeliveredItemIds: new Set<string>(), |
| 504 | inject: (value: ProviderAdapterV2HistoricalContext) => | |
| 504 | inject: (value: ProviderAdapter.ProviderAdapterV2HistoricalContext) => | |
| 505 | 505 | Effect.sync(() => { |
| 506 | 506 | history.push(...historyResponseItems(value.messages, value.context)); |
| 507 | 507 | return true; |
| ... | ... | @@ -588,7 +588,7 @@ describe("handoff delivery", () => { |
| 588 | 588 | ...handoff, |
| 589 | 589 | id: ContextHandoffId.make(`handoff:retry:${index}`), |
| 590 | 590 | })); |
| 591 | let captured: ProviderAdapterV2HistoricalContext | undefined; | |
| 591 | let captured: ProviderAdapter.ProviderAdapterV2HistoricalContext | undefined; | |
| 592 | 592 | const result = yield* deliverContextHandoffs({ |
| 593 | 593 | handoffs: many, |
| 594 | 594 | providerThread, |
apps/server/src/orchestration-v2/ContextHandoffDelivery.ts+2-2| ... | ... | @@ -2,7 +2,7 @@ import type { |
| 2 | 2 | OrchestrationV2ContextHandoff, |
| 3 | 3 | OrchestrationV2ProviderThread, |
| 4 | 4 | } from "@t3tools/contracts"; |
| 5 | import type { ProviderAdapterV2HistoricalContext } from "@t3tools/provider-core/server/ProviderAdapter"; | |
| 5 | import type * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter"; | |
| 6 | 6 | import { ContextHandoffBudgetError } from "@t3tools/provider-core/server/failure"; |
| 7 | 7 | import * as Effect from "effect/Effect"; |
| 8 | 8 | import * as Schema from "effect/Schema"; |
| ... | ... | @@ -25,7 +25,7 @@ export const deliverContextHandoffs = Effect.fn("orchestrationV2.deliverContextH |
| 25 | 25 | readonly deferInline?: boolean; |
| 26 | 26 | readonly alreadyDeliveredItemIds: ReadonlySet<string>; |
| 27 | 27 | readonly inject?: ( |
| 28 | history: ProviderAdapterV2HistoricalContext, | |
| 28 | history: ProviderAdapter.ProviderAdapterV2HistoricalContext, | |
| 29 | 29 | ) => Effect.Effect<boolean, InjectError>; |
| 30 | 30 | readonly persist: (handoff: OrchestrationV2ContextHandoff) => Effect.Effect<void, PersistError>; |
| 31 | 31 | }) { |
apps/server/src/orchestration-v2/CursorOrchestratorV2.live.test.ts+1-1| ... | ... | @@ -29,7 +29,7 @@ import * as CodexInstallation from "../provider/CodexInstallation.ts"; |
| 29 | 29 | import * as ServerEnvironment from "../environment/ServerEnvironment.ts"; |
| 30 | 30 | import * as ModelManifest from "../provider/ModelManifest.ts"; |
| 31 | 31 | import * as ProviderInstanceRegistryHydration from "../provider/ProviderInstanceRegistryHydration.ts"; |
| 32 | import * as ProviderEventLoggers from "../provider/ProviderEventLoggers.ts"; | |
| 32 | import * as ProviderEventLoggers from "@t3tools/provider-core/server/ProviderEventLoggers"; | |
| 33 | 33 | import * as OpenCodeRuntime from "@t3tools/provider-opencode/server/OpenCodeRuntime"; |
| 34 | 34 | import * as OpenCodeServerLedger from "@t3tools/provider-opencode/server/OpenCodeServerLedger"; |
| 35 | 35 | import * as ServerSettings from "../serverSettings.ts"; |
apps/server/src/orchestration-v2/DelegatedCompletionDelivery.test.ts+2-2| ... | ... | @@ -36,10 +36,10 @@ import * as WorkspacePaths from "../workspace/WorkspacePaths.ts"; |
| 36 | 36 | import { CodexProviderCapabilitiesV2 } from "./Adapters/CodexAdapterV2.ts"; |
| 37 | 37 | import * as EventSink from "./EventSink.ts"; |
| 38 | 38 | import * as Orchestrator from "./Orchestrator.ts"; |
| 39 | import type { ProviderAdapterV2Shape } from "@t3tools/provider-core/server/ProviderAdapter"; | |
| 40 | 39 | import { continueRestartedRun } from "./RestartContinuation.ts"; |
| 41 | 40 | import * as RuntimeLayer from "./runtimeLayer.ts"; |
| 42 | 41 | import * as ProviderTurnStartServiceTestkit from "./ProviderTurnStartService.testkit.ts"; |
| 42 | import type * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter"; | |
| 43 | 43 | |
| 44 | 44 | const layerPlatformTest = Layer.merge( |
| 45 | 45 | NodeServices.layer, |
| ... | ... | @@ -74,7 +74,7 @@ const orchestrationAdapter = { |
| 74 | 74 | getCapabilities: () => Effect.succeed(CodexProviderCapabilitiesV2), |
| 75 | 75 | planSelectionTransition: () => Effect.succeed({ type: "apply_on_next_turn" }), |
| 76 | 76 | openSession: () => Effect.die("sessions are not used by delegated completion tests"), |
| 77 | } as ProviderAdapterV2Shape; | |
| 77 | } as ProviderAdapter.ProviderAdapterV2["Service"]; | |
| 78 | 78 | const providerInstance = { |
| 79 | 79 | instanceId: modelSelection.instanceId, |
| 80 | 80 | driverKind: driver, |
apps/server/src/orchestration-v2/DispatchModeLimit.test.ts+2-2| ... | ... | @@ -13,9 +13,9 @@ import { CodexProviderCapabilitiesV2 } from "./Adapters/CodexAdapterV2.ts"; |
| 13 | 13 | import { DispatchModeLimit } from "./DispatchModeLimit.ts"; |
| 14 | 14 | import * as Orchestrator from "./Orchestrator.ts"; |
| 15 | 15 | import * as ProjectionStore from "./ProjectionStore.ts"; |
| 16 | import type { ProviderAdapterV2Shape } from "@t3tools/provider-core/server/ProviderAdapter"; | |
| 17 | 16 | import * as ProviderAdapterRegistry from "./ProviderAdapterRegistry.ts"; |
| 18 | 17 | import * as ProviderReplayHarness from "./testkit/ProviderReplayHarness.ts"; |
| 18 | import type * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter"; | |
| 19 | 19 | |
| 20 | 20 | const instanceId = ProviderInstanceId.make("codex"); |
| 21 | 21 | const adapter = { |
| ... | ... | @@ -24,7 +24,7 @@ const adapter = { |
| 24 | 24 | getCapabilities: () => Effect.succeed(CodexProviderCapabilitiesV2), |
| 25 | 25 | planSelectionTransition: () => Effect.succeed({ type: "apply_on_next_turn" as const }), |
| 26 | 26 | openSession: () => Effect.die("No provider process needed for metadata commands"), |
| 27 | } as ProviderAdapterV2Shape; | |
| 27 | } as ProviderAdapter.ProviderAdapterV2["Service"]; | |
| 28 | 28 | const layerDatabase = SqlitePersistence.layerMemory; |
| 29 | 29 | const layerTest = Layer.mergeAll( |
| 30 | 30 | layerDatabase, |
apps/server/src/orchestration-v2/GrokOrchestratorV2.live.test.ts+1-1| ... | ... | @@ -29,7 +29,7 @@ import * as CodexInstallation from "../provider/CodexInstallation.ts"; |
| 29 | 29 | import * as ServerEnvironment from "../environment/ServerEnvironment.ts"; |
| 30 | 30 | import * as ModelManifest from "../provider/ModelManifest.ts"; |
| 31 | 31 | import * as ProviderInstanceRegistryHydration from "../provider/ProviderInstanceRegistryHydration.ts"; |
| 32 | import * as ProviderEventLoggers from "../provider/ProviderEventLoggers.ts"; | |
| 32 | import * as ProviderEventLoggers from "@t3tools/provider-core/server/ProviderEventLoggers"; | |
| 33 | 33 | import * as OpenCodeRuntime from "@t3tools/provider-opencode/server/OpenCodeRuntime"; |
| 34 | 34 | import * as OpenCodeServerLedger from "@t3tools/provider-opencode/server/OpenCodeServerLedger"; |
| 35 | 35 | import * as ServerSettings from "../serverSettings.ts"; |
apps/server/src/orchestration-v2/OpenCode2OrchestratorV2.live.test.ts+2-2| ... | ... | @@ -54,7 +54,7 @@ import * as CodexInstallation from "../provider/CodexInstallation.ts"; |
| 54 | 54 | import * as ServerEnvironment from "../environment/ServerEnvironment.ts"; |
| 55 | 55 | import * as ModelManifest from "../provider/ModelManifest.ts"; |
| 56 | 56 | import * as ProviderInstanceRegistryHydration from "../provider/ProviderInstanceRegistryHydration.ts"; |
| 57 | import * as ProviderEventLoggers from "../provider/ProviderEventLoggers.ts"; | |
| 57 | import * as ProviderEventLoggers from "@t3tools/provider-core/server/ProviderEventLoggers"; | |
| 58 | 58 | import * as OpenCode2Client from "@t3tools/provider-opencode/server/v2/OpenCode2Client"; |
| 59 | 59 | import * as OpenCodeRuntime from "@t3tools/provider-opencode/server/OpenCodeRuntime"; |
| 60 | 60 | import * as OpenCodeServerLedger from "@t3tools/provider-opencode/server/OpenCodeServerLedger"; |
| ... | ... | @@ -67,7 +67,7 @@ import * as ProviderInstanceRegistry from "../provider/ProviderInstanceRegistry. |
| 67 | 67 | import * as ProviderTurnStartServiceTestkit from "./ProviderTurnStartService.testkit.ts"; |
| 68 | 68 | import * as RuntimeLayer from "./runtimeLayer.ts"; |
| 69 | 69 | import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator"; |
| 70 | import * as ProviderContinuationRequests from "@t3tools/provider-core/server/continuationRequests"; | |
| 70 | import * as ProviderContinuationRequests from "@t3tools/provider-core/server/ProviderContinuationRequests"; | |
| 71 | 71 | import * as ProviderContinuationService from "./ProviderContinuationService.ts"; |
| 72 | 72 | import * as ThreadManagementService from "./ThreadManagementService.ts"; |
| 73 | 73 | import * as McpSessionRegistry from "../mcp/McpSessionRegistry.ts"; |
apps/server/src/orchestration-v2/Orchestrator.control-reads.test.ts+2-2| ... | ... | @@ -25,9 +25,9 @@ import * as SqlitePersistence from "../persistence/Sqlite.ts"; |
| 25 | 25 | import { CodexProviderCapabilitiesV2 } from "./Adapters/CodexAdapterV2.ts"; |
| 26 | 26 | import * as Orchestrator from "./Orchestrator.ts"; |
| 27 | 27 | import * as ProjectionStore from "./ProjectionStore.ts"; |
| 28 | import type { ProviderAdapterV2Shape } from "@t3tools/provider-core/server/ProviderAdapter"; | |
| 29 | 28 | import * as ProviderAdapterRegistry from "./ProviderAdapterRegistry.ts"; |
| 30 | 29 | import * as ProviderReplayHarness from "./testkit/ProviderReplayHarness.ts"; |
| 30 | import type * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter"; | |
| 31 | 31 | |
| 32 | 32 | const instanceId = ProviderInstanceId.make("codex"); |
| 33 | 33 | const modelSelection = { instanceId, model: "gpt-5.1-codex" }; |
| ... | ... | @@ -37,7 +37,7 @@ const adapter = { |
| 37 | 37 | getCapabilities: () => Effect.succeed(CodexProviderCapabilitiesV2), |
| 38 | 38 | planSelectionTransition: () => Effect.succeed({ type: "apply_on_next_turn" as const }), |
| 39 | 39 | openSession: () => Effect.die("No provider process needed for metadata controls"), |
| 40 | } as ProviderAdapterV2Shape; | |
| 40 | } as ProviderAdapter.ProviderAdapterV2["Service"]; | |
| 41 | 41 | const layerDatabase = SqlitePersistence.layerMemory; |
| 42 | 42 | const layerTest = Layer.mergeAll( |
| 43 | 43 | layerDatabase, |
apps/server/src/orchestration-v2/Orchestrator.ts+7-7| ... | ... | @@ -91,7 +91,7 @@ import { isUndeliveredMailboxSteer } from "./NotificationMailbox.ts"; |
| 91 | 91 | import { EventSinkV2 } from "./EventSink.ts"; |
| 92 | 92 | import * as EffectOutbox from "./EffectOutbox.ts"; |
| 93 | 93 | import type { OrchestrationEffectRequestV2, PendingOrchestrationEffectV2 } from "./EffectOutbox.ts"; |
| 94 | import { IdAllocatorV2 } from "@t3tools/provider-core/server/IdAllocator"; | |
| 94 | import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator"; | |
| 95 | 95 | import * as ThreadCommandExecutor from "./ThreadCommandExecutor.ts"; |
| 96 | 96 | import { DispatchModeLimit, exceededDispatchModeLimit } from "./DispatchModeLimit.ts"; |
| 97 | 97 | import { |
| ... | ... | @@ -107,9 +107,8 @@ import { |
| 107 | 107 | type ProjectionCheckpointContext, |
| 108 | 108 | type ShellSnapshotOptions, |
| 109 | 109 | } from "./ProjectionStore.ts"; |
| 110 | import type { ProviderAdapterV2Shape } from "@t3tools/provider-core/server/ProviderAdapter"; | |
| 111 | 110 | import { ProviderAdapterRegistryV2 } from "./ProviderAdapterRegistry.ts"; |
| 112 | import { ProviderContinuationRequests } from "@t3tools/provider-core/server/continuationRequests"; | |
| 111 | import * as ProviderContinuationRequests from "@t3tools/provider-core/server/ProviderContinuationRequests"; | |
| 113 | 112 | import { makeProviderFailure } from "@t3tools/provider-core/server/failure"; |
| 114 | 113 | import * as RunExecutionService from "./RunExecutionService.ts"; |
| 115 | 114 | import { ProviderSessionManagerV2 } from "./ProviderSessionManager.ts"; |
| ... | ... | @@ -128,6 +127,7 @@ import { |
| 128 | 127 | ThreadForkServiceV2, |
| 129 | 128 | } from "./ThreadForkService.ts"; |
| 130 | 129 | import { planThreadDeletion } from "./ThreadDeletion.ts"; |
| 130 | import type * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter"; | |
| 131 | 131 | |
| 132 | 132 | export class OrchestratorDispatchError extends Schema.TaggedError<OrchestratorDispatchError>()( |
| 133 | 133 | "OrchestratorDispatchError", |
| ... | ... | @@ -800,7 +800,7 @@ const makeOrchestrator = Effect.fn("orchestrationV2.Orchestrator.layer")(functio |
| 800 | 800 | const contextHandoffService = yield* ContextHandoffServiceV2; |
| 801 | 801 | const eventSink = yield* EventSinkV2; |
| 802 | 802 | const commandReceipts = yield* CommandReceiptStoreV2; |
| 803 | const idAllocator = yield* IdAllocatorV2; | |
| 803 | const idAllocator = yield* IdAllocator.IdAllocatorV2; | |
| 804 | 804 | const projects = yield* ProjectStore.ProjectStoreV2; |
| 805 | 805 | const projectionStore = yield* ProjectionStoreV2; |
| 806 | 806 | const effectOutbox = yield* EffectOutbox.EffectOutboxV2; |
| ... | ... | @@ -823,7 +823,7 @@ const makeOrchestrator = Effect.fn("orchestrationV2.Orchestrator.layer")(functio |
| 823 | 823 | const fileSystem = yield* FileSystem.FileSystem; |
| 824 | 824 | const path = yield* Path.Path; |
| 825 | 825 | const providerAdapters = yield* ProviderAdapterRegistryV2; |
| 826 | const continuationRequests = yield* ProviderContinuationRequests; | |
| 826 | const continuationRequests = yield* ProviderContinuationRequests.ProviderContinuationRequests; | |
| 827 | 827 | const providerSessions = yield* ProviderSessionManagerV2; |
| 828 | 828 | const providerSwitchService = yield* ProviderSwitchServiceV2; |
| 829 | 829 | const runtimePolicy = yield* RuntimePolicyV2; |
| ... | ... | @@ -857,7 +857,7 @@ const makeOrchestrator = Effect.fn("orchestrationV2.Orchestrator.layer")(functio |
| 857 | 857 | ); |
| 858 | 858 | |
| 859 | 859 | const providerSessionIdFor = (input: { |
| 860 | readonly adapter: ProviderAdapterV2Shape; | |
| 860 | readonly adapter: ProviderAdapter.ProviderAdapterV2["Service"]; | |
| 861 | 861 | readonly providerInstanceId: ProviderInstanceId; |
| 862 | 862 | readonly threadId: ThreadId; |
| 863 | 863 | }) => |
| ... | ... | @@ -10955,7 +10955,7 @@ export const layer: Layer.Layer< |
| 10955 | 10955 | | ContextHandoffServiceV2 |
| 10956 | 10956 | | EffectOutbox.EffectOutboxV2 |
| 10957 | 10957 | | EventSinkV2 |
| 10958 | | IdAllocatorV2 | |
| 10958 | | IdAllocator.IdAllocatorV2 | |
| 10959 | 10959 | | ProjectStore.ProjectStoreV2 |
| 10960 | 10960 | | ProviderAdapterRegistryV2 |
| 10961 | 10961 | | ProviderSessionManagerV2 |
apps/server/src/orchestration-v2/ProviderAdapterRegistry.test.ts+14-11| ... | ... | @@ -18,36 +18,35 @@ import * as Schema from "effect/Schema"; |
| 18 | 18 | import type * as Scope from "effect/Scope"; |
| 19 | 19 | import * as Stream from "effect/Stream"; |
| 20 | 20 | |
| 21 | import * as ProviderAuthFlow from "@t3tools/provider-core/server/ProviderAuthFlow"; | |
| 21 | import * as ProviderAuthFlow from "@t3tools/provider-core/server/providerAuthFlow"; | |
| 22 | 22 | import type { ProviderAuthController } from "../provider/ProviderAuthService.ts"; |
| 23 | 23 | import type { ProviderInstance } from "@t3tools/provider-core/server/driver"; |
| 24 | 24 | import * as ProviderInstanceRegistry from "../provider/ProviderInstanceRegistry.ts"; |
| 25 | import { | |
| 26 | ProviderAdapterOpenSessionError, | |
| 27 | type ProviderAdapterV2Shape, | |
| 28 | } from "@t3tools/provider-core/server/ProviderAdapter"; | |
| 29 | 25 | import { |
| 30 | 26 | ProviderAdapterDriverCreateError, |
| 31 | 27 | type ProviderAdapterDriver, |
| 32 | 28 | } from "@t3tools/provider-core/server/adapterDriver"; |
| 33 | 29 | import * as ProviderAdapterRegistry from "./ProviderAdapterRegistry.ts"; |
| 30 | import * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter"; | |
| 34 | 31 | |
| 35 | 32 | const driver = ProviderDriverKind.make("codex"); |
| 36 | 33 | const personalId = ProviderInstanceId.make("codex_personal"); |
| 37 | 34 | const workId = ProviderInstanceId.make("codex_work"); |
| 38 | 35 | |
| 39 | const makeAdapter = (instanceId: ProviderInstanceId): ProviderAdapterV2Shape => | |
| 36 | const makeAdapter = ( | |
| 37 | instanceId: ProviderInstanceId, | |
| 38 | ): ProviderAdapter.ProviderAdapterV2["Service"] => | |
| 40 | 39 | ({ |
| 41 | 40 | instanceId, |
| 42 | 41 | driver, |
| 43 | 42 | getCapabilities: () => Effect.die("capabilities are not used by this registry test"), |
| 44 | 43 | planSelectionTransition: () => Effect.succeed({ type: "apply_on_next_turn" }), |
| 45 | 44 | openSession: () => Effect.die("sessions are not used by this registry test"), |
| 46 | }) as ProviderAdapterV2Shape; | |
| 45 | }) as ProviderAdapter.ProviderAdapterV2["Service"]; | |
| 47 | 46 | |
| 48 | 47 | const makeInstance = ( |
| 49 | 48 | instanceId: ProviderInstanceId, |
| 50 | orchestrationAdapter: ProviderAdapterV2Shape, | |
| 49 | orchestrationAdapter: ProviderAdapter.ProviderAdapterV2["Service"], | |
| 51 | 50 | ): ProviderInstance => ({ |
| 52 | 51 | instanceId, |
| 53 | 52 | driverKind: driver, |
| ... | ... | @@ -101,7 +100,11 @@ const lifecycleConfigMap: ProviderInstanceConfigMap = { |
| 101 | 100 | const lifecycleAdapter = makeAdapter(lifecycleInstanceId); |
| 102 | 101 | |
| 103 | 102 | const makeLifecycleDriver = ( |
| 104 | create: Effect.Effect<ProviderAdapterV2Shape, ProviderAdapterDriverCreateError, Scope.Scope>, | |
| 103 | create: Effect.Effect< | |
| 104 | ProviderAdapter.ProviderAdapterV2["Service"], | |
| 105 | ProviderAdapterDriverCreateError, | |
| 106 | Scope.Scope | |
| 107 | >, | |
| 105 | 108 | ): ProviderAdapterDriver<Record<string, never>> => ({ |
| 106 | 109 | driverKind: lifecycleDriver, |
| 107 | 110 | configSchema: Schema.Struct({}), |
| ... | ... | @@ -263,7 +266,7 @@ it.effect( |
| 263 | 266 | }, |
| 264 | 267 | }) |
| 265 | 268 | .pipe(Effect.flip); |
| 266 | assert.instanceOf(error, ProviderAdapterOpenSessionError); | |
| 269 | assert.instanceOf(error, ProviderAdapter.ProviderAdapterOpenSessionError); | |
| 267 | 270 | assert.instanceOf(error.cause, ProviderSetupError); |
| 268 | 271 | }), |
| 269 | 272 | ); |
| ... | ... | @@ -287,7 +290,7 @@ it.effect("interrupts admitted session startup when a shared peer signs out", () |
| 287 | 290 | authenticate: () => Effect.void, |
| 288 | 291 | logout: Effect.void, |
| 289 | 292 | }); |
| 290 | const adapter: ProviderAdapterV2Shape = { | |
| 293 | const adapter: ProviderAdapter.ProviderAdapterV2["Service"] = { | |
| 291 | 294 | ...workAdapter, |
| 292 | 295 | openSession: () => |
| 293 | 296 | Effect.gen(function* () { |
apps/server/src/orchestration-v2/ProviderAdapterRegistry.ts+8-8| ... | ... | @@ -48,11 +48,11 @@ export type ProviderAdapterRegistryV2Error = typeof ProviderAdapterRegistryV2Err |
| 48 | 48 | export interface ProviderAdapterRegistryV2Shape { |
| 49 | 49 | readonly get: ( |
| 50 | 50 | instanceId: ProviderInstanceId, |
| 51 | ) => Effect.Effect<ProviderAdapter.ProviderAdapterV2Shape, ProviderAdapterRegistryV2Error>; | |
| 51 | ) => Effect.Effect<ProviderAdapter.ProviderAdapterV2["Service"], ProviderAdapterRegistryV2Error>; | |
| 52 | 52 | readonly list: () => Effect.Effect<ReadonlyArray<ProviderInstanceId>>; |
| 53 | 53 | readonly getMetadata?: (instanceId: ProviderInstanceId) => Effect.Effect< |
| 54 | 54 | { |
| 55 | readonly driver: ProviderAdapter.ProviderAdapterV2Shape["driver"]; | |
| 55 | readonly driver: ProviderAdapter.ProviderAdapterV2["Service"]["driver"]; | |
| 56 | 56 | readonly continuationKey: string; |
| 57 | 57 | readonly enabled: boolean; |
| 58 | 58 | readonly capabilities: OrchestrationV2ProviderCapabilities; |
| ... | ... | @@ -134,7 +134,7 @@ export const layerFromProviderInstanceRegistry: Layer.Layer< |
| 134 | 134 | ), |
| 135 | 135 | ); |
| 136 | 136 | }, |
| 137 | } satisfies ProviderAdapter.ProviderAdapterV2Shape); | |
| 137 | } satisfies ProviderAdapter.ProviderAdapterV2["Service"]); | |
| 138 | 138 | }), |
| 139 | 139 | ), |
| 140 | 140 | list: () => |
| ... | ... | @@ -169,7 +169,7 @@ export const ProviderAdapterRegistryBuildError = Schema.Union([ProviderAdapterDr |
| 169 | 169 | export type ProviderAdapterRegistryBuildError = typeof ProviderAdapterRegistryBuildError.Type; |
| 170 | 170 | |
| 171 | 171 | function makeRegistry( |
| 172 | adapters: ReadonlyArray<ProviderAdapter.ProviderAdapterV2Shape>, | |
| 172 | adapters: ReadonlyArray<ProviderAdapter.ProviderAdapterV2["Service"]>, | |
| 173 | 173 | ): ProviderAdapterRegistryV2Shape { |
| 174 | 174 | return { |
| 175 | 175 | get: (instanceId) => |
| ... | ... | @@ -185,7 +185,7 @@ function makeRegistry( |
| 185 | 185 | } |
| 186 | 186 | |
| 187 | 187 | export function layerFromAdapters( |
| 188 | adapters: ReadonlyArray<ProviderAdapter.ProviderAdapterV2Shape>, | |
| 188 | adapters: ReadonlyArray<ProviderAdapter.ProviderAdapterV2["Service"]>, | |
| 189 | 189 | ): Layer.Layer<ProviderAdapterRegistryV2> { |
| 190 | 190 | return Layer.succeed( |
| 191 | 191 | ProviderAdapterRegistryV2, |
| ... | ... | @@ -194,7 +194,7 @@ export function layerFromAdapters( |
| 194 | 194 | } |
| 195 | 195 | |
| 196 | 196 | export function layerFromAdaptersEffect<R, E>( |
| 197 | adapters: Effect.Effect<ReadonlyArray<ProviderAdapter.ProviderAdapterV2Shape>, E, R>, | |
| 197 | adapters: Effect.Effect<ReadonlyArray<ProviderAdapter.ProviderAdapterV2["Service"]>, E, R>, | |
| 198 | 198 | ): Layer.Layer<ProviderAdapterRegistryV2, E, R> { |
| 199 | 199 | return Layer.effect( |
| 200 | 200 | ProviderAdapterRegistryV2, |
| ... | ... | @@ -203,7 +203,7 @@ export function layerFromAdaptersEffect<R, E>( |
| 203 | 203 | } |
| 204 | 204 | |
| 205 | 205 | export function layerSingle( |
| 206 | adapter: ProviderAdapter.ProviderAdapterV2Shape, | |
| 206 | adapter: ProviderAdapter.ProviderAdapterV2["Service"], | |
| 207 | 207 | ): Layer.Layer<ProviderAdapterRegistryV2> { |
| 208 | 208 | return layerFromAdapters([adapter]); |
| 209 | 209 | } |
| ... | ... | @@ -217,7 +217,7 @@ const decodedConfigEnabled = (config: unknown): boolean | undefined => { |
| 217 | 217 | }; |
| 218 | 218 | |
| 219 | 219 | interface LiveAdapterEntry { |
| 220 | readonly adapter: ProviderAdapter.ProviderAdapterV2Shape; | |
| 220 | readonly adapter: ProviderAdapter.ProviderAdapterV2["Service"]; | |
| 221 | 221 | readonly scope: Scope.Closeable; |
| 222 | 222 | readonly entry: ProviderInstanceConfig; |
| 223 | 223 | } |
apps/server/src/orchestration-v2/ProviderContinuationService.test.ts+1-1| ... | ... | @@ -19,7 +19,7 @@ import * as Semaphore from "effect/Semaphore"; |
| 19 | 19 | import * as TestClock from "effect/testing/TestClock"; |
| 20 | 20 | |
| 21 | 21 | import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator"; |
| 22 | import * as ProviderContinuationRequests from "@t3tools/provider-core/server/continuationRequests"; | |
| 22 | import * as ProviderContinuationRequests from "@t3tools/provider-core/server/ProviderContinuationRequests"; | |
| 23 | 23 | import * as ProviderContinuationService from "./ProviderContinuationService.ts"; |
| 24 | 24 | import * as ThreadManagementService from "./ThreadManagementService.ts"; |
| 25 | 25 |
apps/server/src/orchestration-v2/ProviderContinuationService.ts+1-1| ... | ... | @@ -4,7 +4,7 @@ import * as Layer from "effect/Layer"; |
| 4 | 4 | import * as Ref from "effect/Ref"; |
| 5 | 5 | |
| 6 | 6 | import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator"; |
| 7 | import * as ProviderContinuationRequests from "@t3tools/provider-core/server/continuationRequests"; | |
| 7 | import * as ProviderContinuationRequests from "@t3tools/provider-core/server/ProviderContinuationRequests"; | |
| 8 | 8 | import * as ThreadManagementService from "./ThreadManagementService.ts"; |
| 9 | 9 | import { isUndeliveredMailboxSteer } from "./NotificationMailbox.ts"; |
| 10 | 10 |
apps/server/src/orchestration-v2/ProviderEventIngestor.ts+4-5| ... | ... | @@ -31,7 +31,7 @@ import * as AnalyticsService from "../telemetry/AnalyticsService.ts"; |
| 31 | 31 | import * as EventSink from "./EventSink.ts"; |
| 32 | 32 | import * as ProjectionStore from "./ProjectionStore.ts"; |
| 33 | 33 | import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator"; |
| 34 | import { ProviderAdapterV2Event } from "@t3tools/provider-core/server/ProviderAdapter"; | |
| 34 | import * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter"; | |
| 35 | 35 | import { makeProviderFailureTurnItem } from "@t3tools/provider-core/server/failure"; |
| 36 | 36 | import * as ThreadCommandExecutor from "./ThreadCommandExecutor.ts"; |
| 37 | 37 | import { stripUnservedToolOutputImageBytes } from "./toolOutputImageBytes.ts"; |
| ... | ... | @@ -41,7 +41,7 @@ export class ProviderEventNormalizeError extends Schema.TaggedError<ProviderEven |
| 41 | 41 | { |
| 42 | 42 | providerSessionId: ProviderSessionId, |
| 43 | 43 | threadId: ThreadId, |
| 44 | providerEvent: ProviderAdapterV2Event, | |
| 44 | providerEvent: ProviderAdapter.ProviderAdapterV2Event, | |
| 45 | 45 | cause: Schema.optional(Schema.Defect()), |
| 46 | 46 | }, |
| 47 | 47 | ) { |
| ... | ... | @@ -93,7 +93,7 @@ export const layerAnalytics = Layer.effect( |
| 93 | 93 | ); |
| 94 | 94 | |
| 95 | 95 | function providerTurnAnalyticsProperties(input: { |
| 96 | readonly driver: ProviderAdapterV2Event["driver"]; | |
| 96 | readonly driver: ProviderAdapter.ProviderAdapterV2Event["driver"]; | |
| 97 | 97 | readonly providerTurn: OrchestrationV2ProviderTurn; |
| 98 | 98 | readonly context?: ProviderTurnAnalyticsContext; |
| 99 | 99 | }): Readonly<Record<string, unknown>> { |
| ... | ... | @@ -257,7 +257,7 @@ export interface ProviderEventIngestInput { |
| 257 | 257 | readonly runId?: RunId; |
| 258 | 258 | readonly nodeId?: NodeId; |
| 259 | 259 | readonly rawEventId?: RawEventId; |
| 260 | readonly event: ProviderAdapterV2Event; | |
| 260 | readonly event: ProviderAdapter.ProviderAdapterV2Event; | |
| 261 | 261 | readonly analyticsContext?: ProviderTurnAnalyticsContext; |
| 262 | 262 | } |
| 263 | 263 | |
| ... | ... | @@ -657,7 +657,6 @@ export const layer: Layer.Layer< |
| 657 | 657 | yield* makeDomainEvent(input, { |
| 658 | 658 | type: "turn-item.updated", |
| 659 | 659 | payload: makeProviderFailureTurnItem({ |
| 660 | idAllocator, | |
| 661 | 660 | driver: input.event.driver, |
| 662 | 661 | threadId: input.threadId, |
| 663 | 662 | runId: input.runId ?? null, |
apps/server/src/orchestration-v2/ProviderSessionManager.test.ts+15-19| ... | ... | @@ -44,18 +44,11 @@ import * as EventSink from "./EventSink.ts"; |
| 44 | 44 | import * as EventStore from "./EventStore.ts"; |
| 45 | 45 | import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator"; |
| 46 | 46 | import * as ProjectionStore from "./ProjectionStore.ts"; |
| 47 | import { | |
| 48 | ProviderAdapterEventStreamError, | |
| 49 | type ProviderAdapterV2Event, | |
| 50 | ProviderAdapterProtocolError, | |
| 51 | type ProviderAdapterV2RuntimePolicy, | |
| 52 | type ProviderAdapterV2SessionRuntime, | |
| 53 | type ProviderAdapterV2Shape, | |
| 54 | } from "@t3tools/provider-core/server/ProviderAdapter"; | |
| 55 | 47 | import * as ProviderAdapterRegistry from "./ProviderAdapterRegistry.ts"; |
| 56 | 48 | import * as ProviderEventIngestor from "./ProviderEventIngestor.ts"; |
| 57 | 49 | import * as ThreadCommandExecutor from "./ThreadCommandExecutor.ts"; |
| 58 | 50 | import * as ProviderSessionManager from "./ProviderSessionManager.ts"; |
| 51 | import * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter"; | |
| 59 | 52 | |
| 60 | 53 | const layerTestDatabase = SqlitePersistence.layerMemory; |
| 61 | 54 | const layerTestStores = Layer.merge(EventStore.layer, ProjectionStore.layer).pipe( |
| ... | ... | @@ -167,7 +160,10 @@ interface TestProviderRuntimeState { |
| 167 | 160 | readonly interruptCount: number; |
| 168 | 161 | readonly resumeCount: number; |
| 169 | 162 | readonly unloadedNativeThreadIds: ReadonlyArray<string>; |
| 170 | readonly eventQueues: ReadonlyMap<string, Queue.Queue<ProviderAdapterV2Event, Cause.Done>>; | |
| 163 | readonly eventQueues: ReadonlyMap< | |
| 164 | string, | |
| 165 | Queue.Queue<ProviderAdapter.ProviderAdapterV2Event, Cause.Done> | |
| 166 | >; | |
| 171 | 167 | } |
| 172 | 168 | |
| 173 | 169 | const emptyState: TestProviderRuntimeState = { |
| ... | ... | @@ -189,7 +185,7 @@ const runtimePolicy = { |
| 189 | 185 | runtimeMode: "full-access", |
| 190 | 186 | interactionMode: "default", |
| 191 | 187 | cwd: process.cwd(), |
| 192 | } satisfies ProviderAdapterV2RuntimePolicy; | |
| 188 | } satisfies ProviderAdapter.ProviderAdapterV2RuntimePolicy; | |
| 193 | 189 | |
| 194 | 190 | function makeProviderSession(input: { |
| 195 | 191 | readonly providerSessionId: ProviderSessionId; |
| ... | ... | @@ -211,7 +207,7 @@ function makeProviderSession(input: { |
| 211 | 207 | } |
| 212 | 208 | |
| 213 | 209 | function makeThreadCreatedEvent(input: { |
| 214 | readonly idAllocator: IdAllocator.IdAllocatorV2Shape; | |
| 210 | readonly idAllocator: IdAllocator.IdAllocatorV2["Service"]; | |
| 215 | 211 | readonly threadId: ThreadId; |
| 216 | 212 | readonly now: DateTime.Utc; |
| 217 | 213 | readonly projectId?: ProjectId; |
| ... | ... | @@ -264,7 +260,7 @@ function makeThreadCreatedEvent(input: { |
| 264 | 260 | } |
| 265 | 261 | |
| 266 | 262 | function makeProviderThread(input: { |
| 267 | readonly idAllocator: IdAllocator.IdAllocatorV2Shape; | |
| 263 | readonly idAllocator: IdAllocator.IdAllocatorV2["Service"]; | |
| 268 | 264 | readonly threadId: ThreadId; |
| 269 | 265 | readonly providerSessionId: ProviderSessionId; |
| 270 | 266 | readonly now: DateTime.Utc; |
| ... | ... | @@ -299,7 +295,7 @@ function makeProviderThread(input: { |
| 299 | 295 | |
| 300 | 296 | function unimplemented(detail: string) { |
| 301 | 297 | return Effect.fail( |
| 302 | new ProviderAdapterProtocolError({ | |
| 298 | new ProviderAdapter.ProviderAdapterProtocolError({ | |
| 303 | 299 | driver: CODEX_DRIVER, |
| 304 | 300 | detail, |
| 305 | 301 | }), |
| ... | ... | @@ -328,7 +324,7 @@ function makeProviderAdapter( |
| 328 | 324 | /** Completed when a hanging scope close reaches its wedged finalizer. */ |
| 329 | 325 | readonly scopeCloseReached?: Deferred.Deferred<void>; |
| 330 | 326 | } = {}, |
| 331 | ): ProviderAdapterV2Shape { | |
| 327 | ): ProviderAdapter.ProviderAdapterV2["Service"] { | |
| 332 | 328 | const countClose = Effect.addFinalizer(() => |
| 333 | 329 | Ref.update(state, (current) => ({ |
| 334 | 330 | ...current, |
| ... | ... | @@ -371,7 +367,7 @@ function makeProviderAdapter( |
| 371 | 367 | ]); |
| 372 | 368 | } |
| 373 | 369 | const now = yield* DateTime.now; |
| 374 | const events = yield* Queue.unbounded<ProviderAdapterV2Event, Cause.Done>(); | |
| 370 | const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event, Cause.Done>(); | |
| 375 | 371 | const session = makeProviderSession({ |
| 376 | 372 | providerSessionId: input.providerSessionId, |
| 377 | 373 | now, |
| ... | ... | @@ -398,7 +394,7 @@ function makeProviderAdapter( |
| 398 | 394 | providerSession: session, |
| 399 | 395 | events: options.failEventStream |
| 400 | 396 | ? Stream.fail( |
| 401 | new ProviderAdapterEventStreamError({ | |
| 397 | new ProviderAdapter.ProviderAdapterEventStreamError({ | |
| 402 | 398 | driver: CODEX_DRIVER, |
| 403 | 399 | providerSessionId: input.providerSessionId, |
| 404 | 400 | cause: "process exited", |
| ... | ... | @@ -442,7 +438,7 @@ function makeProviderAdapter( |
| 442 | 438 | readThreadSnapshot: () => unimplemented("readThreadSnapshot unused in test"), |
| 443 | 439 | rollbackThread: () => unimplemented("rollbackThread unused in test"), |
| 444 | 440 | forkThread: () => unimplemented("forkThread unused in test"), |
| 445 | } satisfies ProviderAdapterV2SessionRuntime; | |
| 441 | } satisfies ProviderAdapter.ProviderAdapterV2SessionRuntime; | |
| 446 | 442 | }), |
| 447 | 443 | }; |
| 448 | 444 | } |
| ... | ... | @@ -674,7 +670,7 @@ function runBrowserAccessScenario(input: { |
| 674 | 670 | } |
| 675 | 671 | |
| 676 | 672 | function makePendingRuntimeRequestEvents(input: { |
| 677 | readonly idAllocator: IdAllocator.IdAllocatorV2Shape; | |
| 673 | readonly idAllocator: IdAllocator.IdAllocatorV2["Service"]; | |
| 678 | 674 | readonly threadId: ThreadId; |
| 679 | 675 | readonly providerSessionId: ProviderSessionId; |
| 680 | 676 | readonly providerThread: OrchestrationV2ProviderThread; |
| ... | ... | @@ -795,7 +791,7 @@ function makePendingRuntimeRequestEvents(input: { |
| 795 | 791 | driver: CODEX_DRIVER, |
| 796 | 792 | turnItem, |
| 797 | 793 | }, |
| 798 | ] satisfies ReadonlyArray<ProviderAdapterV2Event>; | |
| 794 | ] satisfies ReadonlyArray<ProviderAdapter.ProviderAdapterV2Event>; | |
| 799 | 795 | return { events, providerEvents, requestId, nodeId }; |
| 800 | 796 | }); |
| 801 | 797 | } |
apps/server/src/orchestration-v2/ProviderSessionManager.ts+37-31| ... | ... | @@ -46,14 +46,7 @@ import * as McpSessionRegistry from "../mcp/McpSessionRegistry.ts"; |
| 46 | 46 | import * as EventSink from "./EventSink.ts"; |
| 47 | 47 | import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator"; |
| 48 | 48 | import * as ProviderEventIngestor from "./ProviderEventIngestor.ts"; |
| 49 | import { | |
| 50 | ProviderAdapterEventStreamError, | |
| 51 | ProviderAdapterV2RuntimePolicy, | |
| 52 | type ProviderAdapterV2Error, | |
| 53 | type ProviderAdapterV2Event, | |
| 54 | type ProviderAdapterV2EventSubscription, | |
| 55 | type ProviderAdapterV2SessionRuntime, | |
| 56 | } from "@t3tools/provider-core/server/ProviderAdapter"; | |
| 49 | import * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter"; | |
| 57 | 50 | import * as ProviderAdapterRegistry from "./ProviderAdapterRegistry.ts"; |
| 58 | 51 | import * as ProjectionStore from "./ProjectionStore.ts"; |
| 59 | 52 | |
| ... | ... | @@ -161,14 +154,20 @@ export interface ProviderSessionManagerV2Shape { |
| 161 | 154 | readonly threadId: ThreadId; |
| 162 | 155 | readonly providerSessionId: ProviderSessionId; |
| 163 | 156 | readonly modelSelection: ModelSelection; |
| 164 | readonly runtimePolicy: ProviderAdapterV2RuntimePolicy; | |
| 157 | readonly runtimePolicy: ProviderAdapter.ProviderAdapterV2RuntimePolicy; | |
| 165 | 158 | readonly resumeFromSession?: OrchestrationV2ProviderSession; |
| 166 | 159 | readonly initialNativeThreadId?: string; |
| 167 | 160 | readonly initialProviderItemIdentityVersion?: 2; |
| 168 | }) => Effect.Effect<ProviderAdapterV2SessionRuntime, ProviderSessionManagerV2Error>; | |
| 161 | }) => Effect.Effect< | |
| 162 | ProviderAdapter.ProviderAdapterV2SessionRuntime, | |
| 163 | ProviderSessionManagerV2Error | |
| 164 | >; | |
| 169 | 165 | readonly get: ( |
| 170 | 166 | providerSessionId: ProviderSessionId, |
| 171 | ) => Effect.Effect<Option.Option<ProviderAdapterV2SessionRuntime>, ProviderSessionManagerV2Error>; | |
| 167 | ) => Effect.Effect< | |
| 168 | Option.Option<ProviderAdapter.ProviderAdapterV2SessionRuntime>, | |
| 169 | ProviderSessionManagerV2Error | |
| 170 | >; | |
| 172 | 171 | readonly close: ( |
| 173 | 172 | providerSessionId: ProviderSessionId, |
| 174 | 173 | ) => Effect.Effect<void, ProviderSessionManagerV2Error>; |
| ... | ... | @@ -211,8 +210,8 @@ interface LiveSessionEntry { |
| 211 | 210 | */ |
| 212 | 211 | readonly mcpCredentialIdByThread: ReadonlyMap<ThreadId, string>; |
| 213 | 212 | readonly supportsMultipleProviderThreads: boolean; |
| 214 | readonly runtime: ProviderAdapterV2SessionRuntime; | |
| 215 | readonly exposedRuntime: ProviderAdapterV2SessionRuntime; | |
| 213 | readonly runtime: ProviderAdapter.ProviderAdapterV2SessionRuntime; | |
| 214 | readonly exposedRuntime: ProviderAdapter.ProviderAdapterV2SessionRuntime; | |
| 216 | 215 | readonly eventSubscribers: Ref.Ref< |
| 217 | 216 | ReadonlyMap<number, Queue.Queue<ProviderSessionEventSignal, Cause.Done>> |
| 218 | 217 | >; |
| ... | ... | @@ -244,10 +243,10 @@ interface IdleThreadUnload { |
| 244 | 243 | } |
| 245 | 244 | |
| 246 | 245 | type ProviderSessionEventSignal = |
| 247 | | { readonly type: "event"; readonly event: ProviderAdapterV2Event } | |
| 246 | | { readonly type: "event"; readonly event: ProviderAdapter.ProviderAdapterV2Event } | |
| 248 | 247 | | { |
| 249 | 248 | readonly type: "failure"; |
| 250 | readonly cause: Cause.Cause<ProviderAdapterV2Error>; | |
| 249 | readonly cause: Cause.Cause<ProviderAdapter.ProviderAdapterV2Error>; | |
| 251 | 250 | }; |
| 252 | 251 | |
| 253 | 252 | export interface ProviderSessionManagerV2LayerOptions { |
| ... | ... | @@ -279,7 +278,9 @@ function sessionKey(providerSessionId: ProviderSessionId): string { |
| 279 | 278 | * Their node and transcript item are runless too, so they bypass the normal |
| 280 | 279 | * per-run subscriber and are persisted by the session event pump. |
| 281 | 280 | */ |
| 282 | function sessionScopedRuntimeRequestThreadId(event: ProviderAdapterV2Event): ThreadId | undefined { | |
| 281 | function sessionScopedRuntimeRequestThreadId( | |
| 282 | event: ProviderAdapter.ProviderAdapterV2Event, | |
| 283 | ): ThreadId | undefined { | |
| 283 | 284 | switch (event.type) { |
| 284 | 285 | case "runtime_request.updated": |
| 285 | 286 | return event.runtimeRequest.providerTurnId === null ? event.threadId : undefined; |
| ... | ... | @@ -298,7 +299,9 @@ function sessionScopedRuntimeRequestThreadId(event: ProviderAdapterV2Event): Thr |
| 298 | 299 | } |
| 299 | 300 | |
| 300 | 301 | function providerThreadRuntimeKey( |
| 301 | providerThread: Parameters<ProviderAdapterV2SessionRuntime["resumeThread"]>[0]["providerThread"], | |
| 302 | providerThread: Parameters< | |
| 303 | ProviderAdapter.ProviderAdapterV2SessionRuntime["resumeThread"] | |
| 304 | >[0]["providerThread"], | |
| 302 | 305 | ): string { |
| 303 | 306 | const nativeThreadRef = providerThread.nativeThreadRef; |
| 304 | 307 | return nativeThreadRef === null |
| ... | ... | @@ -308,10 +311,10 @@ function providerThreadRuntimeKey( |
| 308 | 311 | |
| 309 | 312 | function providerThreadLoadKey(input: { |
| 310 | 313 | readonly providerThread: Parameters< |
| 311 | ProviderAdapterV2SessionRuntime["resumeThread"] | |
| 314 | ProviderAdapter.ProviderAdapterV2SessionRuntime["resumeThread"] | |
| 312 | 315 | >[0]["providerThread"]; |
| 313 | 316 | readonly modelSelection?: ModelSelection; |
| 314 | readonly runtimePolicy?: ProviderAdapterV2RuntimePolicy; | |
| 317 | readonly runtimePolicy?: ProviderAdapter.ProviderAdapterV2RuntimePolicy; | |
| 315 | 318 | }): string { |
| 316 | 319 | return JSON.stringify({ |
| 317 | 320 | providerThread: providerThreadRuntimeKey(input.providerThread), |
| ... | ... | @@ -575,7 +578,7 @@ export const layerWithOptions = ( |
| 575 | 578 | |
| 576 | 579 | const failSubscribers = (entry: LiveSessionEntry, detail: string) => |
| 577 | 580 | Effect.gen(function* () { |
| 578 | const error = new ProviderAdapterEventStreamError({ | |
| 581 | const error = new ProviderAdapter.ProviderAdapterEventStreamError({ | |
| 579 | 582 | driver: entry.runtime.driver, |
| 580 | 583 | providerSessionId: entry.runtime.providerSessionId, |
| 581 | 584 | cause: detail, |
| ... | ... | @@ -617,7 +620,7 @@ export const layerWithOptions = ( |
| 617 | 620 | fiber === null ? Effect.void : Fiber.interrupt(fiber).pipe(Effect.ignore); |
| 618 | 621 | |
| 619 | 622 | const writeProviderSessionEvents = (input: { |
| 620 | readonly runtime: ProviderAdapterV2SessionRuntime; | |
| 623 | readonly runtime: ProviderAdapter.ProviderAdapterV2SessionRuntime; | |
| 621 | 624 | readonly threadIds: Iterable<ThreadId>; |
| 622 | 625 | readonly type: "provider-session.attached" | "provider-session.updated"; |
| 623 | 626 | readonly payload: OrchestrationV2ProviderSession; |
| ... | ... | @@ -1226,7 +1229,7 @@ export const layerWithOptions = ( |
| 1226 | 1229 | const removeThreadAttachment = (input: { |
| 1227 | 1230 | readonly providerSessionId: ProviderSessionId; |
| 1228 | 1231 | readonly threadId: ThreadId; |
| 1229 | readonly runtime: ProviderAdapterV2SessionRuntime; | |
| 1232 | readonly runtime: ProviderAdapter.ProviderAdapterV2SessionRuntime; | |
| 1230 | 1233 | }) => |
| 1231 | 1234 | Ref.update(sessions, (current) => { |
| 1232 | 1235 | const key = sessionKey(input.providerSessionId); |
| ... | ... | @@ -1289,7 +1292,7 @@ export const layerWithOptions = ( |
| 1289 | 1292 | readonly providerInstanceId: ProviderInstanceId; |
| 1290 | 1293 | }) => |
| 1291 | 1294 | Effect.suspend(() => { |
| 1292 | let attachedTo: ProviderAdapterV2SessionRuntime | undefined; | |
| 1295 | let attachedTo: ProviderAdapter.ProviderAdapterV2SessionRuntime | undefined; | |
| 1293 | 1296 | let preparedForCleanup: PreparedMcpCredential | undefined; |
| 1294 | 1297 | let reservationDropped = false; |
| 1295 | 1298 | const dropReservation = () => { |
| ... | ... | @@ -1631,7 +1634,7 @@ export const layerWithOptions = ( |
| 1631 | 1634 | subscribers: Ref.Ref< |
| 1632 | 1635 | ReadonlyMap<number, Queue.Queue<ProviderSessionEventSignal, Cause.Done>> |
| 1633 | 1636 | >, |
| 1634 | ): Effect.Effect<ProviderAdapterV2EventSubscription> => | |
| 1637 | ): Effect.Effect<ProviderAdapter.ProviderAdapterV2EventSubscription> => | |
| 1635 | 1638 | Effect.gen(function* () { |
| 1636 | 1639 | const queue = yield* Queue.unbounded<ProviderSessionEventSignal, Cause.Done>(); |
| 1637 | 1640 | const subscriberId = yield* Ref.getAndUpdate(nextSubscriberId, (value) => value + 1); |
| ... | ... | @@ -1662,15 +1665,15 @@ export const layerWithOptions = ( |
| 1662 | 1665 | ), |
| 1663 | 1666 | Stream.ensuring(close), |
| 1664 | 1667 | ); |
| 1665 | return { events, close } satisfies ProviderAdapterV2EventSubscription; | |
| 1668 | return { events, close } satisfies ProviderAdapter.ProviderAdapterV2EventSubscription; | |
| 1666 | 1669 | }); |
| 1667 | 1670 | |
| 1668 | 1671 | const decorateRuntime = ( |
| 1669 | runtime: ProviderAdapterV2SessionRuntime, | |
| 1672 | runtime: ProviderAdapter.ProviderAdapterV2SessionRuntime, | |
| 1670 | 1673 | eventSubscribers: Ref.Ref< |
| 1671 | 1674 | ReadonlyMap<number, Queue.Queue<ProviderSessionEventSignal, Cause.Done>> |
| 1672 | 1675 | >, |
| 1673 | ): ProviderAdapterV2SessionRuntime => { | |
| 1676 | ): ProviderAdapter.ProviderAdapterV2SessionRuntime => { | |
| 1674 | 1677 | const providerSessionId = runtime.providerSessionId; |
| 1675 | 1678 | const subscribeEvents = makeEventSubscription(eventSubscribers); |
| 1676 | 1679 | // Every provider's turn operations pass through here, so this is where they are |
| ... | ... | @@ -1852,7 +1855,10 @@ export const layerWithOptions = ( |
| 1852 | 1855 | |
| 1853 | 1856 | const persistProviderSessionUpdate = ( |
| 1854 | 1857 | entry: LiveSessionEntry, |
| 1855 | event: Extract<ProviderAdapterV2Event, { readonly type: "provider_session.updated" }>, | |
| 1858 | event: Extract< | |
| 1859 | ProviderAdapter.ProviderAdapterV2Event, | |
| 1860 | { readonly type: "provider_session.updated" } | |
| 1861 | >, | |
| 1856 | 1862 | ) => |
| 1857 | 1863 | Effect.gen(function* () { |
| 1858 | 1864 | const current = (yield* Ref.get(sessions)).get( |
| ... | ... | @@ -1925,7 +1931,7 @@ export const layerWithOptions = ( |
| 1925 | 1931 | .pipe( |
| 1926 | 1932 | Effect.mapError( |
| 1927 | 1933 | (cause) => |
| 1928 | new ProviderAdapterEventStreamError({ | |
| 1934 | new ProviderAdapter.ProviderAdapterEventStreamError({ | |
| 1929 | 1935 | driver: entry.runtime.driver, |
| 1930 | 1936 | providerSessionId: entry.runtime.providerSessionId, |
| 1931 | 1937 | cause, |
| ... | ... | @@ -1962,7 +1968,7 @@ export const layerWithOptions = ( |
| 1962 | 1968 | const cause = Exit.isFailure(exit) |
| 1963 | 1969 | ? exit.cause |
| 1964 | 1970 | : Cause.fail( |
| 1965 | new ProviderAdapterEventStreamError({ | |
| 1971 | new ProviderAdapter.ProviderAdapterEventStreamError({ | |
| 1966 | 1972 | driver: entry.runtime.driver, |
| 1967 | 1973 | providerSessionId: entry.runtime.providerSessionId, |
| 1968 | 1974 | cause: "Provider event stream ended unexpectedly.", |
| ... | ... | @@ -2212,7 +2218,7 @@ export const layerWithOptions = ( |
| 2212 | 2218 | Effect.gen(function* () { |
| 2213 | 2219 | const entry = (yield* Ref.get(sessions)).get(sessionKey(providerSessionId)); |
| 2214 | 2220 | if (entry === undefined) { |
| 2215 | return Option.none<ProviderAdapterV2SessionRuntime>(); | |
| 2221 | return Option.none<ProviderAdapter.ProviderAdapterV2SessionRuntime>(); | |
| 2216 | 2222 | } |
| 2217 | 2223 | yield* touchActivity(providerSessionId); |
| 2218 | 2224 | return Option.some(entry.exposedRuntime); |
apps/server/src/orchestration-v2/ProviderSwitchService.test.ts+5-3| ... | ... | @@ -12,10 +12,10 @@ import * as Effect from "effect/Effect"; |
| 12 | 12 | import * as Layer from "effect/Layer"; |
| 13 | 13 | |
| 14 | 14 | import { CodexProviderCapabilitiesV2 } from "./Adapters/CodexAdapterV2.ts"; |
| 15 | import type { ProviderAdapterV2Shape } from "@t3tools/provider-core/server/ProviderAdapter"; | |
| 16 | 15 | import * as ProviderAdapterRegistry from "./ProviderAdapterRegistry.ts"; |
| 17 | 16 | import { acpSelectionTransition } from "@t3tools/provider-core/server/selectionTransition"; |
| 18 | 17 | import * as ProviderSwitch from "./ProviderSwitchService.ts"; |
| 18 | import type * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter"; | |
| 19 | 19 | |
| 20 | 20 | const driver = ProviderDriverKind.make("codex"); |
| 21 | 21 | const currentInstanceId = ProviderInstanceId.make("codex_primary"); |
| ... | ... | @@ -67,10 +67,12 @@ function deadSessionRecord( |
| 67 | 67 | |
| 68 | 68 | function layerTest( |
| 69 | 69 | metadata: Readonly<Record<string, { continuationKey: string }>>, |
| 70 | planSelectionTransition: ProviderAdapterV2Shape["planSelectionTransition"] = () => | |
| 70 | planSelectionTransition: ProviderAdapter.ProviderAdapterV2["Service"]["planSelectionTransition"] = () => | |
| 71 | 71 | Effect.succeed({ type: "restart_session" }), |
| 72 | 72 | ) { |
| 73 | const adapter = (instanceId: ProviderInstanceId): ProviderAdapterV2Shape => ({ | |
| 73 | const adapter = ( | |
| 74 | instanceId: ProviderInstanceId, | |
| 75 | ): ProviderAdapter.ProviderAdapterV2["Service"] => ({ | |
| 74 | 76 | instanceId, |
| 75 | 77 | driver, |
| 76 | 78 | getCapabilities: () => Effect.succeed(capabilitiesWithoutModelSwitch), |
apps/server/src/orchestration-v2/ProviderTurnControlService.test.ts+2-2| ... | ... | @@ -24,7 +24,7 @@ import * as Stream from "effect/Stream"; |
| 24 | 24 | |
| 25 | 25 | import { CodexProviderCapabilitiesV2 } from "./Adapters/CodexAdapterV2.ts"; |
| 26 | 26 | import * as ProjectionStore from "./ProjectionStore.ts"; |
| 27 | import type { ProviderAdapterV2SessionRuntime } from "@t3tools/provider-core/server/ProviderAdapter"; | |
| 27 | import type * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter"; | |
| 28 | 28 | import * as ProviderSessionManager from "./ProviderSessionManager.ts"; |
| 29 | 29 | import * as ProviderTurnControlService from "./ProviderTurnControlService.ts"; |
| 30 | 30 | |
| ... | ... | @@ -177,7 +177,7 @@ it.effect( |
| 177 | 177 | updatedAt: now, |
| 178 | 178 | lastError: null, |
| 179 | 179 | }; |
| 180 | const runtime: ProviderAdapterV2SessionRuntime = { | |
| 180 | const runtime: ProviderAdapter.ProviderAdapterV2SessionRuntime = { | |
| 181 | 181 | instanceId: providerInstanceId, |
| 182 | 182 | driver, |
| 183 | 183 | providerSessionId: oldSessionId, |
apps/server/src/orchestration-v2/ProviderTurnStartService.test.ts+3-3| ... | ... | @@ -32,7 +32,7 @@ import * as EventSink from "./EventSink.ts"; |
| 32 | 32 | import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator"; |
| 33 | 33 | import { CodexProviderCapabilitiesV2 } from "./Adapters/CodexAdapterV2.ts"; |
| 34 | 34 | import * as ProjectionStore from "./ProjectionStore.ts"; |
| 35 | import { ProviderAdapterEventStreamError } from "@t3tools/provider-core/server/ProviderAdapter"; | |
| 35 | import * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter"; | |
| 36 | 36 | import * as ProviderSessionManager from "./ProviderSessionManager.ts"; |
| 37 | 37 | import * as ProviderTurnStart from "./ProviderTurnStartService.ts"; |
| 38 | 38 | import * as RunExecutionService from "./RunExecutionService.ts"; |
| ... | ... | @@ -372,7 +372,7 @@ function makeLocalCommandHarness(input: { |
| 372 | 372 | }).pipe( |
| 373 | 373 | Effect.andThen( |
| 374 | 374 | Effect.fail( |
| 375 | new ProviderAdapterEventStreamError({ | |
| 375 | new ProviderAdapter.ProviderAdapterEventStreamError({ | |
| 376 | 376 | driver: providerThread.driver, |
| 377 | 377 | providerSessionId, |
| 378 | 378 | cause: input.ensureThreadFailure, |
| ... | ... | @@ -385,7 +385,7 @@ function makeLocalCommandHarness(input: { |
| 385 | 385 | driver: providerThread.driver, |
| 386 | 386 | resumeThread: () => |
| 387 | 387 | Effect.fail( |
| 388 | new ProviderAdapterEventStreamError({ | |
| 388 | new ProviderAdapter.ProviderAdapterEventStreamError({ | |
| 389 | 389 | driver: providerThread.driver, |
| 390 | 390 | providerSessionId, |
| 391 | 391 | cause: "native thread is gone", |
apps/server/src/orchestration-v2/ProviderTurnStartService.ts+9-14| ... | ... | @@ -37,12 +37,7 @@ import { |
| 37 | 37 | latestNativeContextUsage, |
| 38 | 38 | } from "@t3tools/provider-core/server/handoffBudget"; |
| 39 | 39 | import { deliverContextHandoffs } from "./ContextHandoffDelivery.ts"; |
| 40 | import { | |
| 41 | ProviderAdapterTurnStartError, | |
| 42 | type ProviderAdapterV2Error, | |
| 43 | type ProviderAdapterV2HistoricalContext, | |
| 44 | type ProviderAdapterV2SessionRuntime, | |
| 45 | } from "@t3tools/provider-core/server/ProviderAdapter"; | |
| 40 | import * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter"; | |
| 46 | 41 | import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator"; |
| 47 | 42 | import * as ProjectionStore from "./ProjectionStore.ts"; |
| 48 | 43 | import * as ProviderSessionManager from "./ProviderSessionManager.ts"; |
| ... | ... | @@ -184,15 +179,15 @@ export const layer: Layer.Layer< |
| 184 | 179 | }; |
| 185 | 180 | |
| 186 | 181 | const makeDeliverySession = ( |
| 187 | session: ProviderAdapterV2SessionRuntime, | |
| 182 | session: ProviderAdapter.ProviderAdapterV2SessionRuntime, | |
| 188 | 183 | startWithHandoffs: ( |
| 189 | input: Parameters<ProviderAdapterV2SessionRuntime["startTurn"]>[0], | |
| 184 | input: Parameters<ProviderAdapter.ProviderAdapterV2SessionRuntime["startTurn"]>[0], | |
| 190 | 185 | compact?: boolean, |
| 191 | ) => ReturnType<ProviderAdapterV2SessionRuntime["startTurn"]>, | |
| 186 | ) => ReturnType<ProviderAdapter.ProviderAdapterV2SessionRuntime["startTurn"]>, | |
| 192 | 187 | ) => { |
| 193 | 188 | let deliver: typeof startWithHandoffs | undefined = startWithHandoffs; |
| 194 | 189 | const start = ( |
| 195 | input: Parameters<ProviderAdapterV2SessionRuntime["startTurn"]>[0], | |
| 190 | input: Parameters<ProviderAdapter.ProviderAdapterV2SessionRuntime["startTurn"]>[0], | |
| 196 | 191 | compact = false, |
| 197 | 192 | ) => |
| 198 | 193 | Effect.suspend(() => { |
| ... | ... | @@ -595,7 +590,7 @@ export const layer: Layer.Layer< |
| 595 | 590 | // Only the provider's own thread load fails the run on the last attempt; |
| 596 | 591 | // store, id and handoff failures around it keep their typed errors. |
| 597 | 592 | const loadFromProvider = ( |
| 598 | load: Effect.Effect<OrchestrationV2ProviderThread, ProviderAdapterV2Error>, | |
| 593 | load: Effect.Effect<OrchestrationV2ProviderThread, ProviderAdapter.ProviderAdapterV2Error>, | |
| 599 | 594 | ) => |
| 600 | 595 | Effect.gen(function* () { |
| 601 | 596 | const loaded = yield* Effect.result(load); |
| ... | ... | @@ -672,7 +667,7 @@ export const layer: Layer.Layer< |
| 672 | 667 | const resumed = yield* Effect.result( |
| 673 | 668 | uncertainDelivery |
| 674 | 669 | ? Effect.fail( |
| 675 | new ProviderAdapterTurnStartError({ | |
| 670 | new ProviderAdapter.ProviderAdapterTurnStartError({ | |
| 676 | 671 | driver: session.driver, |
| 677 | 672 | threadId: projection.thread.id, |
| 678 | 673 | providerThreadId: providerThread.id, |
| ... | ... | @@ -1143,7 +1138,7 @@ export const layer: Layer.Layer< |
| 1143 | 1138 | ...(session.injectHistory === undefined |
| 1144 | 1139 | ? {} |
| 1145 | 1140 | : { |
| 1146 | inject: (history: ProviderAdapterV2HistoricalContext) => | |
| 1141 | inject: (history: ProviderAdapter.ProviderAdapterV2HistoricalContext) => | |
| 1147 | 1142 | session.injectHistory!({ |
| 1148 | 1143 | providerThread: runningProviderThread, |
| 1149 | 1144 | ...history, |
| ... | ... | @@ -1211,7 +1206,7 @@ export const layer: Layer.Layer< |
| 1211 | 1206 | Effect.mapError((cause) => |
| 1212 | 1207 | cause._tag === "ProviderAdapterTurnStartError" |
| 1213 | 1208 | ? cause |
| 1214 | : new ProviderAdapterTurnStartError({ | |
| 1209 | : new ProviderAdapter.ProviderAdapterTurnStartError({ | |
| 1215 | 1210 | driver: session.driver, |
| 1216 | 1211 | threadId: projection.thread.id, |
| 1217 | 1212 | providerThreadId: providerThread.id, |
apps/server/src/orchestration-v2/RunExecutionService.test.ts+71-72| ... | ... | @@ -43,14 +43,7 @@ import * as CheckpointService from "./CheckpointService.ts"; |
| 43 | 43 | import type { PendingOrchestrationEffectV2 } from "./EffectOutbox.ts"; |
| 44 | 44 | import * as EventSink from "./EventSink.ts"; |
| 45 | 45 | import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator"; |
| 46 | import { | |
| 47 | ProviderAdapterEventStreamError, | |
| 48 | ProviderAdapterTurnStartError, | |
| 49 | type ProviderAdapterV2Error, | |
| 50 | type ProviderAdapterV2Event, | |
| 51 | type ProviderAdapterV2SessionRuntime, | |
| 52 | type ProviderAdapterV2TurnInput, | |
| 53 | } from "@t3tools/provider-core/server/ProviderAdapter"; | |
| 46 | import * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter"; | |
| 54 | 47 | import * as ProjectionStore from "./ProjectionStore.ts"; |
| 55 | 48 | import * as ProviderEventIngestor from "./ProviderEventIngestor.ts"; |
| 56 | 49 | import * as McpAppModelContext from "../mcpApps/McpAppModelContext.ts"; |
| ... | ... | @@ -95,7 +88,7 @@ it.effect("routes shared-runtime events only to their owning root run", () => |
| 95 | 88 | providerThreadId: ProviderThreadId.make("provider-thread:shared-runtime:second"), |
| 96 | 89 | }; |
| 97 | 90 | const firstTurnId = ProviderTurnId.make("provider-turn:shared-runtime:first"); |
| 98 | const turnEvent: ProviderAdapterV2Event = { | |
| 91 | const turnEvent: ProviderAdapter.ProviderAdapterV2Event = { | |
| 99 | 92 | type: "provider_turn.updated", |
| 100 | 93 | driver, |
| 101 | 94 | threadId: first.threadId, |
| ... | ... | @@ -111,7 +104,7 @@ it.effect("routes shared-runtime events only to their owning root run", () => |
| 111 | 104 | completedAt: null, |
| 112 | 105 | }, |
| 113 | 106 | }; |
| 114 | const messageEvent: ProviderAdapterV2Event = { | |
| 107 | const messageEvent: ProviderAdapter.ProviderAdapterV2Event = { | |
| 115 | 108 | type: "message.updated", |
| 116 | 109 | driver, |
| 117 | 110 | message: { |
| ... | ... | @@ -129,7 +122,7 @@ it.effect("routes shared-runtime events only to their owning root run", () => |
| 129 | 122 | updatedAt: now, |
| 130 | 123 | }, |
| 131 | 124 | }; |
| 132 | const terminalEvent: ProviderAdapterV2Event = { | |
| 125 | const terminalEvent: ProviderAdapter.ProviderAdapterV2Event = { | |
| 133 | 126 | type: "turn.terminal", |
| 134 | 127 | driver, |
| 135 | 128 | providerThreadId: first.providerThreadId, |
| ... | ... | @@ -181,7 +174,7 @@ it("leaves a child thread created after the root turn ended to the run that is l |
| 181 | 174 | attemptId: RunAttemptId.make("attempt:late-child"), |
| 182 | 175 | providerThreadId: ProviderThreadId.make("provider-thread:late-child"), |
| 183 | 176 | }; |
| 184 | const childCreated = (childThreadId: ThreadId): ProviderAdapterV2Event => | |
| 177 | const childCreated = (childThreadId: ThreadId): ProviderAdapter.ProviderAdapterV2Event => | |
| 185 | 178 | ({ |
| 186 | 179 | type: "app_thread.created", |
| 187 | 180 | driver, |
| ... | ... | @@ -193,7 +186,7 @@ it("leaves a child thread created after the root turn ended to the run that is l |
| 193 | 186 | rootThreadId: threadId, |
| 194 | 187 | }, |
| 195 | 188 | }, |
| 196 | }) as ProviderAdapterV2Event; | |
| 189 | }) as ProviderAdapter.ProviderAdapterV2Event; | |
| 197 | 190 | const earlyChild = ThreadId.make("thread:late-child:early"); |
| 198 | 191 | const lateChild = ThreadId.make("thread:late-child:late"); |
| 199 | 192 | |
| ... | ... | @@ -246,7 +239,7 @@ it("does not route a superseded attempt through a reused provider thread", () => |
| 246 | 239 | ...oldAttempt, |
| 247 | 240 | attemptId: RunAttemptId.make("attempt:shared-runtime:restart:new"), |
| 248 | 241 | }; |
| 249 | const oldTurnEvent: ProviderAdapterV2Event = { | |
| 242 | const oldTurnEvent: ProviderAdapter.ProviderAdapterV2Event = { | |
| 250 | 243 | type: "provider_turn.updated", |
| 251 | 244 | driver, |
| 252 | 245 | threadId, |
| ... | ... | @@ -299,33 +292,33 @@ it("routes only exact same-thread background items inherited from settled runs", |
| 299 | 292 | type: "subagent", |
| 300 | 293 | status: "running", |
| 301 | 294 | }, |
| 302 | } as Extract<ProviderAdapterV2Event, { type: "turn_item.updated" }>; | |
| 295 | } as Extract<ProviderAdapter.ProviderAdapterV2Event, { type: "turn_item.updated" }>; | |
| 303 | 296 | const unrelatedRunItem = { |
| 304 | 297 | ...inheritedRunning, |
| 305 | 298 | turnItem: { |
| 306 | 299 | ...inheritedRunning.turnItem, |
| 307 | 300 | runId: RunId.make("run:inherited-background-routing:unrelated"), |
| 308 | 301 | }, |
| 309 | } as ProviderAdapterV2Event; | |
| 302 | } as ProviderAdapter.ProviderAdapterV2Event; | |
| 310 | 303 | const unlistedPriorItem = { |
| 311 | 304 | ...inheritedRunning, |
| 312 | 305 | turnItem: { |
| 313 | 306 | ...inheritedRunning.turnItem, |
| 314 | 307 | id: TurnItemId.make("turn-item:inherited-background-routing:unrelated"), |
| 315 | 308 | }, |
| 316 | } as ProviderAdapterV2Event; | |
| 309 | } as ProviderAdapter.ProviderAdapterV2Event; | |
| 317 | 310 | const unrelatedThreadItem = { |
| 318 | 311 | ...inheritedRunning, |
| 319 | 312 | turnItem: { ...inheritedRunning.turnItem, threadId: otherThreadId }, |
| 320 | } as ProviderAdapterV2Event; | |
| 313 | } as ProviderAdapter.ProviderAdapterV2Event; | |
| 321 | 314 | const ordinaryItem = { |
| 322 | 315 | ...inheritedRunning, |
| 323 | 316 | turnItem: { ...inheritedRunning.turnItem, type: "reasoning" as const }, |
| 324 | } as ProviderAdapterV2Event; | |
| 317 | } as ProviderAdapter.ProviderAdapterV2Event; | |
| 325 | 318 | const inheritedTerminal = { |
| 326 | 319 | ...inheritedRunning, |
| 327 | 320 | turnItem: { ...inheritedRunning.turnItem, status: "completed" as const }, |
| 328 | } as ProviderAdapterV2Event; | |
| 321 | } as ProviderAdapter.ProviderAdapterV2Event; | |
| 329 | 322 | |
| 330 | 323 | const [runningAccepted, afterRunning] = RunExecutionService.routeProviderEvent( |
| 331 | 324 | inheritedRunning, |
| ... | ... | @@ -515,7 +508,7 @@ it("does not carry interrupted or still-running child ownership into later attem |
| 515 | 508 | startedAt: null, |
| 516 | 509 | completedAt: null, |
| 517 | 510 | }, |
| 518 | } satisfies ProviderAdapterV2Event; | |
| 511 | } satisfies ProviderAdapter.ProviderAdapterV2Event; | |
| 519 | 512 | |
| 520 | 513 | assert.isFalse(RunExecutionService.routeProviderEvent(lateChildNode, identity, state)[0]); |
| 521 | 514 | }); |
| ... | ... | @@ -550,7 +543,7 @@ it.effect("rechecks run ownership immediately before calling the provider", () = |
| 550 | 543 | const session = { |
| 551 | 544 | events: Stream.never, |
| 552 | 545 | startTurn: () => Ref.update(providerStarts, (count) => count + 1), |
| 553 | } as unknown as ProviderAdapterV2SessionRuntime; | |
| 546 | } as unknown as ProviderAdapter.ProviderAdapterV2SessionRuntime; | |
| 554 | 547 | |
| 555 | 548 | yield* runExecution.startRootRun({ |
| 556 | 549 | commandId: CommandId.make("command:run-execution-start-guard"), |
| ... | ... | @@ -597,14 +590,15 @@ it.effect("rechecks run ownership immediately before calling the provider", () = |
| 597 | 590 | it.effect("passes the thread's MCP app context to the provider under a safe key", () => |
| 598 | 591 | Effect.gen(function* () { |
| 599 | 592 | const runExecution = yield* RunExecutionService.RunExecutionServiceV2; |
| 600 | const started = yield* Deferred.make<ProviderAdapterV2TurnInput>(); | |
| 593 | const started = yield* Deferred.make<ProviderAdapter.ProviderAdapterV2TurnInput>(); | |
| 601 | 594 | const threadId = ThreadId.make("thread:run-execution-app-context"); |
| 602 | 595 | const providerInstanceId = ProviderInstanceId.make("codex"); |
| 603 | 596 | const attemptId = RunAttemptId.make("attempt:run-execution-app-context"); |
| 604 | 597 | const session = { |
| 605 | 598 | events: Stream.never, |
| 606 | startTurn: (input: ProviderAdapterV2TurnInput) => Deferred.succeed(started, input), | |
| 607 | } as unknown as ProviderAdapterV2SessionRuntime; | |
| 599 | startTurn: (input: ProviderAdapter.ProviderAdapterV2TurnInput) => | |
| 600 | Deferred.succeed(started, input), | |
| 601 | } as unknown as ProviderAdapter.ProviderAdapterV2SessionRuntime; | |
| 608 | 602 | |
| 609 | 603 | yield* runExecution.startRootRun({ |
| 610 | 604 | commandId: CommandId.make("command:run-execution-app-context"), |
| ... | ... | @@ -716,7 +710,7 @@ it.effect("fails the run when its ownership check cannot be read before calling |
| 716 | 710 | session: { |
| 717 | 711 | events: Stream.never, |
| 718 | 712 | startTurn: () => Ref.update(providerStarts, (count) => count + 1), |
| 719 | } as unknown as ProviderAdapterV2SessionRuntime, | |
| 713 | } as unknown as ProviderAdapter.ProviderAdapterV2SessionRuntime, | |
| 720 | 714 | run: { id: runId, threadId, ordinal: 1, providerInstanceId } as OrchestrationV2Run, |
| 721 | 715 | rootNode: { |
| 722 | 716 | id: NodeId.make("node:run-execution-start-guard-read"), |
| ... | ... | @@ -821,7 +815,7 @@ it.effect( |
| 821 | 815 | Effect.sync(() => { |
| 822 | 816 | calls.push("compact"); |
| 823 | 817 | }), |
| 824 | } as unknown as ProviderAdapterV2SessionRuntime, | |
| 818 | } as unknown as ProviderAdapter.ProviderAdapterV2SessionRuntime, | |
| 825 | 819 | run: { |
| 826 | 820 | id: RunId.make(`run:compact-routing:${index}`), |
| 827 | 821 | threadId, |
| ... | ... | @@ -888,7 +882,7 @@ it.effect("refreshes MCP credential liveness before calling the provider", () => |
| 888 | 882 | session: { |
| 889 | 883 | events: Stream.never, |
| 890 | 884 | startTurn: () => Ref.update(order, (entries) => [...entries, "start-turn"]), |
| 891 | } as unknown as ProviderAdapterV2SessionRuntime, | |
| 885 | } as unknown as ProviderAdapter.ProviderAdapterV2SessionRuntime, | |
| 892 | 886 | run: { |
| 893 | 887 | id: RunId.make("run:run-execution-mcp-liveness"), |
| 894 | 888 | threadId, |
| ... | ... | @@ -1000,7 +994,7 @@ it.effect("starts the provider when checkpoint baseline capture fails", () => |
| 1000 | 994 | session: { |
| 1001 | 995 | events: Stream.never, |
| 1002 | 996 | startTurn: () => Ref.update(providerStarts, (count) => count + 1), |
| 1003 | } as unknown as ProviderAdapterV2SessionRuntime, | |
| 997 | } as unknown as ProviderAdapter.ProviderAdapterV2SessionRuntime, | |
| 1004 | 998 | run: { |
| 1005 | 999 | id: runId, |
| 1006 | 1000 | threadId, |
| ... | ... | @@ -1137,7 +1131,7 @@ it.effect.each(["failure", "interruption", "stale-attempt", "start-guard"] as co |
| 1137 | 1131 | session: { |
| 1138 | 1132 | events: Stream.never, |
| 1139 | 1133 | startTurn: () => Ref.update(providerStarts, (count) => count + 1), |
| 1140 | } as unknown as ProviderAdapterV2SessionRuntime, | |
| 1134 | } as unknown as ProviderAdapter.ProviderAdapterV2SessionRuntime, | |
| 1141 | 1135 | run: { |
| 1142 | 1136 | id: runId, |
| 1143 | 1137 | threadId, |
| ... | ... | @@ -1288,7 +1282,7 @@ it.effect("keeps ingesting owned child events after the root turn terminalizes", |
| 1288 | 1282 | ), |
| 1289 | 1283 | ), |
| 1290 | 1284 | ); |
| 1291 | const events: ReadonlyArray<ProviderAdapterV2Event> = [ | |
| 1285 | const events: ReadonlyArray<ProviderAdapter.ProviderAdapterV2Event> = [ | |
| 1292 | 1286 | { |
| 1293 | 1287 | type: "app_thread.created", |
| 1294 | 1288 | driver, |
| ... | ... | @@ -1300,7 +1294,7 @@ it.effect("keeps ingesting owned child events after the root turn terminalizes", |
| 1300 | 1294 | rootThreadId: threadId, |
| 1301 | 1295 | }, |
| 1302 | 1296 | }, |
| 1303 | } as ProviderAdapterV2Event, | |
| 1297 | } as ProviderAdapter.ProviderAdapterV2Event, | |
| 1304 | 1298 | { |
| 1305 | 1299 | type: "provider_thread.updated", |
| 1306 | 1300 | driver, |
| ... | ... | @@ -1308,7 +1302,7 @@ it.effect("keeps ingesting owned child events after the root turn terminalizes", |
| 1308 | 1302 | id: childProviderThreadId, |
| 1309 | 1303 | appThreadId: childThreadId, |
| 1310 | 1304 | }, |
| 1311 | } as ProviderAdapterV2Event, | |
| 1305 | } as ProviderAdapter.ProviderAdapterV2Event, | |
| 1312 | 1306 | { |
| 1313 | 1307 | type: "subagent.updated", |
| 1314 | 1308 | driver, |
| ... | ... | @@ -1318,7 +1312,7 @@ it.effect("keeps ingesting owned child events after the root turn terminalizes", |
| 1318 | 1312 | runId, |
| 1319 | 1313 | status: "running", |
| 1320 | 1314 | }, |
| 1321 | } as ProviderAdapterV2Event, | |
| 1315 | } as ProviderAdapter.ProviderAdapterV2Event, | |
| 1322 | 1316 | { |
| 1323 | 1317 | type: "provider_turn.updated", |
| 1324 | 1318 | driver, |
| ... | ... | @@ -1330,7 +1324,7 @@ it.effect("keeps ingesting owned child events after the root turn terminalizes", |
| 1330 | 1324 | runAttemptId: null, |
| 1331 | 1325 | status: "running", |
| 1332 | 1326 | }, |
| 1333 | } as ProviderAdapterV2Event, | |
| 1327 | } as ProviderAdapter.ProviderAdapterV2Event, | |
| 1334 | 1328 | { |
| 1335 | 1329 | type: "turn.terminal", |
| 1336 | 1330 | driver, |
| ... | ... | @@ -1353,7 +1347,7 @@ it.effect("keeps ingesting owned child events after the root turn terminalizes", |
| 1353 | 1347 | text: "Hello.", |
| 1354 | 1348 | streaming: false, |
| 1355 | 1349 | }, |
| 1356 | } as ProviderAdapterV2Event, | |
| 1350 | } as ProviderAdapter.ProviderAdapterV2Event, | |
| 1357 | 1351 | { |
| 1358 | 1352 | type: "provider_turn.updated", |
| 1359 | 1353 | driver, |
| ... | ... | @@ -1365,7 +1359,7 @@ it.effect("keeps ingesting owned child events after the root turn terminalizes", |
| 1365 | 1359 | runAttemptId: null, |
| 1366 | 1360 | status: "completed", |
| 1367 | 1361 | }, |
| 1368 | } as ProviderAdapterV2Event, | |
| 1362 | } as ProviderAdapter.ProviderAdapterV2Event, | |
| 1369 | 1363 | { |
| 1370 | 1364 | type: "subagent.updated", |
| 1371 | 1365 | driver, |
| ... | ... | @@ -1375,7 +1369,7 @@ it.effect("keeps ingesting owned child events after the root turn terminalizes", |
| 1375 | 1369 | runId, |
| 1376 | 1370 | status: "completed", |
| 1377 | 1371 | }, |
| 1378 | } as ProviderAdapterV2Event, | |
| 1372 | } as ProviderAdapter.ProviderAdapterV2Event, | |
| 1379 | 1373 | ]; |
| 1380 | 1374 | |
| 1381 | 1375 | yield* Effect.gen(function* () { |
| ... | ... | @@ -1387,7 +1381,7 @@ it.effect("keeps ingesting owned child events after the root turn terminalizes", |
| 1387 | 1381 | session: { |
| 1388 | 1382 | events: Stream.fromIterable(events), |
| 1389 | 1383 | startTurn: () => Effect.void, |
| 1390 | } as unknown as ProviderAdapterV2SessionRuntime, | |
| 1384 | } as unknown as ProviderAdapter.ProviderAdapterV2SessionRuntime, | |
| 1391 | 1385 | run: { |
| 1392 | 1386 | id: runId, |
| 1393 | 1387 | threadId, |
| ... | ... | @@ -1788,7 +1782,7 @@ it.effect( |
| 1788 | 1782 | ], |
| 1789 | 1783 | updatedAt: now, |
| 1790 | 1784 | }, |
| 1791 | } as ProviderAdapterV2Event, | |
| 1785 | } as ProviderAdapter.ProviderAdapterV2Event, | |
| 1792 | 1786 | rootTerminalEvent(ids, "completed"), |
| 1793 | 1787 | { |
| 1794 | 1788 | type: "provider_thread.updated", |
| ... | ... | @@ -1799,12 +1793,12 @@ it.effect( |
| 1799 | 1793 | pendingBackgroundTasks: [], |
| 1800 | 1794 | updatedAt: now, |
| 1801 | 1795 | }, |
| 1802 | } as ProviderAdapterV2Event, | |
| 1796 | } as ProviderAdapter.ProviderAdapterV2Event, | |
| 1803 | 1797 | ]), |
| 1804 | 1798 | close: Deferred.succeed(ingestionDone, undefined), |
| 1805 | 1799 | }), |
| 1806 | 1800 | startTurn: () => Effect.void, |
| 1807 | } as unknown as ProviderAdapterV2SessionRuntime, | |
| 1801 | } as unknown as ProviderAdapter.ProviderAdapterV2SessionRuntime, | |
| 1808 | 1802 | run: { |
| 1809 | 1803 | id: ids.runId, |
| 1810 | 1804 | threadId: ids.threadId, |
| ... | ... | @@ -2000,7 +1994,7 @@ it.effect("drops late root provider-thread writes from a superseded attempt", () |
| 2000 | 1994 | ], |
| 2001 | 1995 | updatedAt: now, |
| 2002 | 1996 | }, |
| 2003 | } as ProviderAdapterV2Event, | |
| 1997 | } as ProviderAdapter.ProviderAdapterV2Event, | |
| 2004 | 1998 | rootTerminalEvent(ids, "completed"), |
| 2005 | 1999 | // Late snapshot after a replacement attempt claimed the same |
| 2006 | 2000 | // run ordinal. Without attempt gating this would clobber it. |
| ... | ... | @@ -2016,12 +2010,12 @@ it.effect("drops late root provider-thread writes from a superseded attempt", () |
| 2016 | 2010 | ], |
| 2017 | 2011 | updatedAt: now, |
| 2018 | 2012 | }, |
| 2019 | } as ProviderAdapterV2Event, | |
| 2013 | } as ProviderAdapter.ProviderAdapterV2Event, | |
| 2020 | 2014 | ]), |
| 2021 | 2015 | close: Deferred.succeed(ingestionDone, undefined), |
| 2022 | 2016 | }), |
| 2023 | 2017 | startTurn: () => Effect.void, |
| 2024 | } as unknown as ProviderAdapterV2SessionRuntime, | |
| 2018 | } as unknown as ProviderAdapter.ProviderAdapterV2SessionRuntime, | |
| 2025 | 2019 | run: { |
| 2026 | 2020 | id: ids.runId, |
| 2027 | 2021 | threadId: ids.threadId, |
| ... | ... | @@ -2197,7 +2191,7 @@ it.effect( |
| 2197 | 2191 | ], |
| 2198 | 2192 | updatedAt: now, |
| 2199 | 2193 | }, |
| 2200 | } as ProviderAdapterV2Event, | |
| 2194 | } as ProviderAdapter.ProviderAdapterV2Event, | |
| 2201 | 2195 | // Late completion still writable (turn_item writes are not |
| 2202 | 2196 | // ownership-gated); stream must stay open for it. |
| 2203 | 2197 | backgroundTurnItemEvent(ids, "command_execution", "completed", 2), |
| ... | ... | @@ -2205,7 +2199,7 @@ it.effect( |
| 2205 | 2199 | close: Deferred.succeed(ingestionDone, undefined), |
| 2206 | 2200 | }), |
| 2207 | 2201 | startTurn: () => Effect.void, |
| 2208 | } as unknown as ProviderAdapterV2SessionRuntime, | |
| 2202 | } as unknown as ProviderAdapter.ProviderAdapterV2SessionRuntime, | |
| 2209 | 2203 | run: { |
| 2210 | 2204 | id: ids.runId, |
| 2211 | 2205 | threadId: ids.threadId, |
| ... | ... | @@ -2371,12 +2365,12 @@ it.effect( |
| 2371 | 2365 | ], |
| 2372 | 2366 | updatedAt: now, |
| 2373 | 2367 | }, |
| 2374 | } as ProviderAdapterV2Event, | |
| 2368 | } as ProviderAdapter.ProviderAdapterV2Event, | |
| 2375 | 2369 | ]), |
| 2376 | 2370 | close: Deferred.succeed(ingestionDone, undefined), |
| 2377 | 2371 | }), |
| 2378 | 2372 | startTurn: () => Effect.void, |
| 2379 | } as unknown as ProviderAdapterV2SessionRuntime, | |
| 2373 | } as unknown as ProviderAdapter.ProviderAdapterV2SessionRuntime, | |
| 2380 | 2374 | run: { |
| 2381 | 2375 | id: ids.runId, |
| 2382 | 2376 | threadId: ids.threadId, |
| ... | ... | @@ -2439,7 +2433,7 @@ it.effect( |
| 2439 | 2433 | ); |
| 2440 | 2434 | const providerInstanceId = ProviderInstanceId.make("codex"); |
| 2441 | 2435 | const written = yield* Ref.make<ReadonlyArray<OrchestrationV2DomainEvent>>([]); |
| 2442 | const ingested = yield* Ref.make<ReadonlyArray<ProviderAdapterV2Event>>([]); | |
| 2436 | const ingested = yield* Ref.make<ReadonlyArray<ProviderAdapter.ProviderAdapterV2Event>>([]); | |
| 2443 | 2437 | const ingestionDone = yield* Deferred.make<void>(); |
| 2444 | 2438 | const layerTest = RunExecutionService.layer.pipe( |
| 2445 | 2439 | Layer.provide( |
| ... | ... | @@ -2609,11 +2603,11 @@ it.effect( |
| 2609 | 2603 | completedAt: runningChildNode.startedAt, |
| 2610 | 2604 | }, |
| 2611 | 2605 | }, |
| 2612 | ] satisfies ReadonlyArray<ProviderAdapterV2Event>), | |
| 2606 | ] satisfies ReadonlyArray<ProviderAdapter.ProviderAdapterV2Event>), | |
| 2613 | 2607 | close: Deferred.succeed(ingestionDone, undefined), |
| 2614 | 2608 | }), |
| 2615 | 2609 | startTurn: () => Effect.void, |
| 2616 | } as unknown as ProviderAdapterV2SessionRuntime, | |
| 2610 | } as unknown as ProviderAdapter.ProviderAdapterV2SessionRuntime, | |
| 2617 | 2611 | run: { |
| 2618 | 2612 | id: ids.runId, |
| 2619 | 2613 | threadId: ids.threadId, |
| ... | ... | @@ -2940,11 +2934,11 @@ it.effect( |
| 2940 | 2934 | }, |
| 2941 | 2935 | }, |
| 2942 | 2936 | rootTerminalEvent(ids, "interrupted"), |
| 2943 | ] satisfies ReadonlyArray<ProviderAdapterV2Event>), | |
| 2937 | ] satisfies ReadonlyArray<ProviderAdapter.ProviderAdapterV2Event>), | |
| 2944 | 2938 | close: Deferred.succeed(ingestionDone, undefined), |
| 2945 | 2939 | }), |
| 2946 | 2940 | startTurn: () => Effect.void, |
| 2947 | } as unknown as ProviderAdapterV2SessionRuntime, | |
| 2941 | } as unknown as ProviderAdapter.ProviderAdapterV2SessionRuntime, | |
| 2948 | 2942 | run: { |
| 2949 | 2943 | id: ids.runId, |
| 2950 | 2944 | threadId: ids.threadId, |
| ... | ... | @@ -3439,7 +3433,7 @@ it.effect("refreshes pull requests after a provider stream exits with an error", |
| 3439 | 3433 | shouldFinalizeRun: () => Effect.succeed(true), |
| 3440 | 3434 | events: () => |
| 3441 | 3435 | Stream.fail( |
| 3442 | new ProviderAdapterEventStreamError({ | |
| 3436 | new ProviderAdapter.ProviderAdapterEventStreamError({ | |
| 3443 | 3437 | driver, |
| 3444 | 3438 | providerSessionId: ProviderSessionId.make("session:exited"), |
| 3445 | 3439 | cause: "provider process exited", |
| ... | ... | @@ -3468,7 +3462,7 @@ it.effect("refreshes pull requests only once when startup failure closes its eve |
| 3468 | 3462 | Deferred.await(ingestionStarted).pipe( |
| 3469 | 3463 | Effect.andThen( |
| 3470 | 3464 | Effect.fail( |
| 3471 | new ProviderAdapterTurnStartError({ | |
| 3465 | new ProviderAdapter.ProviderAdapterTurnStartError({ | |
| 3472 | 3466 | driver, |
| 3473 | 3467 | threadId: input.threadId, |
| 3474 | 3468 | providerThreadId: input.providerThread.id, |
| ... | ... | @@ -3506,8 +3500,11 @@ function captureRootRunTermination(input: { |
| 3506 | 3500 | readonly seedOpenSubagent?: boolean; |
| 3507 | 3501 | readonly events?: ( |
| 3508 | 3502 | ids: BackgroundScenarioIds, |
| 3509 | ) => Stream.Stream<ProviderAdapterV2Event, ProviderAdapterV2Error>; | |
| 3510 | readonly startTurn?: ProviderAdapterV2SessionRuntime["startTurn"]; | |
| 3503 | ) => Stream.Stream< | |
| 3504 | ProviderAdapter.ProviderAdapterV2Event, | |
| 3505 | ProviderAdapter.ProviderAdapterV2Error | |
| 3506 | >; | |
| 3507 | readonly startTurn?: ProviderAdapter.ProviderAdapterV2SessionRuntime["startTurn"]; | |
| 3511 | 3508 | readonly refreshAfterTurn?: Effect.Effect<void>; |
| 3512 | 3509 | }) { |
| 3513 | 3510 | return Effect.gen(function* () { |
| ... | ... | @@ -3622,11 +3619,11 @@ function captureRootRunTermination(input: { |
| 3622 | 3619 | ] |
| 3623 | 3620 | : []), |
| 3624 | 3621 | rootTerminalEvent(ids, "interrupted"), |
| 3625 | ] satisfies ReadonlyArray<ProviderAdapterV2Event>), | |
| 3622 | ] satisfies ReadonlyArray<ProviderAdapter.ProviderAdapterV2Event>), | |
| 3626 | 3623 | close: Deferred.succeed(ingestionDone, undefined), |
| 3627 | 3624 | }), |
| 3628 | 3625 | startTurn: input.startTurn ?? (() => Effect.void), |
| 3629 | } as unknown as ProviderAdapterV2SessionRuntime, | |
| 3626 | } as unknown as ProviderAdapter.ProviderAdapterV2SessionRuntime, | |
| 3630 | 3627 | run: { |
| 3631 | 3628 | id: ids.runId, |
| 3632 | 3629 | threadId: ids.threadId, |
| ... | ... | @@ -3716,7 +3713,9 @@ function backgroundScenarioIds(key: string): BackgroundScenarioIds { |
| 3716 | 3713 | }; |
| 3717 | 3714 | } |
| 3718 | 3715 | |
| 3719 | function childThreadCreatedEvent(ids: BackgroundScenarioIds): ProviderAdapterV2Event { | |
| 3716 | function childThreadCreatedEvent( | |
| 3717 | ids: BackgroundScenarioIds, | |
| 3718 | ): ProviderAdapter.ProviderAdapterV2Event { | |
| 3720 | 3719 | return { |
| 3721 | 3720 | type: "app_thread.created", |
| 3722 | 3721 | driver, |
| ... | ... | @@ -3728,14 +3727,14 @@ function childThreadCreatedEvent(ids: BackgroundScenarioIds): ProviderAdapterV2E |
| 3728 | 3727 | rootThreadId: ids.threadId, |
| 3729 | 3728 | }, |
| 3730 | 3729 | }, |
| 3731 | } as ProviderAdapterV2Event; | |
| 3730 | } as ProviderAdapter.ProviderAdapterV2Event; | |
| 3732 | 3731 | } |
| 3733 | 3732 | |
| 3734 | 3733 | function childBackgroundTurnItemEvent( |
| 3735 | 3734 | ids: BackgroundScenarioIds, |
| 3736 | 3735 | status: "running" | "completed" | "idle", |
| 3737 | 3736 | ordinal: number, |
| 3738 | ): ProviderAdapterV2Event { | |
| 3737 | ): ProviderAdapter.ProviderAdapterV2Event { | |
| 3739 | 3738 | return { |
| 3740 | 3739 | type: "turn_item.updated", |
| 3741 | 3740 | driver, |
| ... | ... | @@ -3748,7 +3747,7 @@ function childBackgroundTurnItemEvent( |
| 3748 | 3747 | type: "command_execution", |
| 3749 | 3748 | status, |
| 3750 | 3749 | }, |
| 3751 | } as ProviderAdapterV2Event; | |
| 3750 | } as ProviderAdapter.ProviderAdapterV2Event; | |
| 3752 | 3751 | } |
| 3753 | 3752 | |
| 3754 | 3753 | function backgroundTurnItemEvent( |
| ... | ... | @@ -3757,7 +3756,7 @@ function backgroundTurnItemEvent( |
| 3757 | 3756 | status: "running" | "completed" | "idle", |
| 3758 | 3757 | ordinal: number, |
| 3759 | 3758 | itemId?: TurnItemId, |
| 3760 | ): ProviderAdapterV2Event { | |
| 3759 | ): ProviderAdapter.ProviderAdapterV2Event { | |
| 3761 | 3760 | return { |
| 3762 | 3761 | type: "turn_item.updated", |
| 3763 | 3762 | driver, |
| ... | ... | @@ -3770,7 +3769,7 @@ function backgroundTurnItemEvent( |
| 3770 | 3769 | type, |
| 3771 | 3770 | status, |
| 3772 | 3771 | }, |
| 3773 | } as ProviderAdapterV2Event; | |
| 3772 | } as ProviderAdapter.ProviderAdapterV2Event; | |
| 3774 | 3773 | } |
| 3775 | 3774 | |
| 3776 | 3775 | function backgroundTurnItemEventForRun( |
| ... | ... | @@ -3779,7 +3778,7 @@ function backgroundTurnItemEventForRun( |
| 3779 | 3778 | type: "command_execution" | "dynamic_tool" | "subagent", |
| 3780 | 3779 | status: "running" | "completed" | "idle", |
| 3781 | 3780 | ordinal: number, |
| 3782 | ): ProviderAdapterV2Event { | |
| 3781 | ): ProviderAdapter.ProviderAdapterV2Event { | |
| 3783 | 3782 | const event = backgroundTurnItemEvent(ids, type, status, ordinal); |
| 3784 | 3783 | if (event.type !== "turn_item.updated") { |
| 3785 | 3784 | return event; |
| ... | ... | @@ -3790,7 +3789,7 @@ function backgroundTurnItemEventForRun( |
| 3790 | 3789 | function subagentEvent( |
| 3791 | 3790 | ids: BackgroundScenarioIds, |
| 3792 | 3791 | status: "running" | "completed" | "idle", |
| 3793 | ): ProviderAdapterV2Event { | |
| 3792 | ): ProviderAdapter.ProviderAdapterV2Event { | |
| 3794 | 3793 | return { |
| 3795 | 3794 | type: "subagent.updated", |
| 3796 | 3795 | driver, |
| ... | ... | @@ -3800,7 +3799,7 @@ function subagentEvent( |
| 3800 | 3799 | runId: ids.runId, |
| 3801 | 3800 | status, |
| 3802 | 3801 | }, |
| 3803 | } as ProviderAdapterV2Event; | |
| 3802 | } as ProviderAdapter.ProviderAdapterV2Event; | |
| 3804 | 3803 | } |
| 3805 | 3804 | |
| 3806 | 3805 | function makeRunOwnedSubagentFixture(input: { |
| ... | ... | @@ -3977,7 +3976,7 @@ function makeRunOwnedSubagentChildNodeFixture(input: { |
| 3977 | 3976 | function rootTerminalEvent( |
| 3978 | 3977 | ids: BackgroundScenarioIds, |
| 3979 | 3978 | status: "completed" | "interrupted" | "cancelled" | "failed", |
| 3980 | ): ProviderAdapterV2Event { | |
| 3979 | ): ProviderAdapter.ProviderAdapterV2Event { | |
| 3981 | 3980 | const common = { |
| 3982 | 3981 | type: "turn.terminal" as const, |
| 3983 | 3982 | driver, |
| ... | ... | @@ -4003,7 +4002,7 @@ function rootTerminalEvent( |
| 4003 | 4002 | |
| 4004 | 4003 | function runBackgroundItemScenario( |
| 4005 | 4004 | key: string, |
| 4006 | makeEvents: (ids: BackgroundScenarioIds) => ReadonlyArray<ProviderAdapterV2Event>, | |
| 4005 | makeEvents: (ids: BackgroundScenarioIds) => ReadonlyArray<ProviderAdapter.ProviderAdapterV2Event>, | |
| 4007 | 4006 | options?: { |
| 4008 | 4007 | readonly keepEventStreamOpen?: boolean; |
| 4009 | 4008 | readonly loadInheritedBackgroundTurnItems?: () => Effect.Effect< |
| ... | ... | @@ -4082,7 +4081,7 @@ function runBackgroundItemScenario( |
| 4082 | 4081 | }; |
| 4083 | 4082 | }), |
| 4084 | 4083 | startTurn: () => Effect.void, |
| 4085 | } as unknown as ProviderAdapterV2SessionRuntime, | |
| 4084 | } as unknown as ProviderAdapter.ProviderAdapterV2SessionRuntime, | |
| 4086 | 4085 | run: { |
| 4087 | 4086 | id: ids.runId, |
| 4088 | 4087 | threadId: ids.threadId, |
apps/server/src/orchestration-v2/RunExecutionService.ts+15-16| ... | ... | @@ -41,13 +41,7 @@ import * as ServerSettings from "../serverSettings.ts"; |
| 41 | 41 | import * as CheckpointService from "./CheckpointService.ts"; |
| 42 | 42 | import * as EventSink from "./EventSink.ts"; |
| 43 | 43 | import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator"; |
| 44 | import type { | |
| 45 | ProviderAdapterV2Event, | |
| 46 | ProviderAdapterV2RuntimePolicy, | |
| 47 | ProviderAdapterV2SessionRuntime, | |
| 48 | ProviderAdapterV2TurnMessage, | |
| 49 | } from "@t3tools/provider-core/server/ProviderAdapter"; | |
| 50 | import { ProviderAdapterTurnStartError } from "@t3tools/provider-core/server/ProviderAdapter"; | |
| 44 | import * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter"; | |
| 51 | 45 | import * as ProviderEventIngestor from "./ProviderEventIngestor.ts"; |
| 52 | 46 | import type { ProjectionStoreV2Error } from "./ProjectionStore.ts"; |
| 53 | 47 | import { |
| ... | ... | @@ -79,7 +73,10 @@ export interface InheritedBackgroundTurnItemRoute { |
| 79 | 73 | readonly runId: OrchestrationV2Run["id"]; |
| 80 | 74 | } |
| 81 | 75 | |
| 82 | type ProviderTerminalEvent = Extract<ProviderAdapterV2Event, { readonly type: "turn.terminal" }>; | |
| 76 | type ProviderTerminalEvent = Extract< | |
| 77 | ProviderAdapter.ProviderAdapterV2Event, | |
| 78 | { readonly type: "turn.terminal" } | |
| 79 | >; | |
| 83 | 80 | |
| 84 | 81 | function isTerminalProviderTurnStatus(status: OrchestrationV2ProviderTurn["status"]): boolean { |
| 85 | 82 | return ( |
| ... | ... | @@ -351,7 +348,7 @@ export function makeProviderEventRoutingState(input: { |
| 351 | 348 | } |
| 352 | 349 | |
| 353 | 350 | export function routeProviderEvent( |
| 354 | event: ProviderAdapterV2Event, | |
| 351 | event: ProviderAdapter.ProviderAdapterV2Event, | |
| 355 | 352 | input: ProviderEventRouteIdentity, |
| 356 | 353 | state: ProviderEventRoutingState, |
| 357 | 354 | ): readonly [boolean, ProviderEventRoutingState] { |
| ... | ... | @@ -503,7 +500,7 @@ export interface RunExecutionServiceV2StartRootRunInput { |
| 503 | 500 | readonly commandId: CommandId; |
| 504 | 501 | readonly appThread: OrchestrationV2AppThread; |
| 505 | 502 | readonly providerSessionId: ProviderSessionId; |
| 506 | readonly session: ProviderAdapterV2SessionRuntime; | |
| 503 | readonly session: ProviderAdapter.ProviderAdapterV2SessionRuntime; | |
| 507 | 504 | readonly run: OrchestrationV2Run; |
| 508 | 505 | readonly rootNode: OrchestrationV2ExecutionNode; |
| 509 | 506 | readonly checkpointScope: OrchestrationV2CheckpointScope; |
| ... | ... | @@ -521,9 +518,9 @@ export interface RunExecutionServiceV2StartRootRunInput { |
| 521 | 518 | readonly shouldStartProviderTurn?: () => Effect.Effect<boolean, ProjectionStoreV2Error>; |
| 522 | 519 | readonly shouldFinalizeRun?: () => Effect.Effect<boolean, ProjectionStoreV2Error>; |
| 523 | 520 | readonly hasUnpairedRunInterruptRequest?: () => Effect.Effect<boolean, never>; |
| 524 | readonly message: ProviderAdapterV2TurnMessage; | |
| 521 | readonly message: ProviderAdapter.ProviderAdapterV2TurnMessage; | |
| 525 | 522 | readonly modelSelection: ModelSelection; |
| 526 | readonly runtimePolicy: ProviderAdapterV2RuntimePolicy; | |
| 523 | readonly runtimePolicy: ProviderAdapter.ProviderAdapterV2RuntimePolicy; | |
| 527 | 524 | } |
| 528 | 525 | |
| 529 | 526 | export interface RunExecutionServiceV2Shape { |
| ... | ... | @@ -738,7 +735,6 @@ export const layer: Layer.Layer< |
| 738 | 735 | providerInstanceId: input.run.providerInstanceId, |
| 739 | 736 | occurredAt: completedAt, |
| 740 | 737 | payload: makeProviderFailureTurnItem({ |
| 741 | idAllocator, | |
| 742 | 738 | driver: input.terminal.driver, |
| 743 | 739 | threadId: input.run.threadId, |
| 744 | 740 | runId: input.run.id, |
| ... | ... | @@ -1006,7 +1002,10 @@ export const layer: Layer.Layer< |
| 1006 | 1002 | } |
| 1007 | 1003 | yield* Ref.set(rootRunFinalized, true); |
| 1008 | 1004 | }); |
| 1009 | const trackChildLifecycle = (event: ProviderAdapterV2Event, deliverable: boolean) => | |
| 1005 | const trackChildLifecycle = ( | |
| 1006 | event: ProviderAdapter.ProviderAdapterV2Event, | |
| 1007 | deliverable: boolean, | |
| 1008 | ) => | |
| 1010 | 1009 | Effect.gen(function* () { |
| 1011 | 1010 | const routing = yield* Ref.get(eventRouting); |
| 1012 | 1011 | if (event.type === "provider_turn.updated") { |
| ... | ... | @@ -1403,7 +1402,7 @@ export const layer: Layer.Layer< |
| 1403 | 1402 | const startTurn = compact |
| 1404 | 1403 | ? (input.session.compactThread?.(turnInput) ?? |
| 1405 | 1404 | Effect.fail( |
| 1406 | new ProviderAdapterTurnStartError({ | |
| 1405 | new ProviderAdapter.ProviderAdapterTurnStartError({ | |
| 1407 | 1406 | driver: input.session.driver, |
| 1408 | 1407 | threadId: input.run.threadId, |
| 1409 | 1408 | providerThreadId: input.providerThread.id, |
| ... | ... | @@ -1471,7 +1470,7 @@ export const layer: Layer.Layer< |
| 1471 | 1470 | ); |
| 1472 | 1471 | |
| 1473 | 1472 | export function makeInterruptResultTurnItem(input: { |
| 1474 | readonly idAllocator: IdAllocator.IdAllocatorV2Shape; | |
| 1473 | readonly idAllocator: IdAllocator.IdAllocatorV2["Service"]; | |
| 1475 | 1474 | readonly run: OrchestrationV2Run; |
| 1476 | 1475 | readonly rootNode: OrchestrationV2ExecutionNode; |
| 1477 | 1476 | readonly providerThread: OrchestrationV2ProviderThread; |
apps/server/src/orchestration-v2/RuntimePolicy.ts+4-7| ... | ... | @@ -12,10 +12,7 @@ import * as Option from "effect/Option"; |
| 12 | 12 | import * as Schema from "effect/Schema"; |
| 13 | 13 | |
| 14 | 14 | import * as ProviderInstanceRegistry from "../provider/ProviderInstanceRegistry.ts"; |
| 15 | import { | |
| 16 | ProviderAdapterV2RuntimePolicy, | |
| 17 | type ProviderAdapterV2RuntimePolicy as ProviderAdapterV2RuntimePolicyType, | |
| 18 | } from "@t3tools/provider-core/server/ProviderAdapter"; | |
| 15 | import * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter"; | |
| 19 | 16 | import * as ProjectStore from "./ProjectStore.ts"; |
| 20 | 17 | |
| 21 | 18 | /** |
| ... | ... | @@ -52,7 +49,7 @@ export interface RuntimePolicyV2Shape { |
| 52 | 49 | readonly resolve: (input: { |
| 53 | 50 | readonly thread: OrchestrationV2AppThread; |
| 54 | 51 | readonly modelSelection: ModelSelection; |
| 55 | }) => Effect.Effect<ProviderAdapterV2RuntimePolicyType, RuntimePolicyV2Error>; | |
| 52 | }) => Effect.Effect<ProviderAdapter.ProviderAdapterV2RuntimePolicy, RuntimePolicyV2Error>; | |
| 56 | 53 | } |
| 57 | 54 | |
| 58 | 55 | export class RuntimePolicyV2 extends Context.Service<RuntimePolicyV2, RuntimePolicyV2Shape>()( |
| ... | ... | @@ -128,7 +125,7 @@ export const layerFromProjectStore: Layer.Layer< |
| 128 | 125 | }), |
| 129 | 126 | ), |
| 130 | 127 | )); |
| 131 | return ProviderAdapterV2RuntimePolicy.make({ | |
| 128 | return ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({ | |
| 132 | 129 | runtimeMode: providerRuntimeMode(input.thread.runtimeMode, supportedRuntimeModes), |
| 133 | 130 | interactionMode: input.thread.interactionMode, |
| 134 | 131 | cwd, |
| ... | ... | @@ -149,7 +146,7 @@ export function layerWithOverride( |
| 149 | 146 | resolve: (input) => |
| 150 | 147 | base.resolve(input).pipe( |
| 151 | 148 | Effect.map((policy) => |
| 152 | ProviderAdapterV2RuntimePolicy.make({ | |
| 149 | ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({ | |
| 153 | 150 | ...policy, |
| 154 | 151 | ...(override.cwd === undefined ? {} : { cwd: override.cwd }), |
| 155 | 152 | ...(override.approvalPolicy === undefined |
apps/server/src/orchestration-v2/RuntimeRequestService.test.ts+3-3| ... | ... | @@ -12,7 +12,7 @@ import * as Effect from "effect/Effect"; |
| 12 | 12 | import * as Layer from "effect/Layer"; |
| 13 | 13 | import * as Option from "effect/Option"; |
| 14 | 14 | |
| 15 | import type { ProviderAdapterV2RuntimeRequestResponseInput } from "@t3tools/provider-core/server/ProviderAdapter"; | |
| 15 | import type * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter"; | |
| 16 | 16 | import * as ProjectionStore from "./ProjectionStore.ts"; |
| 17 | 17 | import * as ProviderSessionManager from "./ProviderSessionManager.ts"; |
| 18 | 18 | import * as RuntimeRequestService from "./RuntimeRequestService.ts"; |
| ... | ... | @@ -69,7 +69,7 @@ it.effect("forwards orchestrator-resolved runtime requests to the live adapter", |
| 69 | 69 | const providerSessionId = ProviderSessionId.make("provider-session-runtime-request-resolved"); |
| 70 | 70 | const requestId = RuntimeRequestId.make("request-resolved"); |
| 71 | 71 | const respondToRuntimeRequest = vi.fn( |
| 72 | (_input: ProviderAdapterV2RuntimeRequestResponseInput) => Effect.void, | |
| 72 | (_input: ProviderAdapter.ProviderAdapterV2RuntimeRequestResponseInput) => Effect.void, | |
| 73 | 73 | ); |
| 74 | 74 | const getSession = vi.fn(() => |
| 75 | 75 | Effect.succeed( |
| ... | ... | @@ -133,7 +133,7 @@ it.effect("rejects expired runtime requests before invoking the live adapter", ( |
| 133 | 133 | const providerSessionId = ProviderSessionId.make("provider-session-runtime-request-expired"); |
| 134 | 134 | const requestId = RuntimeRequestId.make("request-expired"); |
| 135 | 135 | const respondToRuntimeRequest = vi.fn( |
| 136 | (_input: ProviderAdapterV2RuntimeRequestResponseInput) => Effect.void, | |
| 136 | (_input: ProviderAdapter.ProviderAdapterV2RuntimeRequestResponseInput) => Effect.void, | |
| 137 | 137 | ); |
| 138 | 138 | const getSession = vi.fn(() => |
| 139 | 139 | Effect.succeed( |
apps/server/src/orchestration-v2/SelectionRestart.integration.test.ts+10-13| ... | ... | @@ -28,15 +28,10 @@ import { CodexProviderCapabilitiesV2 } from "./Adapters/CodexAdapterV2.ts"; |
| 28 | 28 | import * as EffectWorker from "./EffectWorker.ts"; |
| 29 | 29 | import * as EventSink from "./EventSink.ts"; |
| 30 | 30 | import * as Orchestrator from "./Orchestrator.ts"; |
| 31 | import { | |
| 32 | ProviderAdapterOpenSessionError, | |
| 33 | type ProviderAdapterV2Event, | |
| 34 | type ProviderAdapterV2Shape, | |
| 35 | type ProviderAdapterV2TurnInput, | |
| 36 | } from "@t3tools/provider-core/server/ProviderAdapter"; | |
| 37 | 31 | import * as ProviderAdapterRegistry from "./ProviderAdapterRegistry.ts"; |
| 38 | 32 | import * as ProviderReplayHarness from "./testkit/ProviderReplayHarness.ts"; |
| 39 | 33 | import { checkpointWorkspace } from "@t3tools/provider-testing/replayWorkspace"; |
| 34 | import * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter"; | |
| 40 | 35 | |
| 41 | 36 | const driver = ProviderDriverKind.make("codex"); |
| 42 | 37 | const providerInstanceId = ProviderInstanceId.make("codex-restart-test"); |
| ... | ... | @@ -69,7 +64,7 @@ const exclusiveCapabilities: OrchestrationV2ProviderCapabilities = { |
| 69 | 64 | }; |
| 70 | 65 | |
| 71 | 66 | interface ActiveTurn { |
| 72 | readonly input: ProviderAdapterV2TurnInput; | |
| 67 | readonly input: ProviderAdapter.ProviderAdapterV2TurnInput; | |
| 73 | 68 | readonly providerTurnId: ProviderTurnId; |
| 74 | 69 | } |
| 75 | 70 | |
| ... | ... | @@ -92,7 +87,7 @@ function makeRestartAdapter( |
| 92 | 87 | state: Ref.Ref<RestartAdapterState>, |
| 93 | 88 | sessionCapabilities: OrchestrationV2ProviderCapabilities = pooledCapabilities, |
| 94 | 89 | providerInstanceId = initialSelection.instanceId, |
| 95 | ): ProviderAdapterV2Shape { | |
| 90 | ): ProviderAdapter.ProviderAdapterV2["Service"] { | |
| 96 | 91 | return { |
| 97 | 92 | instanceId: providerInstanceId, |
| 98 | 93 | driver, |
| ... | ... | @@ -125,14 +120,14 @@ function makeRestartAdapter( |
| 125 | 120 | ] as const; |
| 126 | 121 | }); |
| 127 | 122 | if (failThisOpen) { |
| 128 | return yield* new ProviderAdapterOpenSessionError({ | |
| 123 | return yield* new ProviderAdapter.ProviderAdapterOpenSessionError({ | |
| 129 | 124 | driver, |
| 130 | 125 | providerSessionId: sessionInput.providerSessionId, |
| 131 | 126 | cause: "simulated replacement open failure", |
| 132 | 127 | }); |
| 133 | 128 | } |
| 134 | 129 | |
| 135 | const events = yield* Queue.unbounded<ProviderAdapterV2Event>(); | |
| 130 | const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>(); | |
| 136 | 131 | const now = yield* DateTime.now; |
| 137 | 132 | const providerSession: OrchestrationV2ProviderSession = { |
| 138 | 133 | id: sessionInput.providerSessionId, |
| ... | ... | @@ -290,7 +285,9 @@ function makeRestartAdapter( |
| 290 | 285 | }; |
| 291 | 286 | } |
| 292 | 287 | |
| 293 | function makeCompletingHandoffAdapter(startCount: Ref.Ref<number>): ProviderAdapterV2Shape { | |
| 288 | function makeCompletingHandoffAdapter( | |
| 289 | startCount: Ref.Ref<number>, | |
| 290 | ): ProviderAdapter.ProviderAdapterV2["Service"] { | |
| 294 | 291 | return { |
| 295 | 292 | instanceId: handoffProviderInstanceId, |
| 296 | 293 | driver: handoffDriver, |
| ... | ... | @@ -298,7 +295,7 @@ function makeCompletingHandoffAdapter(startCount: Ref.Ref<number>): ProviderAdap |
| 298 | 295 | planSelectionTransition: () => Effect.succeed({ type: "apply_on_next_turn" }), |
| 299 | 296 | openSession: (sessionInput) => |
| 300 | 297 | Effect.gen(function* () { |
| 301 | const events = yield* Queue.unbounded<ProviderAdapterV2Event>(); | |
| 298 | const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>(); | |
| 302 | 299 | const now = yield* DateTime.now; |
| 303 | 300 | return { |
| 304 | 301 | instanceId: handoffProviderInstanceId, |
| ... | ... | @@ -865,7 +862,7 @@ it.live.each(["active", "idle", "selection-command", "pooled", "separate-home"] |
| 865 | 862 | }), |
| 866 | 863 | })), |
| 867 | 864 | ), |
| 868 | } satisfies ProviderAdapterV2Shape; | |
| 865 | } satisfies ProviderAdapter.ProviderAdapterV2["Service"]; | |
| 869 | 866 | }); |
| 870 | 867 | const layerRegistry = Layer.succeed(ProviderAdapterRegistry.ProviderAdapterRegistryV2, { |
| 871 | 868 | get: (instanceId) => |
apps/server/src/orchestration-v2/SteeringCompletion.integration.test.ts+8-13| ... | ... | @@ -23,15 +23,10 @@ import { CodexProviderCapabilitiesV2 } from "./Adapters/CodexAdapterV2.ts"; |
| 23 | 23 | import * as EffectWorker from "./EffectWorker.ts"; |
| 24 | 24 | import * as EventSink from "./EventSink.ts"; |
| 25 | 25 | import * as Orchestrator from "./Orchestrator.ts"; |
| 26 | import { | |
| 27 | ProviderAdapterSteerRunError, | |
| 28 | type ProviderAdapterV2Event, | |
| 29 | type ProviderAdapterV2Shape, | |
| 30 | type ProviderAdapterV2TurnInput, | |
| 31 | } from "@t3tools/provider-core/server/ProviderAdapter"; | |
| 32 | 26 | import * as ProviderAdapterRegistry from "./ProviderAdapterRegistry.ts"; |
| 33 | 27 | import * as ProviderReplayHarness from "./testkit/ProviderReplayHarness.ts"; |
| 34 | 28 | import { checkpointWorkspace } from "@t3tools/provider-testing/replayWorkspace"; |
| 29 | import * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter"; | |
| 35 | 30 | |
| 36 | 31 | const driver = ProviderDriverKind.make("codex"); |
| 37 | 32 | const instanceId = ProviderInstanceId.make("codex"); |
| ... | ... | @@ -67,8 +62,8 @@ it.effect.each( |
| 67 | 62 | Effect.scoped( |
| 68 | 63 | Effect.gen(function* () { |
| 69 | 64 | const cwd = yield* checkpointWorkspace(`steering-completion-${timing.replaceAll(" ", "-")}`); |
| 70 | const events = yield* Queue.unbounded<ProviderAdapterV2Event>(); | |
| 71 | const started: ProviderAdapterV2TurnInput[] = []; | |
| 65 | const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>(); | |
| 66 | const started: ProviderAdapter.ProviderAdapterV2TurnInput[] = []; | |
| 72 | 67 | const steerEntered = yield* Deferred.make<void>(); |
| 73 | 68 | const rejectSteer = yield* Deferred.make<void>(); |
| 74 | 69 | let steerCalls = 0; |
| ... | ... | @@ -80,7 +75,7 @@ it.effect.each( |
| 80 | 75 | activeSteeringInterruptsTools: timing === "with interrupting native steering", |
| 81 | 76 | }, |
| 82 | 77 | }; |
| 83 | const adapter: ProviderAdapterV2Shape = { | |
| 78 | const adapter: ProviderAdapter.ProviderAdapterV2["Service"] = { | |
| 84 | 79 | instanceId, |
| 85 | 80 | driver, |
| 86 | 81 | getCapabilities: () => Effect.succeed(capabilities), |
| ... | ... | @@ -153,7 +148,7 @@ it.effect.each( |
| 153 | 148 | if (timing === "after delivery") return; |
| 154 | 149 | yield* Deferred.succeed(steerEntered, undefined); |
| 155 | 150 | yield* Deferred.await(rejectSteer); |
| 156 | return yield* new ProviderAdapterSteerRunError({ | |
| 151 | return yield* new ProviderAdapter.ProviderAdapterSteerRunError({ | |
| 157 | 152 | driver, |
| 158 | 153 | providerThreadId: turn.providerThread.id, |
| 159 | 154 | providerTurnId: turn.providerTurnId, |
| ... | ... | @@ -424,8 +419,8 @@ const composerSelection = { |
| 424 | 419 | |
| 425 | 420 | const nextTurnSelectionHarness = Effect.fn("nextTurnSelectionHarness")(function* (name: string) { |
| 426 | 421 | const cwd = yield* checkpointWorkspace(name); |
| 427 | const events = yield* Queue.unbounded<ProviderAdapterV2Event>(); | |
| 428 | const started: ProviderAdapterV2TurnInput[] = []; | |
| 422 | const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>(); | |
| 423 | const started: ProviderAdapter.ProviderAdapterV2TurnInput[] = []; | |
| 429 | 424 | const steered: string[] = []; |
| 430 | 425 | const capabilities = { |
| 431 | 426 | ...CodexProviderCapabilitiesV2, |
| ... | ... | @@ -435,7 +430,7 @@ const nextTurnSelectionHarness = Effect.fn("nextTurnSelectionHarness")(function* |
| 435 | 430 | supportsSteeringByInterruptRestart: false, |
| 436 | 431 | }, |
| 437 | 432 | }; |
| 438 | const adapter: ProviderAdapterV2Shape = { | |
| 433 | const adapter: ProviderAdapter.ProviderAdapterV2["Service"] = { | |
| 439 | 434 | instanceId, |
| 440 | 435 | driver, |
| 441 | 436 | getCapabilities: () => Effect.succeed(capabilities), |
apps/server/src/orchestration-v2/ThreadDeletion.ts+3-3| ... | ... | @@ -7,7 +7,7 @@ import type * as DateTime from "effect/DateTime"; |
| 7 | 7 | import * as Effect from "effect/Effect"; |
| 8 | 8 | |
| 9 | 9 | import type { PendingOrchestrationEffectV2 } from "./EffectOutbox.ts"; |
| 10 | import type { IdAllocatorV2, IdAllocatorV2Error } from "@t3tools/provider-core/server/IdAllocator"; | |
| 10 | import type * as IdAllocator from "@t3tools/provider-core/server/IdAllocator"; | |
| 11 | 11 | |
| 12 | 12 | export interface ThreadDeletionPlan { |
| 13 | 13 | readonly events: ReadonlyArray<OrchestrationV2DomainEvent>; |
| ... | ... | @@ -23,8 +23,8 @@ export const planThreadDeletion = Effect.fn("ThreadDeletion.planThreadDeletion") |
| 23 | 23 | >; |
| 24 | 24 | readonly attachmentIds: ReadonlyArray<string>; |
| 25 | 25 | readonly now: DateTime.Utc; |
| 26 | readonly idAllocator: IdAllocatorV2["Service"]; | |
| 27 | }): Effect.fn.Return<ThreadDeletionPlan, IdAllocatorV2Error> { | |
| 26 | readonly idAllocator: IdAllocator.IdAllocatorV2["Service"]; | |
| 27 | }): Effect.fn.Return<ThreadDeletionPlan, IdAllocator.IdAllocatorV2Error> { | |
| 28 | 28 | const { command, projection, now, idAllocator } = input; |
| 29 | 29 | const events: Array<OrchestrationV2DomainEvent> = []; |
| 30 | 30 | const effects: Array<PendingOrchestrationEffectV2> = []; |
apps/server/src/orchestration-v2/ThreadFork.execution.test.ts+2-2| ... | ... | @@ -21,15 +21,15 @@ import { CodexProviderCapabilitiesV2 } from "./Adapters/CodexAdapterV2.ts"; |
| 21 | 21 | import { ClaudeProviderCapabilitiesV2 } from "./Adapters/ClaudeAdapterV2.ts"; |
| 22 | 22 | import * as EventSink from "./EventSink.ts"; |
| 23 | 23 | import * as Orchestrator from "./Orchestrator.ts"; |
| 24 | import type { ProviderAdapterV2Shape } from "@t3tools/provider-core/server/ProviderAdapter"; | |
| 25 | 24 | import * as ProviderAdapterRegistry from "./ProviderAdapterRegistry.ts"; |
| 26 | 25 | import * as ProviderReplayHarness from "./testkit/ProviderReplayHarness.ts"; |
| 26 | import type * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter"; | |
| 27 | 27 | |
| 28 | 28 | const forkCases = (["codex", "claudeAgent"] as const).flatMap((driverName) => { |
| 29 | 29 | const driver = ProviderDriverKind.make(driverName); |
| 30 | 30 | const instanceId = ProviderInstanceId.make(driver); |
| 31 | 31 | const modelSelection = { instanceId, model: "test-model" }; |
| 32 | const adapter: ProviderAdapterV2Shape = { | |
| 32 | const adapter: ProviderAdapter.ProviderAdapterV2["Service"] = { | |
| 33 | 33 | instanceId, |
| 34 | 34 | driver, |
| 35 | 35 | getCapabilities: () => |
apps/server/src/orchestration-v2/ThreadLaunchService.test.ts+2-2| ... | ... | @@ -54,12 +54,12 @@ import { CodexProviderCapabilitiesV2 } from "./Adapters/CodexAdapterV2.ts"; |
| 54 | 54 | import * as CommandReceiptStore from "./CommandReceiptStore.ts"; |
| 55 | 55 | import * as EffectOutbox from "./EffectOutbox.ts"; |
| 56 | 56 | import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator"; |
| 57 | import type { ProviderAdapterV2Shape } from "@t3tools/provider-core/server/ProviderAdapter"; | |
| 58 | 57 | import * as ProviderAdapterRegistry from "./ProviderAdapterRegistry.ts"; |
| 59 | 58 | import * as ThreadLaunch from "./ThreadLaunchService.ts"; |
| 60 | 59 | import * as ThreadManagement from "./ThreadManagementService.ts"; |
| 61 | 60 | import * as ThreadTitleRegeneration from "./ThreadTitleRegenerationService.ts"; |
| 62 | 61 | import * as ProviderReplayHarness from "./testkit/ProviderReplayHarness.ts"; |
| 62 | import type * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter"; | |
| 63 | 63 | |
| 64 | 64 | const projectId = ProjectId.make("project:launch-test"); |
| 65 | 65 | const otherProjectId = ProjectId.make("project:launch-other"); |
| ... | ... | @@ -94,7 +94,7 @@ const adapter = { |
| 94 | 94 | getCapabilities: () => Effect.succeed(CodexProviderCapabilitiesV2), |
| 95 | 95 | planSelectionTransition: () => Effect.succeed({ type: "apply_on_next_turn" as const }), |
| 96 | 96 | openSession: () => Effect.die("provider execution is disabled in launch tests"), |
| 97 | } as ProviderAdapterV2Shape; | |
| 97 | } as ProviderAdapter.ProviderAdapterV2["Service"]; | |
| 98 | 98 | |
| 99 | 99 | interface HarnessOptions { |
| 100 | 100 | readonly managedFolders?: Layer.Layer<ManagedProjectFolders.ManagedProjectFolders>; |
apps/server/src/orchestration-v2/ThreadStop.test.ts+2-2| ... | ... | @@ -24,10 +24,10 @@ import { CodexProviderCapabilitiesV2 } from "./Adapters/CodexAdapterV2.ts"; |
| 24 | 24 | import { OrchestrationEffectRequestV2 } from "./EffectOutbox.ts"; |
| 25 | 25 | import * as Orchestrator from "./Orchestrator.ts"; |
| 26 | 26 | import * as ProjectionStore from "./ProjectionStore.ts"; |
| 27 | import type { ProviderAdapterV2Shape } from "@t3tools/provider-core/server/ProviderAdapter"; | |
| 28 | 27 | import * as ProviderAdapterRegistry from "./ProviderAdapterRegistry.ts"; |
| 29 | 28 | import * as ThreadManagementService from "./ThreadManagementService.ts"; |
| 30 | 29 | import * as ProviderReplayHarness from "./testkit/ProviderReplayHarness.ts"; |
| 30 | import type * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter"; | |
| 31 | 31 | |
| 32 | 32 | const instanceId = ProviderInstanceId.make("codex"); |
| 33 | 33 | const modelSelection = { instanceId, model: "gpt-5.1-codex" }; |
| ... | ... | @@ -37,7 +37,7 @@ const adapter = { |
| 37 | 37 | getCapabilities: () => Effect.succeed(CodexProviderCapabilitiesV2), |
| 38 | 38 | planSelectionTransition: () => Effect.succeed({ type: "apply_on_next_turn" as const }), |
| 39 | 39 | openSession: () => Effect.die("Runs here never reach a provider"), |
| 40 | } as ProviderAdapterV2Shape; | |
| 40 | } as ProviderAdapter.ProviderAdapterV2["Service"]; | |
| 41 | 41 | const layerDatabase = SqlitePersistence.layerMemory; |
| 42 | 42 | // No effect worker: runs stay unstarted, so Stop ends them without a provider. |
| 43 | 43 | const layerTest = ThreadManagementService.layer.pipe( |
apps/server/src/orchestration-v2/ThreadTitleRegenerationService.test.ts+2-2| ... | ... | @@ -22,11 +22,11 @@ import * as ServerSettings from "../serverSettings.ts"; |
| 22 | 22 | import * as TextGeneration from "../textGeneration/TextGeneration.ts"; |
| 23 | 23 | import { CodexProviderCapabilitiesV2 } from "./Adapters/CodexAdapterV2.ts"; |
| 24 | 24 | import * as EffectOutbox from "./EffectOutbox.ts"; |
| 25 | import type { ProviderAdapterV2Shape } from "@t3tools/provider-core/server/ProviderAdapter"; | |
| 26 | 25 | import * as ProviderAdapterRegistry from "./ProviderAdapterRegistry.ts"; |
| 27 | 26 | import * as ThreadManagement from "./ThreadManagementService.ts"; |
| 28 | 27 | import * as ThreadTitleRegeneration from "./ThreadTitleRegenerationService.ts"; |
| 29 | 28 | import * as ProviderReplayHarness from "./testkit/ProviderReplayHarness.ts"; |
| 29 | import type * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter"; | |
| 30 | 30 | |
| 31 | 31 | const projectId = ProjectId.make("project:title-regeneration"); |
| 32 | 32 | const modelSelection = { |
| ... | ... | @@ -40,7 +40,7 @@ const adapter = { |
| 40 | 40 | getCapabilities: () => Effect.succeed(CodexProviderCapabilitiesV2), |
| 41 | 41 | planSelectionTransition: () => Effect.succeed({ type: "apply_on_next_turn" as const }), |
| 42 | 42 | openSession: () => Effect.die("provider execution is disabled in title regeneration tests"), |
| 43 | } as ProviderAdapterV2Shape; | |
| 43 | } as ProviderAdapter.ProviderAdapterV2["Service"]; | |
| 44 | 44 | |
| 45 | 45 | function makeHarness( |
| 46 | 46 | options: { |
apps/server/src/orchestration-v2/assistantStreaming.test.ts+4-4| ... | ... | @@ -2,9 +2,9 @@ import { describe, expect, it } from "vite-plus/test"; |
| 2 | 2 | import { MessageId, NodeId, ProviderDriverKind, ThreadId, TurnItemId } from "@t3tools/contracts"; |
| 3 | 3 | import * as DateTime from "effect/DateTime"; |
| 4 | 4 | import { makeAssistantStreamingFilter, splitBufferedAssistantText } from "./assistantStreaming.ts"; |
| 5 | import type { ProviderAdapterV2Event } from "@t3tools/provider-core/server/ProviderAdapter"; | |
| 5 | import type * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter"; | |
| 6 | 6 | |
| 7 | const message = (text: string, streaming = true): ProviderAdapterV2Event => ({ | |
| 7 | const message = (text: string, streaming = true): ProviderAdapter.ProviderAdapterV2Event => ({ | |
| 8 | 8 | type: "message.updated", |
| 9 | 9 | driver: ProviderDriverKind.make("codex"), |
| 10 | 10 | message: { |
| ... | ... | @@ -27,7 +27,7 @@ const turnItem = ( |
| 27 | 27 | text: string, |
| 28 | 28 | streaming = true, |
| 29 | 29 | type: "assistant_message" | "reasoning" = "assistant_message", |
| 30 | ): ProviderAdapterV2Event => ({ | |
| 30 | ): ProviderAdapter.ProviderAdapterV2Event => ({ | |
| 31 | 31 | type: "turn_item.updated", |
| 32 | 32 | driver: ProviderDriverKind.make("codex"), |
| 33 | 33 | turnItem: { |
| ... | ... | @@ -108,7 +108,7 @@ describe("V2 assistant streaming", () => { |
| 108 | 108 | "suppresses running assistant nodes in %s mode while delivering tool and completed nodes", |
| 109 | 109 | (mode) => { |
| 110 | 110 | const filter = makeAssistantStreamingFilter(mode); |
| 111 | const running: Extract<ProviderAdapterV2Event, { type: "node.updated" }> = { | |
| 111 | const running: Extract<ProviderAdapter.ProviderAdapterV2Event, { type: "node.updated" }> = { | |
| 112 | 112 | type: "node.updated", |
| 113 | 113 | driver: ProviderDriverKind.make("codex"), |
| 114 | 114 | node: { |
apps/server/src/orchestration-v2/assistantStreaming.ts+5-2| ... | ... | @@ -1,5 +1,5 @@ |
| 1 | 1 | import type { ResponseStreamingMode } from "@t3tools/contracts"; |
| 2 | import type { ProviderAdapterV2Event } from "@t3tools/provider-core/server/ProviderAdapter"; | |
| 2 | import type * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter"; | |
| 3 | 3 | |
| 4 | 4 | // An opening fence may sit at any indentation, since fences inside list |
| 5 | 5 | // items are indented past the marker. A closing fence may be indented at most |
| ... | ... | @@ -91,7 +91,10 @@ export function splitBufferedAssistantText(text: string): { ready: string; rest: |
| 91 | 91 | /** Buffer assistant messages and reasoning alike, keeping unfinished markdown out of the read model. */ |
| 92 | 92 | export function makeAssistantStreamingFilter(mode: ResponseStreamingMode) { |
| 93 | 93 | const delivered = new Map<string, { text: string; at: number }>(); |
| 94 | return (event: ProviderAdapterV2Event, now: number): ProviderAdapterV2Event | null => { | |
| 94 | return ( | |
| 95 | event: ProviderAdapter.ProviderAdapterV2Event, | |
| 96 | now: number, | |
| 97 | ): ProviderAdapter.ProviderAdapterV2Event | null => { | |
| 95 | 98 | if ( |
| 96 | 99 | event.type === "node.updated" && |
| 97 | 100 | event.node.kind === "assistant_message" && |
apps/server/src/orchestration-v2/legacy/LegacyV1Cutover.integration.test.ts+5-9| ... | ... | @@ -44,14 +44,10 @@ import * as LegacyV1ThreadImporter from "./LegacyV1ThreadImporter.ts"; |
| 44 | 44 | import * as Orchestrator from "../Orchestrator.ts"; |
| 45 | 45 | import * as ProjectionMaintenance from "../ProjectionMaintenance.ts"; |
| 46 | 46 | import * as ProjectionStore from "../ProjectionStore.ts"; |
| 47 | import { | |
| 48 | ProviderAdapterProtocolError, | |
| 49 | type ProviderAdapterV2Event, | |
| 50 | type ProviderAdapterV2Shape, | |
| 51 | } from "@t3tools/provider-core/server/ProviderAdapter"; | |
| 52 | 47 | import * as ProviderAdapterRegistry from "../ProviderAdapterRegistry.ts"; |
| 53 | 48 | import * as ProviderReplayHarness from "../testkit/ProviderReplayHarness.ts"; |
| 54 | 49 | import { checkpointWorkspace } from "@t3tools/provider-testing/replayWorkspace"; |
| 50 | import * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter"; | |
| 55 | 51 | |
| 56 | 52 | const PROJECT_ID = "project:cutover"; |
| 57 | 53 | const ACTIVE_THREAD = "thread:cutover:active"; |
| ... | ... | @@ -422,7 +418,7 @@ interface CapturedTurn { |
| 422 | 418 | } |
| 423 | 419 | |
| 424 | 420 | const unimplemented = (detail: string) => |
| 425 | Effect.fail(new ProviderAdapterProtocolError({ driver, detail })); | |
| 421 | Effect.fail(new ProviderAdapter.ProviderAdapterProtocolError({ driver, detail })); | |
| 426 | 422 | |
| 427 | 423 | const makeCodexAdapter = (capturedTurns: Ref.Ref<ReadonlyArray<CapturedTurn>>) => |
| 428 | 424 | ({ |
| ... | ... | @@ -432,7 +428,7 @@ const makeCodexAdapter = (capturedTurns: Ref.Ref<ReadonlyArray<CapturedTurn>>) = |
| 432 | 428 | planSelectionTransition: () => Effect.succeed({ type: "apply_on_next_turn" }), |
| 433 | 429 | openSession: (sessionInput) => |
| 434 | 430 | Effect.gen(function* () { |
| 435 | const events = yield* PubSub.unbounded<ProviderAdapterV2Event>(); | |
| 431 | const events = yield* PubSub.unbounded<ProviderAdapter.ProviderAdapterV2Event>(); | |
| 436 | 432 | const now = yield* DateTime.now; |
| 437 | 433 | const providerSession: OrchestrationV2ProviderSession = { |
| 438 | 434 | id: sessionInput.providerSessionId, |
| ... | ... | @@ -551,7 +547,7 @@ const makeCodexAdapter = (capturedTurns: Ref.Ref<ReadonlyArray<CapturedTurn>>) = |
| 551 | 547 | failure: null, |
| 552 | 548 | threadDisposition: "reusable", |
| 553 | 549 | }, |
| 554 | ] satisfies ReadonlyArray<ProviderAdapterV2Event>); | |
| 550 | ] satisfies ReadonlyArray<ProviderAdapter.ProviderAdapterV2Event>); | |
| 555 | 551 | }), |
| 556 | 552 | steerTurn: () => Effect.void, |
| 557 | 553 | interruptTurn: () => Effect.void, |
| ... | ... | @@ -561,7 +557,7 @@ const makeCodexAdapter = (capturedTurns: Ref.Ref<ReadonlyArray<CapturedTurn>>) = |
| 561 | 557 | forkThread: () => unimplemented("forkThread unused in cutover test"), |
| 562 | 558 | }; |
| 563 | 559 | }), |
| 564 | }) satisfies ProviderAdapterV2Shape; | |
| 560 | }) satisfies ProviderAdapter.ProviderAdapterV2["Service"]; | |
| 565 | 561 | |
| 566 | 562 | const waitForIdle = Effect.fn("LegacyV1Cutover.waitForIdle")(function* (threadId: ThreadId) { |
| 567 | 563 | const orchestrator = yield* Orchestrator.OrchestratorV2; |
apps/server/src/orchestration-v2/runtimeLayer.test.ts+5-6| ... | ... | @@ -67,16 +67,13 @@ import * as PullRequestWatchReactor from "./PullRequestWatchReactor.ts"; |
| 67 | 67 | import { PullRequestProviderError } from "../pullRequest/PullRequestProvider.ts"; |
| 68 | 68 | import * as PullRequestService from "../pullRequest/PullRequestService.ts"; |
| 69 | 69 | import * as ProjectStore from "./ProjectStore.ts"; |
| 70 | import type { | |
| 71 | ProviderAdapterV2SessionRuntime, | |
| 72 | ProviderAdapterV2Shape, | |
| 73 | } from "@t3tools/provider-core/server/ProviderAdapter"; | |
| 74 | 70 | import * as ProviderSessionManager from "./ProviderSessionManager.ts"; |
| 75 | 71 | import * as RuntimeLayer from "./runtimeLayer.ts"; |
| 76 | 72 | import { shellStreamItemFromThreadShell } from "./ShellStream.ts"; |
| 77 | 73 | import { CodexProviderCapabilitiesV2 } from "./Adapters/CodexAdapterV2.ts"; |
| 78 | 74 | import * as ThreadManagementService from "./ThreadManagementService.ts"; |
| 79 | 75 | import * as ThreadCommandExecutor from "./ThreadCommandExecutor.ts"; |
| 76 | import type * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter"; | |
| 80 | 77 | |
| 81 | 78 | const layerPlatformTest = Layer.merge( |
| 82 | 79 | NodeServices.layer, |
| ... | ... | @@ -119,7 +116,7 @@ const orchestrationAdapter = { |
| 119 | 116 | getCapabilities: () => Effect.succeed(CodexProviderCapabilitiesV2), |
| 120 | 117 | planSelectionTransition: () => Effect.succeed({ type: "apply_on_next_turn" }), |
| 121 | 118 | openSession: () => Effect.die("sessions are not used by lifecycle tests"), |
| 122 | } as ProviderAdapterV2Shape; | |
| 119 | } as ProviderAdapter.ProviderAdapterV2["Service"]; | |
| 123 | 120 | const providerInstance = { |
| 124 | 121 | instanceId: modelSelection.instanceId, |
| 125 | 122 | driverKind: driver, |
| ... | ... | @@ -928,7 +925,9 @@ it.layer(layerTest)("OrchestrationV2LayerLive", (it) => { |
| 928 | 925 | const sessionSpy = vi |
| 929 | 926 | .spyOn(sessions, "get") |
| 930 | 927 | .mockReturnValue( |
| 931 | Effect.succeed(Option.some({ providerSession } as ProviderAdapterV2SessionRuntime)), | |
| 928 | Effect.succeed( | |
| 929 | Option.some({ providerSession } as ProviderAdapter.ProviderAdapterV2SessionRuntime), | |
| 930 | ), | |
| 932 | 931 | ); |
| 933 | 932 | yield* Effect.addFinalizer(() => Effect.sync(() => sessionSpy.mockRestore())); |
| 934 | 933 |
apps/server/src/orchestration-v2/runtimeLayer.ts+1-1| ... | ... | @@ -30,7 +30,7 @@ import * as ProjectionStore from "./ProjectionStore.ts"; |
| 30 | 30 | import * as ProjectionMaintenance from "./ProjectionMaintenance.ts"; |
| 31 | 31 | import * as ProjectStore from "./ProjectStore.ts"; |
| 32 | 32 | import * as ProviderAdapterRegistry from "./ProviderAdapterRegistry.ts"; |
| 33 | import * as ProviderContinuationRequests from "@t3tools/provider-core/server/continuationRequests"; | |
| 33 | import * as ProviderContinuationRequests from "@t3tools/provider-core/server/ProviderContinuationRequests"; | |
| 34 | 34 | import * as ProviderContinuationService from "./ProviderContinuationService.ts"; |
| 35 | 35 | import * as ThreadTitleRegenerationService from "./ThreadTitleRegenerationService.ts"; |
| 36 | 36 | import * as ProviderEventIngestor from "./ProviderEventIngestor.ts"; |
apps/server/src/orchestration-v2/testkit/ProviderReplayHarness.ts+1-1| ... | ... | @@ -35,7 +35,7 @@ import * as ProjectionStore from "../ProjectionStore.ts"; |
| 35 | 35 | import * as ProjectStore from "../ProjectStore.ts"; |
| 36 | 36 | import * as ProviderAdapterRegistry from "../ProviderAdapterRegistry.ts"; |
| 37 | 37 | import * as ProviderAuthService from "../../provider/ProviderAuthService.ts"; |
| 38 | import * as ProviderContinuationRequests from "@t3tools/provider-core/server/continuationRequests"; | |
| 38 | import * as ProviderContinuationRequests from "@t3tools/provider-core/server/ProviderContinuationRequests"; | |
| 39 | 39 | import * as ProviderContinuationService from "../ProviderContinuationService.ts"; |
| 40 | 40 | import * as ProviderEventIngestor from "../ProviderEventIngestor.ts"; |
| 41 | 41 | import * as ProviderRuntimeRecoveryService from "../ProviderRuntimeRecoveryService.ts"; |
apps/server/src/orchestration-v2/testkit/ProviderSwitch.integration.test.ts+9-16| ... | ... | @@ -52,14 +52,6 @@ import * as EffectWorker from "../EffectWorker.ts"; |
| 52 | 52 | import * as EffectOutbox from "../EffectOutbox.ts"; |
| 53 | 53 | import * as ProjectionMaintenance from "../ProjectionMaintenance.ts"; |
| 54 | 54 | import * as ProjectionStore from "../ProjectionStore.ts"; |
| 55 | import { | |
| 56 | type ProviderAdapterV2Event, | |
| 57 | type ProviderAdapterV2HistoricalContext, | |
| 58 | ProviderAdapterProtocolError, | |
| 59 | ProviderAdapterTurnStartError, | |
| 60 | type ProviderAdapterV2Shape, | |
| 61 | type ProviderAdapterV2SessionRuntime, | |
| 62 | } from "@t3tools/provider-core/server/ProviderAdapter"; | |
| 63 | 55 | import * as ProviderAdapterRegistry from "../ProviderAdapterRegistry.ts"; |
| 64 | 56 | import { makeProviderFailure } from "@t3tools/provider-core/server/failure"; |
| 65 | 57 | import { |
| ... | ... | @@ -70,6 +62,7 @@ import { |
| 70 | 62 | } from "./fixtures/shared.ts"; |
| 71 | 63 | import * as ProviderReplayHarness from "./ProviderReplayHarness.ts"; |
| 72 | 64 | import { checkpointWorkspace } from "@t3tools/provider-testing/replayWorkspace"; |
| 65 | import * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter"; | |
| 73 | 66 | |
| 74 | 67 | const encodeJson = Schema.encodeEffect(Schema.fromJsonString(Schema.Unknown)); |
| 75 | 68 | |
| ... | ... | @@ -95,7 +88,7 @@ interface CapturedTurn { |
| 95 | 88 | } |
| 96 | 89 | |
| 97 | 90 | function unimplemented(driver: ProviderDriverKind, detail: string) { |
| 98 | return Effect.fail(new ProviderAdapterProtocolError({ driver, detail })); | |
| 91 | return Effect.fail(new ProviderAdapter.ProviderAdapterProtocolError({ driver, detail })); | |
| 99 | 92 | } |
| 100 | 93 | |
| 101 | 94 | function makeTestAdapter(input: { |
| ... | ... | @@ -116,7 +109,7 @@ function makeTestAdapter(input: { |
| 116 | 109 | readonly failResumeOnce?: Ref.Ref<boolean>; |
| 117 | 110 | readonly initialContextUsage?: OrchestrationV2ProviderThread["contextUsage"]; |
| 118 | 111 | readonly getModelContextWindow?: (selection: ModelSelection) => number | undefined; |
| 119 | readonly canReuseContextUsage?: ProviderAdapterV2SessionRuntime["canReuseContextUsage"]; | |
| 112 | readonly canReuseContextUsage?: ProviderAdapter.ProviderAdapterV2SessionRuntime["canReuseContextUsage"]; | |
| 120 | 113 | readonly tokenUsageByRunOrdinal?: Readonly< |
| 121 | 114 | Record<number, Omit<OrchestrationV2ProviderTurnTokenUsage, "updatedAt">> |
| 122 | 115 | >; |
| ... | ... | @@ -125,7 +118,7 @@ function makeTestAdapter(input: { |
| 125 | 118 | readonly holdRunOrdinal?: number; |
| 126 | 119 | readonly holdFirstTurn?: Deferred.Deferred<void>; |
| 127 | 120 | readonly releaseFirstTurn?: Deferred.Deferred<void>; |
| 128 | }): ProviderAdapterV2Shape { | |
| 121 | }): ProviderAdapter.ProviderAdapterV2["Service"] { | |
| 129 | 122 | return { |
| 130 | 123 | instanceId: input.instanceId, |
| 131 | 124 | driver: input.driver, |
| ... | ... | @@ -133,7 +126,7 @@ function makeTestAdapter(input: { |
| 133 | 126 | planSelectionTransition: () => Effect.succeed({ type: "apply_on_next_turn" }), |
| 134 | 127 | openSession: (sessionInput) => |
| 135 | 128 | Effect.gen(function* () { |
| 136 | const events = yield* PubSub.unbounded<ProviderAdapterV2Event>(); | |
| 129 | const events = yield* PubSub.unbounded<ProviderAdapter.ProviderAdapterV2Event>(); | |
| 137 | 130 | const now = yield* DateTime.now; |
| 138 | 131 | const providerSession: OrchestrationV2ProviderSession = { |
| 139 | 132 | id: sessionInput.providerSessionId, |
| ... | ... | @@ -148,7 +141,7 @@ function makeTestAdapter(input: { |
| 148 | 141 | lastError: null, |
| 149 | 142 | }; |
| 150 | 143 | |
| 151 | const runtime: ProviderAdapterV2SessionRuntime = { | |
| 144 | const runtime: ProviderAdapter.ProviderAdapterV2SessionRuntime = { | |
| 152 | 145 | instanceId: input.instanceId, |
| 153 | 146 | driver: input.driver, |
| 154 | 147 | providerSessionId: sessionInput.providerSessionId, |
| ... | ... | @@ -204,7 +197,7 @@ function makeTestAdapter(input: { |
| 204 | 197 | ...(input.injectedHistory === undefined |
| 205 | 198 | ? {} |
| 206 | 199 | : { |
| 207 | injectHistory: (history: ProviderAdapterV2HistoricalContext) => | |
| 200 | injectHistory: (history: ProviderAdapter.ProviderAdapterV2HistoricalContext) => | |
| 208 | 201 | Ref.update(input.injectedHistory!, (current) => [ |
| 209 | 202 | ...current, |
| 210 | 203 | ...historyResponseItems(history.messages, history.context), |
| ... | ... | @@ -236,7 +229,7 @@ function makeTestAdapter(input: { |
| 236 | 229 | input.refuseStarts !== undefined && |
| 237 | 230 | (yield* Ref.getAndUpdate(input.refuseStarts, (left) => Math.max(0, left - 1))) > 0 |
| 238 | 231 | ) |
| 239 | return yield* new ProviderAdapterTurnStartError({ | |
| 232 | return yield* new ProviderAdapter.ProviderAdapterTurnStartError({ | |
| 240 | 233 | driver: input.driver, |
| 241 | 234 | threadId: turnInput.threadId, |
| 242 | 235 | providerThreadId: turnInput.providerThread.id, |
| ... | ... | @@ -280,7 +273,7 @@ function makeTestAdapter(input: { |
| 280 | 273 | input.responseByThreadId?.[turnInput.threadId]?.[turnInput.runOrdinal] ?? |
| 281 | 274 | input.responseByRunOrdinal[turnInput.runOrdinal] ?? |
| 282 | 275 | `${input.driver} response for run ${turnInput.runOrdinal}`; |
| 283 | const providerEvents: ReadonlyArray<ProviderAdapterV2Event> = [ | |
| 276 | const providerEvents: ReadonlyArray<ProviderAdapter.ProviderAdapterV2Event> = [ | |
| 284 | 277 | { |
| 285 | 278 | type: "provider_turn.updated", |
| 286 | 279 | driver: input.driver, |
apps/server/src/provider/CodexChatGptAuth.ts+2-2| ... | ... | @@ -19,8 +19,8 @@ import * as Effect from "effect/Effect"; |
| 19 | 19 | import * as Option from "effect/Option"; |
| 20 | 20 | import * as Schema from "effect/Schema"; |
| 21 | 21 | import * as Semaphore from "effect/Semaphore"; |
| 22 | import * as ProviderAuthFlow from "@t3tools/provider-core/server/ProviderAuthFlow"; | |
| 23 | import type { ProviderAuthFlowContext } from "@t3tools/provider-core/server/ProviderAuthFlow"; | |
| 22 | import * as ProviderAuthFlow from "@t3tools/provider-core/server/providerAuthFlow"; | |
| 23 | import type { ProviderAuthFlowContext } from "@t3tools/provider-core/server/providerAuthFlow"; | |
| 24 | 24 | import { HttpClient, HttpClientRequest } from "effect/http"; |
| 25 | 25 | import * as ProviderCredentialStore from "./ProviderCredentialStore.ts"; |
| 26 | 26 | import { withChatGptSessionLock } from "./CodexChatGptSessionLock.ts"; |
apps/server/src/provider/Drivers/AntigravityDriver.test.ts+1-1| ... | ... | @@ -29,7 +29,7 @@ import { |
| 29 | 29 | ANTIGRAVITY_AUTH_STDOUT_PREFIX, |
| 30 | 30 | resolveAntigravityInstanceDirectories, |
| 31 | 31 | } from "../antigravityAuthSupport.ts"; |
| 32 | import * as ProviderEventLoggers from "../ProviderEventLoggers.ts"; | |
| 32 | import * as ProviderEventLoggers from "@t3tools/provider-core/server/ProviderEventLoggers"; | |
| 33 | 33 | import * as ModelManifest from "../ModelManifest.ts"; |
| 34 | 34 | import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator"; |
| 35 | 35 | import { AntigravityDriver } from "./AntigravityDriver.ts"; |
apps/server/src/provider/Drivers/AntigravityDriver.ts+5-5| ... | ... | @@ -18,7 +18,7 @@ import * as Stream from "effect/Stream"; |
| 18 | 18 | import * as ChildProcessSpawner from "effect/process/ChildProcessSpawner"; |
| 19 | 19 | import type { AcpError } from "effect-acp/errors"; |
| 20 | 20 | |
| 21 | import { ProviderHost } from "@t3tools/provider-core/server/ProviderHost"; | |
| 21 | import * as ProviderHost from "@t3tools/provider-core/server/ProviderHost"; | |
| 22 | 22 | import { |
| 23 | 23 | isAntigravityTextGenerationAvailable, |
| 24 | 24 | makeAntigravityTextGeneration, |
| ... | ... | @@ -46,12 +46,12 @@ import { |
| 46 | 46 | removeAntigravitySessionFiles, |
| 47 | 47 | } from "../acp/AntigravitySessionFiles.ts"; |
| 48 | 48 | import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator"; |
| 49 | import * as ProviderContinuationRequests from "@t3tools/provider-core/server/continuationRequests"; | |
| 49 | import * as ProviderContinuationRequests from "@t3tools/provider-core/server/ProviderContinuationRequests"; | |
| 50 | 50 | import { makeAntigravityAdapterV2 } from "../../orchestration-v2/Adapters/AntigravityAdapterV2.ts"; |
| 51 | 51 | import { makeAcpNativeLoggerFactory } from "@t3tools/provider-acp/server/nativeLogging"; |
| 52 | 52 | import { ProviderDriverError } from "../Errors.ts"; |
| 53 | 53 | import { makeAntigravityProvider } from "../AntigravityProvider.ts"; |
| 54 | import * as ProviderEventLoggers from "../ProviderEventLoggers.ts"; | |
| 54 | import * as ProviderEventLoggers from "@t3tools/provider-core/server/ProviderEventLoggers"; | |
| 55 | 55 | import * as ModelCatalog from "@t3tools/provider-core/server/ModelCatalog"; |
| 56 | 56 | import { |
| 57 | 57 | defaultProviderContinuationIdentity, |
| ... | ... | @@ -68,7 +68,7 @@ const isNodeRuntimeUnavailableError = Schema.is(NodeRuntimeUnavailableError); |
| 68 | 68 | |
| 69 | 69 | export type AntigravityDriverEnv = |
| 70 | 70 | | AntigravityInstallation.AntigravityInstallation |
| 71 | | ProviderHost | |
| 71 | | ProviderHost.ProviderHost | |
| 72 | 72 | | ChildProcessSpawner.ChildProcessSpawner |
| 73 | 73 | | Crypto.Crypto |
| 74 | 74 | | FileSystem.FileSystem |
| ... | ... | @@ -89,7 +89,7 @@ export const AntigravityDriver: ProviderDriver<AntigravitySettings, AntigravityD |
| 89 | 89 | const fileSystem = yield* FileSystem.FileSystem; |
| 90 | 90 | const path = yield* Path.Path; |
| 91 | 91 | const spawner = yield* ChildProcessSpawner.ChildProcessSpawner; |
| 92 | const host = yield* ProviderHost; | |
| 92 | const host = yield* ProviderHost.ProviderHost; | |
| 93 | 93 | const selfInvocation = yield* resolveSelfInvocation(); |
| 94 | 94 | const installation = yield* AntigravityInstallation.AntigravityInstallation; |
| 95 | 95 | const loggers = yield* ProviderEventLoggers.ProviderEventLoggers; |
apps/server/src/provider/Drivers/ClaudeDriver.ts+4-5| ... | ... | @@ -24,10 +24,10 @@ import { HttpClient } from "effect/http"; |
| 24 | 24 | import { ChildProcessSpawner } from "effect/process"; |
| 25 | 25 | |
| 26 | 26 | import { makeClaudeTextGeneration } from "../../textGeneration/ClaudeTextGeneration.ts"; |
| 27 | import { ProviderHost } from "@t3tools/provider-core/server/ProviderHost"; | |
| 27 | import * as ProviderHost from "@t3tools/provider-core/server/ProviderHost"; | |
| 28 | 28 | import * as ServerConfig from "../../config.ts"; |
| 29 | 29 | import { expandHomePath } from "@t3tools/provider-core/server/pathExpansion"; |
| 30 | import * as ProviderEventLoggers from "../ProviderEventLoggers.ts"; | |
| 30 | import * as ProviderEventLoggers from "@t3tools/provider-core/server/ProviderEventLoggers"; | |
| 31 | 31 | import { |
| 32 | 32 | createClaudeAdapterV2, |
| 33 | 33 | type ClaudeAdapterV2DriverEnv, |
| ... | ... | @@ -94,7 +94,7 @@ const UPDATE = makePackageManagedProviderMaintenanceResolver({ |
| 94 | 94 | |
| 95 | 95 | export type ClaudeDriverEnv = |
| 96 | 96 | | ClaudeAdapterV2DriverEnv |
| 97 | | ProviderHost | |
| 97 | | ProviderHost.ProviderHost | |
| 98 | 98 | | ChildProcessSpawner.ChildProcessSpawner |
| 99 | 99 | | ResetCreditCoordinator.ResetCreditCoordinator |
| 100 | 100 | | Crypto.Crypto |
| ... | ... | @@ -121,7 +121,6 @@ export const ClaudeDriver: ProviderDriver<ClaudeSettings, ClaudeDriverEnv> = { |
| 121 | 121 | const { cwd } = yield* ServerConfig.ServerConfig; |
| 122 | 122 | const httpClient = yield* HttpClient.HttpClient; |
| 123 | 123 | const resetCreditCoordinator = yield* ResetCreditCoordinator.ResetCreditCoordinator; |
| 124 | const host = yield* ProviderHost; | |
| 125 | 124 | const catalogService = yield* ModelCatalog.ModelCatalog; |
| 126 | 125 | const modelCatalog = catalogService |
| 127 | 126 | .current(DRIVER_KIND) |
| ... | ... | @@ -237,7 +236,7 @@ export const ClaudeDriver: ProviderDriver<ClaudeSettings, ClaudeDriverEnv> = { |
| 237 | 236 | Effect.provideService(Path.Path, path), |
| 238 | 237 | ); |
| 239 | 238 | |
| 240 | const snapshotSettings = makeProviderSnapshotSettingsSource(effectiveConfig, host.settings); | |
| 239 | const snapshotSettings = yield* makeProviderSnapshotSettingsSource(effectiveConfig); | |
| 241 | 240 | const snapshot = yield* makeManagedServerProvider<ProviderSnapshotSettings<ClaudeSettings>>({ |
| 242 | 241 | resolveMaintenance, |
| 243 | 242 | getSettings: snapshotSettings.getSettings, |
apps/server/src/provider/Drivers/CodexDriver.test.ts+3-3| ... | ... | @@ -26,7 +26,7 @@ import * as BackgroundPolicy from "../../background/BackgroundPolicy.ts"; |
| 26 | 26 | import * as ServerConfig from "../../config.ts"; |
| 27 | 27 | import * as ServerSettings from "../../serverSettings.ts"; |
| 28 | 28 | import * as ResetCreditCoordinator from "../resetCreditCoordinator.ts"; |
| 29 | import * as ProviderEventLoggers from "../ProviderEventLoggers.ts"; | |
| 29 | import * as ProviderEventLoggers from "@t3tools/provider-core/server/ProviderEventLoggers"; | |
| 30 | 30 | import * as ModelManifest from "../ModelManifest.ts"; |
| 31 | 31 | import { |
| 32 | 32 | createProviderVersionAdvisory, |
| ... | ... | @@ -36,7 +36,7 @@ import { |
| 36 | 36 | import { CodexDriver } from "./CodexDriver.ts"; |
| 37 | 37 | import * as CodexAdapterV2 from "../../orchestration-v2/Adapters/CodexAdapterV2.ts"; |
| 38 | 38 | import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator"; |
| 39 | import { ProviderAdapterV2RuntimePolicy } from "@t3tools/provider-core/server/ProviderAdapter"; | |
| 39 | import * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter"; | |
| 40 | 40 | import * as ProviderCredentialStore from "../ProviderCredentialStore.ts"; |
| 41 | 41 | import * as ProviderHostLive from "../ProviderHostLive.ts"; |
| 42 | 42 | |
| ... | ... | @@ -214,7 +214,7 @@ it.layer(layerTest)("CodexDriver", (it) => { |
| 214 | 214 | threadId, |
| 215 | 215 | providerSessionId: ProviderSessionId.make("managed-account-session"), |
| 216 | 216 | modelSelection: { instanceId, model: "gpt-5.4" }, |
| 217 | runtimePolicy: ProviderAdapterV2RuntimePolicy.make({ | |
| 217 | runtimePolicy: ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({ | |
| 218 | 218 | runtimeMode: "full-access", |
| 219 | 219 | interactionMode: "default", |
| 220 | 220 | cwd: serverConfig.stateDir, |
apps/server/src/provider/Drivers/CodexDriver.ts+4-5| ... | ... | @@ -31,10 +31,10 @@ import { HttpClient } from "effect/http"; |
| 31 | 31 | import { ChildProcessSpawner } from "effect/process"; |
| 32 | 32 | |
| 33 | 33 | import { makeCodexTextGeneration } from "../../textGeneration/CodexTextGeneration.ts"; |
| 34 | import { ProviderHost } from "@t3tools/provider-core/server/ProviderHost"; | |
| 34 | import * as ProviderHost from "@t3tools/provider-core/server/ProviderHost"; | |
| 35 | 35 | import * as ServerConfig from "../../config.ts"; |
| 36 | 36 | import { expandHomePath } from "@t3tools/provider-core/server/pathExpansion"; |
| 37 | import * as ProviderEventLoggers from "../ProviderEventLoggers.ts"; | |
| 37 | import * as ProviderEventLoggers from "@t3tools/provider-core/server/ProviderEventLoggers"; | |
| 38 | 38 | import { |
| 39 | 39 | createCodexAdapterV2, |
| 40 | 40 | type CodexAdapterV2DriverEnv, |
| ... | ... | @@ -109,7 +109,7 @@ function makeCodexMaintenanceResolver(sharedHomePath: string) { |
| 109 | 109 | */ |
| 110 | 110 | export type CodexDriverEnv = |
| 111 | 111 | | CodexAdapterV2DriverEnv |
| 112 | | ProviderHost | |
| 112 | | ProviderHost.ProviderHost | |
| 113 | 113 | | ChildProcessSpawner.ChildProcessSpawner |
| 114 | 114 | | ResetCreditCoordinator.ResetCreditCoordinator |
| 115 | 115 | | Crypto.Crypto |
| ... | ... | @@ -147,7 +147,6 @@ export const CodexDriver: ProviderDriver<CodexSettings, CodexDriverEnv> = { |
| 147 | 147 | const fileSystem = yield* FileSystem.FileSystem; |
| 148 | 148 | const pathService = yield* Path.Path; |
| 149 | 149 | const httpClient = yield* HttpClient.HttpClient; |
| 150 | const host = yield* ProviderHost; | |
| 151 | 150 | const modelCatalog = yield* ModelCatalog.ModelCatalog; |
| 152 | 151 | const currentCatalog = modelCatalog.current(DRIVER_KIND); |
| 153 | 152 | const processEnv = mergeProviderInstanceEnvironment(environment); |
| ... | ... | @@ -231,7 +230,7 @@ export const CodexDriver: ProviderDriver<CodexSettings, CodexDriverEnv> = { |
| 231 | 230 | ), |
| 232 | 231 | Effect.provideService(ChildProcessSpawner.ChildProcessSpawner, spawner), |
| 233 | 232 | ); |
| 234 | const snapshotSettings = makeProviderSnapshotSettingsSource(effectiveConfig, host.settings); | |
| 233 | const snapshotSettings = yield* makeProviderSnapshotSettingsSource(effectiveConfig); | |
| 235 | 234 | const snapshot = yield* makeManagedServerProvider<ProviderSnapshotSettings<CodexSettings>>({ |
| 236 | 235 | resolveMaintenance, |
| 237 | 236 | getSettings: snapshotSettings.getSettings, |
apps/server/src/provider/Drivers/CodexManagedProvider.ts+2-2| ... | ... | @@ -4,7 +4,7 @@ import * as Stream from "effect/Stream"; |
| 4 | 4 | import * as Option from "effect/Option"; |
| 5 | 5 | import { ChildProcessSpawner } from "effect/process"; |
| 6 | 6 | import { makeCodexTextGeneration } from "../../textGeneration/CodexTextGeneration.ts"; |
| 7 | import { ProviderHost } from "@t3tools/provider-core/server/ProviderHost"; | |
| 7 | import * as ProviderHost from "@t3tools/provider-core/server/ProviderHost"; | |
| 8 | 8 | import { chatGptModels } from "../CodexChatGptModels.ts"; |
| 9 | 9 | import { makeCodexManagedRuntime } from "../CodexManagedRuntime.ts"; |
| 10 | 10 | import { ProviderDriverError } from "../Errors.ts"; |
| ... | ... | @@ -31,7 +31,7 @@ export const makeManagedCodexProvider = Effect.fn("makeManagedCodexProvider")(fu |
| 31 | 31 | const { instanceId, enabled, displayName, accentColor, config } = input; |
| 32 | 32 | const http = yield* HttpClient.HttpClient; |
| 33 | 33 | const spawner = yield* ChildProcessSpawner.ChildProcessSpawner; |
| 34 | const host = yield* ProviderHost; | |
| 34 | const host = yield* ProviderHost.ProviderHost; | |
| 35 | 35 | const runtime = yield* makeCodexManagedRuntime({ |
| 36 | 36 | instanceId, |
| 37 | 37 | enabled, |
apps/server/src/provider/EventNdjsonLogger.ts+13-11| ... | ... | @@ -9,7 +9,7 @@ import * as NodeFS from "node:fs"; |
| 9 | 9 | import * as NodePath from "node:path"; |
| 10 | 10 | |
| 11 | 11 | import type { ThreadId } from "@t3tools/contracts"; |
| 12 | import type { EventNdjsonLogger } from "@t3tools/provider-core/server/ProviderEventLoggers"; | |
| 12 | import type * as ProviderEventLoggers from "@t3tools/provider-core/server/ProviderEventLoggers"; | |
| 13 | 13 | import { RotatingFileSink } from "@t3tools/shared/logging"; |
| 14 | 14 | import { errorTag } from "@t3tools/shared/observability"; |
| 15 | 15 | import * as Clock from "effect/Clock"; |
| ... | ... | @@ -21,7 +21,7 @@ import * as Scope from "effect/Scope"; |
| 21 | 21 | import * as SynchronizedRef from "effect/SynchronizedRef"; |
| 22 | 22 | |
| 23 | 23 | import { toSafeThreadAttachmentSegment } from "../attachmentStore.ts"; |
| 24 | import type { ResourceAttribution } from "../resourceTelemetry/ResourceAttribution.ts"; | |
| 24 | import type * as ResourceAttribution from "../resourceTelemetry/ResourceAttribution.ts"; | |
| 25 | 25 | |
| 26 | 26 | const MEBIBYTE = 1024 * 1024; |
| 27 | 27 | const DAY_MS = 24 * 60 * 60 * 1_000; |
| ... | ... | @@ -64,11 +64,9 @@ const transientAcpUpdates = new Set(["agent_message_chunk", "agent_thought_chunk |
| 64 | 64 | |
| 65 | 65 | export type EventNdjsonStream = "native" | "canonical" | "orchestration"; |
| 66 | 66 | |
| 67 | export type { EventNdjsonLogger }; | |
| 68 | ||
| 69 | 67 | export interface EventNdjsonLogStore { |
| 70 | 68 | readonly filePath: string; |
| 71 | readonly logger: (stream: EventNdjsonStream) => EventNdjsonLogger; | |
| 69 | readonly logger: (stream: EventNdjsonStream) => ProviderEventLoggers.EventNdjsonLogger; | |
| 72 | 70 | readonly close: () => Effect.Effect<void>; |
| 73 | 71 | } |
| 74 | 72 | |
| ... | ... | @@ -81,7 +79,7 @@ export interface EventNdjsonLogStoreOptions { |
| 81 | 79 | readonly retentionCheckIntervalMs?: number; |
| 82 | 80 | readonly maxBufferedBytes?: number; |
| 83 | 81 | readonly maxBufferedRecords?: number; |
| 84 | readonly attribution?: ResourceAttribution["Service"]; | |
| 82 | readonly attribution?: ResourceAttribution.ResourceAttribution["Service"]; | |
| 85 | 83 | } |
| 86 | 84 | |
| 87 | 85 | export interface EventNdjsonLoggerOptions extends EventNdjsonLogStoreOptions { |
| ... | ... | @@ -127,7 +125,7 @@ interface ResolvedOptions { |
| 127 | 125 | readonly retentionCheckIntervalMs: number; |
| 128 | 126 | readonly maxBufferedBytes: number; |
| 129 | 127 | readonly maxBufferedRecords: number; |
| 130 | readonly attribution: ResourceAttribution["Service"] | undefined; | |
| 128 | readonly attribution: ResourceAttribution.ResourceAttribution["Service"] | undefined; | |
| 131 | 129 | } |
| 132 | 130 | |
| 133 | 131 | export interface PendingRecord { |
| ... | ... | @@ -731,8 +729,8 @@ export const makeEventNdjsonLogStore = Effect.fnUntraced(function* ( |
| 731 | 729 | yield* Scope.close(timerScope, Exit.void); |
| 732 | 730 | }); |
| 733 | 731 | |
| 734 | const loggerViews = new Map<EventNdjsonStream, EventNdjsonLogger>(); | |
| 735 | const logger = (stream: EventNdjsonStream): EventNdjsonLogger => { | |
| 732 | const loggerViews = new Map<EventNdjsonStream, ProviderEventLoggers.EventNdjsonLogger>(); | |
| 733 | const logger = (stream: EventNdjsonStream): ProviderEventLoggers.EventNdjsonLogger => { | |
| 736 | 734 | const existing = loggerViews.get(stream); |
| 737 | 735 | if (existing) return existing; |
| 738 | 736 | |
| ... | ... | @@ -778,7 +776,11 @@ export const makeEventNdjsonLogStore = Effect.fnUntraced(function* ( |
| 778 | 776 | } |
| 779 | 777 | }); |
| 780 | 778 | |
| 781 | const view = { filePath, write, close: () => Effect.void } satisfies EventNdjsonLogger; | |
| 779 | const view = { | |
| 780 | filePath, | |
| 781 | write, | |
| 782 | close: () => Effect.void, | |
| 783 | } satisfies ProviderEventLoggers.EventNdjsonLogger; | |
| 782 | 784 | loggerViews.set(stream, view); |
| 783 | 785 | return view; |
| 784 | 786 | }; |
| ... | ... | @@ -789,7 +791,7 @@ export const makeEventNdjsonLogStore = Effect.fnUntraced(function* ( |
| 789 | 791 | export const makeEventNdjsonLogger = Effect.fnUntraced(function* ( |
| 790 | 792 | filePath: string, |
| 791 | 793 | options: EventNdjsonLoggerOptions, |
| 792 | ): Effect.fn.Return<EventNdjsonLogger | undefined> { | |
| 794 | ): Effect.fn.Return<ProviderEventLoggers.EventNdjsonLogger | undefined> { | |
| 793 | 795 | const store = yield* makeEventNdjsonLogStore(filePath, options).pipe( |
| 794 | 796 | Effect.catch((error) => |
| 795 | 797 | logWarning(error.message, { error }).pipe( |
apps/server/src/provider/ProviderEventLoggers.ts deleted-71| ... | ... | @@ -1,71 +0,0 @@ |
| 1 | /** | |
| 2 | * ProviderEventLoggers — single observability service that owns the shared | |
| 3 | * provider event log store and exposes its two runtime views: | |
| 4 | * | |
| 5 | * - `native` — provider-protocol events as the SDK emits them, written | |
| 6 | * from inside each `<X>Adapter` factory. | |
| 7 | * - `canonical` — runtime events after `ProviderService` has normalized | |
| 8 | * them onto `ProviderRuntimeEvent`. | |
| 9 | * | |
| 10 | * Why a service tag and not constructor options? | |
| 11 | * | |
| 12 | * - Adapters are now constructed *inside* drivers (`<X>Driver.create()`), | |
| 13 | * not at the boot Layer. There is no longer a single `make<X>AdapterLive(options)` | |
| 14 | * call site where we can hand an `EventNdjsonLogger` in by hand. | |
| 15 | * - Multiple driver instances per kind (`codex_personal`, `codex_work`) | |
| 16 | * must share one underlying log store — opening N writers against the | |
| 17 | * same rotating file would race the rotation logic. Owning the loggers on | |
| 18 | * a single tag keeps that invariant intact. | |
| 19 | * - Tests can swap one (or both) loggers with in-memory recorders by | |
| 20 | * `Layer.succeed(ProviderEventLoggers, { native, canonical })` instead of | |
| 21 | * juggling per-Layer option threading. | |
| 22 | * | |
| 23 | * Both fields are optional because observability must not prevent startup. | |
| 24 | * | |
| 25 | * @module provider/ProviderEventLoggers | |
| 26 | */ | |
| 27 | import * as Effect from "effect/Effect"; | |
| 28 | import * as Layer from "effect/Layer"; | |
| 29 | ||
| 30 | import * as ServerConfig from "../config.ts"; | |
| 31 | import * as ResourceAttribution from "../resourceTelemetry/ResourceAttribution.ts"; | |
| 32 | import { | |
| 33 | NoOpProviderEventLoggers, | |
| 34 | ProviderEventLoggers, | |
| 35 | } from "@t3tools/provider-core/server/ProviderEventLoggers"; | |
| 36 | import * as EventNdjsonLogger from "./EventNdjsonLogger.ts"; | |
| 37 | ||
| 38 | export { NoOpProviderEventLoggers, ProviderEventLoggers }; | |
| 39 | ||
| 40 | /** | |
| 41 | * Builds both stream views over one shared store. Setup failures are logged | |
| 42 | * and downgraded to the no-op service so diagnostics never block startup. | |
| 43 | * | |
| 44 | * @public Service construction is part of the canonical Effect module API. | |
| 45 | */ | |
| 46 | export const make = Effect.gen(function* () { | |
| 47 | const { providerEventLogPath } = yield* ServerConfig.ServerConfig; | |
| 48 | const attribution = yield* ResourceAttribution.ResourceAttribution; | |
| 49 | const store = yield* EventNdjsonLogger.makeEventNdjsonLogStore(providerEventLogPath, { | |
| 50 | attribution, | |
| 51 | }).pipe( | |
| 52 | Effect.catch((error) => | |
| 53 | Effect.logWarning(error.message, { error }).pipe( | |
| 54 | Effect.annotateLogs({ scope: "provider-observability" }), | |
| 55 | Effect.as<EventNdjsonLogger.EventNdjsonLogStore | undefined>(undefined), | |
| 56 | ), | |
| 57 | ), | |
| 58 | ); | |
| 59 | ||
| 60 | if (!store) { | |
| 61 | return ProviderEventLoggers.of(NoOpProviderEventLoggers); | |
| 62 | } | |
| 63 | ||
| 64 | yield* Effect.addFinalizer(() => store.close()); | |
| 65 | return ProviderEventLoggers.of({ | |
| 66 | native: store.logger("native"), | |
| 67 | canonical: store.logger("canonical"), | |
| 68 | }); | |
| 69 | }); | |
| 70 | ||
| 71 | export const layer = Layer.effect(ProviderEventLoggers, make); |
apps/server/src/provider/ProviderHostLive.ts+4-4| ... | ... | @@ -1,10 +1,10 @@ |
| 1 | 1 | /** |
| 2 | * The server's implementation of `ProviderHost`, the only server surface | |
| 2 | * The server's implementation of `ProviderHost.ProviderHost`, the only server surface | |
| 3 | 3 | * provider drivers and adapters may use. |
| 4 | 4 | * |
| 5 | 5 | * @module provider/ProviderHostLive |
| 6 | 6 | */ |
| 7 | import { ProviderHost } from "@t3tools/provider-core/server/ProviderHost"; | |
| 7 | import * as ProviderHost from "@t3tools/provider-core/server/ProviderHost"; | |
| 8 | 8 | import { ProviderCredentialError } from "@t3tools/provider-core/server/errors"; |
| 9 | 9 | import * as Crypto from "effect/Crypto"; |
| 10 | 10 | import * as Effect from "effect/Effect"; |
| ... | ... | @@ -18,14 +18,14 @@ import * as ServerSettings from "../serverSettings.ts"; |
| 18 | 18 | import * as ProviderCredentialStore from "./ProviderCredentialStore.ts"; |
| 19 | 19 | |
| 20 | 20 | export const layer = Layer.effect( |
| 21 | ProviderHost, | |
| 21 | ProviderHost.ProviderHost, | |
| 22 | 22 | Effect.gen(function* () { |
| 23 | 23 | const config = yield* ServerConfig.ServerConfig; |
| 24 | 24 | const serverSettings = yield* ServerSettings.ServerSettingsService; |
| 25 | 25 | const backgroundPolicy = yield* BackgroundPolicy.BackgroundPolicy; |
| 26 | 26 | const secrets = yield* ServerSecretStore.ServerSecretStore; |
| 27 | 27 | const crypto = yield* Crypto.Crypto; |
| 28 | return ProviderHost.of({ | |
| 28 | return ProviderHost.ProviderHost.of({ | |
| 29 | 29 | paths: { |
| 30 | 30 | cwd: config.cwd, |
| 31 | 31 | baseDir: config.baseDir, |
apps/server/src/provider/ProviderInstanceRegistry.test.ts+1-1| ... | ... | @@ -61,7 +61,7 @@ import * as ModelManifest from "./ModelManifest.ts"; |
| 61 | 61 | import * as OpenCodeRuntime from "@t3tools/provider-opencode/server/OpenCodeRuntime"; |
| 62 | 62 | import * as OpenCodeServerLedger from "@t3tools/provider-opencode/server/OpenCodeServerLedger"; |
| 63 | 63 | import * as ResetCreditCoordinator from "./resetCreditCoordinator.ts"; |
| 64 | import * as ProviderEventLoggers from "./ProviderEventLoggers.ts"; | |
| 64 | import * as ProviderEventLoggers from "@t3tools/provider-core/server/ProviderEventLoggers"; | |
| 65 | 65 | import { makeProviderInstanceRegistry } from "./ProviderInstanceRegistry.ts"; |
| 66 | 66 | import * as ProviderOrchestrationAdapterInfrastructure from "./ProviderOrchestrationAdapterInfrastructure.ts"; |
| 67 | 67 | import * as ProviderHostLive from "./ProviderHostLive.ts"; |
apps/server/src/provider/ProviderInstanceRegistryHydration.ts+3-3| ... | ... | @@ -40,7 +40,7 @@ import * as ProviderInstanceRegistryMutator from "./ProviderInstanceRegistryMuta |
| 40 | 40 | import * as ProviderOrchestrationAdapterInfrastructure from "./ProviderOrchestrationAdapterInfrastructure.ts"; |
| 41 | 41 | import * as AcpRegistrySupport from "@t3tools/provider-acp-registry/server/AcpRegistrySupport"; |
| 42 | 42 | import * as ProviderHostLive from "./ProviderHostLive.ts"; |
| 43 | import type { ProviderHost } from "@t3tools/provider-core/server/ProviderHost"; | |
| 43 | import type * as ProviderHost from "@t3tools/provider-core/server/ProviderHost"; | |
| 44 | 44 | import type * as BackgroundPolicy from "../background/BackgroundPolicy.ts"; |
| 45 | 45 | import type * as ServerConfig from "../config.ts"; |
| 46 | 46 | |
| ... | ... | @@ -49,10 +49,10 @@ type ProviderInstanceRegistryHydrationEnv = |
| 49 | 49 | BuiltInDriversEnv, |
| 50 | 50 | | ProviderOrchestrationAdapterInfrastructure.ProviderOrchestrationAdapterInfrastructure |
| 51 | 51 | | AcpRegistrySupport.AcpRegistryCatalog |
| 52 | | ProviderHost | |
| 52 | | ProviderHost.ProviderHost | |
| 53 | 53 | > |
| 54 | 54 | | Settings.ServerSettingsService |
| 55 | // Requirements of the `ProviderHost` the drivers receive. | |
| 55 | // Requirements of the `ProviderHost.ProviderHost` the drivers receive. | |
| 56 | 56 | | BackgroundPolicy.BackgroundPolicy |
| 57 | 57 | | ServerConfig.ServerConfig; |
| 58 | 58 |
apps/server/src/provider/ProviderOrchestrationAdapterInfrastructure.ts+1-1| ... | ... | @@ -4,7 +4,7 @@ import * as ClaudeAdapterV2 from "../orchestration-v2/Adapters/ClaudeAdapterV2.t |
| 4 | 4 | import * as CodexAdapterV2 from "../orchestration-v2/Adapters/CodexAdapterV2.ts"; |
| 5 | 5 | import * as CursorAgentSdk from "@t3tools/provider-cursor/server/CursorAgentSdk"; |
| 6 | 6 | import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator"; |
| 7 | import * as ProviderContinuationRequests from "@t3tools/provider-core/server/continuationRequests"; | |
| 7 | import * as ProviderContinuationRequests from "@t3tools/provider-core/server/ProviderContinuationRequests"; | |
| 8 | 8 | |
| 9 | 9 | export type ProviderOrchestrationAdapterInfrastructure = |
| 10 | 10 | | ClaudeAdapterV2.ClaudeAgentSdkQueryRunner |
apps/server/src/provider/ProviderRegistry.test.ts+1-1| ... | ... | @@ -47,7 +47,7 @@ import { applyProviderCompatibility } from "./providerCompatibility.ts"; |
| 47 | 47 | import * as ResetCreditCoordinator from "./resetCreditCoordinator.ts"; |
| 48 | 48 | import * as OpenCodeRuntime from "@t3tools/provider-opencode/server/OpenCodeRuntime"; |
| 49 | 49 | import * as OpenCodeServerLedger from "@t3tools/provider-opencode/server/OpenCodeServerLedger"; |
| 50 | import * as ProviderEventLoggers from "./ProviderEventLoggers.ts"; | |
| 50 | import * as ProviderEventLoggers from "@t3tools/provider-core/server/ProviderEventLoggers"; | |
| 51 | 51 | import * as ProviderInstanceRegistryHydration from "./ProviderInstanceRegistryHydration.ts"; |
| 52 | 52 | import * as ServerConfig from "../config.ts"; |
| 53 | 53 | import * as ServerSettingsModule from "../serverSettings.ts"; |
apps/server/src/server.ts+29-7| ... | ... | @@ -1,3 +1,4 @@ |
| 1 | import * as ProviderEventLoggers from "@t3tools/provider-core/server/ProviderEventLoggers"; | |
| 1 | 2 | import type { RelayManagedEndpointRuntimeConfig } from "@t3tools/contracts/relay"; |
| 2 | 3 | import * as Clock from "effect/Clock"; |
| 3 | 4 | import * as Random from "effect/Random"; |
| ... | ... | @@ -42,7 +43,7 @@ import * as AnalyticsService from "./telemetry/AnalyticsService.ts"; |
| 42 | 43 | import * as ProviderEventIngestor from "./orchestration-v2/ProviderEventIngestor.ts"; |
| 43 | 44 | import * as ModelManifest from "./provider/ModelManifest.ts"; |
| 44 | 45 | import * as ResetCreditCoordinator from "./provider/resetCreditCoordinator.ts"; |
| 45 | import * as ProviderEventLoggers from "./provider/ProviderEventLoggers.ts"; | |
| 46 | import * as EventNdjsonLogger from "./provider/EventNdjsonLogger.ts"; | |
| 46 | 47 | import * as OpenCodeRuntime from "@t3tools/provider-opencode/server/OpenCodeRuntime"; |
| 47 | 48 | import * as OpenCodeServerLedger from "@t3tools/provider-opencode/server/OpenCodeServerLedger"; |
| 48 | 49 | import * as ProviderHostLive from "./provider/ProviderHostLive.ts"; |
| ... | ... | @@ -193,6 +194,30 @@ const layerApplicationObservability = EventLoopMonitor.layer.pipe( |
| 193 | 194 | Layer.provideMerge(layerResourceAttribution), |
| 194 | 195 | ); |
| 195 | 196 | |
| 197 | // One rotating store behind both logger views, so driver instances never race | |
| 198 | // its rotation. Diagnostics must not block startup: a store that cannot open | |
| 199 | // degrades to the no-op loggers. | |
| 200 | const layerProviderEventLoggers = Layer.effect( | |
| 201 | ProviderEventLoggers.ProviderEventLoggers, | |
| 202 | Effect.gen(function* () { | |
| 203 | const { providerEventLogPath } = yield* ServerConfig.ServerConfig; | |
| 204 | const attribution = yield* ResourceAttribution.ResourceAttribution; | |
| 205 | const store = yield* EventNdjsonLogger.makeEventNdjsonLogStore(providerEventLogPath, { | |
| 206 | attribution, | |
| 207 | }).pipe( | |
| 208 | Effect.catch((error) => | |
| 209 | Effect.logWarning(error.message, { error }).pipe( | |
| 210 | Effect.annotateLogs({ scope: "provider-observability" }), | |
| 211 | Effect.as(undefined), | |
| 212 | ), | |
| 213 | ), | |
| 214 | ); | |
| 215 | if (!store) return ProviderEventLoggers.NoOpProviderEventLoggers; | |
| 216 | yield* Effect.addFinalizer(() => store.close()); | |
| 217 | return { native: store.logger("native"), canonical: store.logger("canonical") }; | |
| 218 | }), | |
| 219 | ); | |
| 220 | ||
| 196 | 221 | const layerPtyAdapter = NodePtyAdapter.layer; |
| 197 | 222 | |
| 198 | 223 | const layerServerSettings = ServerSettings.layer.pipe( |
| ... | ... | @@ -599,17 +624,14 @@ const layerRuntimeCoreDependencies = layerRuntimeCoreDependenciesBase.pipe( |
| 599 | 624 | // Search, prepare, status inspection, and turn launch share one registry |
| 600 | 625 | // cache so every client and provider instance sees the same prepared agents. |
| 601 | 626 | Layer.provideMerge(AcpRegistrySupport.layerFromHost.pipe(Layer.provide(ProviderHostLive.layer))), |
| 602 | // Shared native/canonical NDJSON writers used by both the per-instance | |
| 603 | // V2 drivers and the orchestration runtime. Provide resource attribution so | |
| 604 | // the rewritten telemetry pipeline can account for logical NDJSON writes. | |
| 605 | // Provided once at the runtime level so every consumer sees the same | |
| 606 | // logger instances. | |
| 627 | // Provider event loggers are shared by every driver instance and the | |
| 628 | // orchestration runtime, so they are provided once here. | |
| 607 | 629 | // `ModelManifest.layer` is the model manifest, refreshed from the repo's |
| 608 | 630 | // `model-manifest.json` on `main`. Drivers read their entry through the |
| 609 | 631 | // `ModelCatalog` port it provides. |
| 610 | 632 | Layer.provideMerge( |
| 611 | 633 | Layer.mergeAll( |
| 612 | ProviderEventLoggers.layer, | |
| 634 | layerProviderEventLoggers, | |
| 613 | 635 | ModelManifest.layerModelCatalog.pipe(Layer.provideMerge(ModelManifest.layer)), |
| 614 | 636 | ResetCreditCoordinator.layer, |
| 615 | 637 | ), |
packages/provider-acp-registry/src/server/auth.ts+1-1| ... | ... | @@ -16,7 +16,7 @@ import { ChildProcessSpawner } from "effect/process"; |
| 16 | 16 | import type * as AcpSchema from "effect-acp/compat"; |
| 17 | 17 | |
| 18 | 18 | import * as PtyAdapter from "@t3tools/shared/PtyAdapter"; |
| 19 | import * as ProviderAuthFlow from "@t3tools/provider-core/server/ProviderAuthFlow"; | |
| 19 | import * as ProviderAuthFlow from "@t3tools/provider-core/server/providerAuthFlow"; | |
| 20 | 20 | import { normalizeAcpRegistryAuthMethods, normalizeAcpRegistryWebUrl } from "./probe.ts"; |
| 21 | 21 | import * as AcpRegistrySupport from "./AcpRegistrySupport.ts"; |
| 22 | 22 | import * as AcpRegistryRuntimeCoordinator from "./AcpRegistryRuntimeCoordinator.ts"; |
packages/provider-acp-registry/src/server/driver.ts+4-4| ... | ... | @@ -21,7 +21,7 @@ import * as Schema from "effect/Schema"; |
| 21 | 21 | import * as Semaphore from "effect/Semaphore"; |
| 22 | 22 | import { ChildProcessSpawner } from "effect/process"; |
| 23 | 23 | |
| 24 | import { ProviderHost } from "@t3tools/provider-core/server/ProviderHost"; | |
| 24 | import * as ProviderHost from "@t3tools/provider-core/server/ProviderHost"; | |
| 25 | 25 | import { AcpRegistryAdapterV2Driver, type AcpRegistryAdapterV2DriverEnv } from "./adapter.ts"; |
| 26 | 26 | import type { ProviderTextGeneration } from "@t3tools/provider-core/server/textGeneration"; |
| 27 | 27 | import { ProviderDriverError } from "@t3tools/provider-core/server/errors"; |
| ... | ... | @@ -455,7 +455,7 @@ export const checkAcpRegistryProviderReadiness = Effect.fn( |
| 455 | 455 | : snapshot; |
| 456 | 456 | }); |
| 457 | 457 | |
| 458 | export type AcpRegistryDriverEnv = AcpRegistryAdapterV2DriverEnv | ProviderHost; | |
| 458 | export type AcpRegistryDriverEnv = AcpRegistryAdapterV2DriverEnv | ProviderHost.ProviderHost; | |
| 459 | 459 | |
| 460 | 460 | /** Canonical provider-instance wrapper for ACP Registry orchestration adapters. */ |
| 461 | 461 | export const AcpRegistryDriver: ProviderDriver<AcpRegistrySettings, AcpRegistryDriverEnv> = { |
| ... | ... | @@ -485,7 +485,7 @@ export const AcpRegistryDriver: ProviderDriver<AcpRegistrySettings, AcpRegistryD |
| 485 | 485 | const crypto = yield* Crypto.Crypto; |
| 486 | 486 | const spawner = yield* ChildProcessSpawner.ChildProcessSpawner; |
| 487 | 487 | const hostEnvironment = yield* HostProcessEnvironment; |
| 488 | const host = yield* ProviderHost; | |
| 488 | const host = yield* ProviderHost.ProviderHost; | |
| 489 | 489 | const continuationIdentity = defaultProviderContinuationIdentity({ |
| 490 | 490 | driverKind: DRIVER_KIND, |
| 491 | 491 | instanceId, |
| ... | ... | @@ -619,7 +619,7 @@ export const AcpRegistryDriver: ProviderDriver<AcpRegistrySettings, AcpRegistryD |
| 619 | 619 | } |
| 620 | 620 | return { provider: enriched, generation: cacheState.generation }; |
| 621 | 621 | }); |
| 622 | const snapshotSettings = makeProviderSnapshotSettingsSource(effectiveConfig, host.settings); | |
| 622 | const snapshotSettings = yield* makeProviderSnapshotSettingsSource(effectiveConfig); | |
| 623 | 623 | const snapshot = yield* makeManagedServerProvider< |
| 624 | 624 | ProviderSnapshotSettings<AcpRegistrySettings> |
| 625 | 625 | >({ |
packages/provider-acp/src/server/adapter.ts+5-4| ... | ... | @@ -95,7 +95,7 @@ import { |
| 95 | 95 | } from "@t3tools/provider-core/server/orchestrationInstructions"; |
| 96 | 96 | import { buildRuntimeInstructions } from "@t3tools/provider-core/server/runtimeInstructions"; |
| 97 | 97 | import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator"; |
| 98 | import { type ProviderContinuationRequest } from "@t3tools/provider-core/server/continuationRequests"; | |
| 98 | import type * as ProviderContinuationRequests from "@t3tools/provider-core/server/ProviderContinuationRequests"; | |
| 99 | 99 | import { |
| 100 | 100 | type BackgroundWork, |
| 101 | 101 | type BackgroundWorkReport, |
| ... | ... | @@ -499,7 +499,9 @@ export interface AcpAdapterV2Options { |
| 499 | 499 | * / history-only behavior for null-activeTurn updates. |
| 500 | 500 | */ |
| 501 | 501 | readonly continuationRequests?: { |
| 502 | readonly offer: (request: ProviderContinuationRequest) => Effect.Effect<void>; | |
| 502 | readonly offer: ( | |
| 503 | request: ProviderContinuationRequests.ProviderContinuationRequest, | |
| 504 | ) => Effect.Effect<void>; | |
| 503 | 505 | }; |
| 504 | 506 | readonly testHooks?: { |
| 505 | 507 | /** |
| ... | ... | @@ -743,7 +745,7 @@ function nativeThreadId( |
| 743 | 745 | function makeProviderThread(input: { |
| 744 | 746 | readonly driver: ProviderDriverKind; |
| 745 | 747 | readonly providerInstanceId: ProviderInstanceId; |
| 746 | readonly idAllocator: IdAllocator.IdAllocatorV2Shape; | |
| 748 | readonly idAllocator: IdAllocator.IdAllocatorV2["Service"]; | |
| 747 | 749 | readonly appThreadId: OrchestrationV2ProviderThread["appThreadId"]; |
| 748 | 750 | readonly providerSessionId: OrchestrationV2ProviderThread["providerSessionId"]; |
| 749 | 751 | readonly nativeThreadId: string; |
| ... | ... | @@ -2226,7 +2228,6 @@ export const makeAcpAdapterV2 = Effect.fn("makeAcpAdapterV2")(function* ( |
| 2226 | 2228 | type: "turn_item.updated", |
| 2227 | 2229 | driver, |
| 2228 | 2230 | turnItem: makeProviderRetryTurnItem({ |
| 2229 | idAllocator, | |
| 2230 | 2231 | driver, |
| 2231 | 2232 | threadId: context.input.threadId, |
| 2232 | 2233 | runId: context.input.runId, |
packages/provider-core/package.json+7-7| ... | ... | @@ -23,10 +23,6 @@ |
| 23 | 23 | "types": "./src/server/collectStreamText.ts", |
| 24 | 24 | "import": "./src/server/collectStreamText.ts" |
| 25 | 25 | }, |
| 26 | "./server/continuationRequests": { | |
| 27 | "types": "./src/server/continuationRequests.ts", | |
| 28 | "import": "./src/server/continuationRequests.ts" | |
| 29 | }, | |
| 30 | 26 | "./server/driver": { |
| 31 | 27 | "types": "./src/server/driver.ts", |
| 32 | 28 | "import": "./src/server/driver.ts" |
| ... | ... | @@ -103,9 +99,13 @@ |
| 103 | 99 | "types": "./src/server/ProviderAdapter.ts", |
| 104 | 100 | "import": "./src/server/ProviderAdapter.ts" |
| 105 | 101 | }, |
| 106 | "./server/ProviderAuthFlow": { | |
| 107 | "types": "./src/server/ProviderAuthFlow.ts", | |
| 108 | "import": "./src/server/ProviderAuthFlow.ts" | |
| 102 | "./server/providerAuthFlow": { | |
| 103 | "types": "./src/server/providerAuthFlow.ts", | |
| 104 | "import": "./src/server/providerAuthFlow.ts" | |
| 105 | }, | |
| 106 | "./server/ProviderContinuationRequests": { | |
| 107 | "types": "./src/server/ProviderContinuationRequests.ts", | |
| 108 | "import": "./src/server/ProviderContinuationRequests.ts" | |
| 109 | 109 | }, |
| 110 | 110 | "./server/ProviderEventLoggers": { |
| 111 | 111 | "types": "./src/server/ProviderEventLoggers.ts", |
packages/provider-core/src/server/IdAllocator.ts+53-55| ... | ... | @@ -67,7 +67,7 @@ export class IdAllocatorV2AllocationError extends Schema.TaggedError<IdAllocator |
| 67 | 67 | export const IdAllocatorV2Error = Schema.Union([IdAllocatorV2AllocationError]); |
| 68 | 68 | export type IdAllocatorV2Error = typeof IdAllocatorV2Error.Type; |
| 69 | 69 | |
| 70 | export interface IdAllocatorV2AllocateShape { | |
| 70 | export interface IdAllocatorV2Allocate { | |
| 71 | 71 | readonly command: (input: { |
| 72 | 72 | readonly fixtureName: string; |
| 73 | 73 | readonly commandName: string; |
| ... | ... | @@ -126,7 +126,7 @@ export interface IdAllocatorV2AllocateShape { |
| 126 | 126 | }) => Effect.Effect<PlanId, IdAllocatorV2Error>; |
| 127 | 127 | } |
| 128 | 128 | |
| 129 | export interface IdAllocatorV2DeriveShape { | |
| 129 | export interface IdAllocatorV2Derive { | |
| 130 | 130 | readonly providerSession: (input: { |
| 131 | 131 | readonly providerInstanceId: ProviderInstanceId; |
| 132 | 132 | }) => ProviderSessionId; |
| ... | ... | @@ -180,14 +180,13 @@ export interface IdAllocatorV2DeriveShape { |
| 180 | 180 | readonly approvalTurnItem: (input: { readonly requestId: RuntimeRequestId }) => TurnItemId; |
| 181 | 181 | } |
| 182 | 182 | |
| 183 | export interface IdAllocatorV2Shape { | |
| 184 | readonly allocate: IdAllocatorV2AllocateShape; | |
| 185 | readonly derive: IdAllocatorV2DeriveShape; | |
| 186 | } | |
| 187 | ||
| 188 | export class IdAllocatorV2 extends Context.Service<IdAllocatorV2, IdAllocatorV2Shape>()( | |
| 189 | "@t3tools/provider-core/server/IdAllocator/IdAllocatorV2", | |
| 190 | ) {} | |
| 183 | export class IdAllocatorV2 extends Context.Service< | |
| 184 | IdAllocatorV2, | |
| 185 | { | |
| 186 | readonly allocate: IdAllocatorV2Allocate; | |
| 187 | readonly derive: IdAllocatorV2Derive; | |
| 188 | } | |
| 189 | >()("@t3tools/provider-core/server/IdAllocator/IdAllocatorV2") {} | |
| 191 | 190 | |
| 192 | 191 | const encodePart = (part: string | number): string => encodeURIComponent(String(part)); |
| 193 | 192 | |
| ... | ... | @@ -252,6 +251,49 @@ const randomId = |
| 252 | 251 | ), |
| 253 | 252 | ); |
| 254 | 253 | |
| 254 | /** Deterministic id derivations; pure, so callers that only derive need no allocator. */ | |
| 255 | export const derive: IdAllocatorV2Derive = { | |
| 256 | providerSession: (input) => | |
| 257 | ProviderSessionId.make( | |
| 258 | joinId("provider-session", "provider-instance", input.providerInstanceId, "shared"), | |
| 259 | ), | |
| 260 | delegatedTaskNode: (input) => NodeId.make(joinId("node", "delegated-task", input.commandId)), | |
| 261 | delegatedTaskThread: (input) => | |
| 262 | ThreadId.make(joinId("thread", "delegated-task", input.commandId)), | |
| 263 | delegatedTaskMessage: (input) => | |
| 264 | MessageId.make(joinId("message", "delegated-task", input.commandId)), | |
| 265 | delegatedTaskTurnItem: (input) => | |
| 266 | TurnItemId.make(joinId("turn-item", "delegated-task", input.commandId)), | |
| 267 | createdThreadTurnItem: (input) => | |
| 268 | TurnItemId.make(joinId("turn-item", "created-thread", input.commandId)), | |
| 269 | threadFromProviderThread: deriveThreadFromProviderThread, | |
| 270 | run: (input) => RunId.make(joinId("run", "thread", input.threadId, "ordinal", input.ordinal)), | |
| 271 | runAttempt: (input) => | |
| 272 | RunAttemptId.make(joinId("run-attempt", "run", input.runId, "attempt", input.attemptOrdinal)), | |
| 273 | rootNode: (input) => NodeId.make(joinId("node", "run", input.runId, "root")), | |
| 274 | rootNodeAttempt: (input) => | |
| 275 | NodeId.make(joinId("node", "run", input.runId, "attempt", input.attemptOrdinal, "root")), | |
| 276 | userTurnItem: (input) => TurnItemId.make(joinId("turn-item", "message", input.messageId)), | |
| 277 | runSignalTurnItem: (input) => | |
| 278 | TurnItemId.make(joinId("turn-item", "run", input.runId, "signal", input.signal)), | |
| 279 | providerThread: deriveProviderThread, | |
| 280 | providerTurn: (input) => | |
| 281 | ProviderTurnId.make( | |
| 282 | joinId("provider-turn", "provider", input.driver, "native-turn", input.nativeTurnId), | |
| 283 | ), | |
| 284 | nodeFromProviderItem: (input) => | |
| 285 | NodeId.make(joinId("node", "provider", input.driver, "native-item", input.nativeItemId)), | |
| 286 | messageFromProviderItem: (input) => | |
| 287 | MessageId.make(joinId("message", "provider", input.driver, "native-item", input.nativeItemId)), | |
| 288 | turnItemFromProviderItem: (input) => | |
| 289 | TurnItemId.make( | |
| 290 | joinId("turn-item", "provider", input.driver, "native-item", input.nativeItemId), | |
| 291 | ), | |
| 292 | approvalNode: (input) => NodeId.make(joinId("node", "runtime-request", input.requestId)), | |
| 293 | approvalTurnItem: (input) => | |
| 294 | TurnItemId.make(joinId("turn-item", "runtime-request", input.requestId)), | |
| 295 | }; | |
| 296 | ||
| 255 | 297 | export const layer: Layer.Layer<IdAllocatorV2> = Layer.succeed( |
| 256 | 298 | IdAllocatorV2, |
| 257 | 299 | IdAllocatorV2.of({ |
| ... | ... | @@ -386,50 +428,6 @@ export const layer: Layer.Layer<IdAllocatorV2> = Layer.succeed( |
| 386 | 428 | make: PlanId.make, |
| 387 | 429 | })(input), |
| 388 | 430 | }, |
| 389 | derive: { | |
| 390 | providerSession: (input) => | |
| 391 | ProviderSessionId.make( | |
| 392 | joinId("provider-session", "provider-instance", input.providerInstanceId, "shared"), | |
| 393 | ), | |
| 394 | delegatedTaskNode: (input) => NodeId.make(joinId("node", "delegated-task", input.commandId)), | |
| 395 | delegatedTaskThread: (input) => | |
| 396 | ThreadId.make(joinId("thread", "delegated-task", input.commandId)), | |
| 397 | delegatedTaskMessage: (input) => | |
| 398 | MessageId.make(joinId("message", "delegated-task", input.commandId)), | |
| 399 | delegatedTaskTurnItem: (input) => | |
| 400 | TurnItemId.make(joinId("turn-item", "delegated-task", input.commandId)), | |
| 401 | createdThreadTurnItem: (input) => | |
| 402 | TurnItemId.make(joinId("turn-item", "created-thread", input.commandId)), | |
| 403 | threadFromProviderThread: deriveThreadFromProviderThread, | |
| 404 | run: (input) => RunId.make(joinId("run", "thread", input.threadId, "ordinal", input.ordinal)), | |
| 405 | runAttempt: (input) => | |
| 406 | RunAttemptId.make( | |
| 407 | joinId("run-attempt", "run", input.runId, "attempt", input.attemptOrdinal), | |
| 408 | ), | |
| 409 | rootNode: (input) => NodeId.make(joinId("node", "run", input.runId, "root")), | |
| 410 | rootNodeAttempt: (input) => | |
| 411 | NodeId.make(joinId("node", "run", input.runId, "attempt", input.attemptOrdinal, "root")), | |
| 412 | userTurnItem: (input) => TurnItemId.make(joinId("turn-item", "message", input.messageId)), | |
| 413 | runSignalTurnItem: (input) => | |
| 414 | TurnItemId.make(joinId("turn-item", "run", input.runId, "signal", input.signal)), | |
| 415 | providerThread: deriveProviderThread, | |
| 416 | providerTurn: (input) => | |
| 417 | ProviderTurnId.make( | |
| 418 | joinId("provider-turn", "provider", input.driver, "native-turn", input.nativeTurnId), | |
| 419 | ), | |
| 420 | nodeFromProviderItem: (input) => | |
| 421 | NodeId.make(joinId("node", "provider", input.driver, "native-item", input.nativeItemId)), | |
| 422 | messageFromProviderItem: (input) => | |
| 423 | MessageId.make( | |
| 424 | joinId("message", "provider", input.driver, "native-item", input.nativeItemId), | |
| 425 | ), | |
| 426 | turnItemFromProviderItem: (input) => | |
| 427 | TurnItemId.make( | |
| 428 | joinId("turn-item", "provider", input.driver, "native-item", input.nativeItemId), | |
| 429 | ), | |
| 430 | approvalNode: (input) => NodeId.make(joinId("node", "runtime-request", input.requestId)), | |
| 431 | approvalTurnItem: (input) => | |
| 432 | TurnItemId.make(joinId("turn-item", "runtime-request", input.requestId)), | |
| 433 | }, | |
| 431 | derive, | |
| 434 | 432 | }), |
| 435 | 433 | ); |
packages/provider-core/src/server/ProviderAdapter.ts+17-18| ... | ... | @@ -632,21 +632,20 @@ export interface ProviderAdapterV2SessionRuntime { |
| 632 | 632 | ) => Effect.Effect<OrchestrationV2ProviderThread, ProviderAdapterV2Error>; |
| 633 | 633 | } |
| 634 | 634 | |
| 635 | export interface ProviderAdapterV2Shape { | |
| 636 | readonly instanceId: ProviderInstanceId; | |
| 637 | readonly driver: ProviderDriverKind; | |
| 638 | readonly getCapabilities: () => Effect.Effect< | |
| 639 | OrchestrationV2ProviderCapabilities, | |
| 640 | ProviderAdapterV2Error | |
| 641 | >; | |
| 642 | readonly planSelectionTransition: ( | |
| 643 | input: ProviderSelectionTransitionInput, | |
| 644 | ) => Effect.Effect<ProviderSelectionTransitionPlan, ProviderAdapterV2Error>; | |
| 645 | readonly openSession: ( | |
| 646 | input: ProviderAdapterV2OpenSessionInput, | |
| 647 | ) => Effect.Effect<ProviderAdapterV2SessionRuntime, ProviderAdapterV2Error, Scope.Scope>; | |
| 648 | } | |
| 649 | ||
| 650 | export class ProviderAdapterV2 extends Context.Service<ProviderAdapterV2, ProviderAdapterV2Shape>()( | |
| 651 | "@t3tools/provider-core/server/ProviderAdapter/ProviderAdapterV2", | |
| 652 | ) {} | |
| 635 | export class ProviderAdapterV2 extends Context.Service< | |
| 636 | ProviderAdapterV2, | |
| 637 | { | |
| 638 | readonly instanceId: ProviderInstanceId; | |
| 639 | readonly driver: ProviderDriverKind; | |
| 640 | readonly getCapabilities: () => Effect.Effect< | |
| 641 | OrchestrationV2ProviderCapabilities, | |
| 642 | ProviderAdapterV2Error | |
| 643 | >; | |
| 644 | readonly planSelectionTransition: ( | |
| 645 | input: ProviderSelectionTransitionInput, | |
| 646 | ) => Effect.Effect<ProviderSelectionTransitionPlan, ProviderAdapterV2Error>; | |
| 647 | readonly openSession: ( | |
| 648 | input: ProviderAdapterV2OpenSessionInput, | |
| 649 | ) => Effect.Effect<ProviderAdapterV2SessionRuntime, ProviderAdapterV2Error, Scope.Scope>; | |
| 650 | } | |
| 651 | >()("@t3tools/provider-core/server/ProviderAdapter/ProviderAdapterV2") {} |
packages/provider-core/src/server/ProviderAuthFlow.test.ts deleted-559| ... | ... | @@ -1,559 +0,0 @@ |
| 1 | import * as NodeServices from "@effect/platform-node/NodeServices"; | |
| 2 | import { assert, it } from "@effect/vitest"; | |
| 3 | import { | |
| 4 | ProviderInstanceId, | |
| 5 | ProviderSetupError, | |
| 6 | type ProviderAuthInteraction, | |
| 7 | type ProviderAuthResponse, | |
| 8 | type ProviderAuthState, | |
| 9 | } from "@t3tools/contracts"; | |
| 10 | import * as Fiber from "effect/Fiber"; | |
| 11 | import * as Exit from "effect/Exit"; | |
| 12 | import * as Scope from "effect/Scope"; | |
| 13 | import * as Deferred from "effect/Deferred"; | |
| 14 | import * as Effect from "effect/Effect"; | |
| 15 | import * as Option from "effect/Option"; | |
| 16 | import * as Stream from "effect/Stream"; | |
| 17 | import * as TestClock from "effect/testing/TestClock"; | |
| 18 | ||
| 19 | import * as ProviderAuthFlow from "./ProviderAuthFlow.ts"; | |
| 20 | ||
| 21 | const instanceId = ProviderInstanceId.make("auth-flow-test"); | |
| 22 | const method = { id: "browser", name: "Browser", description: null, type: "agent" as const }; | |
| 23 | ||
| 24 | it.effect("distinguishes pending method discovery from an agent with no sign-in methods", () => | |
| 25 | Effect.gen(function* () { | |
| 26 | const discovered = yield* Deferred.make<ReadonlyArray<typeof method>>(); | |
| 27 | const controller = yield* ProviderAuthFlow.make({ | |
| 28 | instanceId, | |
| 29 | credentialBinding: { owner: "provider", key: "shared-agent" }, | |
| 30 | methods: Deferred.await(discovered), | |
| 31 | authenticate: () => Effect.void, | |
| 32 | logout: Effect.void, | |
| 33 | }); | |
| 34 | const pending = yield* controller | |
| 35 | .subscribe("owner") | |
| 36 | .pipe(Stream.runHead, Effect.map(Option.getOrThrow)); | |
| 37 | assert.isUndefined(pending.methods); | |
| 38 | yield* Deferred.succeed(discovered, []); | |
| 39 | const ready = yield* controller.subscribe("owner").pipe( | |
| 40 | Stream.filter((state) => state.methods !== undefined), | |
| 41 | Stream.runHead, | |
| 42 | Effect.map(Option.getOrThrow), | |
| 43 | ); | |
| 44 | assert.deepEqual(ready.methods, []); | |
| 45 | }).pipe(Effect.scoped, Effect.provide(NodeServices.layer)), | |
| 46 | ); | |
| 47 | ||
| 48 | const makeHarness = Effect.gen(function* () { | |
| 49 | const approved = yield* Deferred.make<void>(); | |
| 50 | const verified = yield* Deferred.make<void>(); | |
| 51 | const started = yield* Deferred.make<void>(); | |
| 52 | let attempts = 0; | |
| 53 | const controller = yield* ProviderAuthFlow.make({ | |
| 54 | instanceId, | |
| 55 | credentialBinding: { owner: "provider", key: "shared-agent" }, | |
| 56 | methods: Effect.succeed([method]), | |
| 57 | authenticate: (_, context) => | |
| 58 | Effect.gen(function* () { | |
| 59 | attempts++; | |
| 60 | yield* context.setInteraction( | |
| 61 | { | |
| 62 | type: "browser", | |
| 63 | id: "consent", | |
| 64 | url: "https://example.com/login", | |
| 65 | requiresConsent: true, | |
| 66 | }, | |
| 67 | (response) => | |
| 68 | response.type === "browser" && response.action === "accept" | |
| 69 | ? Deferred.succeed(approved, undefined).pipe(Effect.asVoid) | |
| 70 | : Effect.void, | |
| 71 | ); | |
| 72 | yield* Deferred.succeed(started, undefined); | |
| 73 | yield* Deferred.await(approved); | |
| 74 | yield* context.verifying; | |
| 75 | yield* Deferred.await(verified); | |
| 76 | }), | |
| 77 | logout: Effect.void, | |
| 78 | }); | |
| 79 | const phase = (phase: ProviderAuthState["phase"], owner = "owner") => | |
| 80 | controller.subscribe(owner).pipe( | |
| 81 | Stream.filter((state) => state.phase === phase), | |
| 82 | Stream.runHead, | |
| 83 | Effect.map(Option.getOrThrow), | |
| 84 | ); | |
| 85 | return { controller, phase, started, verified, attempts: () => attempts }; | |
| 86 | }); | |
| 87 | ||
| 88 | it.effect("requires owner consent and provider verification before success", () => | |
| 89 | Effect.gen(function* () { | |
| 90 | const { controller, phase, started, verified, attempts } = yield* makeHarness; | |
| 91 | const state = yield* controller.start("owner"); | |
| 92 | yield* Deferred.await(started); | |
| 93 | const response = { | |
| 94 | instanceId, | |
| 95 | flowId: state.flowId!, | |
| 96 | interactionId: "consent", | |
| 97 | response: { type: "browser" as const, action: "accept" as const }, | |
| 98 | }; | |
| 99 | const other = yield* phase("waiting", "other"); | |
| 100 | assert.isNull(other.interaction); | |
| 101 | assert.isNull(other.flowId); | |
| 102 | assert.isTrue( | |
| 103 | (yield* controller.respond!("other", response).pipe(Effect.result))._tag === "Failure", | |
| 104 | ); | |
| 105 | assert.isTrue( | |
| 106 | (yield* controller.respond!("owner", { ...response, interactionId: "stale" }).pipe( | |
| 107 | Effect.result, | |
| 108 | ))._tag === "Failure", | |
| 109 | ); | |
| 110 | yield* controller.start("owner"); | |
| 111 | assert.strictEqual(attempts(), 1); | |
| 112 | yield* controller.respond!("owner", response); | |
| 113 | yield* phase("verifying"); | |
| 114 | yield* Deferred.succeed(verified, undefined); | |
| 115 | assert.strictEqual((yield* phase("succeeded")).phase, "succeeded"); | |
| 116 | }).pipe(Effect.scoped, Effect.provide(NodeServices.layer)), | |
| 117 | ); | |
| 118 | ||
| 119 | it.effect("cancellation rejects late consent and permits another login", () => | |
| 120 | Effect.gen(function* () { | |
| 121 | const { controller, started, phase } = yield* makeHarness; | |
| 122 | const state = yield* controller.start("owner"); | |
| 123 | yield* Deferred.await(started); | |
| 124 | yield* controller.cancel("owner", state.flowId!); | |
| 125 | assert.strictEqual((yield* phase("cancelled")).phase, "cancelled"); | |
| 126 | assert.isTrue( | |
| 127 | (yield* controller.respond!("owner", { | |
| 128 | instanceId, | |
| 129 | flowId: state.flowId!, | |
| 130 | interactionId: "consent", | |
| 131 | response: { type: "browser", action: "accept" }, | |
| 132 | }).pipe(Effect.result))._tag === "Failure", | |
| 133 | ); | |
| 134 | const next = yield* controller.start("other"); | |
| 135 | assert.notStrictEqual(next.flowId, state.flowId); | |
| 136 | }).pipe(Effect.scoped, Effect.provide(NodeServices.layer)), | |
| 137 | ); | |
| 138 | ||
| 139 | it.effect("expires pending login and rejects its response", () => | |
| 140 | Effect.gen(function* () { | |
| 141 | const { controller, started, phase } = yield* makeHarness; | |
| 142 | const state = yield* controller.start("owner"); | |
| 143 | yield* Deferred.await(started); | |
| 144 | yield* TestClock.adjust(300_001); | |
| 145 | assert.include((yield* phase("failed")).message ?? "", "expired"); | |
| 146 | assert.isTrue( | |
| 147 | (yield* controller.respond!("owner", { | |
| 148 | instanceId, | |
| 149 | flowId: state.flowId!, | |
| 150 | interactionId: "consent", | |
| 151 | response: { type: "browser", action: "accept" }, | |
| 152 | }).pipe(Effect.result))._tag === "Failure", | |
| 153 | ); | |
| 154 | }).pipe(Effect.scoped, Effect.provide(NodeServices.layer)), | |
| 155 | ); | |
| 156 | ||
| 157 | it.effect("closes admitted provider processes and blocks new ones while login is pending", () => | |
| 158 | Effect.gen(function* () { | |
| 159 | const { controller, started } = yield* makeHarness; | |
| 160 | let closed = false; | |
| 161 | yield* controller.withAccess!( | |
| 162 | Effect.addFinalizer(() => | |
| 163 | Effect.sync(() => { | |
| 164 | closed = true; | |
| 165 | }), | |
| 166 | ), | |
| 167 | ); | |
| 168 | yield* controller.start("owner"); | |
| 169 | yield* Deferred.await(started); | |
| 170 | assert.isTrue(closed); | |
| 171 | assert.isTrue( | |
| 172 | (yield* controller.withAccess!(Effect.void).pipe(Effect.result))._tag === "Failure", | |
| 173 | ); | |
| 174 | }).pipe(Effect.scoped, Effect.provide(NodeServices.layer)), | |
| 175 | ); | |
| 176 | ||
| 177 | it.effect("shared credential invalidation closes admitted processes before permitting reuse", () => | |
| 178 | Effect.gen(function* () { | |
| 179 | const { controller } = yield* makeHarness; | |
| 180 | let closed = false; | |
| 181 | yield* controller.withAccess!( | |
| 182 | Effect.addFinalizer(() => | |
| 183 | Effect.sync(() => { | |
| 184 | closed = true; | |
| 185 | }), | |
| 186 | ), | |
| 187 | ); | |
| 188 | yield* controller.invalidate!; | |
| 189 | assert.isTrue(closed); | |
| 190 | assert.strictEqual(yield* controller.withAccess!(Effect.succeed("new process")), "new process"); | |
| 191 | }).pipe(Effect.scoped, Effect.provide(NodeServices.layer)), | |
| 192 | ); | |
| 193 | ||
| 194 | it.effect.each([ | |
| 195 | { | |
| 196 | interaction: { type: "terminal", id: "terminal", output: "Log in" }, | |
| 197 | response: { type: "terminal", data: "input", size: { cols: 80, rows: 24 } }, | |
| 198 | }, | |
| 199 | { | |
| 200 | interaction: { | |
| 201 | type: "credentials", | |
| 202 | id: "credentials", | |
| 203 | fields: [{ name: "token", label: "Token", secret: true }], | |
| 204 | }, | |
| 205 | response: { type: "credentials", values: { token: "private-token" } }, | |
| 206 | }, | |
| 207 | ] satisfies ReadonlyArray<{ | |
| 208 | interaction: ProviderAuthInteraction; | |
| 209 | response: ProviderAuthResponse; | |
| 210 | }>)( | |
| 211 | "keeps $interaction.type responses private and waits for adapter verification", | |
| 212 | ({ interaction, response }) => | |
| 213 | Effect.gen(function* () { | |
| 214 | const received = yield* Deferred.make<ProviderAuthResponse>(); | |
| 215 | const controller = yield* ProviderAuthFlow.make({ | |
| 216 | instanceId, | |
| 217 | credentialBinding: { owner: "t3", key: "binding" }, | |
| 218 | methods: Effect.succeed([method]), | |
| 219 | authenticate: (_, context) => | |
| 220 | Effect.gen(function* () { | |
| 221 | yield* context.setInteraction(interaction, (response) => | |
| 222 | Deferred.succeed(received, response).pipe(Effect.asVoid), | |
| 223 | ); | |
| 224 | yield* Deferred.await(received); | |
| 225 | yield* context.verifying; | |
| 226 | return yield* Effect.never; | |
| 227 | }), | |
| 228 | logout: Effect.void, | |
| 229 | }); | |
| 230 | const state = yield* controller.start("owner"); | |
| 231 | const pending = yield* controller.subscribe("owner").pipe( | |
| 232 | Stream.filter((state) => state.phase === "waiting"), | |
| 233 | Stream.runHead, | |
| 234 | Effect.map(Option.getOrThrow), | |
| 235 | ); | |
| 236 | assert.deepStrictEqual(pending.interaction, interaction); | |
| 237 | const hidden = yield* controller | |
| 238 | .subscribe("other") | |
| 239 | .pipe(Stream.runHead, Effect.map(Option.getOrThrow)); | |
| 240 | assert.isNull(hidden.interaction); | |
| 241 | yield* controller.respond!("owner", { | |
| 242 | instanceId, | |
| 243 | flowId: state.flowId!, | |
| 244 | interactionId: interaction.id, | |
| 245 | response, | |
| 246 | }); | |
| 247 | assert.deepStrictEqual(yield* Deferred.await(received), response); | |
| 248 | const verifying = yield* controller.subscribe("owner").pipe( | |
| 249 | Stream.filter((state) => state.phase === "verifying"), | |
| 250 | Stream.runHead, | |
| 251 | Effect.map(Option.getOrThrow), | |
| 252 | ); | |
| 253 | assert.isNull(verifying.interaction); | |
| 254 | assert.notInclude(verifying.message ?? "", "private-token"); | |
| 255 | yield* controller.cancel("owner", state.flowId!); | |
| 256 | }).pipe(Effect.scoped, Effect.provide(NodeServices.layer)), | |
| 257 | ); | |
| 258 | ||
| 259 | it.effect("shows a device code only to its owner without treating it as authenticated", () => | |
| 260 | Effect.gen(function* () { | |
| 261 | const controller = yield* ProviderAuthFlow.make({ | |
| 262 | instanceId, | |
| 263 | credentialBinding: { owner: "provider", key: "device" }, | |
| 264 | methods: Effect.succeed([method]), | |
| 265 | authenticate: (_, context) => | |
| 266 | context | |
| 267 | .setInteraction({ | |
| 268 | type: "deviceCode", | |
| 269 | id: "code", | |
| 270 | url: "https://example.com/device", | |
| 271 | userCode: "ABCD-EFGH", | |
| 272 | }) | |
| 273 | .pipe(Effect.andThen(Effect.never)), | |
| 274 | logout: Effect.void, | |
| 275 | }); | |
| 276 | const start = yield* controller.start("owner"); | |
| 277 | const waiting = yield* controller.subscribe("owner").pipe( | |
| 278 | Stream.filter((state) => state.phase === "waiting"), | |
| 279 | Stream.runHead, | |
| 280 | Effect.map(Option.getOrThrow), | |
| 281 | ); | |
| 282 | assert.strictEqual(waiting.interaction?.type, "deviceCode"); | |
| 283 | assert.strictEqual(waiting.authorizationUrl, "https://example.com/device"); | |
| 284 | const other = yield* controller | |
| 285 | .subscribe("other") | |
| 286 | .pipe(Stream.runHead, Effect.map(Option.getOrThrow)); | |
| 287 | assert.isNull(other.interaction); | |
| 288 | assert.isNull(other.authorizationUrl); | |
| 289 | yield* controller.cancel("owner", start.flowId!); | |
| 290 | }).pipe(Effect.scoped, Effect.provide(NodeServices.layer)), | |
| 291 | ); | |
| 292 | ||
| 293 | it.effect.each([ | |
| 294 | { | |
| 295 | failure: Effect.fail( | |
| 296 | new ProviderSetupError({ | |
| 297 | instanceId, | |
| 298 | operation: "authenticate", | |
| 299 | detail: "This account cannot sign in. Choose another account.", | |
| 300 | }), | |
| 301 | ), | |
| 302 | message: "This account cannot sign in. Choose another account.", | |
| 303 | }, | |
| 304 | { | |
| 305 | failure: Effect.die(new Error("private-token in native diagnostics")), | |
| 306 | message: "Sign-in failed. Start again.", | |
| 307 | }, | |
| 308 | ])("publishes only safe authentication failure text %#", ({ failure, message }) => | |
| 309 | Effect.gen(function* () { | |
| 310 | const controller = yield* ProviderAuthFlow.make({ | |
| 311 | instanceId, | |
| 312 | credentialBinding: { owner: "t3", key: "failure" }, | |
| 313 | methods: Effect.succeed([method]), | |
| 314 | authenticate: () => failure, | |
| 315 | logout: Effect.void, | |
| 316 | }); | |
| 317 | yield* controller.start("owner"); | |
| 318 | const failed = yield* controller.subscribe("owner").pipe( | |
| 319 | Stream.filter((state) => state.phase === "failed"), | |
| 320 | Stream.runHead, | |
| 321 | Effect.map(Option.getOrThrow), | |
| 322 | ); | |
| 323 | assert.strictEqual(failed.message, message); | |
| 324 | }).pipe(Effect.scoped, Effect.provide(NodeServices.layer)), | |
| 325 | ); | |
| 326 | ||
| 327 | const makeBlockingResponseHarness = Effect.gen(function* () { | |
| 328 | const entered = yield* Deferred.make<void>(); | |
| 329 | const cleanupStarted = yield* Deferred.make<void>(); | |
| 330 | const cleanupReleased = yield* Deferred.make<void>(); | |
| 331 | const cleanupFinished = yield* Deferred.make<void>(); | |
| 332 | const authenticationFinished = yield* Deferred.make<void>(); | |
| 333 | let responses = 0; | |
| 334 | const controller = yield* ProviderAuthFlow.make({ | |
| 335 | instanceId, | |
| 336 | credentialBinding: { owner: "t3", key: "blocked-response" }, | |
| 337 | methods: Effect.succeed([method]), | |
| 338 | authenticate: (_, context) => | |
| 339 | Effect.gen(function* () { | |
| 340 | yield* context.setInteraction( | |
| 341 | { | |
| 342 | type: "browser", | |
| 343 | id: "blocked", | |
| 344 | url: "https://example.com/login", | |
| 345 | requiresConsent: true, | |
| 346 | }, | |
| 347 | () => | |
| 348 | Effect.gen(function* () { | |
| 349 | responses++; | |
| 350 | yield* Deferred.succeed(entered, undefined); | |
| 351 | return yield* Effect.never; | |
| 352 | }).pipe( | |
| 353 | Effect.ensuring( | |
| 354 | Effect.gen(function* () { | |
| 355 | yield* Deferred.succeed(cleanupStarted, undefined); | |
| 356 | yield* Deferred.await(cleanupReleased); | |
| 357 | yield* Deferred.succeed(cleanupFinished, undefined); | |
| 358 | }), | |
| 359 | ), | |
| 360 | ), | |
| 361 | ); | |
| 362 | yield* Deferred.await(authenticationFinished); | |
| 363 | }), | |
| 364 | logout: Effect.void, | |
| 365 | }); | |
| 366 | const start = yield* controller.start("owner"); | |
| 367 | yield* controller.subscribe("owner").pipe( | |
| 368 | Stream.filter((state) => state.phase === "waiting"), | |
| 369 | Stream.runHead, | |
| 370 | ); | |
| 371 | const input = { | |
| 372 | instanceId, | |
| 373 | flowId: start.flowId!, | |
| 374 | interactionId: "blocked", | |
| 375 | response: { type: "browser" as const, action: "accept" as const }, | |
| 376 | }; | |
| 377 | const response = yield* controller.respond!("owner", input).pipe(Effect.exit, Effect.forkChild); | |
| 378 | yield* Deferred.await(entered); | |
| 379 | return { | |
| 380 | controller, | |
| 381 | input, | |
| 382 | response, | |
| 383 | cleanupStarted, | |
| 384 | cleanupReleased, | |
| 385 | cleanupFinished, | |
| 386 | authenticationFinished, | |
| 387 | responses: () => responses, | |
| 388 | }; | |
| 389 | }); | |
| 390 | ||
| 391 | it.effect.each(["cancel", "logout"] as const)( | |
| 392 | "%s interrupts and awaits a blocked adapter response before admitting another sign-in", | |
| 393 | (action) => | |
| 394 | Effect.gen(function* () { | |
| 395 | const harness = yield* makeBlockingResponseHarness; | |
| 396 | const duplicate = yield* Effect.flip(harness.controller.respond!("owner", harness.input)); | |
| 397 | assert.include(duplicate.detail, "already in progress"); | |
| 398 | assert.equal(harness.responses(), 1); | |
| 399 | const stopping = yield* ( | |
| 400 | action === "cancel" | |
| 401 | ? harness.controller.cancel("owner", harness.input.flowId) | |
| 402 | : harness.controller.logout(Effect.void) | |
| 403 | ).pipe(Effect.forkChild); | |
| 404 | yield* Deferred.await(harness.cleanupStarted); | |
| 405 | assert.isTrue(yield* harness.controller.isChangingCredentials!); | |
| 406 | assert.isUndefined(stopping.pollUnsafe()); | |
| 407 | const premature = yield* Effect.flip(harness.controller.start("other")); | |
| 408 | assert.include(premature.detail, "in progress"); | |
| 409 | yield* Deferred.succeed(harness.cleanupReleased, undefined); | |
| 410 | yield* Fiber.join(stopping); | |
| 411 | assert.isTrue(yield* Deferred.isDone(harness.cleanupFinished)); | |
| 412 | assert.isTrue(Exit.isFailure(yield* Fiber.join(harness.response))); | |
| 413 | assert.isFalse(yield* harness.controller.isChangingCredentials!); | |
| 414 | assert.notEqual((yield* harness.controller.start("other")).flowId, harness.input.flowId); | |
| 415 | }).pipe(Effect.scoped, Effect.provide(NodeServices.layer)), | |
| 416 | ); | |
| 417 | ||
| 418 | it.effect("timeout interrupts and drains a blocked response before publishing failure", () => | |
| 419 | Effect.gen(function* () { | |
| 420 | const harness = yield* makeBlockingResponseHarness; | |
| 421 | yield* TestClock.adjust(300_001); | |
| 422 | yield* Deferred.await(harness.cleanupStarted); | |
| 423 | assert.isTrue(yield* harness.controller.isChangingCredentials!); | |
| 424 | const duringCleanup = yield* harness.controller | |
| 425 | .subscribe("owner") | |
| 426 | .pipe(Stream.runHead, Effect.map(Option.getOrThrow)); | |
| 427 | assert.notEqual(duringCleanup.phase, "failed"); | |
| 428 | yield* Deferred.succeed(harness.cleanupReleased, undefined); | |
| 429 | const failed = yield* harness.controller.subscribe("owner").pipe( | |
| 430 | Stream.filter((state) => state.phase === "failed"), | |
| 431 | Stream.runHead, | |
| 432 | Effect.map(Option.getOrThrow), | |
| 433 | ); | |
| 434 | assert.include(failed.message ?? "", "expired"); | |
| 435 | assert.isTrue(yield* Deferred.isDone(harness.cleanupFinished)); | |
| 436 | assert.isTrue(Exit.isFailure(yield* Fiber.join(harness.response))); | |
| 437 | }).pipe(Effect.scoped, Effect.provide(NodeServices.layer)), | |
| 438 | ); | |
| 439 | ||
| 440 | it.effect( | |
| 441 | "successful authentication drains a still-running response before admitting provider access", | |
| 442 | () => | |
| 443 | Effect.gen(function* () { | |
| 444 | const harness = yield* makeBlockingResponseHarness; | |
| 445 | yield* Deferred.succeed(harness.authenticationFinished, undefined); | |
| 446 | yield* Deferred.await(harness.cleanupStarted); | |
| 447 | const denied = yield* Effect.flip(harness.controller.withAccess!(Effect.void)); | |
| 448 | assert.include(denied.detail, "changing"); | |
| 449 | yield* Deferred.succeed(harness.cleanupReleased, undefined); | |
| 450 | const succeeded = yield* harness.controller.subscribe("owner").pipe( | |
| 451 | Stream.filter((state) => state.phase === "succeeded"), | |
| 452 | Stream.runHead, | |
| 453 | Effect.map(Option.getOrThrow), | |
| 454 | ); | |
| 455 | assert.equal(succeeded.phase, "succeeded"); | |
| 456 | assert.isTrue(yield* Deferred.isDone(harness.cleanupFinished)); | |
| 457 | assert.isTrue(Exit.isFailure(yield* Fiber.join(harness.response))); | |
| 458 | yield* harness.controller.withAccess!(Effect.void); | |
| 459 | }).pipe(Effect.scoped, Effect.provide(NodeServices.layer)), | |
| 460 | ); | |
| 461 | ||
| 462 | it.effect("closing the controller scope interrupts and drains its adapter response", () => | |
| 463 | Effect.gen(function* () { | |
| 464 | const scope = yield* Scope.make(); | |
| 465 | const harness = yield* makeBlockingResponseHarness.pipe( | |
| 466 | Effect.provideService(Scope.Scope, scope), | |
| 467 | ); | |
| 468 | const closing = yield* Scope.close(scope, Exit.void).pipe(Effect.forkChild); | |
| 469 | yield* Deferred.await(harness.cleanupStarted); | |
| 470 | assert.isUndefined(closing.pollUnsafe()); | |
| 471 | yield* Deferred.succeed(harness.cleanupReleased, undefined); | |
| 472 | yield* Fiber.join(closing); | |
| 473 | assert.isTrue(yield* Deferred.isDone(harness.cleanupFinished)); | |
| 474 | assert.isTrue(Exit.isFailure(yield* Fiber.join(harness.response))); | |
| 475 | }).pipe(Effect.scoped, Effect.provide(NodeServices.layer)), | |
| 476 | ); | |
| 477 | ||
| 478 | it.effect("a failed method refresh keeps the last discovered methods", () => | |
| 479 | Effect.gen(function* () { | |
| 480 | let fail = false; | |
| 481 | const controller = yield* ProviderAuthFlow.make({ | |
| 482 | instanceId, | |
| 483 | credentialBinding: { owner: "provider", key: "shared-agent" }, | |
| 484 | methods: Effect.suspend(() => | |
| 485 | fail | |
| 486 | ? Effect.fail( | |
| 487 | new ProviderSetupError({ instanceId, operation: "status", detail: "interrupted" }), | |
| 488 | ) | |
| 489 | : Effect.succeed([method]), | |
| 490 | ), | |
| 491 | authenticate: () => Effect.void, | |
| 492 | logout: Effect.void, | |
| 493 | }); | |
| 494 | yield* controller.refreshMethods!; | |
| 495 | fail = true; | |
| 496 | yield* controller.refreshMethods!; | |
| 497 | const state = yield* controller | |
| 498 | .subscribe("owner") | |
| 499 | .pipe(Stream.runHead, Effect.map(Option.getOrThrow)); | |
| 500 | assert.deepEqual(state.methods, [method]); | |
| 501 | assert.strictEqual(state.message, "interrupted"); | |
| 502 | }).pipe(Effect.scoped, Effect.provide(NodeServices.layer)), | |
| 503 | ); | |
| 504 | ||
| 505 | it.effect("rebuilding a controller leaves sessions it admitted to their owners", () => | |
| 506 | Effect.gen(function* () { | |
| 507 | const scope = yield* Scope.make(); | |
| 508 | const { controller } = yield* makeHarness.pipe(Effect.provideService(Scope.Scope, scope)); | |
| 509 | let closed = false; | |
| 510 | yield* controller.withAccess!( | |
| 511 | Effect.addFinalizer(() => | |
| 512 | Effect.sync(() => { | |
| 513 | closed = true; | |
| 514 | }), | |
| 515 | ), | |
| 516 | ); | |
| 517 | yield* Scope.close(scope, Exit.void); | |
| 518 | assert.isFalse(closed); | |
| 519 | }).pipe(Effect.scoped, Effect.provide(NodeServices.layer)), | |
| 520 | ); | |
| 521 | ||
| 522 | it.effect("profile import stops owned sessions and gates access until credentials are saved", () => | |
| 523 | Effect.gen(function* () { | |
| 524 | const controller = yield* ProviderAuthFlow.make({ | |
| 525 | instanceId, | |
| 526 | credentialBinding: { owner: "t3", key: "handoff-binding" }, | |
| 527 | methods: Effect.succeed([method]), | |
| 528 | authenticate: () => Effect.void, | |
| 529 | logout: Effect.void, | |
| 530 | }); | |
| 531 | let closed = false; | |
| 532 | yield* controller.withAccess!( | |
| 533 | Effect.addFinalizer(() => | |
| 534 | Effect.sync(() => { | |
| 535 | closed = true; | |
| 536 | }), | |
| 537 | ), | |
| 538 | ); | |
| 539 | const writing = yield* Deferred.make<void>(); | |
| 540 | const release = yield* Deferred.make<void>(); | |
| 541 | const imported = yield* controller.adoptCredentials!( | |
| 542 | Deferred.succeed(writing, undefined).pipe(Effect.andThen(Deferred.await(release))), | |
| 543 | Effect.sync(() => { | |
| 544 | assert.isTrue(closed); | |
| 545 | }), | |
| 546 | ).pipe(Effect.forkChild); | |
| 547 | yield* Deferred.await(writing); | |
| 548 | const denied = yield* controller.withAccess!(Effect.succeed("old credential process")).pipe( | |
| 549 | Effect.result, | |
| 550 | ); | |
| 551 | assert.strictEqual(denied._tag, "Failure"); | |
| 552 | yield* Deferred.succeed(release, undefined); | |
| 553 | assert.strictEqual((yield* Fiber.join(imported)).phase, "succeeded"); | |
| 554 | assert.strictEqual( | |
| 555 | yield* controller.withAccess!(Effect.succeed("new credential process")), | |
| 556 | "new credential process", | |
| 557 | ); | |
| 558 | }).pipe(Effect.scoped, Effect.provide(NodeServices.layer)), | |
| 559 | ); |
packages/provider-core/src/server/ProviderAuthFlow.ts deleted-525| ... | ... | @@ -1,525 +0,0 @@ |
| 1 | import { | |
| 2 | ProviderSetupError, | |
| 3 | type ProviderAuthInteraction, | |
| 4 | type ProviderAuthMethod, | |
| 5 | type ProviderAuthResponse, | |
| 6 | type ProviderAuthState, | |
| 7 | type ProviderInstanceId, | |
| 8 | } from "@t3tools/contracts"; | |
| 9 | import * as Cause from "effect/Cause"; | |
| 10 | import * as Option from "effect/Option"; | |
| 11 | import * as Schema from "effect/Schema"; | |
| 12 | import * as Clock from "effect/Clock"; | |
| 13 | import * as Crypto from "effect/Crypto"; | |
| 14 | import * as DateTime from "effect/DateTime"; | |
| 15 | import * as Effect from "effect/Effect"; | |
| 16 | import * as Deferred from "effect/Deferred"; | |
| 17 | import * as Exit from "effect/Exit"; | |
| 18 | import * as Fiber from "effect/Fiber"; | |
| 19 | import * as Scope from "effect/Scope"; | |
| 20 | import * as Semaphore from "effect/Semaphore"; | |
| 21 | import * as Stream from "effect/Stream"; | |
| 22 | import * as SubscriptionRef from "effect/SubscriptionRef"; | |
| 23 | ||
| 24 | import type * as ProviderAuth from "./auth.ts"; | |
| 25 | ||
| 26 | export interface ProviderAuthFlowContext { | |
| 27 | readonly flowId: string; | |
| 28 | /** The lifetime timeout interrupts authenticate before publishing its expired result. */ | |
| 29 | readonly expiresAt: number; | |
| 30 | readonly returnUrl?: string; | |
| 31 | readonly callbackMode?: "server" | "client"; | |
| 32 | readonly setInteraction: ( | |
| 33 | interaction: ProviderAuthInteraction, | |
| 34 | respond?: (response: ProviderAuthResponse) => Effect.Effect<void, ProviderSetupError>, | |
| 35 | complete?: (callbackUrl: string) => Effect.Effect<void, ProviderSetupError>, | |
| 36 | ) => Effect.Effect<void>; | |
| 37 | readonly verifying: Effect.Effect<void>; | |
| 38 | } | |
| 39 | ||
| 40 | const isSetupError = Schema.is(ProviderSetupError); | |
| 41 | ||
| 42 | const failureMessage = (cause: Cause.Cause<ProviderSetupError>) => { | |
| 43 | const error = Cause.findErrorOption(cause); | |
| 44 | return Option.isSome(error) && isSetupError(error.value) | |
| 45 | ? error.value.detail | |
| 46 | : "Sign-in failed. Start again."; | |
| 47 | }; | |
| 48 | ||
| 49 | interface Flow { | |
| 50 | readonly id: string; | |
| 51 | readonly owner: string; | |
| 52 | readonly expiresAt: number; | |
| 53 | complete?: ((callbackUrl: string) => Effect.Effect<void, ProviderSetupError>) | undefined; | |
| 54 | fiber?: Fiber.Fiber<void>; | |
| 55 | responseFiber?: Fiber.Fiber<void, ProviderSetupError>; | |
| 56 | respond: | |
| 57 | | ((response: ProviderAuthResponse) => Effect.Effect<void, ProviderSetupError>) | |
| 58 | | undefined; | |
| 59 | } | |
| 60 | ||
| 61 | /** Adapters do login and credential handling; this owns client consent and flow lifetime. */ | |
| 62 | export const make = Effect.fn("ProviderAuthFlow.make")(function* (options: { | |
| 63 | readonly instanceId: ProviderInstanceId; | |
| 64 | readonly credentialBinding: NonNullable<ProviderAuth.ProviderAuthController["credentialBinding"]>; | |
| 65 | readonly methods: Effect.Effect<ReadonlyArray<ProviderAuthMethod>, ProviderSetupError>; | |
| 66 | readonly defaultMethodId?: string; | |
| 67 | /** Stored account profiles can change the advertised methods after auth/logout. */ | |
| 68 | readonly refreshMethodsAfterAuth?: boolean; | |
| 69 | /** Fail with ProviderSetupError containing safe text for the user, never native token data. */ | |
| 70 | readonly authenticate: ( | |
| 71 | methodId: string, | |
| 72 | context: ProviderAuthFlowContext, | |
| 73 | ) => Effect.Effect<void, ProviderSetupError, Scope.Scope>; | |
| 74 | readonly logout: Effect.Effect<void | string, ProviderSetupError>; | |
| 75 | readonly timeoutMs?: number; | |
| 76 | }) { | |
| 77 | const scope = yield* Scope.Scope; | |
| 78 | const crypto = yield* Crypto.Crypto; | |
| 79 | const lock = yield* Semaphore.make(1); | |
| 80 | const timeoutMs = options.timeoutMs ?? 300_000; | |
| 81 | const empty: ProviderAuthState = { | |
| 82 | instanceId: options.instanceId, | |
| 83 | phase: "idle", | |
| 84 | flowId: null, | |
| 85 | authorizationUrl: null, | |
| 86 | expiresAt: null, | |
| 87 | message: null, | |
| 88 | interaction: null, | |
| 89 | credentialOwner: options.credentialBinding.owner, | |
| 90 | }; | |
| 91 | const snapshot = yield* SubscriptionRef.make({ owner: null as string | null, state: empty }); | |
| 92 | let active: Flow | undefined; | |
| 93 | let operation: "idle" | "auth" | "stopping" | "closed" = "idle"; | |
| 94 | const sessions = new Set<Scope.Closeable>(); | |
| 95 | const stopOwnedSessions = Effect.suspend(() => | |
| 96 | Effect.forEach(Array.from(sessions), (session) => Scope.close(session, Exit.void), { | |
| 97 | discard: true, | |
| 98 | concurrency: "unbounded", | |
| 99 | }), | |
| 100 | ); | |
| 101 | const publish = (flow: Flow, patch: Partial<ProviderAuthState>) => | |
| 102 | Effect.suspend(() => | |
| 103 | active === flow | |
| 104 | ? SubscriptionRef.update(snapshot, (current) => ({ | |
| 105 | owner: flow.owner, | |
| 106 | state: { ...current.state, ...patch }, | |
| 107 | })) | |
| 108 | : Effect.void, | |
| 109 | ); | |
| 110 | ||
| 111 | const requireFlow = Effect.fnUntraced(function* (owner: string, id: string) { | |
| 112 | if ( | |
| 113 | operation !== "auth" || | |
| 114 | !active || | |
| 115 | active.owner !== owner || | |
| 116 | active.id !== id || | |
| 117 | (yield* Clock.currentTimeMillis) >= active.expiresAt | |
| 118 | ) { | |
| 119 | return yield* new ProviderSetupError({ | |
| 120 | instanceId: options.instanceId, | |
| 121 | operation: "respond", | |
| 122 | detail: "This sign-in is no longer active in this client.", | |
| 123 | }); | |
| 124 | } | |
| 125 | return active; | |
| 126 | }); | |
| 127 | ||
| 128 | // Method discovery does not start a sign-in flow. | |
| 129 | const refreshMethods = options.methods.pipe( | |
| 130 | Effect.flatMap((methods) => | |
| 131 | SubscriptionRef.update(snapshot, (current) => ({ | |
| 132 | ...current, | |
| 133 | state: { ...current.state, methods }, | |
| 134 | })), | |
| 135 | ), | |
| 136 | // Keep the last known methods when a refresh fails or is interrupted. | |
| 137 | Effect.catch((error) => | |
| 138 | SubscriptionRef.update(snapshot, (current) => ({ | |
| 139 | ...current, | |
| 140 | state: { ...current.state, methods: current.state.methods ?? [], message: error.detail }, | |
| 141 | })), | |
| 142 | ), | |
| 143 | ); | |
| 144 | yield* refreshMethods.pipe(Effect.forkIn(scope)); | |
| 145 | ||
| 146 | const controller: ProviderAuth.ProviderAuthController = { | |
| 147 | credentialBinding: options.credentialBinding, | |
| 148 | adoptCredentials: (update, stopSessions) => | |
| 149 | Effect.gen(function* () { | |
| 150 | yield* lock.withPermit( | |
| 151 | Effect.gen(function* () { | |
| 152 | if (operation !== "idle") | |
| 153 | return yield* new ProviderSetupError({ | |
| 154 | instanceId: options.instanceId, | |
| 155 | operation: "import", | |
| 156 | detail: "Finish or cancel the existing sign-in first.", | |
| 157 | }); | |
| 158 | operation = "stopping"; | |
| 159 | }), | |
| 160 | ); | |
| 161 | const result = yield* Effect.gen(function* () { | |
| 162 | yield* stopOwnedSessions; | |
| 163 | yield* stopSessions; | |
| 164 | yield* update; | |
| 165 | yield* refreshMethods; | |
| 166 | }).pipe(Effect.exit); | |
| 167 | const state: ProviderAuthState = { | |
| 168 | ...empty, | |
| 169 | methods: snapshot.value.state.methods ?? [], | |
| 170 | phase: Exit.isSuccess(result) ? "succeeded" : "failed", | |
| 171 | message: Exit.isSuccess(result) ? null : failureMessage(result.cause), | |
| 172 | }; | |
| 173 | yield* lock.withPermit( | |
| 174 | Effect.gen(function* () { | |
| 175 | yield* SubscriptionRef.set(snapshot, { owner: null, state }); | |
| 176 | operation = "idle"; | |
| 177 | }), | |
| 178 | ); | |
| 179 | if (Exit.isFailure(result)) return yield* Effect.failCause(result.cause); | |
| 180 | return state; | |
| 181 | }).pipe(Effect.uninterruptible), | |
| 182 | refreshMethods, | |
| 183 | invalidate: lock.withPermit( | |
| 184 | Effect.gen(function* () { | |
| 185 | if (operation !== "idle") return; | |
| 186 | yield* stopOwnedSessions; | |
| 187 | yield* SubscriptionRef.set(snapshot, { | |
| 188 | owner: null, | |
| 189 | state: { | |
| 190 | ...empty, | |
| 191 | methods: snapshot.value.state.methods ?? [], | |
| 192 | message: "This provider's shared sign-in changed.", | |
| 193 | }, | |
| 194 | }); | |
| 195 | }), | |
| 196 | ), | |
| 197 | isChangingCredentials: Effect.sync(() => operation !== "idle"), | |
| 198 | withAccess: (task) => | |
| 199 | Effect.uninterruptibleMask((restore) => | |
| 200 | Effect.gen(function* () { | |
| 201 | const parent = yield* Scope.Scope; | |
| 202 | const child = yield* lock.withPermit( | |
| 203 | Effect.gen(function* () { | |
| 204 | if (operation !== "idle") | |
| 205 | return yield* new ProviderSetupError({ | |
| 206 | instanceId: options.instanceId, | |
| 207 | operation: "session", | |
| 208 | detail: "Provider sign-in is changing. Try again after it finishes.", | |
| 209 | }); | |
| 210 | const child = yield* Scope.make(); | |
| 211 | sessions.add(child); | |
| 212 | yield* Scope.addFinalizer( | |
| 213 | child, | |
| 214 | Effect.sync(() => { | |
| 215 | sessions.delete(child); | |
| 216 | }), | |
| 217 | ); | |
| 218 | yield* Scope.addFinalizer(parent, Scope.close(child, Exit.void)); | |
| 219 | return child; | |
| 220 | }), | |
| 221 | ); | |
| 222 | const fiber = yield* restore(task).pipe( | |
| 223 | Effect.provideService(Scope.Scope, child), | |
| 224 | Effect.forkIn(child), | |
| 225 | ); | |
| 226 | return yield* restore(Fiber.await(fiber)).pipe( | |
| 227 | Effect.flatMap((result) => result), | |
| 228 | Effect.onExit((result) => | |
| 229 | Exit.isFailure(result) ? Scope.close(child, Exit.void) : Effect.void, | |
| 230 | ), | |
| 231 | ); | |
| 232 | }), | |
| 233 | ), | |
| 234 | start: (owner, stopSessions = Effect.void, selectedMethodId, returnUrl, callbackMode) => | |
| 235 | lock.withPermit( | |
| 236 | Effect.gen(function* () { | |
| 237 | if (operation === "auth" && active?.owner === owner) return snapshot.value.state; | |
| 238 | if (operation !== "idle") | |
| 239 | return yield* new ProviderSetupError({ | |
| 240 | instanceId: options.instanceId, | |
| 241 | operation: "start", | |
| 242 | detail: "Provider setup is already in progress.", | |
| 243 | }); | |
| 244 | const id = yield* crypto.randomUUIDv4.pipe( | |
| 245 | Effect.mapError( | |
| 246 | () => | |
| 247 | new ProviderSetupError({ | |
| 248 | instanceId: options.instanceId, | |
| 249 | operation: "start", | |
| 250 | detail: "Could not start sign-in. Try again.", | |
| 251 | }), | |
| 252 | ), | |
| 253 | ); | |
| 254 | const flow: Flow = { | |
| 255 | id, | |
| 256 | owner, | |
| 257 | expiresAt: (yield* Clock.currentTimeMillis) + timeoutMs, | |
| 258 | respond: undefined, | |
| 259 | }; | |
| 260 | active = flow; | |
| 261 | operation = "auth"; | |
| 262 | const state: ProviderAuthState = { | |
| 263 | ...empty, | |
| 264 | methods: snapshot.value.state.methods ?? [], | |
| 265 | phase: "starting", | |
| 266 | flowId: id, | |
| 267 | expiresAt: DateTime.formatIso(DateTime.makeUnsafe(flow.expiresAt)), | |
| 268 | message: "Starting sign-in.", | |
| 269 | }; | |
| 270 | yield* SubscriptionRef.set(snapshot, { owner, state }); | |
| 271 | flow.fiber = yield* Effect.gen(function* () { | |
| 272 | const methods = yield* options.methods; | |
| 273 | yield* publish(flow, { methods }); | |
| 274 | const methodId = selectedMethodId ?? options.defaultMethodId ?? methods[0]?.id; | |
| 275 | if (!methodId || !methods.some((method) => method.id === methodId)) | |
| 276 | return yield* new ProviderSetupError({ | |
| 277 | instanceId: options.instanceId, | |
| 278 | operation: "start", | |
| 279 | detail: "The provider did not advertise this sign-in method.", | |
| 280 | }); | |
| 281 | yield* stopSessions.pipe(Effect.ensuring(stopOwnedSessions)); | |
| 282 | yield* options.authenticate(methodId, { | |
| 283 | flowId: id, | |
| 284 | expiresAt: flow.expiresAt, | |
| 285 | ...(returnUrl ? { returnUrl } : {}), | |
| 286 | ...(callbackMode ? { callbackMode } : {}), | |
| 287 | setInteraction: (interaction, respond, complete) => | |
| 288 | Effect.gen(function* () { | |
| 289 | if (active !== flow) return; | |
| 290 | flow.respond = respond; | |
| 291 | flow.complete = complete; | |
| 292 | yield* publish(flow, { | |
| 293 | phase: "waiting", | |
| 294 | interaction, | |
| 295 | authorizationUrl: | |
| 296 | interaction.type === "browser" || interaction.type === "deviceCode" | |
| 297 | ? interaction.url | |
| 298 | : null, | |
| 299 | message: "Complete sign-in to continue.", | |
| 300 | }); | |
| 301 | }), | |
| 302 | verifying: Effect.gen(function* () { | |
| 303 | flow.respond = undefined; | |
| 304 | flow.complete = undefined; | |
| 305 | yield* publish(flow, { | |
| 306 | phase: "verifying", | |
| 307 | interaction: null, | |
| 308 | authorizationUrl: null, | |
| 309 | message: "Checking provider sign-in.", | |
| 310 | }); | |
| 311 | }), | |
| 312 | }); | |
| 313 | }).pipe( | |
| 314 | Effect.scoped, | |
| 315 | Effect.timeoutOrElse({ | |
| 316 | duration: timeoutMs, | |
| 317 | orElse: () => | |
| 318 | Effect.fail( | |
| 319 | new ProviderSetupError({ | |
| 320 | instanceId: options.instanceId, | |
| 321 | operation: "start", | |
| 322 | detail: "Sign-in expired. Start again.", | |
| 323 | }), | |
| 324 | ), | |
| 325 | }), | |
| 326 | Effect.exit, | |
| 327 | Effect.flatMap((result) => | |
| 328 | Effect.gen(function* () { | |
| 329 | const finishing = yield* lock.withPermit( | |
| 330 | Effect.sync(() => { | |
| 331 | if (active !== flow) return false; | |
| 332 | operation = "stopping"; | |
| 333 | return true; | |
| 334 | }), | |
| 335 | ); | |
| 336 | if (!finishing) return; | |
| 337 | if (flow.responseFiber) yield* Fiber.interrupt(flow.responseFiber); | |
| 338 | yield* lock.withPermit( | |
| 339 | Effect.gen(function* () { | |
| 340 | if (active !== flow) return; | |
| 341 | if (options.refreshMethodsAfterAuth) yield* refreshMethods; | |
| 342 | yield* publish(flow, { | |
| 343 | phase: Exit.isSuccess(result) ? "succeeded" : "failed", | |
| 344 | interaction: null, | |
| 345 | authorizationUrl: null, | |
| 346 | expiresAt: null, | |
| 347 | message: Exit.isSuccess(result) | |
| 348 | ? "Sign-in complete." | |
| 349 | : failureMessage(result.cause), | |
| 350 | }); | |
| 351 | active = undefined; | |
| 352 | operation = "idle"; | |
| 353 | }), | |
| 354 | ); | |
| 355 | }), | |
| 356 | ), | |
| 357 | Effect.interruptible, | |
| 358 | Effect.forkIn(scope), | |
| 359 | ); | |
| 360 | return state; | |
| 361 | }).pipe(Effect.uninterruptible), | |
| 362 | ), | |
| 363 | respond: (owner, input) => | |
| 364 | Effect.uninterruptibleMask((restore) => | |
| 365 | Effect.gen(function* () { | |
| 366 | const { flow, fiber, ready } = yield* lock.withPermit( | |
| 367 | Effect.gen(function* () { | |
| 368 | const flow = yield* requireFlow(owner, input.flowId); | |
| 369 | const interaction = snapshot.value.state.interaction; | |
| 370 | if ( | |
| 371 | !interaction || | |
| 372 | interaction.id !== input.interactionId || | |
| 373 | interaction.type !== input.response.type || | |
| 374 | !flow.respond | |
| 375 | ) | |
| 376 | return yield* new ProviderSetupError({ | |
| 377 | instanceId: options.instanceId, | |
| 378 | operation: "respond", | |
| 379 | detail: "This sign-in interaction is no longer available.", | |
| 380 | }); | |
| 381 | if (flow.responseFiber) | |
| 382 | return yield* new ProviderSetupError({ | |
| 383 | instanceId: options.instanceId, | |
| 384 | operation: "respond", | |
| 385 | detail: "A sign-in response is already in progress.", | |
| 386 | }); | |
| 387 | const ready = yield* Deferred.make<void>(); | |
| 388 | const callback = flow.respond; | |
| 389 | const fiber = yield* Deferred.await(ready).pipe( | |
| 390 | Effect.andThen(Effect.suspend(() => callback(input.response))), | |
| 391 | Effect.interruptible, | |
| 392 | Effect.forkIn(scope), | |
| 393 | ); | |
| 394 | flow.responseFiber = fiber; | |
| 395 | return { flow, fiber, ready }; | |
| 396 | }), | |
| 397 | ); | |
| 398 | yield* Deferred.succeed(ready, undefined); | |
| 399 | yield* restore(Fiber.join(fiber)).pipe( | |
| 400 | Effect.onInterrupt(() => Fiber.interrupt(fiber).pipe(Effect.asVoid)), | |
| 401 | Effect.ensuring( | |
| 402 | lock.withPermit( | |
| 403 | Effect.sync(() => { | |
| 404 | if (flow.responseFiber === fiber) delete flow.responseFiber; | |
| 405 | }), | |
| 406 | ), | |
| 407 | ), | |
| 408 | ); | |
| 409 | return snapshot.value.state; | |
| 410 | }), | |
| 411 | ), | |
| 412 | complete: (owner, input) => | |
| 413 | lock.withPermit( | |
| 414 | Effect.gen(function* () { | |
| 415 | const flow = yield* requireFlow(owner, input.flowId); | |
| 416 | const interaction = snapshot.value.state.interaction; | |
| 417 | if ( | |
| 418 | snapshot.value.state.phase !== "waiting" || | |
| 419 | interaction?.type !== "browser" || | |
| 420 | !interaction.acceptsCallback || | |
| 421 | !flow.complete | |
| 422 | ) | |
| 423 | return yield* new ProviderSetupError({ | |
| 424 | instanceId: options.instanceId, | |
| 425 | operation: "complete", | |
| 426 | detail: "This sign-in does not accept a redirect URL.", | |
| 427 | }); | |
| 428 | yield* flow.complete(input.callbackUrl); | |
| 429 | return snapshot.value.state; | |
| 430 | }), | |
| 431 | ), | |
| 432 | cancel: (owner, id) => | |
| 433 | Effect.gen(function* () { | |
| 434 | const flow = yield* lock.withPermit( | |
| 435 | Effect.gen(function* () { | |
| 436 | const flow = yield* requireFlow(owner, id); | |
| 437 | yield* publish(flow, { | |
| 438 | phase: "cancelled", | |
| 439 | interaction: null, | |
| 440 | authorizationUrl: null, | |
| 441 | expiresAt: null, | |
| 442 | message: "Sign-in cancelled.", | |
| 443 | }); | |
| 444 | active = undefined; | |
| 445 | operation = "stopping"; | |
| 446 | return flow; | |
| 447 | }), | |
| 448 | ); | |
| 449 | if (flow.responseFiber) yield* Fiber.interrupt(flow.responseFiber); | |
| 450 | if (flow.fiber) yield* Fiber.interrupt(flow.fiber); | |
| 451 | operation = "idle"; | |
| 452 | return snapshot.value.state; | |
| 453 | }).pipe(Effect.uninterruptible), | |
| 454 | logout: (stopSessions) => | |
| 455 | Effect.gen(function* () { | |
| 456 | const flow = yield* lock.withPermit( | |
| 457 | Effect.gen(function* () { | |
| 458 | if (operation !== "idle" && operation !== "auth") | |
| 459 | return yield* new ProviderSetupError({ | |
| 460 | instanceId: options.instanceId, | |
| 461 | operation: "logout", | |
| 462 | detail: "Provider setup is already stopping.", | |
| 463 | }); | |
| 464 | operation = "stopping"; | |
| 465 | const flow = active; | |
| 466 | active = undefined; | |
| 467 | return flow; | |
| 468 | }), | |
| 469 | ); | |
| 470 | const result = yield* Effect.gen(function* () { | |
| 471 | if (flow?.responseFiber) yield* Fiber.interrupt(flow.responseFiber); | |
| 472 | if (flow?.fiber) yield* Fiber.interrupt(flow.fiber); | |
| 473 | yield* stopSessions.pipe(Effect.ensuring(stopOwnedSessions)); | |
| 474 | const message = yield* options.logout; | |
| 475 | if (options.refreshMethodsAfterAuth) yield* refreshMethods; | |
| 476 | return message; | |
| 477 | }).pipe(Effect.exit); | |
| 478 | const state: ProviderAuthState = { | |
| 479 | ...empty, | |
| 480 | methods: snapshot.value.state.methods ?? [], | |
| 481 | phase: Exit.isSuccess(result) ? "idle" : "failed", | |
| 482 | message: Exit.isSuccess(result) | |
| 483 | ? (result.value ?? "Signed out.") | |
| 484 | : "Could not sign out. Try again.", | |
| 485 | }; | |
| 486 | yield* lock.withPermit( | |
| 487 | Effect.gen(function* () { | |
| 488 | yield* SubscriptionRef.set(snapshot, { owner: null, state }); | |
| 489 | operation = "idle"; | |
| 490 | }), | |
| 491 | ); | |
| 492 | if (Exit.isFailure(result)) return yield* Effect.failCause(result.cause); | |
| 493 | return state; | |
| 494 | }).pipe(Effect.uninterruptible), | |
| 495 | subscribe: (owner) => | |
| 496 | SubscriptionRef.changes(snapshot).pipe( | |
| 497 | Stream.map((current) => | |
| 498 | current.owner === null || current.owner === owner | |
| 499 | ? current.state | |
| 500 | : { | |
| 501 | ...current.state, | |
| 502 | flowId: null, | |
| 503 | authorizationUrl: null, | |
| 504 | interaction: null, | |
| 505 | expiresAt: null, | |
| 506 | message: active | |
| 507 | ? "Sign-in is in progress in another client." | |
| 508 | : current.state.message, | |
| 509 | }, | |
| 510 | ), | |
| 511 | ), | |
| 512 | }; | |
| 513 | yield* Effect.addFinalizer(() => | |
| 514 | Effect.gen(function* () { | |
| 515 | operation = "closed"; | |
| 516 | const flow = active; | |
| 517 | active = undefined; | |
| 518 | if (flow?.responseFiber) yield* Fiber.interrupt(flow.responseFiber); | |
| 519 | if (flow?.fiber) yield* Fiber.interrupt(flow.fiber); | |
| 520 | // Settings edits rebuild instances; admitted sessions (including those of | |
| 521 | // peers sharing this binding) end with their own scopes, not this one. | |
| 522 | }), | |
| 523 | ); | |
| 524 | return controller; | |
| 525 | }); |
packages/provider-core/src/server/ProviderContinuationRequests.ts created+76| ... | ... | @@ -0,0 +1,76 @@ |
| 1 | import { | |
| 2 | type OrchestrationV2Notification, | |
| 3 | MessageId, | |
| 4 | ProviderDriverKind, | |
| 5 | ProviderThreadId, | |
| 6 | RunId, | |
| 7 | ThreadId, | |
| 8 | } 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 Option from "effect/Option"; | |
| 13 | import * as Queue from "effect/Queue"; | |
| 14 | ||
| 15 | export interface ProviderContinuationRequest { | |
| 16 | readonly threadId: ThreadId; | |
| 17 | readonly providerThreadId: ProviderThreadId; | |
| 18 | readonly driver: ProviderDriverKind; | |
| 19 | readonly detail: string | null; | |
| 20 | readonly notification?: OrchestrationV2Notification; | |
| 21 | /** | |
| 22 | * Durable ownership for an app-owned delegated-task completion delivery. | |
| 23 | * The continuation worker re-reads the cohort before dispatching so a later | |
| 24 | * task can join a queued wake and a stopped or acknowledged cohort is dropped. | |
| 25 | */ | |
| 26 | readonly delegatedCompletion?: { | |
| 27 | readonly parentRunId: RunId; | |
| 28 | readonly generation: number; | |
| 29 | readonly messageId: MessageId; | |
| 30 | }; | |
| 31 | /** | |
| 32 | * How the continuation turn gets its content. | |
| 33 | * | |
| 34 | * `adapter_buffered` (default) is the provider-native wake: the adapter has | |
| 35 | * already buffered the CLI's wake output, and the dispatched message only | |
| 36 | * triggers ingestion. `ClaudeAdapterV2` deliberately discards the message | |
| 37 | * text on that path. | |
| 38 | * | |
| 39 | * `message_text` is for app-owned work with no buffered provider output, such | |
| 40 | * as a delegated child finishing. The text is the entire wake, so it must | |
| 41 | * reach the provider as a real prompt. | |
| 42 | */ | |
| 43 | readonly delivery?: "adapter_buffered" | "message_text"; | |
| 44 | readonly dispatchIfCurrent?: <A, E, R>( | |
| 45 | effect: Effect.Effect<A, E, R>, | |
| 46 | ) => Effect.Effect<Option.Option<A>, E, R>; | |
| 47 | /** Clears a pending offer that the continuation worker intentionally drops. */ | |
| 48 | readonly clearIfCurrent?: () => Effect.Effect<void>; | |
| 49 | } | |
| 50 | ||
| 51 | /** | |
| 52 | * Adapters offer a continuation request when provider-native work completes | |
| 53 | * outside an active turn (for example a Claude background task wake turn) so | |
| 54 | * the orchestrator can start a run that ingests it. The default reference | |
| 55 | * drops requests, keeping adapter construction dependency-free in tests; the | |
| 56 | * live layer must be shared with the ProviderContinuationService worker that | |
| 57 | * drains it. | |
| 58 | */ | |
| 59 | export class ProviderContinuationRequests extends Context.Reference<{ | |
| 60 | readonly offer: (request: ProviderContinuationRequest) => Effect.Effect<void>; | |
| 61 | readonly take: Effect.Effect<ProviderContinuationRequest>; | |
| 62 | }>("@t3tools/provider-core/server/ProviderContinuationRequests", { | |
| 63 | defaultValue: () => ({ offer: () => Effect.void, take: Effect.never }), | |
| 64 | }) {} | |
| 65 | ||
| 66 | export const layer = Layer.effect( | |
| 67 | ProviderContinuationRequests, | |
| 68 | Effect.gen(function* () { | |
| 69 | const queue = yield* Queue.unbounded<ProviderContinuationRequest>(); | |
| 70 | return { | |
| 71 | offer: (request: ProviderContinuationRequest) => | |
| 72 | Queue.offer(queue, request).pipe(Effect.asVoid), | |
| 73 | take: Queue.take(queue), | |
| 74 | }; | |
| 75 | }), | |
| 76 | ); |
packages/provider-core/src/server/ProviderHost.ts+29-30| ... | ... | @@ -45,33 +45,32 @@ export interface ProviderCredentials { |
| 45 | 45 | readonly remove: Effect.Effect<void, ProviderCredentialError>; |
| 46 | 46 | } |
| 47 | 47 | |
| 48 | interface ProviderHostService { | |
| 49 | readonly paths: ProviderHostPaths; | |
| 50 | readonly settings: { | |
| 51 | readonly get: Effect.Effect<ServerSettings, ServerSettingsError>; | |
| 52 | /** Runs `use` on a settings snapshot that no settings write can change until it finishes. */ | |
| 53 | readonly withSnapshot: <A, E, R>( | |
| 54 | use: (settings: ServerSettings) => Effect.Effect<A, E, R>, | |
| 55 | ) => Effect.Effect<A, E | ServerSettingsError, R>; | |
| 56 | /** Every settings change after subscription, starting with the next one. */ | |
| 57 | readonly changes: Stream.Stream<ServerSettings>; | |
| 58 | /** Changes buffered from the moment the scoped subscription is acquired. */ | |
| 59 | readonly subscribe: Effect.Effect<Stream.Stream<ServerSettings>, never, Scope.Scope>; | |
| 60 | }; | |
| 61 | /** Whether background work for `scope` should run now (client demand and host power). */ | |
| 62 | readonly shouldRunBackgroundWork: (scope: BackgroundScope) => Effect.Effect<boolean>; | |
| 63 | /** | |
| 64 | * Absolute path of a stored chat attachment, or `null` when its id does not | |
| 65 | * resolve inside the attachments directory. | |
| 66 | */ | |
| 67 | readonly resolveAttachmentPath: (attachment: ChatAttachment) => string | null; | |
| 68 | /** The credentials stored for `bindingId`, usually an instance id, under a provider-chosen namespace. */ | |
| 69 | readonly credentials: ( | |
| 70 | namespace: string, | |
| 71 | bindingId: string, | |
| 72 | ) => Effect.Effect<ProviderCredentials>; | |
| 73 | } | |
| 74 | ||
| 75 | export class ProviderHost extends Context.Service<ProviderHost, ProviderHostService>()( | |
| 76 | "@t3tools/provider-core/server/ProviderHost", | |
| 77 | ) {} | |
| 48 | export class ProviderHost extends Context.Service< | |
| 49 | ProviderHost, | |
| 50 | { | |
| 51 | readonly paths: ProviderHostPaths; | |
| 52 | readonly settings: { | |
| 53 | readonly get: Effect.Effect<ServerSettings, ServerSettingsError>; | |
| 54 | /** Runs `use` on a settings snapshot that no settings write can change until it finishes. */ | |
| 55 | readonly withSnapshot: <A, E, R>( | |
| 56 | use: (settings: ServerSettings) => Effect.Effect<A, E, R>, | |
| 57 | ) => Effect.Effect<A, E | ServerSettingsError, R>; | |
| 58 | /** Every settings change after subscription, starting with the next one. */ | |
| 59 | readonly changes: Stream.Stream<ServerSettings>; | |
| 60 | /** Changes buffered from the moment the scoped subscription is acquired. */ | |
| 61 | readonly subscribe: Effect.Effect<Stream.Stream<ServerSettings>, never, Scope.Scope>; | |
| 62 | }; | |
| 63 | /** Whether background work for `scope` should run now (client demand and host power). */ | |
| 64 | readonly shouldRunBackgroundWork: (scope: BackgroundScope) => Effect.Effect<boolean>; | |
| 65 | /** | |
| 66 | * Absolute path of a stored chat attachment, or `null` when its id does not | |
| 67 | * resolve inside the attachments directory. | |
| 68 | */ | |
| 69 | readonly resolveAttachmentPath: (attachment: ChatAttachment) => string | null; | |
| 70 | /** The credentials stored for `bindingId`, usually an instance id, under a provider-chosen namespace. */ | |
| 71 | readonly credentials: ( | |
| 72 | namespace: string, | |
| 73 | bindingId: string, | |
| 74 | ) => Effect.Effect<ProviderCredentials>; | |
| 75 | } | |
| 76 | >()("@t3tools/provider-core/server/ProviderHost") {} |
packages/provider-core/src/server/adapterDriver.ts+1-1| ... | ... | @@ -39,7 +39,7 @@ export interface ProviderAdapterDriver<Config, R = never> { |
| 39 | 39 | readonly create: ( |
| 40 | 40 | input: ProviderAdapterDriverCreateInput<Config>, |
| 41 | 41 | ) => Effect.Effect< |
| 42 | ProviderAdapter.ProviderAdapterV2Shape, | |
| 42 | ProviderAdapter.ProviderAdapterV2["Service"], | |
| 43 | 43 | ProviderAdapterDriverCreateError, |
| 44 | 44 | R | Scope.Scope |
| 45 | 45 | >; |
packages/provider-core/src/server/continuationRequests.ts deleted-76| ... | ... | @@ -1,76 +0,0 @@ |
| 1 | import { | |
| 2 | type OrchestrationV2Notification, | |
| 3 | MessageId, | |
| 4 | ProviderDriverKind, | |
| 5 | ProviderThreadId, | |
| 6 | RunId, | |
| 7 | ThreadId, | |
| 8 | } 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 Option from "effect/Option"; | |
| 13 | import * as Queue from "effect/Queue"; | |
| 14 | ||
| 15 | export interface ProviderContinuationRequest { | |
| 16 | readonly threadId: ThreadId; | |
| 17 | readonly providerThreadId: ProviderThreadId; | |
| 18 | readonly driver: ProviderDriverKind; | |
| 19 | readonly detail: string | null; | |
| 20 | readonly notification?: OrchestrationV2Notification; | |
| 21 | /** | |
| 22 | * Durable ownership for an app-owned delegated-task completion delivery. | |
| 23 | * The continuation worker re-reads the cohort before dispatching so a later | |
| 24 | * task can join a queued wake and a stopped or acknowledged cohort is dropped. | |
| 25 | */ | |
| 26 | readonly delegatedCompletion?: { | |
| 27 | readonly parentRunId: RunId; | |
| 28 | readonly generation: number; | |
| 29 | readonly messageId: MessageId; | |
| 30 | }; | |
| 31 | /** | |
| 32 | * How the continuation turn gets its content. | |
| 33 | * | |
| 34 | * `adapter_buffered` (default) is the provider-native wake: the adapter has | |
| 35 | * already buffered the CLI's wake output, and the dispatched message only | |
| 36 | * triggers ingestion. `ClaudeAdapterV2` deliberately discards the message | |
| 37 | * text on that path. | |
| 38 | * | |
| 39 | * `message_text` is for app-owned work with no buffered provider output, such | |
| 40 | * as a delegated child finishing. The text is the entire wake, so it must | |
| 41 | * reach the provider as a real prompt. | |
| 42 | */ | |
| 43 | readonly delivery?: "adapter_buffered" | "message_text"; | |
| 44 | readonly dispatchIfCurrent?: <A, E, R>( | |
| 45 | effect: Effect.Effect<A, E, R>, | |
| 46 | ) => Effect.Effect<Option.Option<A>, E, R>; | |
| 47 | /** Clears a pending offer that the continuation worker intentionally drops. */ | |
| 48 | readonly clearIfCurrent?: () => Effect.Effect<void>; | |
| 49 | } | |
| 50 | ||
| 51 | /** | |
| 52 | * Adapters offer a continuation request when provider-native work completes | |
| 53 | * outside an active turn (for example a Claude background task wake turn) so | |
| 54 | * the orchestrator can start a run that ingests it. The default reference | |
| 55 | * drops requests, keeping adapter construction dependency-free in tests; the | |
| 56 | * live layer must be shared with the ProviderContinuationService worker that | |
| 57 | * drains it. | |
| 58 | */ | |
| 59 | export class ProviderContinuationRequests extends Context.Reference<{ | |
| 60 | readonly offer: (request: ProviderContinuationRequest) => Effect.Effect<void>; | |
| 61 | readonly take: Effect.Effect<ProviderContinuationRequest>; | |
| 62 | }>("@t3tools/provider-core/server/continuationRequests/ProviderContinuationRequests", { | |
| 63 | defaultValue: () => ({ offer: () => Effect.void, take: Effect.never }), | |
| 64 | }) {} | |
| 65 | ||
| 66 | export const layer = Layer.effect( | |
| 67 | ProviderContinuationRequests, | |
| 68 | Effect.gen(function* () { | |
| 69 | const queue = yield* Queue.unbounded<ProviderContinuationRequest>(); | |
| 70 | return { | |
| 71 | offer: (request: ProviderContinuationRequest) => | |
| 72 | Queue.offer(queue, request).pipe(Effect.asVoid), | |
| 73 | take: Queue.take(queue), | |
| 74 | }; | |
| 75 | }), | |
| 76 | ); |
packages/provider-core/src/server/driver.ts+1-1| ... | ... | @@ -101,7 +101,7 @@ export interface ProviderInstance { |
| 101 | 101 | ProviderConsumeResetCreditOutcome, |
| 102 | 102 | ProviderDriverError |
| 103 | 103 | >; |
| 104 | readonly orchestrationAdapter: ProviderAdapter.ProviderAdapterV2Shape; | |
| 104 | readonly orchestrationAdapter: ProviderAdapter.ProviderAdapterV2["Service"]; | |
| 105 | 105 | readonly textGeneration: ProviderTextGeneration; |
| 106 | 106 | readonly auth?: ProviderAuthController; |
| 107 | 107 | readonly acpSessionManagement?: { |
packages/provider-core/src/server/failure.ts+3-5| ... | ... | @@ -13,7 +13,7 @@ import type { |
| 13 | 13 | import * as DateTime from "effect/DateTime"; |
| 14 | 14 | import * as Cause from "effect/Cause"; |
| 15 | 15 | |
| 16 | import type * as IdAllocator from "./IdAllocator.ts"; | |
| 16 | import * as IdAllocator from "./IdAllocator.ts"; | |
| 17 | 17 | import * as Schema from "effect/Schema"; |
| 18 | 18 | |
| 19 | 19 | /** Raised when portable history does not fit the target provider's context window. */ |
| ... | ... | @@ -172,7 +172,6 @@ export function makeProviderFailure(input: { |
| 172 | 172 | } |
| 173 | 173 | |
| 174 | 174 | export function makeProviderFailureTurnItem(input: { |
| 175 | readonly idAllocator: IdAllocator.IdAllocatorV2Shape; | |
| 176 | 175 | readonly driver: ProviderDriverKind; |
| 177 | 176 | readonly threadId: ThreadId; |
| 178 | 177 | readonly runId: RunId | null; |
| ... | ... | @@ -186,7 +185,7 @@ export function makeProviderFailureTurnItem(input: { |
| 186 | 185 | readonly occurredAt: DateTime.Utc; |
| 187 | 186 | }): Extract<OrchestrationV2TurnItem, { readonly type: "error" }> { |
| 188 | 187 | return { |
| 189 | id: input.idAllocator.derive.turnItemFromProviderItem({ | |
| 188 | id: IdAllocator.derive.turnItemFromProviderItem({ | |
| 190 | 189 | driver: input.driver, |
| 191 | 190 | nativeItemId: `terminal-failure:${input.providerTurnId}`, |
| 192 | 191 | }), |
| ... | ... | @@ -210,7 +209,6 @@ export function makeProviderFailureTurnItem(input: { |
| 210 | 209 | } |
| 211 | 210 | |
| 212 | 211 | export function makeProviderRetryTurnItem(input: { |
| 213 | readonly idAllocator: IdAllocator.IdAllocatorV2Shape; | |
| 214 | 212 | readonly driver: ProviderDriverKind; |
| 215 | 213 | readonly threadId: ThreadId; |
| 216 | 214 | readonly runId: RunId | null; |
| ... | ... | @@ -237,7 +235,7 @@ export function makeProviderRetryTurnItem(input: { |
| 237 | 235 | title = "Provider retry stopped"; |
| 238 | 236 | } |
| 239 | 237 | return { |
| 240 | id: input.idAllocator.derive.turnItemFromProviderItem({ | |
| 238 | id: IdAllocator.derive.turnItemFromProviderItem({ | |
| 241 | 239 | driver: input.driver, |
| 242 | 240 | nativeItemId: `terminal-failure:${input.providerTurnId}`, |
| 243 | 241 | }), |
packages/provider-core/src/server/providerAuthFlow.test.ts created+559| ... | ... | @@ -0,0 +1,559 @@ |
| 1 | import * as NodeServices from "@effect/platform-node/NodeServices"; | |
| 2 | import { assert, it } from "@effect/vitest"; | |
| 3 | import { | |
| 4 | ProviderInstanceId, | |
| 5 | ProviderSetupError, | |
| 6 | type ProviderAuthInteraction, | |
| 7 | type ProviderAuthResponse, | |
| 8 | type ProviderAuthState, | |
| 9 | } from "@t3tools/contracts"; | |
| 10 | import * as Fiber from "effect/Fiber"; | |
| 11 | import * as Exit from "effect/Exit"; | |
| 12 | import * as Scope from "effect/Scope"; | |
| 13 | import * as Deferred from "effect/Deferred"; | |
| 14 | import * as Effect from "effect/Effect"; | |
| 15 | import * as Option from "effect/Option"; | |
| 16 | import * as Stream from "effect/Stream"; | |
| 17 | import * as TestClock from "effect/testing/TestClock"; | |
| 18 | ||
| 19 | import * as ProviderAuthFlow from "./providerAuthFlow.ts"; | |
| 20 | ||
| 21 | const instanceId = ProviderInstanceId.make("auth-flow-test"); | |
| 22 | const method = { id: "browser", name: "Browser", description: null, type: "agent" as const }; | |
| 23 | ||
| 24 | it.effect("distinguishes pending method discovery from an agent with no sign-in methods", () => | |
| 25 | Effect.gen(function* () { | |
| 26 | const discovered = yield* Deferred.make<ReadonlyArray<typeof method>>(); | |
| 27 | const controller = yield* ProviderAuthFlow.make({ | |
| 28 | instanceId, | |
| 29 | credentialBinding: { owner: "provider", key: "shared-agent" }, | |
| 30 | methods: Deferred.await(discovered), | |
| 31 | authenticate: () => Effect.void, | |
| 32 | logout: Effect.void, | |
| 33 | }); | |
| 34 | const pending = yield* controller | |
| 35 | .subscribe("owner") | |
| 36 | .pipe(Stream.runHead, Effect.map(Option.getOrThrow)); | |
| 37 | assert.isUndefined(pending.methods); | |
| 38 | yield* Deferred.succeed(discovered, []); | |
| 39 | const ready = yield* controller.subscribe("owner").pipe( | |
| 40 | Stream.filter((state) => state.methods !== undefined), | |
| 41 | Stream.runHead, | |
| 42 | Effect.map(Option.getOrThrow), | |
| 43 | ); | |
| 44 | assert.deepEqual(ready.methods, []); | |
| 45 | }).pipe(Effect.scoped, Effect.provide(NodeServices.layer)), | |
| 46 | ); | |
| 47 | ||
| 48 | const makeHarness = Effect.gen(function* () { | |
| 49 | const approved = yield* Deferred.make<void>(); | |
| 50 | const verified = yield* Deferred.make<void>(); | |
| 51 | const started = yield* Deferred.make<void>(); | |
| 52 | let attempts = 0; | |
| 53 | const controller = yield* ProviderAuthFlow.make({ | |
| 54 | instanceId, | |
| 55 | credentialBinding: { owner: "provider", key: "shared-agent" }, | |
| 56 | methods: Effect.succeed([method]), | |
| 57 | authenticate: (_, context) => | |
| 58 | Effect.gen(function* () { | |
| 59 | attempts++; | |
| 60 | yield* context.setInteraction( | |
| 61 | { | |
| 62 | type: "browser", | |
| 63 | id: "consent", | |
| 64 | url: "https://example.com/login", | |
| 65 | requiresConsent: true, | |
| 66 | }, | |
| 67 | (response) => | |
| 68 | response.type === "browser" && response.action === "accept" | |
| 69 | ? Deferred.succeed(approved, undefined).pipe(Effect.asVoid) | |
| 70 | : Effect.void, | |
| 71 | ); | |
| 72 | yield* Deferred.succeed(started, undefined); | |
| 73 | yield* Deferred.await(approved); | |
| 74 | yield* context.verifying; | |
| 75 | yield* Deferred.await(verified); | |
| 76 | }), | |
| 77 | logout: Effect.void, | |
| 78 | }); | |
| 79 | const phase = (phase: ProviderAuthState["phase"], owner = "owner") => | |
| 80 | controller.subscribe(owner).pipe( | |
| 81 | Stream.filter((state) => state.phase === phase), | |
| 82 | Stream.runHead, | |
| 83 | Effect.map(Option.getOrThrow), | |
| 84 | ); | |
| 85 | return { controller, phase, started, verified, attempts: () => attempts }; | |
| 86 | }); | |
| 87 | ||
| 88 | it.effect("requires owner consent and provider verification before success", () => | |
| 89 | Effect.gen(function* () { | |
| 90 | const { controller, phase, started, verified, attempts } = yield* makeHarness; | |
| 91 | const state = yield* controller.start("owner"); | |
| 92 | yield* Deferred.await(started); | |
| 93 | const response = { | |
| 94 | instanceId, | |
| 95 | flowId: state.flowId!, | |
| 96 | interactionId: "consent", | |
| 97 | response: { type: "browser" as const, action: "accept" as const }, | |
| 98 | }; | |
| 99 | const other = yield* phase("waiting", "other"); | |
| 100 | assert.isNull(other.interaction); | |
| 101 | assert.isNull(other.flowId); | |
| 102 | assert.isTrue( | |
| 103 | (yield* controller.respond!("other", response).pipe(Effect.result))._tag === "Failure", | |
| 104 | ); | |
| 105 | assert.isTrue( | |
| 106 | (yield* controller.respond!("owner", { ...response, interactionId: "stale" }).pipe( | |
| 107 | Effect.result, | |
| 108 | ))._tag === "Failure", | |
| 109 | ); | |
| 110 | yield* controller.start("owner"); | |
| 111 | assert.strictEqual(attempts(), 1); | |
| 112 | yield* controller.respond!("owner", response); | |
| 113 | yield* phase("verifying"); | |
| 114 | yield* Deferred.succeed(verified, undefined); | |
| 115 | assert.strictEqual((yield* phase("succeeded")).phase, "succeeded"); | |
| 116 | }).pipe(Effect.scoped, Effect.provide(NodeServices.layer)), | |
| 117 | ); | |
| 118 | ||
| 119 | it.effect("cancellation rejects late consent and permits another login", () => | |
| 120 | Effect.gen(function* () { | |
| 121 | const { controller, started, phase } = yield* makeHarness; | |
| 122 | const state = yield* controller.start("owner"); | |
| 123 | yield* Deferred.await(started); | |
| 124 | yield* controller.cancel("owner", state.flowId!); | |
| 125 | assert.strictEqual((yield* phase("cancelled")).phase, "cancelled"); | |
| 126 | assert.isTrue( | |
| 127 | (yield* controller.respond!("owner", { | |
| 128 | instanceId, | |
| 129 | flowId: state.flowId!, | |
| 130 | interactionId: "consent", | |
| 131 | response: { type: "browser", action: "accept" }, | |
| 132 | }).pipe(Effect.result))._tag === "Failure", | |
| 133 | ); | |
| 134 | const next = yield* controller.start("other"); | |
| 135 | assert.notStrictEqual(next.flowId, state.flowId); | |
| 136 | }).pipe(Effect.scoped, Effect.provide(NodeServices.layer)), | |
| 137 | ); | |
| 138 | ||
| 139 | it.effect("expires pending login and rejects its response", () => | |
| 140 | Effect.gen(function* () { | |
| 141 | const { controller, started, phase } = yield* makeHarness; | |
| 142 | const state = yield* controller.start("owner"); | |
| 143 | yield* Deferred.await(started); | |
| 144 | yield* TestClock.adjust(300_001); | |
| 145 | assert.include((yield* phase("failed")).message ?? "", "expired"); | |
| 146 | assert.isTrue( | |
| 147 | (yield* controller.respond!("owner", { | |
| 148 | instanceId, | |
| 149 | flowId: state.flowId!, | |
| 150 | interactionId: "consent", | |
| 151 | response: { type: "browser", action: "accept" }, | |
| 152 | }).pipe(Effect.result))._tag === "Failure", | |
| 153 | ); | |
| 154 | }).pipe(Effect.scoped, Effect.provide(NodeServices.layer)), | |
| 155 | ); | |
| 156 | ||
| 157 | it.effect("closes admitted provider processes and blocks new ones while login is pending", () => | |
| 158 | Effect.gen(function* () { | |
| 159 | const { controller, started } = yield* makeHarness; | |
| 160 | let closed = false; | |
| 161 | yield* controller.withAccess!( | |
| 162 | Effect.addFinalizer(() => | |
| 163 | Effect.sync(() => { | |
| 164 | closed = true; | |
| 165 | }), | |
| 166 | ), | |
| 167 | ); | |
| 168 | yield* controller.start("owner"); | |
| 169 | yield* Deferred.await(started); | |
| 170 | assert.isTrue(closed); | |
| 171 | assert.isTrue( | |
| 172 | (yield* controller.withAccess!(Effect.void).pipe(Effect.result))._tag === "Failure", | |
| 173 | ); | |
| 174 | }).pipe(Effect.scoped, Effect.provide(NodeServices.layer)), | |
| 175 | ); | |
| 176 | ||
| 177 | it.effect("shared credential invalidation closes admitted processes before permitting reuse", () => | |
| 178 | Effect.gen(function* () { | |
| 179 | const { controller } = yield* makeHarness; | |
| 180 | let closed = false; | |
| 181 | yield* controller.withAccess!( | |
| 182 | Effect.addFinalizer(() => | |
| 183 | Effect.sync(() => { | |
| 184 | closed = true; | |
| 185 | }), | |
| 186 | ), | |
| 187 | ); | |
| 188 | yield* controller.invalidate!; | |
| 189 | assert.isTrue(closed); | |
| 190 | assert.strictEqual(yield* controller.withAccess!(Effect.succeed("new process")), "new process"); | |
| 191 | }).pipe(Effect.scoped, Effect.provide(NodeServices.layer)), | |
| 192 | ); | |
| 193 | ||
| 194 | it.effect.each([ | |
| 195 | { | |
| 196 | interaction: { type: "terminal", id: "terminal", output: "Log in" }, | |
| 197 | response: { type: "terminal", data: "input", size: { cols: 80, rows: 24 } }, | |
| 198 | }, | |
| 199 | { | |
| 200 | interaction: { | |
| 201 | type: "credentials", | |
| 202 | id: "credentials", | |
| 203 | fields: [{ name: "token", label: "Token", secret: true }], | |
| 204 | }, | |
| 205 | response: { type: "credentials", values: { token: "private-token" } }, | |
| 206 | }, | |
| 207 | ] satisfies ReadonlyArray<{ | |
| 208 | interaction: ProviderAuthInteraction; | |
| 209 | response: ProviderAuthResponse; | |
| 210 | }>)( | |
| 211 | "keeps $interaction.type responses private and waits for adapter verification", | |
| 212 | ({ interaction, response }) => | |
| 213 | Effect.gen(function* () { | |
| 214 | const received = yield* Deferred.make<ProviderAuthResponse>(); | |
| 215 | const controller = yield* ProviderAuthFlow.make({ | |
| 216 | instanceId, | |
| 217 | credentialBinding: { owner: "t3", key: "binding" }, | |
| 218 | methods: Effect.succeed([method]), | |
| 219 | authenticate: (_, context) => | |
| 220 | Effect.gen(function* () { | |
| 221 | yield* context.setInteraction(interaction, (response) => | |
| 222 | Deferred.succeed(received, response).pipe(Effect.asVoid), | |
| 223 | ); | |
| 224 | yield* Deferred.await(received); | |
| 225 | yield* context.verifying; | |
| 226 | return yield* Effect.never; | |
| 227 | }), | |
| 228 | logout: Effect.void, | |
| 229 | }); | |
| 230 | const state = yield* controller.start("owner"); | |
| 231 | const pending = yield* controller.subscribe("owner").pipe( | |
| 232 | Stream.filter((state) => state.phase === "waiting"), | |
| 233 | Stream.runHead, | |
| 234 | Effect.map(Option.getOrThrow), | |
| 235 | ); | |
| 236 | assert.deepStrictEqual(pending.interaction, interaction); | |
| 237 | const hidden = yield* controller | |
| 238 | .subscribe("other") | |
| 239 | .pipe(Stream.runHead, Effect.map(Option.getOrThrow)); | |
| 240 | assert.isNull(hidden.interaction); | |
| 241 | yield* controller.respond!("owner", { | |
| 242 | instanceId, | |
| 243 | flowId: state.flowId!, | |
| 244 | interactionId: interaction.id, | |
| 245 | response, | |
| 246 | }); | |
| 247 | assert.deepStrictEqual(yield* Deferred.await(received), response); | |
| 248 | const verifying = yield* controller.subscribe("owner").pipe( | |
| 249 | Stream.filter((state) => state.phase === "verifying"), | |
| 250 | Stream.runHead, | |
| 251 | Effect.map(Option.getOrThrow), | |
| 252 | ); | |
| 253 | assert.isNull(verifying.interaction); | |
| 254 | assert.notInclude(verifying.message ?? "", "private-token"); | |
| 255 | yield* controller.cancel("owner", state.flowId!); | |
| 256 | }).pipe(Effect.scoped, Effect.provide(NodeServices.layer)), | |
| 257 | ); | |
| 258 | ||
| 259 | it.effect("shows a device code only to its owner without treating it as authenticated", () => | |
| 260 | Effect.gen(function* () { | |
| 261 | const controller = yield* ProviderAuthFlow.make({ | |
| 262 | instanceId, | |
| 263 | credentialBinding: { owner: "provider", key: "device" }, | |
| 264 | methods: Effect.succeed([method]), | |
| 265 | authenticate: (_, context) => | |
| 266 | context | |
| 267 | .setInteraction({ | |
| 268 | type: "deviceCode", | |
| 269 | id: "code", | |
| 270 | url: "https://example.com/device", | |
| 271 | userCode: "ABCD-EFGH", | |
| 272 | }) | |
| 273 | .pipe(Effect.andThen(Effect.never)), | |
| 274 | logout: Effect.void, | |
| 275 | }); | |
| 276 | const start = yield* controller.start("owner"); | |
| 277 | const waiting = yield* controller.subscribe("owner").pipe( | |
| 278 | Stream.filter((state) => state.phase === "waiting"), | |
| 279 | Stream.runHead, | |
| 280 | Effect.map(Option.getOrThrow), | |
| 281 | ); | |
| 282 | assert.strictEqual(waiting.interaction?.type, "deviceCode"); | |
| 283 | assert.strictEqual(waiting.authorizationUrl, "https://example.com/device"); | |
| 284 | const other = yield* controller | |
| 285 | .subscribe("other") | |
| 286 | .pipe(Stream.runHead, Effect.map(Option.getOrThrow)); | |
| 287 | assert.isNull(other.interaction); | |
| 288 | assert.isNull(other.authorizationUrl); | |
| 289 | yield* controller.cancel("owner", start.flowId!); | |
| 290 | }).pipe(Effect.scoped, Effect.provide(NodeServices.layer)), | |
| 291 | ); | |
| 292 | ||
| 293 | it.effect.each([ | |
| 294 | { | |
| 295 | failure: Effect.fail( | |
| 296 | new ProviderSetupError({ | |
| 297 | instanceId, | |
| 298 | operation: "authenticate", | |
| 299 | detail: "This account cannot sign in. Choose another account.", | |
| 300 | }), | |
| 301 | ), | |
| 302 | message: "This account cannot sign in. Choose another account.", | |
| 303 | }, | |
| 304 | { | |
| 305 | failure: Effect.die(new Error("private-token in native diagnostics")), | |
| 306 | message: "Sign-in failed. Start again.", | |
| 307 | }, | |
| 308 | ])("publishes only safe authentication failure text %#", ({ failure, message }) => | |
| 309 | Effect.gen(function* () { | |
| 310 | const controller = yield* ProviderAuthFlow.make({ | |
| 311 | instanceId, | |
| 312 | credentialBinding: { owner: "t3", key: "failure" }, | |
| 313 | methods: Effect.succeed([method]), | |
| 314 | authenticate: () => failure, | |
| 315 | logout: Effect.void, | |
| 316 | }); | |
| 317 | yield* controller.start("owner"); | |
| 318 | const failed = yield* controller.subscribe("owner").pipe( | |
| 319 | Stream.filter((state) => state.phase === "failed"), | |
| 320 | Stream.runHead, | |
| 321 | Effect.map(Option.getOrThrow), | |
| 322 | ); | |
| 323 | assert.strictEqual(failed.message, message); | |
| 324 | }).pipe(Effect.scoped, Effect.provide(NodeServices.layer)), | |
| 325 | ); | |
| 326 | ||
| 327 | const makeBlockingResponseHarness = Effect.gen(function* () { | |
| 328 | const entered = yield* Deferred.make<void>(); | |
| 329 | const cleanupStarted = yield* Deferred.make<void>(); | |
| 330 | const cleanupReleased = yield* Deferred.make<void>(); | |
| 331 | const cleanupFinished = yield* Deferred.make<void>(); | |
| 332 | const authenticationFinished = yield* Deferred.make<void>(); | |
| 333 | let responses = 0; | |
| 334 | const controller = yield* ProviderAuthFlow.make({ | |
| 335 | instanceId, | |
| 336 | credentialBinding: { owner: "t3", key: "blocked-response" }, | |
| 337 | methods: Effect.succeed([method]), | |
| 338 | authenticate: (_, context) => | |
| 339 | Effect.gen(function* () { | |
| 340 | yield* context.setInteraction( | |
| 341 | { | |
| 342 | type: "browser", | |
| 343 | id: "blocked", | |
| 344 | url: "https://example.com/login", | |
| 345 | requiresConsent: true, | |
| 346 | }, | |
| 347 | () => | |
| 348 | Effect.gen(function* () { | |
| 349 | responses++; | |
| 350 | yield* Deferred.succeed(entered, undefined); | |
| 351 | return yield* Effect.never; | |
| 352 | }).pipe( | |
| 353 | Effect.ensuring( | |
| 354 | Effect.gen(function* () { | |
| 355 | yield* Deferred.succeed(cleanupStarted, undefined); | |
| 356 | yield* Deferred.await(cleanupReleased); | |
| 357 | yield* Deferred.succeed(cleanupFinished, undefined); | |
| 358 | }), | |
| 359 | ), | |
| 360 | ), | |
| 361 | ); | |
| 362 | yield* Deferred.await(authenticationFinished); | |
| 363 | }), | |
| 364 | logout: Effect.void, | |
| 365 | }); | |
| 366 | const start = yield* controller.start("owner"); | |
| 367 | yield* controller.subscribe("owner").pipe( | |
| 368 | Stream.filter((state) => state.phase === "waiting"), | |
| 369 | Stream.runHead, | |
| 370 | ); | |
| 371 | const input = { | |
| 372 | instanceId, | |
| 373 | flowId: start.flowId!, | |
| 374 | interactionId: "blocked", | |
| 375 | response: { type: "browser" as const, action: "accept" as const }, | |
| 376 | }; | |
| 377 | const response = yield* controller.respond!("owner", input).pipe(Effect.exit, Effect.forkChild); | |
| 378 | yield* Deferred.await(entered); | |
| 379 | return { | |
| 380 | controller, | |
| 381 | input, | |
| 382 | response, | |
| 383 | cleanupStarted, | |
| 384 | cleanupReleased, | |
| 385 | cleanupFinished, | |
| 386 | authenticationFinished, | |
| 387 | responses: () => responses, | |
| 388 | }; | |
| 389 | }); | |
| 390 | ||
| 391 | it.effect.each(["cancel", "logout"] as const)( | |
| 392 | "%s interrupts and awaits a blocked adapter response before admitting another sign-in", | |
| 393 | (action) => | |
| 394 | Effect.gen(function* () { | |
| 395 | const harness = yield* makeBlockingResponseHarness; | |
| 396 | const duplicate = yield* Effect.flip(harness.controller.respond!("owner", harness.input)); | |
| 397 | assert.include(duplicate.detail, "already in progress"); | |
| 398 | assert.equal(harness.responses(), 1); | |
| 399 | const stopping = yield* ( | |
| 400 | action === "cancel" | |
| 401 | ? harness.controller.cancel("owner", harness.input.flowId) | |
| 402 | : harness.controller.logout(Effect.void) | |
| 403 | ).pipe(Effect.forkChild); | |
| 404 | yield* Deferred.await(harness.cleanupStarted); | |
| 405 | assert.isTrue(yield* harness.controller.isChangingCredentials!); | |
| 406 | assert.isUndefined(stopping.pollUnsafe()); | |
| 407 | const premature = yield* Effect.flip(harness.controller.start("other")); | |
| 408 | assert.include(premature.detail, "in progress"); | |
| 409 | yield* Deferred.succeed(harness.cleanupReleased, undefined); | |
| 410 | yield* Fiber.join(stopping); | |
| 411 | assert.isTrue(yield* Deferred.isDone(harness.cleanupFinished)); | |
| 412 | assert.isTrue(Exit.isFailure(yield* Fiber.join(harness.response))); | |
| 413 | assert.isFalse(yield* harness.controller.isChangingCredentials!); | |
| 414 | assert.notEqual((yield* harness.controller.start("other")).flowId, harness.input.flowId); | |
| 415 | }).pipe(Effect.scoped, Effect.provide(NodeServices.layer)), | |
| 416 | ); | |
| 417 | ||
| 418 | it.effect("timeout interrupts and drains a blocked response before publishing failure", () => | |
| 419 | Effect.gen(function* () { | |
| 420 | const harness = yield* makeBlockingResponseHarness; | |
| 421 | yield* TestClock.adjust(300_001); | |
| 422 | yield* Deferred.await(harness.cleanupStarted); | |
| 423 | assert.isTrue(yield* harness.controller.isChangingCredentials!); | |
| 424 | const duringCleanup = yield* harness.controller | |
| 425 | .subscribe("owner") | |
| 426 | .pipe(Stream.runHead, Effect.map(Option.getOrThrow)); | |
| 427 | assert.notEqual(duringCleanup.phase, "failed"); | |
| 428 | yield* Deferred.succeed(harness.cleanupReleased, undefined); | |
| 429 | const failed = yield* harness.controller.subscribe("owner").pipe( | |
| 430 | Stream.filter((state) => state.phase === "failed"), | |
| 431 | Stream.runHead, | |
| 432 | Effect.map(Option.getOrThrow), | |
| 433 | ); | |
| 434 | assert.include(failed.message ?? "", "expired"); | |
| 435 | assert.isTrue(yield* Deferred.isDone(harness.cleanupFinished)); | |
| 436 | assert.isTrue(Exit.isFailure(yield* Fiber.join(harness.response))); | |
| 437 | }).pipe(Effect.scoped, Effect.provide(NodeServices.layer)), | |
| 438 | ); | |
| 439 | ||
| 440 | it.effect( | |
| 441 | "successful authentication drains a still-running response before admitting provider access", | |
| 442 | () => | |
| 443 | Effect.gen(function* () { | |
| 444 | const harness = yield* makeBlockingResponseHarness; | |
| 445 | yield* Deferred.succeed(harness.authenticationFinished, undefined); | |
| 446 | yield* Deferred.await(harness.cleanupStarted); | |
| 447 | const denied = yield* Effect.flip(harness.controller.withAccess!(Effect.void)); | |
| 448 | assert.include(denied.detail, "changing"); | |
| 449 | yield* Deferred.succeed(harness.cleanupReleased, undefined); | |
| 450 | const succeeded = yield* harness.controller.subscribe("owner").pipe( | |
| 451 | Stream.filter((state) => state.phase === "succeeded"), | |
| 452 | Stream.runHead, | |
| 453 | Effect.map(Option.getOrThrow), | |
| 454 | ); | |
| 455 | assert.equal(succeeded.phase, "succeeded"); | |
| 456 | assert.isTrue(yield* Deferred.isDone(harness.cleanupFinished)); | |
| 457 | assert.isTrue(Exit.isFailure(yield* Fiber.join(harness.response))); | |
| 458 | yield* harness.controller.withAccess!(Effect.void); | |
| 459 | }).pipe(Effect.scoped, Effect.provide(NodeServices.layer)), | |
| 460 | ); | |
| 461 | ||
| 462 | it.effect("closing the controller scope interrupts and drains its adapter response", () => | |
| 463 | Effect.gen(function* () { | |
| 464 | const scope = yield* Scope.make(); | |
| 465 | const harness = yield* makeBlockingResponseHarness.pipe( | |
| 466 | Effect.provideService(Scope.Scope, scope), | |
| 467 | ); | |
| 468 | const closing = yield* Scope.close(scope, Exit.void).pipe(Effect.forkChild); | |
| 469 | yield* Deferred.await(harness.cleanupStarted); | |
| 470 | assert.isUndefined(closing.pollUnsafe()); | |
| 471 | yield* Deferred.succeed(harness.cleanupReleased, undefined); | |
| 472 | yield* Fiber.join(closing); | |
| 473 | assert.isTrue(yield* Deferred.isDone(harness.cleanupFinished)); | |
| 474 | assert.isTrue(Exit.isFailure(yield* Fiber.join(harness.response))); | |
| 475 | }).pipe(Effect.scoped, Effect.provide(NodeServices.layer)), | |
| 476 | ); | |
| 477 | ||
| 478 | it.effect("a failed method refresh keeps the last discovered methods", () => | |
| 479 | Effect.gen(function* () { | |
| 480 | let fail = false; | |
| 481 | const controller = yield* ProviderAuthFlow.make({ | |
| 482 | instanceId, | |
| 483 | credentialBinding: { owner: "provider", key: "shared-agent" }, | |
| 484 | methods: Effect.suspend(() => | |
| 485 | fail | |
| 486 | ? Effect.fail( | |
| 487 | new ProviderSetupError({ instanceId, operation: "status", detail: "interrupted" }), | |
| 488 | ) | |
| 489 | : Effect.succeed([method]), | |
| 490 | ), | |
| 491 | authenticate: () => Effect.void, | |
| 492 | logout: Effect.void, | |
| 493 | }); | |
| 494 | yield* controller.refreshMethods!; | |
| 495 | fail = true; | |
| 496 | yield* controller.refreshMethods!; | |
| 497 | const state = yield* controller | |
| 498 | .subscribe("owner") | |
| 499 | .pipe(Stream.runHead, Effect.map(Option.getOrThrow)); | |
| 500 | assert.deepEqual(state.methods, [method]); | |
| 501 | assert.strictEqual(state.message, "interrupted"); | |
| 502 | }).pipe(Effect.scoped, Effect.provide(NodeServices.layer)), | |
| 503 | ); | |
| 504 | ||
| 505 | it.effect("rebuilding a controller leaves sessions it admitted to their owners", () => | |
| 506 | Effect.gen(function* () { | |
| 507 | const scope = yield* Scope.make(); | |
| 508 | const { controller } = yield* makeHarness.pipe(Effect.provideService(Scope.Scope, scope)); | |
| 509 | let closed = false; | |
| 510 | yield* controller.withAccess!( | |
| 511 | Effect.addFinalizer(() => | |
| 512 | Effect.sync(() => { | |
| 513 | closed = true; | |
| 514 | }), | |
| 515 | ), | |
| 516 | ); | |
| 517 | yield* Scope.close(scope, Exit.void); | |
| 518 | assert.isFalse(closed); | |
| 519 | }).pipe(Effect.scoped, Effect.provide(NodeServices.layer)), | |
| 520 | ); | |
| 521 | ||
| 522 | it.effect("profile import stops owned sessions and gates access until credentials are saved", () => | |
| 523 | Effect.gen(function* () { | |
| 524 | const controller = yield* ProviderAuthFlow.make({ | |
| 525 | instanceId, | |
| 526 | credentialBinding: { owner: "t3", key: "handoff-binding" }, | |
| 527 | methods: Effect.succeed([method]), | |
| 528 | authenticate: () => Effect.void, | |
| 529 | logout: Effect.void, | |
| 530 | }); | |
| 531 | let closed = false; | |
| 532 | yield* controller.withAccess!( | |
| 533 | Effect.addFinalizer(() => | |
| 534 | Effect.sync(() => { | |
| 535 | closed = true; | |
| 536 | }), | |
| 537 | ), | |
| 538 | ); | |
| 539 | const writing = yield* Deferred.make<void>(); | |
| 540 | const release = yield* Deferred.make<void>(); | |
| 541 | const imported = yield* controller.adoptCredentials!( | |
| 542 | Deferred.succeed(writing, undefined).pipe(Effect.andThen(Deferred.await(release))), | |
| 543 | Effect.sync(() => { | |
| 544 | assert.isTrue(closed); | |
| 545 | }), | |
| 546 | ).pipe(Effect.forkChild); | |
| 547 | yield* Deferred.await(writing); | |
| 548 | const denied = yield* controller.withAccess!(Effect.succeed("old credential process")).pipe( | |
| 549 | Effect.result, | |
| 550 | ); | |
| 551 | assert.strictEqual(denied._tag, "Failure"); | |
| 552 | yield* Deferred.succeed(release, undefined); | |
| 553 | assert.strictEqual((yield* Fiber.join(imported)).phase, "succeeded"); | |
| 554 | assert.strictEqual( | |
| 555 | yield* controller.withAccess!(Effect.succeed("new credential process")), | |
| 556 | "new credential process", | |
| 557 | ); | |
| 558 | }).pipe(Effect.scoped, Effect.provide(NodeServices.layer)), | |
| 559 | ); |
packages/provider-core/src/server/providerAuthFlow.ts created+525| ... | ... | @@ -0,0 +1,525 @@ |
| 1 | import { | |
| 2 | ProviderSetupError, | |
| 3 | type ProviderAuthInteraction, | |
| 4 | type ProviderAuthMethod, | |
| 5 | type ProviderAuthResponse, | |
| 6 | type ProviderAuthState, | |
| 7 | type ProviderInstanceId, | |
| 8 | } from "@t3tools/contracts"; | |
| 9 | import * as Cause from "effect/Cause"; | |
| 10 | import * as Option from "effect/Option"; | |
| 11 | import * as Schema from "effect/Schema"; | |
| 12 | import * as Clock from "effect/Clock"; | |
| 13 | import * as Crypto from "effect/Crypto"; | |
| 14 | import * as DateTime from "effect/DateTime"; | |
| 15 | import * as Effect from "effect/Effect"; | |
| 16 | import * as Deferred from "effect/Deferred"; | |
| 17 | import * as Exit from "effect/Exit"; | |
| 18 | import * as Fiber from "effect/Fiber"; | |
| 19 | import * as Scope from "effect/Scope"; | |
| 20 | import * as Semaphore from "effect/Semaphore"; | |
| 21 | import * as Stream from "effect/Stream"; | |
| 22 | import * as SubscriptionRef from "effect/SubscriptionRef"; | |
| 23 | ||
| 24 | import type * as ProviderAuth from "./auth.ts"; | |
| 25 | ||
| 26 | export interface ProviderAuthFlowContext { | |
| 27 | readonly flowId: string; | |
| 28 | /** The lifetime timeout interrupts authenticate before publishing its expired result. */ | |
| 29 | readonly expiresAt: number; | |
| 30 | readonly returnUrl?: string; | |
| 31 | readonly callbackMode?: "server" | "client"; | |
| 32 | readonly setInteraction: ( | |
| 33 | interaction: ProviderAuthInteraction, | |
| 34 | respond?: (response: ProviderAuthResponse) => Effect.Effect<void, ProviderSetupError>, | |
| 35 | complete?: (callbackUrl: string) => Effect.Effect<void, ProviderSetupError>, | |
| 36 | ) => Effect.Effect<void>; | |
| 37 | readonly verifying: Effect.Effect<void>; | |
| 38 | } | |
| 39 | ||
| 40 | const isSetupError = Schema.is(ProviderSetupError); | |
| 41 | ||
| 42 | const failureMessage = (cause: Cause.Cause<ProviderSetupError>) => { | |
| 43 | const error = Cause.findErrorOption(cause); | |
| 44 | return Option.isSome(error) && isSetupError(error.value) | |
| 45 | ? error.value.detail | |
| 46 | : "Sign-in failed. Start again."; | |
| 47 | }; | |
| 48 | ||
| 49 | interface Flow { | |
| 50 | readonly id: string; | |
| 51 | readonly owner: string; | |
| 52 | readonly expiresAt: number; | |
| 53 | complete?: ((callbackUrl: string) => Effect.Effect<void, ProviderSetupError>) | undefined; | |
| 54 | fiber?: Fiber.Fiber<void>; | |
| 55 | responseFiber?: Fiber.Fiber<void, ProviderSetupError>; | |
| 56 | respond: | |
| 57 | | ((response: ProviderAuthResponse) => Effect.Effect<void, ProviderSetupError>) | |
| 58 | | undefined; | |
| 59 | } | |
| 60 | ||
| 61 | /** Adapters do login and credential handling; this owns client consent and flow lifetime. */ | |
| 62 | export const make = Effect.fn("ProviderAuthFlow.make")(function* (options: { | |
| 63 | readonly instanceId: ProviderInstanceId; | |
| 64 | readonly credentialBinding: NonNullable<ProviderAuth.ProviderAuthController["credentialBinding"]>; | |
| 65 | readonly methods: Effect.Effect<ReadonlyArray<ProviderAuthMethod>, ProviderSetupError>; | |
| 66 | readonly defaultMethodId?: string; | |
| 67 | /** Stored account profiles can change the advertised methods after auth/logout. */ | |
| 68 | readonly refreshMethodsAfterAuth?: boolean; | |
| 69 | /** Fail with ProviderSetupError containing safe text for the user, never native token data. */ | |
| 70 | readonly authenticate: ( | |
| 71 | methodId: string, | |
| 72 | context: ProviderAuthFlowContext, | |
| 73 | ) => Effect.Effect<void, ProviderSetupError, Scope.Scope>; | |
| 74 | readonly logout: Effect.Effect<void | string, ProviderSetupError>; | |
| 75 | readonly timeoutMs?: number; | |
| 76 | }) { | |
| 77 | const scope = yield* Scope.Scope; | |
| 78 | const crypto = yield* Crypto.Crypto; | |
| 79 | const lock = yield* Semaphore.make(1); | |
| 80 | const timeoutMs = options.timeoutMs ?? 300_000; | |
| 81 | const empty: ProviderAuthState = { | |
| 82 | instanceId: options.instanceId, | |
| 83 | phase: "idle", | |
| 84 | flowId: null, | |
| 85 | authorizationUrl: null, | |
| 86 | expiresAt: null, | |
| 87 | message: null, | |
| 88 | interaction: null, | |
| 89 | credentialOwner: options.credentialBinding.owner, | |
| 90 | }; | |
| 91 | const snapshot = yield* SubscriptionRef.make({ owner: null as string | null, state: empty }); | |
| 92 | let active: Flow | undefined; | |
| 93 | let operation: "idle" | "auth" | "stopping" | "closed" = "idle"; | |
| 94 | const sessions = new Set<Scope.Closeable>(); | |
| 95 | const stopOwnedSessions = Effect.suspend(() => | |
| 96 | Effect.forEach(Array.from(sessions), (session) => Scope.close(session, Exit.void), { | |
| 97 | discard: true, | |
| 98 | concurrency: "unbounded", | |
| 99 | }), | |
| 100 | ); | |
| 101 | const publish = (flow: Flow, patch: Partial<ProviderAuthState>) => | |
| 102 | Effect.suspend(() => | |
| 103 | active === flow | |
| 104 | ? SubscriptionRef.update(snapshot, (current) => ({ | |
| 105 | owner: flow.owner, | |
| 106 | state: { ...current.state, ...patch }, | |
| 107 | })) | |
| 108 | : Effect.void, | |
| 109 | ); | |
| 110 | ||
| 111 | const requireFlow = Effect.fnUntraced(function* (owner: string, id: string) { | |
| 112 | if ( | |
| 113 | operation !== "auth" || | |
| 114 | !active || | |
| 115 | active.owner !== owner || | |
| 116 | active.id !== id || | |
| 117 | (yield* Clock.currentTimeMillis) >= active.expiresAt | |
| 118 | ) { | |
| 119 | return yield* new ProviderSetupError({ | |
| 120 | instanceId: options.instanceId, | |
| 121 | operation: "respond", | |
| 122 | detail: "This sign-in is no longer active in this client.", | |
| 123 | }); | |
| 124 | } | |
| 125 | return active; | |
| 126 | }); | |
| 127 | ||
| 128 | // Method discovery does not start a sign-in flow. | |
| 129 | const refreshMethods = options.methods.pipe( | |
| 130 | Effect.flatMap((methods) => | |
| 131 | SubscriptionRef.update(snapshot, (current) => ({ | |
| 132 | ...current, | |
| 133 | state: { ...current.state, methods }, | |
| 134 | })), | |
| 135 | ), | |
| 136 | // Keep the last known methods when a refresh fails or is interrupted. | |
| 137 | Effect.catch((error) => | |
| 138 | SubscriptionRef.update(snapshot, (current) => ({ | |
| 139 | ...current, | |
| 140 | state: { ...current.state, methods: current.state.methods ?? [], message: error.detail }, | |
| 141 | })), | |
| 142 | ), | |
| 143 | ); | |
| 144 | yield* refreshMethods.pipe(Effect.forkIn(scope)); | |
| 145 | ||
| 146 | const controller: ProviderAuth.ProviderAuthController = { | |
| 147 | credentialBinding: options.credentialBinding, | |
| 148 | adoptCredentials: (update, stopSessions) => | |
| 149 | Effect.gen(function* () { | |
| 150 | yield* lock.withPermit( | |
| 151 | Effect.gen(function* () { | |
| 152 | if (operation !== "idle") | |
| 153 | return yield* new ProviderSetupError({ | |
| 154 | instanceId: options.instanceId, | |
| 155 | operation: "import", | |
| 156 | detail: "Finish or cancel the existing sign-in first.", | |
| 157 | }); | |
| 158 | operation = "stopping"; | |
| 159 | }), | |
| 160 | ); | |
| 161 | const result = yield* Effect.gen(function* () { | |
| 162 | yield* stopOwnedSessions; | |
| 163 | yield* stopSessions; | |
| 164 | yield* update; | |
| 165 | yield* refreshMethods; | |
| 166 | }).pipe(Effect.exit); | |
| 167 | const state: ProviderAuthState = { | |
| 168 | ...empty, | |
| 169 | methods: snapshot.value.state.methods ?? [], | |
| 170 | phase: Exit.isSuccess(result) ? "succeeded" : "failed", | |
| 171 | message: Exit.isSuccess(result) ? null : failureMessage(result.cause), | |
| 172 | }; | |
| 173 | yield* lock.withPermit( | |
| 174 | Effect.gen(function* () { | |
| 175 | yield* SubscriptionRef.set(snapshot, { owner: null, state }); | |
| 176 | operation = "idle"; | |
| 177 | }), | |
| 178 | ); | |
| 179 | if (Exit.isFailure(result)) return yield* Effect.failCause(result.cause); | |
| 180 | return state; | |
| 181 | }).pipe(Effect.uninterruptible), | |
| 182 | refreshMethods, | |
| 183 | invalidate: lock.withPermit( | |
| 184 | Effect.gen(function* () { | |
| 185 | if (operation !== "idle") return; | |
| 186 | yield* stopOwnedSessions; | |
| 187 | yield* SubscriptionRef.set(snapshot, { | |
| 188 | owner: null, | |
| 189 | state: { | |
| 190 | ...empty, | |
| 191 | methods: snapshot.value.state.methods ?? [], | |
| 192 | message: "This provider's shared sign-in changed.", | |
| 193 | }, | |
| 194 | }); | |
| 195 | }), | |
| 196 | ), | |
| 197 | isChangingCredentials: Effect.sync(() => operation !== "idle"), | |
| 198 | withAccess: (task) => | |
| 199 | Effect.uninterruptibleMask((restore) => | |
| 200 | Effect.gen(function* () { | |
| 201 | const parent = yield* Scope.Scope; | |
| 202 | const child = yield* lock.withPermit( | |
| 203 | Effect.gen(function* () { | |
| 204 | if (operation !== "idle") | |
| 205 | return yield* new ProviderSetupError({ | |
| 206 | instanceId: options.instanceId, | |
| 207 | operation: "session", | |
| 208 | detail: "Provider sign-in is changing. Try again after it finishes.", | |
| 209 | }); | |
| 210 | const child = yield* Scope.make(); | |
| 211 | sessions.add(child); | |
| 212 | yield* Scope.addFinalizer( | |
| 213 | child, | |
| 214 | Effect.sync(() => { | |
| 215 | sessions.delete(child); | |
| 216 | }), | |
| 217 | ); | |
| 218 | yield* Scope.addFinalizer(parent, Scope.close(child, Exit.void)); | |
| 219 | return child; | |
| 220 | }), | |
| 221 | ); | |
| 222 | const fiber = yield* restore(task).pipe( | |
| 223 | Effect.provideService(Scope.Scope, child), | |
| 224 | Effect.forkIn(child), | |
| 225 | ); | |
| 226 | return yield* restore(Fiber.await(fiber)).pipe( | |
| 227 | Effect.flatMap((result) => result), | |
| 228 | Effect.onExit((result) => | |
| 229 | Exit.isFailure(result) ? Scope.close(child, Exit.void) : Effect.void, | |
| 230 | ), | |
| 231 | ); | |
| 232 | }), | |
| 233 | ), | |
| 234 | start: (owner, stopSessions = Effect.void, selectedMethodId, returnUrl, callbackMode) => | |
| 235 | lock.withPermit( | |
| 236 | Effect.gen(function* () { | |
| 237 | if (operation === "auth" && active?.owner === owner) return snapshot.value.state; | |
| 238 | if (operation !== "idle") | |
| 239 | return yield* new ProviderSetupError({ | |
| 240 | instanceId: options.instanceId, | |
| 241 | operation: "start", | |
| 242 | detail: "Provider setup is already in progress.", | |
| 243 | }); | |
| 244 | const id = yield* crypto.randomUUIDv4.pipe( | |
| 245 | Effect.mapError( | |
| 246 | () => | |
| 247 | new ProviderSetupError({ | |
| 248 | instanceId: options.instanceId, | |
| 249 | operation: "start", | |
| 250 | detail: "Could not start sign-in. Try again.", | |
| 251 | }), | |
| 252 | ), | |
| 253 | ); | |
| 254 | const flow: Flow = { | |
| 255 | id, | |
| 256 | owner, | |
| 257 | expiresAt: (yield* Clock.currentTimeMillis) + timeoutMs, | |
| 258 | respond: undefined, | |
| 259 | }; | |
| 260 | active = flow; | |
| 261 | operation = "auth"; | |
| 262 | const state: ProviderAuthState = { | |
| 263 | ...empty, | |
| 264 | methods: snapshot.value.state.methods ?? [], | |
| 265 | phase: "starting", | |
| 266 | flowId: id, | |
| 267 | expiresAt: DateTime.formatIso(DateTime.makeUnsafe(flow.expiresAt)), | |
| 268 | message: "Starting sign-in.", | |
| 269 | }; | |
| 270 | yield* SubscriptionRef.set(snapshot, { owner, state }); | |
| 271 | flow.fiber = yield* Effect.gen(function* () { | |
| 272 | const methods = yield* options.methods; | |
| 273 | yield* publish(flow, { methods }); | |
| 274 | const methodId = selectedMethodId ?? options.defaultMethodId ?? methods[0]?.id; | |
| 275 | if (!methodId || !methods.some((method) => method.id === methodId)) | |
| 276 | return yield* new ProviderSetupError({ | |
| 277 | instanceId: options.instanceId, | |
| 278 | operation: "start", | |
| 279 | detail: "The provider did not advertise this sign-in method.", | |
| 280 | }); | |
| 281 | yield* stopSessions.pipe(Effect.ensuring(stopOwnedSessions)); | |
| 282 | yield* options.authenticate(methodId, { | |
| 283 | flowId: id, | |
| 284 | expiresAt: flow.expiresAt, | |
| 285 | ...(returnUrl ? { returnUrl } : {}), | |
| 286 | ...(callbackMode ? { callbackMode } : {}), | |
| 287 | setInteraction: (interaction, respond, complete) => | |
| 288 | Effect.gen(function* () { | |
| 289 | if (active !== flow) return; | |
| 290 | flow.respond = respond; | |
| 291 | flow.complete = complete; | |
| 292 | yield* publish(flow, { | |
| 293 | phase: "waiting", | |
| 294 | interaction, | |
| 295 | authorizationUrl: | |
| 296 | interaction.type === "browser" || interaction.type === "deviceCode" | |
| 297 | ? interaction.url | |
| 298 | : null, | |
| 299 | message: "Complete sign-in to continue.", | |
| 300 | }); | |
| 301 | }), | |
| 302 | verifying: Effect.gen(function* () { | |
| 303 | flow.respond = undefined; | |
| 304 | flow.complete = undefined; | |
| 305 | yield* publish(flow, { | |
| 306 | phase: "verifying", | |
| 307 | interaction: null, | |
| 308 | authorizationUrl: null, | |
| 309 | message: "Checking provider sign-in.", | |
| 310 | }); | |
| 311 | }), | |
| 312 | }); | |
| 313 | }).pipe( | |
| 314 | Effect.scoped, | |
| 315 | Effect.timeoutOrElse({ | |
| 316 | duration: timeoutMs, | |
| 317 | orElse: () => | |
| 318 | Effect.fail( | |
| 319 | new ProviderSetupError({ | |
| 320 | instanceId: options.instanceId, | |
| 321 | operation: "start", | |
| 322 | detail: "Sign-in expired. Start again.", | |
| 323 | }), | |
| 324 | ), | |
| 325 | }), | |
| 326 | Effect.exit, | |
| 327 | Effect.flatMap((result) => | |
| 328 | Effect.gen(function* () { | |
| 329 | const finishing = yield* lock.withPermit( | |
| 330 | Effect.sync(() => { | |
| 331 | if (active !== flow) return false; | |
| 332 | operation = "stopping"; | |
| 333 | return true; | |
| 334 | }), | |
| 335 | ); | |
| 336 | if (!finishing) return; | |
| 337 | if (flow.responseFiber) yield* Fiber.interrupt(flow.responseFiber); | |
| 338 | yield* lock.withPermit( | |
| 339 | Effect.gen(function* () { | |
| 340 | if (active !== flow) return; | |
| 341 | if (options.refreshMethodsAfterAuth) yield* refreshMethods; | |
| 342 | yield* publish(flow, { | |
| 343 | phase: Exit.isSuccess(result) ? "succeeded" : "failed", | |
| 344 | interaction: null, | |
| 345 | authorizationUrl: null, | |
| 346 | expiresAt: null, | |
| 347 | message: Exit.isSuccess(result) | |
| 348 | ? "Sign-in complete." | |
| 349 | : failureMessage(result.cause), | |
| 350 | }); | |
| 351 | active = undefined; | |
| 352 | operation = "idle"; | |
| 353 | }), | |
| 354 | ); | |
| 355 | }), | |
| 356 | ), | |
| 357 | Effect.interruptible, | |
| 358 | Effect.forkIn(scope), | |
| 359 | ); | |
| 360 | return state; | |
| 361 | }).pipe(Effect.uninterruptible), | |
| 362 | ), | |
| 363 | respond: (owner, input) => | |
| 364 | Effect.uninterruptibleMask((restore) => | |
| 365 | Effect.gen(function* () { | |
| 366 | const { flow, fiber, ready } = yield* lock.withPermit( | |
| 367 | Effect.gen(function* () { | |
| 368 | const flow = yield* requireFlow(owner, input.flowId); | |
| 369 | const interaction = snapshot.value.state.interaction; | |
| 370 | if ( | |
| 371 | !interaction || | |
| 372 | interaction.id !== input.interactionId || | |
| 373 | interaction.type !== input.response.type || | |
| 374 | !flow.respond | |
| 375 | ) | |
| 376 | return yield* new ProviderSetupError({ | |
| 377 | instanceId: options.instanceId, | |
| 378 | operation: "respond", | |
| 379 | detail: "This sign-in interaction is no longer available.", | |
| 380 | }); | |
| 381 | if (flow.responseFiber) | |
| 382 | return yield* new ProviderSetupError({ | |
| 383 | instanceId: options.instanceId, | |
| 384 | operation: "respond", | |
| 385 | detail: "A sign-in response is already in progress.", | |
| 386 | }); | |
| 387 | const ready = yield* Deferred.make<void>(); | |
| 388 | const callback = flow.respond; | |
| 389 | const fiber = yield* Deferred.await(ready).pipe( | |
| 390 | Effect.andThen(Effect.suspend(() => callback(input.response))), | |
| 391 | Effect.interruptible, | |
| 392 | Effect.forkIn(scope), | |
| 393 | ); | |
| 394 | flow.responseFiber = fiber; | |
| 395 | return { flow, fiber, ready }; | |
| 396 | }), | |
| 397 | ); | |
| 398 | yield* Deferred.succeed(ready, undefined); | |
| 399 | yield* restore(Fiber.join(fiber)).pipe( | |
| 400 | Effect.onInterrupt(() => Fiber.interrupt(fiber).pipe(Effect.asVoid)), | |
| 401 | Effect.ensuring( | |
| 402 | lock.withPermit( | |
| 403 | Effect.sync(() => { | |
| 404 | if (flow.responseFiber === fiber) delete flow.responseFiber; | |
| 405 | }), | |
| 406 | ), | |
| 407 | ), | |
| 408 | ); | |
| 409 | return snapshot.value.state; | |
| 410 | }), | |
| 411 | ), | |
| 412 | complete: (owner, input) => | |
| 413 | lock.withPermit( | |
| 414 | Effect.gen(function* () { | |
| 415 | const flow = yield* requireFlow(owner, input.flowId); | |
| 416 | const interaction = snapshot.value.state.interaction; | |
| 417 | if ( | |
| 418 | snapshot.value.state.phase !== "waiting" || | |
| 419 | interaction?.type !== "browser" || | |
| 420 | !interaction.acceptsCallback || | |
| 421 | !flow.complete | |
| 422 | ) | |
| 423 | return yield* new ProviderSetupError({ | |
| 424 | instanceId: options.instanceId, | |
| 425 | operation: "complete", | |
| 426 | detail: "This sign-in does not accept a redirect URL.", | |
| 427 | }); | |
| 428 | yield* flow.complete(input.callbackUrl); | |
| 429 | return snapshot.value.state; | |
| 430 | }), | |
| 431 | ), | |
| 432 | cancel: (owner, id) => | |
| 433 | Effect.gen(function* () { | |
| 434 | const flow = yield* lock.withPermit( | |
| 435 | Effect.gen(function* () { | |
| 436 | const flow = yield* requireFlow(owner, id); | |
| 437 | yield* publish(flow, { | |
| 438 | phase: "cancelled", | |
| 439 | interaction: null, | |
| 440 | authorizationUrl: null, | |
| 441 | expiresAt: null, | |
| 442 | message: "Sign-in cancelled.", | |
| 443 | }); | |
| 444 | active = undefined; | |
| 445 | operation = "stopping"; | |
| 446 | return flow; | |
| 447 | }), | |
| 448 | ); | |
| 449 | if (flow.responseFiber) yield* Fiber.interrupt(flow.responseFiber); | |
| 450 | if (flow.fiber) yield* Fiber.interrupt(flow.fiber); | |
| 451 | operation = "idle"; | |
| 452 | return snapshot.value.state; | |
| 453 | }).pipe(Effect.uninterruptible), | |
| 454 | logout: (stopSessions) => | |
| 455 | Effect.gen(function* () { | |
| 456 | const flow = yield* lock.withPermit( | |
| 457 | Effect.gen(function* () { | |
| 458 | if (operation !== "idle" && operation !== "auth") | |
| 459 | return yield* new ProviderSetupError({ | |
| 460 | instanceId: options.instanceId, | |
| 461 | operation: "logout", | |
| 462 | detail: "Provider setup is already stopping.", | |
| 463 | }); | |
| 464 | operation = "stopping"; | |
| 465 | const flow = active; | |
| 466 | active = undefined; | |
| 467 | return flow; | |
| 468 | }), | |
| 469 | ); | |
| 470 | const result = yield* Effect.gen(function* () { | |
| 471 | if (flow?.responseFiber) yield* Fiber.interrupt(flow.responseFiber); | |
| 472 | if (flow?.fiber) yield* Fiber.interrupt(flow.fiber); | |
| 473 | yield* stopSessions.pipe(Effect.ensuring(stopOwnedSessions)); | |
| 474 | const message = yield* options.logout; | |
| 475 | if (options.refreshMethodsAfterAuth) yield* refreshMethods; | |
| 476 | return message; | |
| 477 | }).pipe(Effect.exit); | |
| 478 | const state: ProviderAuthState = { | |
| 479 | ...empty, | |
| 480 | methods: snapshot.value.state.methods ?? [], | |
| 481 | phase: Exit.isSuccess(result) ? "idle" : "failed", | |
| 482 | message: Exit.isSuccess(result) | |
| 483 | ? (result.value ?? "Signed out.") | |
| 484 | : "Could not sign out. Try again.", | |
| 485 | }; | |
| 486 | yield* lock.withPermit( | |
| 487 | Effect.gen(function* () { | |
| 488 | yield* SubscriptionRef.set(snapshot, { owner: null, state }); | |
| 489 | operation = "idle"; | |
| 490 | }), | |
| 491 | ); | |
| 492 | if (Exit.isFailure(result)) return yield* Effect.failCause(result.cause); | |
| 493 | return state; | |
| 494 | }).pipe(Effect.uninterruptible), | |
| 495 | subscribe: (owner) => | |
| 496 | SubscriptionRef.changes(snapshot).pipe( | |
| 497 | Stream.map((current) => | |
| 498 | current.owner === null || current.owner === owner | |
| 499 | ? current.state | |
| 500 | : { | |
| 501 | ...current.state, | |
| 502 | flowId: null, | |
| 503 | authorizationUrl: null, | |
| 504 | interaction: null, | |
| 505 | expiresAt: null, | |
| 506 | message: active | |
| 507 | ? "Sign-in is in progress in another client." | |
| 508 | : current.state.message, | |
| 509 | }, | |
| 510 | ), | |
| 511 | ), | |
| 512 | }; | |
| 513 | yield* Effect.addFinalizer(() => | |
| 514 | Effect.gen(function* () { | |
| 515 | operation = "closed"; | |
| 516 | const flow = active; | |
| 517 | active = undefined; | |
| 518 | if (flow?.responseFiber) yield* Fiber.interrupt(flow.responseFiber); | |
| 519 | if (flow?.fiber) yield* Fiber.interrupt(flow.fiber); | |
| 520 | // Settings edits rebuild instances; admitted sessions (including those of | |
| 521 | // peers sharing this binding) end with their own scopes, not this one. | |
| 522 | }), | |
| 523 | ); | |
| 524 | return controller; | |
| 525 | }); |
packages/provider-core/src/server/snapshotSettings.ts+20-14| ... | ... | @@ -3,7 +3,7 @@ import * as Effect from "effect/Effect"; |
| 3 | 3 | import * as Equal from "effect/Equal"; |
| 4 | 4 | import * as Stream from "effect/Stream"; |
| 5 | 5 | |
| 6 | import type * as ProviderHost from "./ProviderHost.ts"; | |
| 6 | import * as ProviderHost from "./ProviderHost.ts"; | |
| 7 | 7 | |
| 8 | 8 | export interface ProviderSnapshotSettings<Settings> { |
| 9 | 9 | readonly provider: Settings; |
| ... | ... | @@ -27,17 +27,23 @@ export function haveProviderSnapshotSettingsChanged<Settings>( |
| 27 | 27 | return !Equal.equals(previous, next); |
| 28 | 28 | } |
| 29 | 29 | |
| 30 | export function makeProviderSnapshotSettingsSource<Settings>( | |
| 30 | /** The instance's settings paired with the host's server settings, as snapshots read them. */ | |
| 31 | export const makeProviderSnapshotSettingsSource = <Settings>( | |
| 31 | 32 | provider: Settings, |
| 32 | settings: ProviderHost.ProviderHost["Service"]["settings"], | |
| 33 | ): { | |
| 34 | readonly getSettings: Effect.Effect<ProviderSnapshotSettings<Settings>, ServerSettingsError>; | |
| 35 | readonly streamSettings: Stream.Stream<ProviderSnapshotSettings<Settings>>; | |
| 36 | } { | |
| 37 | const mapSettings = (settings: ServerSettings) => | |
| 38 | makeProviderSnapshotSettings(provider, settings); | |
| 39 | return { | |
| 40 | getSettings: settings.get.pipe(Effect.map(mapSettings)), | |
| 41 | streamSettings: settings.changes.pipe(Stream.map(mapSettings)), | |
| 42 | }; | |
| 43 | } | |
| 33 | ): Effect.Effect< | |
| 34 | { | |
| 35 | readonly getSettings: Effect.Effect<ProviderSnapshotSettings<Settings>, ServerSettingsError>; | |
| 36 | readonly streamSettings: Stream.Stream<ProviderSnapshotSettings<Settings>>; | |
| 37 | }, | |
| 38 | never, | |
| 39 | ProviderHost.ProviderHost | |
| 40 | > => | |
| 41 | Effect.gen(function* () { | |
| 42 | const { settings } = yield* ProviderHost.ProviderHost; | |
| 43 | const mapSettings = (current: ServerSettings) => | |
| 44 | makeProviderSnapshotSettings(provider, current); | |
| 45 | return { | |
| 46 | getSettings: settings.get.pipe(Effect.map(mapSettings)), | |
| 47 | streamSettings: settings.changes.pipe(Stream.map(mapSettings)), | |
| 48 | }; | |
| 49 | }); |
packages/provider-cursor/src/server/adapter.ts+1-1| ... | ... | @@ -239,7 +239,7 @@ function providerSession(input: { |
| 239 | 239 | } |
| 240 | 240 | |
| 241 | 241 | function makeProviderThread(input: { |
| 242 | readonly idAllocator: IdAllocator.IdAllocatorV2Shape; | |
| 242 | readonly idAllocator: IdAllocator.IdAllocatorV2["Service"]; | |
| 243 | 243 | readonly providerInstanceId: ProviderInstanceId; |
| 244 | 244 | readonly appThreadId: OrchestrationV2ProviderThread["appThreadId"]; |
| 245 | 245 | readonly providerSessionId: OrchestrationV2ProviderThread["providerSessionId"]; |
packages/provider-cursor/src/server/driver.ts+1-1| ... | ... | @@ -223,7 +223,7 @@ export const CursorDriver: ProviderDriver<CursorSettings, CursorDriverEnv> = { |
| 223 | 223 | Effect.provide(CursorSdkCatalog.layer), |
| 224 | 224 | ); |
| 225 | 225 | |
| 226 | const snapshotSettings = makeProviderSnapshotSettingsSource(effectiveConfig, host.settings); | |
| 226 | const snapshotSettings = yield* makeProviderSnapshotSettingsSource(effectiveConfig); | |
| 227 | 227 | const snapshot = yield* makeManagedServerProvider<ProviderSnapshotSettings<CursorSettings>>({ |
| 228 | 228 | resolveMaintenance: () => Effect.succeed(MAINTENANCE_CAPABILITIES), |
| 229 | 229 | getSettings: snapshotSettings.getSettings, |
packages/provider-grok/src/server/adapter.ts+1-1| ... | ... | @@ -58,7 +58,7 @@ import { acpPermissionDisposition } from "@t3tools/provider-acp/server/clientPol |
| 58 | 58 | import * as AcpSessionRuntime from "@t3tools/provider-acp/server/AcpSessionRuntime"; |
| 59 | 59 | import * as ProviderEventLoggers from "@t3tools/provider-core/server/ProviderEventLoggers"; |
| 60 | 60 | import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator"; |
| 61 | import * as ProviderContinuationRequests from "@t3tools/provider-core/server/continuationRequests"; | |
| 61 | import * as ProviderContinuationRequests from "@t3tools/provider-core/server/ProviderContinuationRequests"; | |
| 62 | 62 | import * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter"; |
| 63 | 63 | import { |
| 64 | 64 | ProviderAdapterDriverCreateError, |
packages/provider-grok/src/server/driver.ts+1-1| ... | ... | @@ -160,7 +160,7 @@ export const GrokDriver: ProviderDriver<GrokSettings, GrokDriverEnv> = { |
| 160 | 160 | Effect.provideService(ChildProcessSpawner.ChildProcessSpawner, spawner), |
| 161 | 161 | ); |
| 162 | 162 | |
| 163 | const snapshotSettings = makeProviderSnapshotSettingsSource(effectiveConfig, host.settings); | |
| 163 | const snapshotSettings = yield* makeProviderSnapshotSettingsSource(effectiveConfig); | |
| 164 | 164 | const snapshot = yield* makeManagedServerProvider<ProviderSnapshotSettings<GrokSettings>>({ |
| 165 | 165 | resolveMaintenance, |
| 166 | 166 | getSettings: snapshotSettings.getSettings, |
packages/provider-muse/src/server/adapter.test.ts+2-2| ... | ... | @@ -36,7 +36,7 @@ import type { MuseItem } from "./protocol.ts"; |
| 36 | 36 | import type { MuseSdkHost } from "./sdk.ts"; |
| 37 | 37 | import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator"; |
| 38 | 38 | import * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter"; |
| 39 | import type { ProviderContinuationRequest } from "@t3tools/provider-core/server/continuationRequests"; | |
| 39 | import type * as ProviderContinuationRequests from "@t3tools/provider-core/server/ProviderContinuationRequests"; | |
| 40 | 40 | import { makeMuseAdapterV2, type MuseAdapterV2Options } from "./adapter.ts"; |
| 41 | 41 | |
| 42 | 42 | const testLayer = Layer.mergeAll( |
| ... | ... | @@ -766,7 +766,7 @@ describe("MuseAdapterV2", () => { |
| 766 | 766 | it.effect("lets a user turn take a held Muse report turn, approvals included", () => |
| 767 | 767 | Effect.gen(function* () { |
| 768 | 768 | const fake = yield* makeFakeMuse(); |
| 769 | const offers: Array<ProviderContinuationRequest> = []; | |
| 769 | const offers: Array<ProviderContinuationRequests.ProviderContinuationRequest> = []; | |
| 770 | 770 | const harness = yield* makeHarness( |
| 771 | 771 | fake, |
| 772 | 772 | INSTANCE_ID, |
packages/provider-muse/src/server/adapter.ts+1-1| ... | ... | @@ -74,7 +74,7 @@ import { |
| 74 | 74 | backgroundWorkNotification, |
| 75 | 75 | type BackgroundWorkReport, |
| 76 | 76 | } from "@t3tools/provider-core/server/notification"; |
| 77 | import type * as ProviderContinuationRequests from "@t3tools/provider-core/server/continuationRequests"; | |
| 77 | import type * as ProviderContinuationRequests from "@t3tools/provider-core/server/ProviderContinuationRequests"; | |
| 78 | 78 | import { makeProviderFailure } from "@t3tools/provider-core/server/failure"; |
| 79 | 79 | import { turnScopedSelectionTransition } from "@t3tools/provider-core/server/selectionTransition"; |
| 80 | 80 | import { museItemStatus, museToolPresentation } from "./itemPresentation.ts"; |
packages/provider-muse/src/server/driver.ts+2-2| ... | ... | @@ -14,7 +14,7 @@ import { makeMuseTextGeneration } from "./textGeneration.ts"; |
| 14 | 14 | import { ProviderDriverError } from "@t3tools/provider-core/server/errors"; |
| 15 | 15 | import { makeMuseAdapterV2 } from "./adapter.ts"; |
| 16 | 16 | import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator"; |
| 17 | import * as ProviderContinuationRequests from "@t3tools/provider-core/server/continuationRequests"; | |
| 17 | import * as ProviderContinuationRequests from "@t3tools/provider-core/server/ProviderContinuationRequests"; | |
| 18 | 18 | import { checkMuseProviderStatus, makePendingMuseProvider } from "./status.ts"; |
| 19 | 19 | import * as ProviderEventLoggers from "@t3tools/provider-core/server/ProviderEventLoggers"; |
| 20 | 20 | import { makeManagedServerProvider } from "@t3tools/provider-core/server/managedProvider"; |
| ... | ... | @@ -86,7 +86,7 @@ export const MuseDriver: ProviderDriver<MuseSettings, MuseDriverEnv> = { |
| 86 | 86 | accentColor, |
| 87 | 87 | continuationGroupKey: continuationIdentity.continuationKey, |
| 88 | 88 | }); |
| 89 | const snapshotSettings = makeProviderSnapshotSettingsSource(effectiveConfig, host.settings); | |
| 89 | const snapshotSettings = yield* makeProviderSnapshotSettingsSource(effectiveConfig); | |
| 90 | 90 | const resolveInstallation = yield* makeCachedProviderMaintenanceResolution( |
| 91 | 91 | resolveProviderMaintenanceCapabilitiesEffect(museMaintenance, { |
| 92 | 92 | binaryPath: effectiveConfig.binaryPath, |
packages/provider-opencode/src/server/adapter.ts+2-2| ... | ... | @@ -459,7 +459,7 @@ function formatOpenCodeProtocolLogPayload(event: OpenCodeProtocolLogEvent) { |
| 459 | 459 | |
| 460 | 460 | export function makeOpenCodeProtocolLogger(input: { |
| 461 | 461 | readonly nativeEventLogger: ProviderEventLoggers.EventNdjsonLogger | undefined; |
| 462 | readonly idAllocator: IdAllocator.IdAllocatorV2Shape; | |
| 462 | readonly idAllocator: IdAllocator.IdAllocatorV2["Service"]; | |
| 463 | 463 | readonly providerInstanceId: ProviderInstanceId; |
| 464 | 464 | readonly providerSessionId: ProviderSessionId; |
| 465 | 465 | readonly threadId: ThreadId; |
| ... | ... | @@ -830,7 +830,7 @@ function taskSessionId(part: ToolPart): string | null { |
| 830 | 830 | } |
| 831 | 831 | |
| 832 | 832 | function makeProviderThread(input: { |
| 833 | readonly idAllocator: IdAllocator.IdAllocatorV2Shape; | |
| 833 | readonly idAllocator: IdAllocator.IdAllocatorV2["Service"]; | |
| 834 | 834 | readonly providerInstanceId: ProviderInstanceId; |
| 835 | 835 | readonly providerSessionId: OrchestrationV2ProviderThread["providerSessionId"]; |
| 836 | 836 | readonly appThreadId: OrchestrationV2ProviderThread["appThreadId"]; |
packages/provider-opencode/src/server/driver.ts+4-4| ... | ... | @@ -123,9 +123,9 @@ function byOpenCodeRuntime<A, E, R, PE>( |
| 123 | 123 | */ |
| 124 | 124 | function selectOpenCodeRuntimeAdapter(input: { |
| 125 | 125 | readonly probe: OpenCodeRuntimeProbe; |
| 126 | readonly v1: ProviderAdapter.ProviderAdapterV2Shape; | |
| 127 | readonly v2: ProviderAdapter.ProviderAdapterV2Shape; | |
| 128 | }): ProviderAdapter.ProviderAdapterV2Shape { | |
| 126 | readonly v1: ProviderAdapter.ProviderAdapterV2["Service"]; | |
| 127 | readonly v2: ProviderAdapter.ProviderAdapterV2["Service"]; | |
| 128 | }): ProviderAdapter.ProviderAdapterV2["Service"] { | |
| 129 | 129 | const pick = <PE>(probed: Effect.Effect<ProbedOpenCode | undefined, PE>) => |
| 130 | 130 | byOpenCodeRuntime(probed, { v1: Effect.succeed(input.v1), v2: Effect.succeed(input.v2) }); |
| 131 | 131 | const hot = pick(Effect.map(input.probe.lastSuccess, Option.getOrUndefined)); |
| ... | ... | @@ -445,7 +445,7 @@ export const OpenCodeDriver: ProviderDriver<OpenCodeSettings, OpenCodeDriverEnv> |
| 445 | 445 | ), |
| 446 | 446 | ); |
| 447 | 447 | |
| 448 | const snapshotSettings = makeProviderSnapshotSettingsSource(effectiveConfig, host.settings); | |
| 448 | const snapshotSettings = yield* makeProviderSnapshotSettingsSource(effectiveConfig); | |
| 449 | 449 | const snapshot = yield* makeManagedServerProvider<ProviderSnapshotSettings<OpenCodeSettings>>( |
| 450 | 450 | { |
| 451 | 451 | resolveMaintenance, |
packages/provider-opencode/src/server/v2/adapter.ts+2-2| ... | ... | @@ -88,7 +88,7 @@ import { |
| 88 | 88 | backgroundWorkNotification, |
| 89 | 89 | type BackgroundWorkReport, |
| 90 | 90 | } from "@t3tools/provider-core/server/notification"; |
| 91 | import * as ProviderContinuationRequests from "@t3tools/provider-core/server/continuationRequests"; | |
| 91 | import * as ProviderContinuationRequests from "@t3tools/provider-core/server/ProviderContinuationRequests"; | |
| 92 | 92 | import { makeProviderFailure } from "@t3tools/provider-core/server/failure"; |
| 93 | 93 | import { |
| 94 | 94 | makeSubagentChildThread, |
| ... | ... | @@ -858,7 +858,7 @@ export const make = Effect.fn("OpenCode2Adapter.make")(function* (instanceId: Pr |
| 858 | 858 | }); |
| 859 | 859 | |
| 860 | 860 | const openSession = Effect.fn("OpenCode2Adapter.openSession")(function* ( |
| 861 | input: Parameters<ProviderAdapter.ProviderAdapterV2Shape["openSession"]>[0], | |
| 861 | input: Parameters<ProviderAdapter.ProviderAdapterV2["Service"]["openSession"]>[0], | |
| 862 | 862 | initial: { |
| 863 | 863 | readonly connection: OpenCode2Server.OpenCode2Connection; |
| 864 | 864 | readonly scope: Scope.Closeable; |
packages/provider-pi/src/server/adapter.test.ts+1-1| ... | ... | @@ -32,7 +32,7 @@ import * as TestClock from "effect/testing/TestClock"; |
| 32 | 32 | import { ChildProcess, ChildProcessSpawner } from "effect/process"; |
| 33 | 33 | |
| 34 | 34 | import { layerTestProviderHost } from "@t3tools/provider-testing/host"; |
| 35 | import * as ProviderContinuationRequests from "@t3tools/provider-core/server/continuationRequests"; | |
| 35 | import * as ProviderContinuationRequests from "@t3tools/provider-core/server/ProviderContinuationRequests"; | |
| 36 | 36 | import * as McpProviderSession from "@t3tools/provider-core/server/mcpSession"; |
| 37 | 37 | import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator"; |
| 38 | 38 | import * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter"; |
packages/provider-pi/src/server/adapter.ts+1-2| ... | ... | @@ -67,7 +67,7 @@ import { parsePiCompactCommand, type PiCompactCommand } from "./commands.ts"; |
| 67 | 67 | import { mergeProviderInstanceEnvironment } from "@t3tools/provider-core/server/instanceEnvironment"; |
| 68 | 68 | import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator"; |
| 69 | 69 | import * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter"; |
| 70 | import * as ProviderContinuationRequests from "@t3tools/provider-core/server/continuationRequests"; | |
| 70 | import * as ProviderContinuationRequests from "@t3tools/provider-core/server/ProviderContinuationRequests"; | |
| 71 | 71 | import { |
| 72 | 72 | ProviderAdapterDriverCreateError, |
| 73 | 73 | type ProviderAdapterDriver, |
| ... | ... | @@ -775,7 +775,6 @@ export const makePiAdapterV2 = Effect.fn("makePiAdapterV2")(function* ( |
| 775 | 775 | type: "turn_item.updated", |
| 776 | 776 | driver: PI_PROVIDER, |
| 777 | 777 | turnItem: makeProviderRetryTurnItem({ |
| 778 | idAllocator, | |
| 779 | 778 | driver: PI_PROVIDER, |
| 780 | 779 | threadId: turn.turnInput.threadId, |
| 781 | 780 | runId: turn.turnInput.runId, |
packages/provider-pi/src/server/driver.ts+1-1| ... | ... | @@ -142,7 +142,7 @@ export const PiDriver: ProviderDriver<PiSettings, PiDriverEnv> = { |
| 142 | 142 | Effect.provideService(ChildProcessSpawner.ChildProcessSpawner, spawner), |
| 143 | 143 | ); |
| 144 | 144 | |
| 145 | const snapshotSettings = makeProviderSnapshotSettingsSource(effectiveConfig, host.settings); | |
| 145 | const snapshotSettings = yield* makeProviderSnapshotSettingsSource(effectiveConfig); | |
| 146 | 146 | const snapshot = yield* makeManagedServerProvider<ProviderSnapshotSettings<PiSettings>>({ |
| 147 | 147 | resolveMaintenance, |
| 148 | 148 | getSettings: snapshotSettings.getSettings, |