authorgravatar for julius0216@outlook.comJulius Marminge <julius0216@outlook.com> 2026-10-09 09:15:21-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-10-09 09:15:21-07:00
logb66dd402e86899c3fb4341d46c32d0d69df1eefc
tree932594fff9ca5cd68ebd5cc0d7bad7ea5fb58677
parent2c1915b3193f16e2493709b480e6622645d0ca4e
signature Signed by PGP key B5690EEEBB952194

refactor(provider-core): follow the Effect service conventions throughout (#17427)

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 +10,7 @@ import {
10 CLAUDE_AGENT_SDK_REPLAY_PROTOCOL,10 CLAUDE_AGENT_SDK_REPLAY_PROTOCOL,
11} from "../src/orchestration-v2/Adapters/ClaudeAdapterV2.testkit.ts";11} from "../src/orchestration-v2/Adapters/ClaudeAdapterV2.testkit.ts";
12import { claudeRuntimeQueryPolicyForRuntimePolicy } from "../src/orchestration-v2/Adapters/ClaudeAdapterV2.ts";12import { claudeRuntimeQueryPolicyForRuntimePolicy } from "../src/orchestration-v2/Adapters/ClaudeAdapterV2.ts";
13import {13import * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter";
14 ProviderAdapterV2RuntimePolicy,
15 type ProviderAdapterV2RuntimePolicy as ProviderAdapterV2RuntimePolicyType,
16} from "@t3tools/provider-core/server/ProviderAdapter";
17import type { RuntimePolicyV2Override } from "../src/orchestration-v2/RuntimePolicy.ts";14import type { RuntimePolicyV2Override } from "../src/orchestration-v2/RuntimePolicy.ts";
18import { makeCheckpointWorkspace } from "@t3tools/provider-testing/replayWorkspace";15import { makeCheckpointWorkspace } from "@t3tools/provider-testing/replayWorkspace";
19import { CLAUDE_MODEL_SELECTION } from "../src/orchestration-v2/testkit/fixtures/shared.ts";16import { CLAUDE_MODEL_SELECTION } from "../src/orchestration-v2/testkit/fixtures/shared.ts";
...@@ -440,8 +437,8 @@ validateClaudeReplayRecordingSelection({...@@ -440,8 +437,8 @@ validateClaudeReplayRecordingSelection({
440function runtimePolicyForRecording(input: {437function runtimePolicyForRecording(input: {
441 readonly cwd: string;438 readonly cwd: string;
442 readonly override?: RuntimePolicyV2Override;439 readonly override?: RuntimePolicyV2Override;
443}): ProviderAdapterV2RuntimePolicyType {440}): ProviderAdapter.ProviderAdapterV2RuntimePolicy {
444 return ProviderAdapterV2RuntimePolicy.make({441 return ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
445 runtimeMode: "full-access",442 runtimeMode: "full-access",
446 interactionMode: "default",443 interactionMode: "default",
447 cwd: input.override?.cwd ?? input.cwd,444 cwd: input.override?.cwd ?? input.cwd,
apps/server/scripts/record-grok-acp-replay-fixture.ts+3-3
...@@ -33,8 +33,8 @@ import {...@@ -33,8 +33,8 @@ import {
33} from "@t3tools/provider-grok/testing";33} from "@t3tools/provider-grok/testing";
34import { ACP_PROTOCOL } from "@t3tools/provider-acp/server/adapter";34import { ACP_PROTOCOL } from "@t3tools/provider-acp/server/adapter";
35import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator";35import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator";
36import type { ProviderAdapterV2SessionRuntime } from "@t3tools/provider-core/server/ProviderAdapter";36import type * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter";
37import * as ProviderContinuationRequests from "@t3tools/provider-core/server/continuationRequests";37import * as ProviderContinuationRequests from "@t3tools/provider-core/server/ProviderContinuationRequests";
38import * as ProviderAdapterRegistry from "../src/orchestration-v2/ProviderAdapterRegistry.ts";38import * as ProviderAdapterRegistry from "../src/orchestration-v2/ProviderAdapterRegistry.ts";
39import { provideDeterministicTestRuntime } from "../src/orchestration-v2/testkit/DeterministicRuntime.ts";39import { provideDeterministicTestRuntime } from "../src/orchestration-v2/testkit/DeterministicRuntime.ts";
40import { ORCHESTRATOR_REPLAY_FIXTURES } from "../src/orchestration-v2/testkit/fixtures/index.ts";40import { ORCHESTRATOR_REPLAY_FIXTURES } from "../src/orchestration-v2/testkit/fixtures/index.ts";
...@@ -474,7 +474,7 @@ const recordScenario = Effect.fn("recordGrokScenario")(function* (fixtureName: s...@@ -474,7 +474,7 @@ const recordScenario = Effect.fn("recordGrokScenario")(function* (fixtureName: s
474 ...adapter,474 ...adapter,
475 openSession: (input) =>475 openSession: (input) =>
476 adapter.openSession(input).pipe(476 adapter.openSession(input).pipe(
477 Effect.map((session): ProviderAdapterV2SessionRuntime => ({477 Effect.map((session): ProviderAdapter.ProviderAdapterV2SessionRuntime => ({
478 ...session,478 ...session,
479 startTurn: (turnInput) => onWallClock(session.startTurn(turnInput)),479 startTurn: (turnInput) => onWallClock(session.startTurn(turnInput)),
480 steerTurn: (turnInput) => onWallClock(session.steerTurn(turnInput)),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,13 +19,13 @@ import { DispatchModeLimit } from "../orchestration-v2/DispatchModeLimit.ts";
19import { CodexProviderCapabilitiesV2 } from "../orchestration-v2/Adapters/CodexAdapterV2.ts";19import { CodexProviderCapabilitiesV2 } from "../orchestration-v2/Adapters/CodexAdapterV2.ts";
20import * as Orchestrator from "../orchestration-v2/Orchestrator.ts";20import * as Orchestrator from "../orchestration-v2/Orchestrator.ts";
21import * as ProjectionStore from "../orchestration-v2/ProjectionStore.ts";21import * as ProjectionStore from "../orchestration-v2/ProjectionStore.ts";
22import type { ProviderAdapterV2Shape } from "@t3tools/provider-core/server/ProviderAdapter";
23import * as ProviderAdapterRegistry from "../orchestration-v2/ProviderAdapterRegistry.ts";22import * as ProviderAdapterRegistry from "../orchestration-v2/ProviderAdapterRegistry.ts";
24import * as ThreadManagement from "../orchestration-v2/ThreadManagementService.ts";23import * as ThreadManagement from "../orchestration-v2/ThreadManagementService.ts";
25import * as ProviderReplayHarness from "../orchestration-v2/testkit/ProviderReplayHarness.ts";24import * as ProviderReplayHarness from "../orchestration-v2/testkit/ProviderReplayHarness.ts";
26import * as McpHttpServer from "./McpHttpServer.ts";25import * as McpHttpServer from "./McpHttpServer.ts";
27import * as McpInvocationContext from "./McpInvocationContext.ts";26import * as McpInvocationContext from "./McpInvocationContext.ts";
28import * as McpToolAccess from "./McpToolAccess.ts";27import * as McpToolAccess from "./McpToolAccess.ts";
28import type * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter";
2929
30// A Supervised outside agent renames a thread through a `writesThreads` tool.30// A Supervised outside agent renames a thread through a `writesThreads` tool.
31// The thread's user raises it to full access after the tool's check but31// The thread's user raises it to full access after the tool's check but
...@@ -38,7 +38,7 @@ const adapter = {...@@ -38,7 +38,7 @@ const adapter = {
38 getCapabilities: () => Effect.succeed(CodexProviderCapabilitiesV2),38 getCapabilities: () => Effect.succeed(CodexProviderCapabilitiesV2),
39 planSelectionTransition: () => Effect.succeed({ type: "apply_on_next_turn" as const }),39 planSelectionTransition: () => Effect.succeed({ type: "apply_on_next_turn" as const }),
40 openSession: () => Effect.die("No provider process needed for metadata commands"),40 openSession: () => Effect.die("No provider process needed for metadata commands"),
41} as ProviderAdapterV2Shape;41} as ProviderAdapter.ProviderAdapterV2["Service"];
42const layerDatabase = SqlitePersistence.layerMemory;42const layerDatabase = SqlitePersistence.layerMemory;
43const layerOrchestrator = Layer.mergeAll(43const layerOrchestrator = Layer.mergeAll(
44 layerDatabase,44 layerDatabase,
apps/server/src/mcp/OrchestratorMcpService.test.ts+4-2
...@@ -28,7 +28,6 @@ import {...@@ -28,7 +28,6 @@ import {
28 OrchestratorProjectionError,28 OrchestratorProjectionError,
29 OrchestratorThreadAboveModeLimitError,29 OrchestratorThreadAboveModeLimitError,
30} from "../orchestration-v2/Orchestrator.ts";30} from "../orchestration-v2/Orchestrator.ts";
31import type { ProviderAdapterV2Shape } from "@t3tools/provider-core/server/ProviderAdapter";
32import * as ProviderAdapterRegistry from "../orchestration-v2/ProviderAdapterRegistry.ts";31import * as ProviderAdapterRegistry from "../orchestration-v2/ProviderAdapterRegistry.ts";
33import * as ThreadManagementService from "../orchestration-v2/ThreadManagementService.ts";32import * as ThreadManagementService from "../orchestration-v2/ThreadManagementService.ts";
34import * as ProviderRegistry from "../provider/ProviderRegistry.ts";33import * as ProviderRegistry from "../provider/ProviderRegistry.ts";
...@@ -39,6 +38,7 @@ import * as SecretRequests from "../secrets/SecretRequests.ts";...@@ -39,6 +38,7 @@ import * as SecretRequests from "../secrets/SecretRequests.ts";
39import type { McpInvocationScope } from "./McpInvocationContext.ts";38import type { McpInvocationScope } from "./McpInvocationContext.ts";
40import { idleThreadProjection, liveThreadShell } from "./McpToolAccess.testkit.ts";39import { idleThreadProjection, liveThreadShell } from "./McpToolAccess.testkit.ts";
41import * as OrchestratorMcpService from "./OrchestratorMcpService.ts";40import * as OrchestratorMcpService from "./OrchestratorMcpService.ts";
41import type * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter";
4242
43describe("OrchestratorMcpService", () => {43describe("OrchestratorMcpService", () => {
44 it.effect("retries terminal acknowledgement with a fresh command id", () =>44 it.effect("retries terminal acknowledgement with a fresh command id", () =>
...@@ -810,7 +810,9 @@ describe("OrchestratorMcpService provider resolution", () => {...@@ -810,7 +810,9 @@ describe("OrchestratorMcpService provider resolution", () => {
810 list: () => Effect.succeed(instanceIds),810 list: () => Effect.succeed(instanceIds),
811 get: (instanceId) =>811 get: (instanceId) =>
812 instanceIds.includes(instanceId)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 : Effect.fail(816 : Effect.fail(
815 new ProviderAdapterRegistry.ProviderAdapterRegistryLookupError({ instanceId }),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,14 +57,8 @@ import { threadShellFromProjection } from "../orchestration-v2/ProjectionStore.t
57import * as EventSink from "../orchestration-v2/EventSink.ts";57import * as EventSink from "../orchestration-v2/EventSink.ts";
58import * as Orchestrator from "../orchestration-v2/Orchestrator.ts";58import * as Orchestrator from "../orchestration-v2/Orchestrator.ts";
59import * as ThreadManagementService from "../orchestration-v2/ThreadManagementService.ts";59import * as ThreadManagementService from "../orchestration-v2/ThreadManagementService.ts";
60import {
61 type ProviderAdapterV2Event,
62 ProviderAdapterProtocolError,
63 type ProviderAdapterV2Shape,
64 type ProviderAdapterV2TurnInput,
65} from "@t3tools/provider-core/server/ProviderAdapter";
66import * as ProviderAdapterRegistry from "../orchestration-v2/ProviderAdapterRegistry.ts";60import * as ProviderAdapterRegistry from "../orchestration-v2/ProviderAdapterRegistry.ts";
67import * as ProviderContinuationRequests from "@t3tools/provider-core/server/continuationRequests";61import * as ProviderContinuationRequests from "@t3tools/provider-core/server/ProviderContinuationRequests";
68import { checkpointWorkspace } from "@t3tools/provider-testing/replayWorkspace";62import { checkpointWorkspace } from "@t3tools/provider-testing/replayWorkspace";
69import * as ProviderReplayHarness from "../orchestration-v2/testkit/ProviderReplayHarness.ts";63import * as ProviderReplayHarness from "../orchestration-v2/testkit/ProviderReplayHarness.ts";
70import {64import {
...@@ -79,6 +73,7 @@ import * as ServerSecretStore from "../auth/ServerSecretStore.ts";...@@ -79,6 +73,7 @@ import * as ServerSecretStore from "../auth/ServerSecretStore.ts";
79import * as McpHttpServer from "./McpHttpServer.ts";73import * as McpHttpServer from "./McpHttpServer.ts";
80import * as McpInvocationContext from "./McpInvocationContext.ts";74import * as McpInvocationContext from "./McpInvocationContext.ts";
81import { delegatedTaskRun, hasPendingChildRuns } from "./OrchestratorMcpService.ts";75import { delegatedTaskRun, hasPendingChildRuns } from "./OrchestratorMcpService.ts";
76import * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter";
8277
83// Effect returns a declared tool failure as `isError` with its encoded payload78// Effect returns a declared tool failure as `isError` with its encoded payload
84// as JSON text, never as `structuredContent`.79// as JSON text, never as `structuredContent`.
...@@ -146,7 +141,7 @@ interface CapturedTurn {...@@ -146,7 +141,7 @@ interface CapturedTurn {
146}141}
147142
148function unsupported(driver: ProviderDriverKind, detail: string) {143function unsupported(driver: ProviderDriverKind, detail: string) {
149 return Effect.fail(new ProviderAdapterProtocolError({ driver, detail }));144 return Effect.fail(new ProviderAdapter.ProviderAdapterProtocolError({ driver, detail }));
150}145}
151146
152function makeProviderSnapshot(input: {147function makeProviderSnapshot(input: {
...@@ -185,10 +180,12 @@ function makeDeterministicAdapter(input: {...@@ -185,10 +180,12 @@ function makeDeterministicAdapter(input: {
185 readonly driver: ProviderDriverKind;180 readonly driver: ProviderDriverKind;
186 readonly capabilities: OrchestrationV2ProviderCapabilities;181 readonly capabilities: OrchestrationV2ProviderCapabilities;
187 readonly capturedTurns: Ref.Ref<ReadonlyArray<CapturedTurn>>;182 readonly capturedTurns: Ref.Ref<ReadonlyArray<CapturedTurn>>;
188 readonly shouldComplete: (turn: ProviderAdapterV2TurnInput) => boolean;183 readonly shouldComplete: (turn: ProviderAdapter.ProviderAdapterV2TurnInput) => boolean;
189 readonly terminalGate?: (turn: ProviderAdapterV2TurnInput) => Deferred.Deferred<void> | undefined;184 readonly terminalGate?: (
190 readonly response: (turn: ProviderAdapterV2TurnInput) => string;185 turn: ProviderAdapter.ProviderAdapterV2TurnInput,
191}): ProviderAdapterV2Shape {186 ) => Deferred.Deferred<void> | undefined;
187 readonly response: (turn: ProviderAdapter.ProviderAdapterV2TurnInput) => string;
188}): ProviderAdapter.ProviderAdapterV2["Service"] {
192 return {189 return {
193 instanceId: input.instanceId,190 instanceId: input.instanceId,
194 driver: input.driver,191 driver: input.driver,
...@@ -196,7 +193,7 @@ function makeDeterministicAdapter(input: {...@@ -196,7 +193,7 @@ function makeDeterministicAdapter(input: {
196 planSelectionTransition: () => Effect.succeed({ type: "apply_on_next_turn" }),193 planSelectionTransition: () => Effect.succeed({ type: "apply_on_next_turn" }),
197 openSession: (sessionInput) =>194 openSession: (sessionInput) =>
198 Effect.gen(function* () {195 Effect.gen(function* () {
199 const events = yield* PubSub.unbounded<ProviderAdapterV2Event>();196 const events = yield* PubSub.unbounded<ProviderAdapter.ProviderAdapterV2Event>();
200 const now = yield* DateTime.now;197 const now = yield* DateTime.now;
201 const providerSession: OrchestrationV2ProviderSession = {198 const providerSession: OrchestrationV2ProviderSession = {
202 id: sessionInput.providerSessionId,199 id: sessionInput.providerSessionId,
...@@ -211,12 +208,12 @@ function makeDeterministicAdapter(input: {...@@ -211,12 +208,12 @@ function makeDeterministicAdapter(input: {
211 lastError: null,208 lastError: null,
212 };209 };
213210
214 const publish = (providerEvents: ReadonlyArray<ProviderAdapterV2Event>) =>211 const publish = (providerEvents: ReadonlyArray<ProviderAdapter.ProviderAdapterV2Event>) =>
215 Effect.forEach(providerEvents, (event) => PubSub.publish(events, event), {212 Effect.forEach(providerEvents, (event) => PubSub.publish(events, event), {
216 discard: true,213 discard: true,
217 });214 });
218 const runOrdinals = new Map<ProviderTurnId, number>();215 const runOrdinals = new Map<ProviderTurnId, number>();
219 const turnInputs = new Map<ProviderTurnId, ProviderAdapterV2TurnInput>();216 const turnInputs = new Map<ProviderTurnId, ProviderAdapter.ProviderAdapterV2TurnInput>();
220217
221 return {218 return {
222 instanceId: input.instanceId,219 instanceId: input.instanceId,
apps/server/src/mcpApps/McpAppRequests.test.ts+6-7
...@@ -14,10 +14,7 @@ import * as Layer from "effect/Layer";...@@ -14,10 +14,7 @@ import * as Layer from "effect/Layer";
14import * as Option from "effect/Option";14import * as Option from "effect/Option";
1515
16import * as Orchestrator from "../orchestration-v2/Orchestrator.ts";16import * as Orchestrator from "../orchestration-v2/Orchestrator.ts";
17import type {17import type * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter";
18 ProviderAdapterV2McpApps,
19 ProviderAdapterV2SessionRuntime,
20} from "@t3tools/provider-core/server/ProviderAdapter";
21import * as ProviderSessionManager from "../orchestration-v2/ProviderSessionManager.ts";18import * as ProviderSessionManager from "../orchestration-v2/ProviderSessionManager.ts";
22import * as ThreadManagementService from "../orchestration-v2/ThreadManagementService.ts";19import * as ThreadManagementService from "../orchestration-v2/ThreadManagementService.ts";
23import * as McpAppModelContext from "./McpAppModelContext.ts";20import * as McpAppModelContext from "./McpAppModelContext.ts";
...@@ -71,7 +68,7 @@ const storedContext = new Map<string, string>();...@@ -71,7 +68,7 @@ const storedContext = new Map<string, string>();
7168
72function makeLayer(input: {69function makeLayer(input: {
73 readonly item: OrchestrationV2TurnItem | null;70 readonly item: OrchestrationV2TurnItem | null;
74 readonly mcpApps?: ProviderAdapterV2McpApps;71 readonly mcpApps?: ProviderAdapter.ProviderAdapterV2McpApps;
75 readonly live?: boolean;72 readonly live?: boolean;
76}) {73}) {
77 return McpAppRequests.layer.pipe(74 return McpAppRequests.layer.pipe(
...@@ -121,7 +118,9 @@ function makeLayer(input: {...@@ -121,7 +118,9 @@ function makeLayer(input: {
121 : Option.some(118 : Option.some(
122 (input.mcpApps === undefined119 (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,7 +137,7 @@ const reason = <A, R>(effect: Effect.Effect<A, McpAppRequestError, R>) =>
138137
139describe("McpAppRequests", () => {138describe("McpAppRequests", () => {
140 const calls: Array<{ server: string; tool: string }> = [];139 const calls: Array<{ server: string; tool: string }> = [];
141 const mcpApps: ProviderAdapterV2McpApps = {140 const mcpApps: ProviderAdapter.ProviderAdapterV2McpApps = {
142 listTools: () =>141 listTools: () =>
143 Effect.succeed([142 Effect.succeed([
144 { name: "refresh", annotations: { readOnlyHint: true } },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,10 +21,7 @@ import * as Predicate from "effect/Predicate";
21import * as Schema from "effect/Schema";21import * as Schema from "effect/Schema";
2222
23import * as Orchestrator from "../orchestration-v2/Orchestrator.ts";23import * as Orchestrator from "../orchestration-v2/Orchestrator.ts";
24import type {24import type * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter";
25 ProviderAdapterV2McpApps,
26 ProviderAdapterV2McpTool,
27} from "@t3tools/provider-core/server/ProviderAdapter";
28import * as ProviderSessionManager from "../orchestration-v2/ProviderSessionManager.ts";25import * as ProviderSessionManager from "../orchestration-v2/ProviderSessionManager.ts";
29import * as ThreadManagementService from "../orchestration-v2/ThreadManagementService.ts";26import * as ThreadManagementService from "../orchestration-v2/ThreadManagementService.ts";
30import * as McpAppModelContext from "./McpAppModelContext.ts";27import * as McpAppModelContext from "./McpAppModelContext.ts";
...@@ -57,10 +54,10 @@ export class McpAppRequests extends Context.Service<...@@ -57,10 +54,10 @@ export class McpAppRequests extends Context.Service<
57const encodeJson = Schema.encodeSync(Schema.fromJsonString(Schema.Unknown));54const encodeJson = Schema.encodeSync(Schema.fromJsonString(Schema.Unknown));
58const utf8 = new TextEncoder();55const utf8 = new TextEncoder();
5956
60const readOnlyHint = (tool: ProviderAdapterV2McpTool) =>57const readOnlyHint = (tool: ProviderAdapter.ProviderAdapterV2McpTool) =>
61 Predicate.isObject(tool.annotations) && tool.annotations.readOnlyHint === true;58 Predicate.isObject(tool.annotations) && tool.annotations.readOnlyHint === true;
6259
63const toolTitle = (tool: ProviderAdapterV2McpTool) =>60const toolTitle = (tool: ProviderAdapter.ProviderAdapterV2McpTool) =>
64 Predicate.isObject(tool.annotations) && typeof tool.annotations.title === "string"61 Predicate.isObject(tool.annotations) && typeof tool.annotations.title === "string"
65 ? tool.annotations.title62 ? tool.annotations.title
66 : undefined;63 : undefined;
...@@ -112,7 +109,7 @@ const make = Effect.gen(function* () {...@@ -112,7 +109,7 @@ const make = Effect.gen(function* () {
112 .pipe(Effect.mapError((cause) => fail(input.threadId, "request-failed", cause))),109 .pipe(Effect.mapError((cause) => fail(input.threadId, "request-failed", cause))),
113 );110 );
114 if (runtime === undefined) return yield* fail(input.threadId, "session-stopped");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 if (mcpApps === undefined) return yield* fail(input.threadId, "provider-unsupported");113 if (mcpApps === undefined) return yield* fail(input.threadId, "provider-unsupported");
117 return { app, providerThread, mcpApps };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,7 +29,7 @@ import * as CodexInstallation from "../provider/CodexInstallation.ts";
29import * as ServerEnvironment from "../environment/ServerEnvironment.ts";29import * as ServerEnvironment from "../environment/ServerEnvironment.ts";
30import * as ModelManifest from "../provider/ModelManifest.ts";30import * as ModelManifest from "../provider/ModelManifest.ts";
31import * as ProviderInstanceRegistryHydration from "../provider/ProviderInstanceRegistryHydration.ts";31import * as ProviderInstanceRegistryHydration from "../provider/ProviderInstanceRegistryHydration.ts";
32import * as ProviderEventLoggers from "../provider/ProviderEventLoggers.ts";32import * as ProviderEventLoggers from "@t3tools/provider-core/server/ProviderEventLoggers";
33import * as OpenCodeRuntime from "@t3tools/provider-opencode/server/OpenCodeRuntime";33import * as OpenCodeRuntime from "@t3tools/provider-opencode/server/OpenCodeRuntime";
34import * as OpenCodeServerLedger from "@t3tools/provider-opencode/server/OpenCodeServerLedger";34import * as OpenCodeServerLedger from "@t3tools/provider-opencode/server/OpenCodeServerLedger";
35import * as ServerSettings from "../serverSettings.ts";35import * as ServerSettings from "../serverSettings.ts";
apps/server/src/orchestration-v2/Adapters/AcpAdapterV2.test.ts+189-171
...@@ -64,13 +64,8 @@ import {...@@ -64,13 +64,8 @@ import {
64 registerXAiBackgroundTaskTracking,64 registerXAiBackgroundTaskTracking,
65} from "@t3tools/provider-grok/testing";65} from "@t3tools/provider-grok/testing";
66import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator";66import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator";
67import {67import * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter";
68 ProviderAdapterProtocolError,68import type * as ProviderContinuationRequests from "@t3tools/provider-core/server/ProviderContinuationRequests";
69 ProviderAdapterV2RuntimePolicy,
70 type ProviderAdapterV2Event,
71 type ProviderAdapterV2TurnInput,
72} from "@t3tools/provider-core/server/ProviderAdapter";
73import type { ProviderContinuationRequest } from "@t3tools/provider-core/server/continuationRequests";
74import {69import {
75 AcpProviderCapabilitiesV2,70 AcpProviderCapabilitiesV2,
76 acpProviderItemNativeId,71 acpProviderItemNativeId,
...@@ -540,7 +535,7 @@ function makeTurnInput(input: {...@@ -540,7 +535,7 @@ function makeTurnInput(input: {
540 readonly threadId: ThreadId;535 readonly threadId: ThreadId;
541 readonly providerThread: OrchestrationV2ProviderThread;536 readonly providerThread: OrchestrationV2ProviderThread;
542 readonly instanceId: ProviderInstanceId;537 readonly instanceId: ProviderInstanceId;
543 readonly runtimePolicy: ProviderAdapterV2RuntimePolicy;538 readonly runtimePolicy: ProviderAdapter.ProviderAdapterV2RuntimePolicy;
544 readonly now: DateTime.Utc;539 readonly now: DateTime.Utc;
545 readonly ordinal?: number;540 readonly ordinal?: number;
546 readonly modelSelection?: ModelSelection;541 readonly modelSelection?: ModelSelection;
...@@ -548,7 +543,7 @@ function makeTurnInput(input: {...@@ -548,7 +543,7 @@ function makeTurnInput(input: {
548 readonly messageCreatedBy?: "user" | "agent";543 readonly messageCreatedBy?: "user" | "agent";
549 readonly messageCreationSource?: "web" | "mobile" | "mcp" | "provider" | "server";544 readonly messageCreationSource?: "web" | "mobile" | "mcp" | "provider" | "server";
550 readonly messageText?: string;545 readonly messageText?: string;
551}): ProviderAdapterV2TurnInput {546}): ProviderAdapter.ProviderAdapterV2TurnInput {
552 const ordinal = input.ordinal ?? 1;547 const ordinal = input.ordinal ?? 1;
553 const suffix = `${input.threadId}:${ordinal}`;548 const suffix = `${input.threadId}:${ordinal}`;
554 const modelSelection =549 const modelSelection =
...@@ -625,7 +620,7 @@ describe("AcpAdapterV2", () => {...@@ -625,7 +620,7 @@ describe("AcpAdapterV2", () => {
625 }),620 }),
626 },621 },
627 });622 });
628 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({623 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
629 runtimeMode: "full-access",624 runtimeMode: "full-access",
630 interactionMode: "default",625 interactionMode: "default",
631 cwd: process.cwd(),626 cwd: process.cwd(),
...@@ -742,7 +737,7 @@ describe("AcpAdapterV2", () => {...@@ -742,7 +737,7 @@ describe("AcpAdapterV2", () => {
742 },737 },
743 selfInvocation,738 selfInvocation,
744 });739 });
745 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({740 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
746 runtimeMode: "full-access",741 runtimeMode: "full-access",
747 interactionMode: "default",742 interactionMode: "default",
748 cwd: process.cwd(),743 cwd: process.cwd(),
...@@ -799,7 +794,7 @@ describe("AcpAdapterV2", () => {...@@ -799,7 +794,7 @@ describe("AcpAdapterV2", () => {
799 selfInvocation,794 selfInvocation,
800 });795 });
801 const policy = (interactionMode: "default" | "plan") =>796 const policy = (interactionMode: "default" | "plan") =>
802 ProviderAdapterV2RuntimePolicy.make({797 ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
803 runtimeMode: "full-access",798 runtimeMode: "full-access",
804 interactionMode,799 interactionMode,
805 cwd: process.cwd(),800 cwd: process.cwd(),
...@@ -820,7 +815,7 @@ describe("AcpAdapterV2", () => {...@@ -820,7 +815,7 @@ describe("AcpAdapterV2", () => {
820 const now = yield* DateTime.now;815 const now = yield* DateTime.now;
821 const runTurn = Effect.fnUntraced(function* (816 const runTurn = Effect.fnUntraced(function* (
822 ordinal: number,817 ordinal: number,
823 runtimePolicy: ProviderAdapterV2RuntimePolicy,818 runtimePolicy: ProviderAdapter.ProviderAdapterV2RuntimePolicy,
824 messageText: string,819 messageText: string,
825 ) {820 ) {
826 yield* runtime.startTurn(821 yield* runtime.startTurn(
...@@ -917,7 +912,7 @@ describe("AcpAdapterV2", () => {...@@ -917,7 +912,7 @@ describe("AcpAdapterV2", () => {
917 selfInvocation,912 selfInvocation,
918 });913 });
919 const threadId = ThreadId.make("thread-acp-v2-plan-replay-boundary");914 const threadId = ThreadId.make("thread-acp-v2-plan-replay-boundary");
920 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({915 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
921 runtimeMode: "full-access",916 runtimeMode: "full-access",
922 interactionMode: "default",917 interactionMode: "default",
923 cwd: process.cwd(),918 cwd: process.cwd(),
...@@ -1091,7 +1086,7 @@ describe("AcpAdapterV2", () => {...@@ -1091,7 +1086,7 @@ describe("AcpAdapterV2", () => {
1091 });1086 });
1092 const threadId = ThreadId.make("devin-streamed-write");1087 const threadId = ThreadId.make("devin-streamed-write");
1093 const modelSelection = { instanceId, model: "default" };1088 const modelSelection = { instanceId, model: "default" };
1094 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({1089 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
1095 runtimeMode: "full-access",1090 runtimeMode: "full-access",
1096 interactionMode: "default",1091 interactionMode: "default",
1097 cwd: process.cwd(),1092 cwd: process.cwd(),
...@@ -1342,7 +1337,7 @@ describe("AcpAdapterV2", () => {...@@ -1342,7 +1337,7 @@ describe("AcpAdapterV2", () => {
1342 });1337 });
1343 const threadId = ThreadId.make("devin-replay-parent");1338 const threadId = ThreadId.make("devin-replay-parent");
1344 const modelSelection = { instanceId, model: "default" };1339 const modelSelection = { instanceId, model: "default" };
1345 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({1340 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
1346 runtimeMode: "full-access",1341 runtimeMode: "full-access",
1347 interactionMode: "default",1342 interactionMode: "default",
1348 cwd: process.cwd(),1343 cwd: process.cwd(),
...@@ -1504,7 +1499,7 @@ describe("AcpAdapterV2", () => {...@@ -1504,7 +1499,7 @@ describe("AcpAdapterV2", () => {
1504 selfInvocation,1499 selfInvocation,
1505 });1500 });
1506 const threadId = ThreadId.make("thread-acp-v2-fidelity");1501 const threadId = ThreadId.make("thread-acp-v2-fidelity");
1507 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({1502 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
1508 runtimeMode: "full-access",1503 runtimeMode: "full-access",
1509 interactionMode: "default",1504 interactionMode: "default",
1510 cwd: process.cwd(),1505 cwd: process.cwd(),
...@@ -1684,7 +1679,7 @@ describe("AcpAdapterV2", () => {...@@ -1684,7 +1679,7 @@ describe("AcpAdapterV2", () => {
1684 selfInvocation,1679 selfInvocation,
1685 });1680 });
1686 const threadId = ThreadId.make("thread-acp-eager-resume");1681 const threadId = ThreadId.make("thread-acp-eager-resume");
1687 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({1682 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
1688 runtimeMode: "full-access",1683 runtimeMode: "full-access",
1689 interactionMode: "default",1684 interactionMode: "default",
1690 cwd: process.cwd(),1685 cwd: process.cwd(),
...@@ -1765,7 +1760,7 @@ describe("AcpAdapterV2", () => {...@@ -1765,7 +1760,7 @@ describe("AcpAdapterV2", () => {
1765 selfInvocation,1760 selfInvocation,
1766 });1761 });
1767 const threadId = ThreadId.make("thread-acp-stale-eager-resume");1762 const threadId = ThreadId.make("thread-acp-stale-eager-resume");
1768 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({1763 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
1769 runtimeMode: "full-access",1764 runtimeMode: "full-access",
1770 interactionMode: "default",1765 interactionMode: "default",
1771 cwd: process.cwd(),1766 cwd: process.cwd(),
...@@ -1910,7 +1905,7 @@ describe("AcpAdapterV2", () => {...@@ -1910,7 +1905,7 @@ describe("AcpAdapterV2", () => {
1910 selfInvocation,1905 selfInvocation,
1911 });1906 });
1912 const threadId = ThreadId.make("thread-acp-unexpected-termination");1907 const threadId = ThreadId.make("thread-acp-unexpected-termination");
1913 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({1908 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
1914 runtimeMode: "full-access",1909 runtimeMode: "full-access",
1915 interactionMode: "default",1910 interactionMode: "default",
1916 cwd: process.cwd(),1911 cwd: process.cwd(),
...@@ -1993,7 +1988,7 @@ describe("AcpAdapterV2", () => {...@@ -1993,7 +1988,7 @@ describe("AcpAdapterV2", () => {
1993 selfInvocation,1988 selfInvocation,
1994 });1989 });
1995 const threadId = ThreadId.make("thread-acp-provider-exit-running-command");1990 const threadId = ThreadId.make("thread-acp-provider-exit-running-command");
1996 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({1991 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
1997 runtimeMode: "full-access",1992 runtimeMode: "full-access",
1998 interactionMode: "default",1993 interactionMode: "default",
1999 cwd: process.cwd(),1994 cwd: process.cwd(),
...@@ -2065,7 +2060,7 @@ describe("AcpAdapterV2", () => {...@@ -2065,7 +2060,7 @@ describe("AcpAdapterV2", () => {
2065 selfInvocation,2060 selfInvocation,
2066 });2061 });
2067 const threadId = ThreadId.make("thread-acp-cgroup-unavailable");2062 const threadId = ThreadId.make("thread-acp-cgroup-unavailable");
2068 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({2063 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
2069 runtimeMode: "full-access",2064 runtimeMode: "full-access",
2070 interactionMode: "default",2065 interactionMode: "default",
2071 cwd: process.cwd(),2066 cwd: process.cwd(),
...@@ -2130,7 +2125,7 @@ describe("AcpAdapterV2", () => {...@@ -2130,7 +2125,7 @@ describe("AcpAdapterV2", () => {
2130 selfInvocation,2125 selfInvocation,
2131 });2126 });
2132 const threadId = ThreadId.make("thread-acp-cgroup-join-failure");2127 const threadId = ThreadId.make("thread-acp-cgroup-join-failure");
2133 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({2128 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
2134 runtimeMode: "full-access",2129 runtimeMode: "full-access",
2135 interactionMode: "default",2130 interactionMode: "default",
2136 cwd: process.cwd(),2131 cwd: process.cwd(),
...@@ -2226,7 +2221,7 @@ describe("AcpAdapterV2", () => {...@@ -2226,7 +2221,7 @@ describe("AcpAdapterV2", () => {
2226 McpProviderSession.clearMcpProviderSession(targetThreadId);2221 McpProviderSession.clearMcpProviderSession(targetThreadId);
2227 }),2222 }),
2228 );2223 );
2229 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({2224 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
2230 runtimeMode: "full-access",2225 runtimeMode: "full-access",
2231 interactionMode: "default",2226 interactionMode: "default",
2232 cwd: process.cwd(),2227 cwd: process.cwd(),
...@@ -2392,7 +2387,7 @@ describe("AcpAdapterV2", () => {...@@ -2392,7 +2387,7 @@ describe("AcpAdapterV2", () => {
2392 selfInvocation,2387 selfInvocation,
2393 });2388 });
2394 const threadId = ThreadId.make("thread-acp-no-client-fs");2389 const threadId = ThreadId.make("thread-acp-no-client-fs");
2395 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({2390 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
2396 runtimeMode: "full-access",2391 runtimeMode: "full-access",
2397 interactionMode: "default",2392 interactionMode: "default",
2398 cwd: workspace,2393 cwd: workspace,
...@@ -2493,7 +2488,7 @@ describe("AcpAdapterV2", () => {...@@ -2493,7 +2488,7 @@ describe("AcpAdapterV2", () => {
2493 clientTerminals: { childProcessSpawner },2488 clientTerminals: { childProcessSpawner },
2494 });2489 });
2495 const threadId = ThreadId.make("thread-acp-unknown-permission-grant");2490 const threadId = ThreadId.make("thread-acp-unknown-permission-grant");
2496 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({2491 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
2497 runtimeMode: "approval-required",2492 runtimeMode: "approval-required",
2498 interactionMode: "default",2493 interactionMode: "default",
2499 cwd: process.cwd(),2494 cwd: process.cwd(),
...@@ -2598,7 +2593,7 @@ describe("AcpAdapterV2", () => {...@@ -2598,7 +2593,7 @@ describe("AcpAdapterV2", () => {
2598 selfInvocation,2593 selfInvocation,
2599 });2594 });
2600 const threadId = ThreadId.make("thread-acp-missing-native-thread");2595 const threadId = ThreadId.make("thread-acp-missing-native-thread");
2601 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({2596 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
2602 runtimeMode: "full-access",2597 runtimeMode: "full-access",
2603 interactionMode: "default",2598 interactionMode: "default",
2604 cwd: process.cwd(),2599 cwd: process.cwd(),
...@@ -2629,7 +2624,7 @@ describe("AcpAdapterV2", () => {...@@ -2629,7 +2624,7 @@ describe("AcpAdapterV2", () => {
2629 .pipe(Effect.flip);2624 .pipe(Effect.flip);
26302625
2631 assert.equal(error._tag, "ProviderAdapterTurnStartError");2626 assert.equal(error._tag, "ProviderAdapterTurnStartError");
2632 assert.instanceOf(error.cause, ProviderAdapterProtocolError);2627 assert.instanceOf(error.cause, ProviderAdapter.ProviderAdapterProtocolError);
2633 assert.include(String(error.cause), "missing its ACP session id");2628 assert.include(String(error.cause), "missing its ACP session id");
2634 }).pipe(Effect.provide(layerTest), Effect.scoped),2629 }).pipe(Effect.provide(layerTest), Effect.scoped),
2635 );2630 );
...@@ -2676,7 +2671,7 @@ describe("AcpAdapterV2", () => {...@@ -2676,7 +2671,7 @@ describe("AcpAdapterV2", () => {
2676 selfInvocation,2671 selfInvocation,
2677 });2672 });
2678 const threadId = ThreadId.make("thread-acp-rollback-session");2673 const threadId = ThreadId.make("thread-acp-rollback-session");
2679 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({2674 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
2680 runtimeMode: "full-access",2675 runtimeMode: "full-access",
2681 interactionMode: "default",2676 interactionMode: "default",
2682 cwd: process.cwd(),2677 cwd: process.cwd(),
...@@ -2689,7 +2684,7 @@ describe("AcpAdapterV2", () => {...@@ -2689,7 +2684,7 @@ describe("AcpAdapterV2", () => {
2689 runtimePolicy,2684 runtimePolicy,
2690 });2685 });
2691 assert.isTrue(runtime.providerSession.capabilities.threads.canRollbackThread);2686 assert.isTrue(runtime.providerSession.capabilities.threads.canRollbackThread);
2692 const events = yield* Queue.unbounded<ProviderAdapterV2Event>();2687 const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>();
2693 yield* runtime.events.pipe(2688 yield* runtime.events.pipe(
2694 Stream.runForEach((event) => Queue.offer(events, event)),2689 Stream.runForEach((event) => Queue.offer(events, event)),
2695 Effect.forkScoped,2690 Effect.forkScoped,
...@@ -2883,7 +2878,7 @@ describe("AcpAdapterV2", () => {...@@ -2883,7 +2878,7 @@ describe("AcpAdapterV2", () => {
2883 continuationRequests: { offer: () => Effect.void },2878 continuationRequests: { offer: () => Effect.void },
2884 });2879 });
2885 const threadId = ThreadId.make("thread-acp-rollback-retry-generation");2880 const threadId = ThreadId.make("thread-acp-rollback-retry-generation");
2886 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({2881 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
2887 runtimeMode: "full-access",2882 runtimeMode: "full-access",
2888 interactionMode: "default",2883 interactionMode: "default",
2889 cwd: process.cwd(),2884 cwd: process.cwd(),
...@@ -2899,7 +2894,7 @@ describe("AcpAdapterV2", () => {...@@ -2899,7 +2894,7 @@ describe("AcpAdapterV2", () => {
2899 return yield* Effect.die("ACP runtime must expose background work state");2894 return yield* Effect.die("ACP runtime must expose background work state");
2900 }2895 }
2901 const hasPendingBackgroundWork = runtime.hasPendingBackgroundWork;2896 const hasPendingBackgroundWork = runtime.hasPendingBackgroundWork;
2902 const events = yield* Queue.unbounded<ProviderAdapterV2Event>();2897 const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>();
2903 yield* runtime.events.pipe(2898 yield* runtime.events.pipe(
2904 Stream.runForEach((event) => Queue.offer(events, event)),2899 Stream.runForEach((event) => Queue.offer(events, event)),
2905 Effect.forkScoped,2900 Effect.forkScoped,
...@@ -2995,7 +2990,7 @@ describe("AcpAdapterV2", () => {...@@ -2995,7 +2990,7 @@ describe("AcpAdapterV2", () => {
2995 selfInvocation,2990 selfInvocation,
2996 });2991 });
2997 const threadId = ThreadId.make("thread-acp-rollback-failure-compensation");2992 const threadId = ThreadId.make("thread-acp-rollback-failure-compensation");
2998 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({2993 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
2999 runtimeMode: "full-access",2994 runtimeMode: "full-access",
3000 interactionMode: "default",2995 interactionMode: "default",
3001 cwd: process.cwd(),2996 cwd: process.cwd(),
...@@ -3009,7 +3004,7 @@ describe("AcpAdapterV2", () => {...@@ -3009,7 +3004,7 @@ describe("AcpAdapterV2", () => {
3009 modelSelection,3004 modelSelection,
3010 runtimePolicy,3005 runtimePolicy,
3011 });3006 });
3012 const events = yield* Queue.unbounded<ProviderAdapterV2Event>();3007 const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>();
3013 yield* runtime.events.pipe(3008 yield* runtime.events.pipe(
3014 Stream.runForEach((event) => Queue.offer(events, event)),3009 Stream.runForEach((event) => Queue.offer(events, event)),
3015 Effect.forkScoped,3010 Effect.forkScoped,
...@@ -3079,7 +3074,7 @@ describe("AcpAdapterV2", () => {...@@ -3079,7 +3074,7 @@ describe("AcpAdapterV2", () => {
3079 selfInvocation,3074 selfInvocation,
3080 });3075 });
3081 const threadId = ThreadId.make("thread-acp-idle-finalizer");3076 const threadId = ThreadId.make("thread-acp-idle-finalizer");
3082 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({3077 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
3083 runtimeMode: "full-access",3078 runtimeMode: "full-access",
3084 interactionMode: "default",3079 interactionMode: "default",
3085 cwd: process.cwd(),3080 cwd: process.cwd(),
...@@ -3129,7 +3124,7 @@ describe("AcpAdapterV2", () => {...@@ -3129,7 +3124,7 @@ describe("AcpAdapterV2", () => {
3129 threadId: ThreadId.make(`grok-model-${model}`),3124 threadId: ThreadId.make(`grok-model-${model}`),
3130 providerSessionId: ProviderSessionId.make(`grok-model-${model}`),3125 providerSessionId: ProviderSessionId.make(`grok-model-${model}`),
3131 modelSelection: { instanceId, model },3126 modelSelection: { instanceId, model },
3132 runtimePolicy: ProviderAdapterV2RuntimePolicy.make({3127 runtimePolicy: ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
3133 runtimeMode: "full-access",3128 runtimeMode: "full-access",
3134 interactionMode: "default",3129 interactionMode: "default",
3135 cwd: process.cwd(),3130 cwd: process.cwd(),
...@@ -3173,7 +3168,7 @@ describe("AcpAdapterV2", () => {...@@ -3173,7 +3168,7 @@ describe("AcpAdapterV2", () => {
3173 ),3168 ),
3174 });3169 });
3175 const threadId = ThreadId.make("grok-model-switch-back");3170 const threadId = ThreadId.make("grok-model-switch-back");
3176 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({3171 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
3177 runtimeMode: "full-access",3172 runtimeMode: "full-access",
3178 interactionMode: "default",3173 interactionMode: "default",
3179 cwd: process.cwd(),3174 cwd: process.cwd(),
...@@ -3242,7 +3237,7 @@ describe("AcpAdapterV2", () => {...@@ -3242,7 +3237,7 @@ describe("AcpAdapterV2", () => {
3242 selfInvocation,3237 selfInvocation,
3243 });3238 });
3244 const threadId = ThreadId.make("thread-acp-unsupported-option");3239 const threadId = ThreadId.make("thread-acp-unsupported-option");
3245 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({3240 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
3246 runtimeMode: "full-access",3241 runtimeMode: "full-access",
3247 interactionMode: "default",3242 interactionMode: "default",
3248 cwd: process.cwd(),3243 cwd: process.cwd(),
...@@ -3286,7 +3281,7 @@ describe("AcpAdapterV2", () => {...@@ -3286,7 +3281,7 @@ describe("AcpAdapterV2", () => {
3286 });3281 });
3287 const firstThreadId = ThreadId.make("thread-acp-active-setup:first");3282 const firstThreadId = ThreadId.make("thread-acp-active-setup:first");
3288 const secondThreadId = ThreadId.make("thread-acp-active-setup:second");3283 const secondThreadId = ThreadId.make("thread-acp-active-setup:second");
3289 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({3284 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
3290 runtimeMode: "full-access",3285 runtimeMode: "full-access",
3291 interactionMode: "default",3286 interactionMode: "default",
3292 cwd: process.cwd(),3287 cwd: process.cwd(),
...@@ -3400,7 +3395,7 @@ describe("AcpAdapterV2", () => {...@@ -3400,7 +3395,7 @@ describe("AcpAdapterV2", () => {
3400 selfInvocation,3395 selfInvocation,
3401 });3396 });
3402 const threadId = ThreadId.make("thread-acp-empty-successful-bash");3397 const threadId = ThreadId.make("thread-acp-empty-successful-bash");
3403 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({3398 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
3404 runtimeMode: "full-access",3399 runtimeMode: "full-access",
3405 interactionMode: "default",3400 interactionMode: "default",
3406 cwd: process.cwd(),3401 cwd: process.cwd(),
...@@ -3412,7 +3407,7 @@ describe("AcpAdapterV2", () => {...@@ -3412,7 +3407,7 @@ describe("AcpAdapterV2", () => {
3412 modelSelection,3407 modelSelection,
3413 runtimePolicy,3408 runtimePolicy,
3414 });3409 });
3415 const events = yield* Queue.unbounded<ProviderAdapterV2Event>();3410 const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>();
3416 yield* runtime.events.pipe(3411 yield* runtime.events.pipe(
3417 Stream.runForEach((event) => Queue.offer(events, event)),3412 Stream.runForEach((event) => Queue.offer(events, event)),
3418 Effect.forkScoped,3413 Effect.forkScoped,
...@@ -3540,7 +3535,7 @@ describe("AcpAdapterV2", () => {...@@ -3540,7 +3535,7 @@ describe("AcpAdapterV2", () => {
3540 selfInvocation,3535 selfInvocation,
3541 });3536 });
3542 const threadId = ThreadId.make("thread-acp-native-cancel");3537 const threadId = ThreadId.make("thread-acp-native-cancel");
3543 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({3538 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
3544 runtimeMode: "full-access",3539 runtimeMode: "full-access",
3545 interactionMode: "default",3540 interactionMode: "default",
3546 cwd: process.cwd(),3541 cwd: process.cwd(),
...@@ -3642,7 +3637,7 @@ describe("AcpAdapterV2", () => {...@@ -3642,7 +3637,7 @@ describe("AcpAdapterV2", () => {
3642 selfInvocation,3637 selfInvocation,
3643 });3638 });
3644 const threadId = ThreadId.make("thread-acp-cancel-permission");3639 const threadId = ThreadId.make("thread-acp-cancel-permission");
3645 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({3640 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
3646 runtimeMode: "approval-required",3641 runtimeMode: "approval-required",
3647 interactionMode: "default",3642 interactionMode: "default",
3648 cwd: process.cwd(),3643 cwd: process.cwd(),
...@@ -3741,7 +3736,7 @@ describe("AcpAdapterV2", () => {...@@ -3741,7 +3736,7 @@ describe("AcpAdapterV2", () => {
3741 selfInvocation,3736 selfInvocation,
3742 });3737 });
3743 const threadId = ThreadId.make("thread-acp-response-wins-permission");3738 const threadId = ThreadId.make("thread-acp-response-wins-permission");
3744 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({3739 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
3745 runtimeMode: "approval-required",3740 runtimeMode: "approval-required",
3746 interactionMode: "default",3741 interactionMode: "default",
3747 cwd: process.cwd(),3742 cwd: process.cwd(),
...@@ -3833,7 +3828,7 @@ describe("AcpAdapterV2", () => {...@@ -3833,7 +3828,7 @@ describe("AcpAdapterV2", () => {
3833 selfInvocation,3828 selfInvocation,
3834 });3829 });
3835 const threadId = ThreadId.make("thread-acp-reordered-elicitation");3830 const threadId = ThreadId.make("thread-acp-reordered-elicitation");
3836 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({3831 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
3837 runtimeMode: "approval-required",3832 runtimeMode: "approval-required",
3838 interactionMode: "default",3833 interactionMode: "default",
3839 cwd: process.cwd(),3834 cwd: process.cwd(),
...@@ -3908,7 +3903,7 @@ describe("AcpAdapterV2", () => {...@@ -3908,7 +3903,7 @@ describe("AcpAdapterV2", () => {
3908 selfInvocation,3903 selfInvocation,
3909 });3904 });
3910 const threadId = ThreadId.make("thread-acp-mcp-approval-elicitation");3905 const threadId = ThreadId.make("thread-acp-mcp-approval-elicitation");
3911 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({3906 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
3912 runtimeMode: "full-access",3907 runtimeMode: "full-access",
3913 interactionMode: "default",3908 interactionMode: "default",
3914 cwd: process.cwd(),3909 cwd: process.cwd(),
...@@ -3983,7 +3978,7 @@ describe("AcpAdapterV2", () => {...@@ -3983,7 +3978,7 @@ describe("AcpAdapterV2", () => {
3983 },3978 },
3984 });3979 });
3985 const threadId = ThreadId.make("thread-acp-normal-close-held-response");3980 const threadId = ThreadId.make("thread-acp-normal-close-held-response");
3986 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({3981 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
3987 runtimeMode: "approval-required",3982 runtimeMode: "approval-required",
3988 interactionMode: "default",3983 interactionMode: "default",
3989 cwd: process.cwd(),3984 cwd: process.cwd(),
...@@ -4089,7 +4084,7 @@ describe("AcpAdapterV2", () => {...@@ -4089,7 +4084,7 @@ describe("AcpAdapterV2", () => {
4089 },4084 },
4090 });4085 });
4091 const threadId = ThreadId.make("thread-acp-close-wins-registration");4086 const threadId = ThreadId.make("thread-acp-close-wins-registration");
4092 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({4087 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
4093 runtimeMode: "approval-required",4088 runtimeMode: "approval-required",
4094 interactionMode: "default",4089 interactionMode: "default",
4095 cwd: process.cwd(),4090 cwd: process.cwd(),
...@@ -4208,7 +4203,7 @@ describe("AcpAdapterV2", () => {...@@ -4208,7 +4203,7 @@ describe("AcpAdapterV2", () => {
4208 },4203 },
4209 });4204 });
4210 const threadId = ThreadId.make("thread-acp-pending-response-timeout");4205 const threadId = ThreadId.make("thread-acp-pending-response-timeout");
4211 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({4206 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
4212 runtimeMode: "approval-required",4207 runtimeMode: "approval-required",
4213 interactionMode: "default",4208 interactionMode: "default",
4214 cwd: process.cwd(),4209 cwd: process.cwd(),
...@@ -4333,7 +4328,7 @@ describe("AcpAdapterV2", () => {...@@ -4333,7 +4328,7 @@ describe("AcpAdapterV2", () => {
4333 selfInvocation,4328 selfInvocation,
4334 });4329 });
4335 const threadId = ThreadId.make("thread-acp-pending-response-cancel");4330 const threadId = ThreadId.make("thread-acp-pending-response-cancel");
4336 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({4331 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
4337 runtimeMode: "approval-required",4332 runtimeMode: "approval-required",
4338 interactionMode: "default",4333 interactionMode: "default",
4339 cwd: process.cwd(),4334 cwd: process.cwd(),
...@@ -4442,7 +4437,7 @@ describe("AcpAdapterV2", () => {...@@ -4442,7 +4437,7 @@ describe("AcpAdapterV2", () => {
4442 selfInvocation,4437 selfInvocation,
4443 });4438 });
4444 const threadId = ThreadId.make(`thread-acp-immediate-permission-${name}`);4439 const threadId = ThreadId.make(`thread-acp-immediate-permission-${name}`);
4445 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({4440 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
4446 runtimeMode: "full-access",4441 runtimeMode: "full-access",
4447 interactionMode: "default",4442 interactionMode: "default",
4448 approvalPolicy: "never",4443 approvalPolicy: "never",
...@@ -4535,7 +4530,7 @@ describe("AcpAdapterV2", () => {...@@ -4535,7 +4530,7 @@ describe("AcpAdapterV2", () => {
4535 selfInvocation,4530 selfInvocation,
4536 });4531 });
4537 const threadId = ThreadId.make("thread-acp-immediate-url-elicitation");4532 const threadId = ThreadId.make("thread-acp-immediate-url-elicitation");
4538 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({4533 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
4539 runtimeMode: "full-access",4534 runtimeMode: "full-access",
4540 interactionMode: "default",4535 interactionMode: "default",
4541 cwd: process.cwd(),4536 cwd: process.cwd(),
...@@ -4626,7 +4621,7 @@ describe("AcpAdapterV2", () => {...@@ -4626,7 +4621,7 @@ describe("AcpAdapterV2", () => {
4626 selfInvocation,4621 selfInvocation,
4627 });4622 });
4628 const threadId = ThreadId.make("thread-acp-missing-response-ack");4623 const threadId = ThreadId.make("thread-acp-missing-response-ack");
4629 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({4624 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
4630 runtimeMode: "full-access",4625 runtimeMode: "full-access",
4631 interactionMode: "default",4626 interactionMode: "default",
4632 approvalPolicy: "never",4627 approvalPolicy: "never",
...@@ -4708,7 +4703,7 @@ describe("AcpAdapterV2", () => {...@@ -4708,7 +4703,7 @@ describe("AcpAdapterV2", () => {
4708 selfInvocation,4703 selfInvocation,
4709 });4704 });
4710 const threadId = ThreadId.make("thread-acp-teardown-wins-elicitation");4705 const threadId = ThreadId.make("thread-acp-teardown-wins-elicitation");
4711 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({4706 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
4712 runtimeMode: "approval-required",4707 runtimeMode: "approval-required",
4713 interactionMode: "default",4708 interactionMode: "default",
4714 cwd: process.cwd(),4709 cwd: process.cwd(),
...@@ -4802,7 +4797,7 @@ describe("AcpAdapterV2", () => {...@@ -4802,7 +4797,7 @@ describe("AcpAdapterV2", () => {
4802 selfInvocation,4797 selfInvocation,
4803 });4798 });
4804 const threadId = ThreadId.make("thread-acp-cancel-timeout");4799 const threadId = ThreadId.make("thread-acp-cancel-timeout");
4805 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({4800 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
4806 runtimeMode: "full-access",4801 runtimeMode: "full-access",
4807 interactionMode: "default",4802 interactionMode: "default",
4808 cwd: process.cwd(),4803 cwd: process.cwd(),
...@@ -4903,7 +4898,7 @@ describe("AcpAdapterV2", () => {...@@ -4903,7 +4898,7 @@ describe("AcpAdapterV2", () => {
4903 selfInvocation,4898 selfInvocation,
4904 });4899 });
4905 const threadId = ThreadId.make("thread-acp-double-stop");4900 const threadId = ThreadId.make("thread-acp-double-stop");
4906 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({4901 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
4907 runtimeMode: "full-access",4902 runtimeMode: "full-access",
4908 interactionMode: "default",4903 interactionMode: "default",
4909 cwd: process.cwd(),4904 cwd: process.cwd(),
...@@ -4915,7 +4910,7 @@ describe("AcpAdapterV2", () => {...@@ -4915,7 +4910,7 @@ describe("AcpAdapterV2", () => {
4915 modelSelection,4910 modelSelection,
4916 runtimePolicy,4911 runtimePolicy,
4917 });4912 });
4918 const events = yield* Queue.unbounded<ProviderAdapterV2Event>();4913 const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>();
4919 yield* runtime.events.pipe(4914 yield* runtime.events.pipe(
4920 Stream.runForEach((event) => Queue.offer(events, event)),4915 Stream.runForEach((event) => Queue.offer(events, event)),
4921 Effect.forkScoped,4916 Effect.forkScoped,
...@@ -5007,7 +5002,7 @@ describe("AcpAdapterV2", () => {...@@ -5007,7 +5002,7 @@ describe("AcpAdapterV2", () => {
5007 selfInvocation,5002 selfInvocation,
5008 });5003 });
5009 const threadId = ThreadId.make("thread-acp-interrupt-background-hold");5004 const threadId = ThreadId.make("thread-acp-interrupt-background-hold");
5010 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({5005 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
5011 runtimeMode: "full-access",5006 runtimeMode: "full-access",
5012 interactionMode: "default",5007 interactionMode: "default",
5013 cwd: process.cwd(),5008 cwd: process.cwd(),
...@@ -5019,7 +5014,7 @@ describe("AcpAdapterV2", () => {...@@ -5019,7 +5014,7 @@ describe("AcpAdapterV2", () => {
5019 modelSelection,5014 modelSelection,
5020 runtimePolicy,5015 runtimePolicy,
5021 });5016 });
5022 const events = yield* Queue.unbounded<ProviderAdapterV2Event>();5017 const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>();
5023 yield* runtime.events.pipe(5018 yield* runtime.events.pipe(
5024 Stream.runForEach((event) => Queue.offer(events, event)),5019 Stream.runForEach((event) => Queue.offer(events, event)),
5025 Effect.forkScoped,5020 Effect.forkScoped,
...@@ -5111,7 +5106,7 @@ describe("AcpAdapterV2", () => {...@@ -5111,7 +5106,7 @@ describe("AcpAdapterV2", () => {
5111 selfInvocation,5106 selfInvocation,
5112 });5107 });
5113 const threadId = ThreadId.make("thread-acp-subagent-carryover");5108 const threadId = ThreadId.make("thread-acp-subagent-carryover");
5114 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({5109 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
5115 runtimeMode: "full-access",5110 runtimeMode: "full-access",
5116 interactionMode: "default",5111 interactionMode: "default",
5117 cwd: process.cwd(),5112 cwd: process.cwd(),
...@@ -5123,7 +5118,7 @@ describe("AcpAdapterV2", () => {...@@ -5123,7 +5118,7 @@ describe("AcpAdapterV2", () => {
5123 modelSelection,5118 modelSelection,
5124 runtimePolicy,5119 runtimePolicy,
5125 });5120 });
5126 const events = yield* Queue.unbounded<ProviderAdapterV2Event>();5121 const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>();
5127 yield* runtime.events.pipe(5122 yield* runtime.events.pipe(
5128 Stream.runForEach((event) => Queue.offer(events, event)),5123 Stream.runForEach((event) => Queue.offer(events, event)),
5129 Effect.forkScoped,5124 Effect.forkScoped,
...@@ -5252,7 +5247,7 @@ describe("AcpAdapterV2", () => {...@@ -5252,7 +5247,7 @@ describe("AcpAdapterV2", () => {
5252 continuationRequests: { offer: () => Effect.void },5247 continuationRequests: { offer: () => Effect.void },
5253 });5248 });
5254 const threadId = ThreadId.make("thread-acp-carryover-pending-pin");5249 const threadId = ThreadId.make("thread-acp-carryover-pending-pin");
5255 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({5250 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
5256 runtimeMode: "full-access",5251 runtimeMode: "full-access",
5257 interactionMode: "default",5252 interactionMode: "default",
5258 cwd: process.cwd(),5253 cwd: process.cwd(),
...@@ -5270,7 +5265,7 @@ describe("AcpAdapterV2", () => {...@@ -5270,7 +5265,7 @@ describe("AcpAdapterV2", () => {
5270 );5265 );
5271 }5266 }
5272 const hasPendingBackgroundWork = runtime.hasPendingBackgroundWork;5267 const hasPendingBackgroundWork = runtime.hasPendingBackgroundWork;
5273 const events = yield* Queue.unbounded<ProviderAdapterV2Event>();5268 const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>();
5274 yield* runtime.events.pipe(5269 yield* runtime.events.pipe(
5275 Stream.runForEach((event) => Queue.offer(events, event)),5270 Stream.runForEach((event) => Queue.offer(events, event)),
5276 Effect.forkScoped,5271 Effect.forkScoped,
...@@ -5416,7 +5411,7 @@ describe("AcpAdapterV2", () => {...@@ -5416,7 +5411,7 @@ describe("AcpAdapterV2", () => {
5416 continuationRequests: { offer: () => Effect.void },5411 continuationRequests: { offer: () => Effect.void },
5417 });5412 });
5418 const threadId = ThreadId.make("thread-acp-active-carryover-pending-pin");5413 const threadId = ThreadId.make("thread-acp-active-carryover-pending-pin");
5419 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({5414 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
5420 runtimeMode: "full-access",5415 runtimeMode: "full-access",
5421 interactionMode: "default",5416 interactionMode: "default",
5422 cwd: process.cwd(),5417 cwd: process.cwd(),
...@@ -5436,7 +5431,7 @@ describe("AcpAdapterV2", () => {...@@ -5436,7 +5431,7 @@ describe("AcpAdapterV2", () => {
5436 );5431 );
5437 }5432 }
5438 const hasPendingBackgroundWork = runtime.hasPendingBackgroundWork;5433 const hasPendingBackgroundWork = runtime.hasPendingBackgroundWork;
5439 const events = yield* Queue.unbounded<ProviderAdapterV2Event>();5434 const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>();
5440 yield* runtime.events.pipe(5435 yield* runtime.events.pipe(
5441 Stream.runForEach((event) => Queue.offer(events, event)),5436 Stream.runForEach((event) => Queue.offer(events, event)),
5442 Effect.forkScoped,5437 Effect.forkScoped,
...@@ -5661,7 +5656,7 @@ describe("AcpAdapterV2", () => {...@@ -5661,7 +5656,7 @@ describe("AcpAdapterV2", () => {
5661 continuationRequests: { offer: () => Effect.void },5656 continuationRequests: { offer: () => Effect.void },
5662 });5657 });
5663 const threadId = ThreadId.make("thread-acp-subagent-finalize-window");5658 const threadId = ThreadId.make("thread-acp-subagent-finalize-window");
5664 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({5659 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
5665 runtimeMode: "full-access",5660 runtimeMode: "full-access",
5666 interactionMode: "default",5661 interactionMode: "default",
5667 cwd: process.cwd(),5662 cwd: process.cwd(),
...@@ -5681,7 +5676,7 @@ describe("AcpAdapterV2", () => {...@@ -5681,7 +5676,7 @@ describe("AcpAdapterV2", () => {
5681 );5676 );
5682 }5677 }
5683 const hasPendingBackgroundWork = runtime.hasPendingBackgroundWork;5678 const hasPendingBackgroundWork = runtime.hasPendingBackgroundWork;
5684 const events = yield* Queue.unbounded<ProviderAdapterV2Event>();5679 const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>();
5685 yield* runtime.events.pipe(5680 yield* runtime.events.pipe(
5686 Stream.runForEach((event) => Queue.offer(events, event)),5681 Stream.runForEach((event) => Queue.offer(events, event)),
5687 Effect.forkScoped,5682 Effect.forkScoped,
...@@ -5796,7 +5791,8 @@ describe("AcpAdapterV2", () => {...@@ -5796,7 +5791,8 @@ describe("AcpAdapterV2", () => {
5796 new URL("../../../scripts/acp-mock-agent.ts", import.meta.url),5791 new URL("../../../scripts/acp-mock-agent.ts", import.meta.url),
5797 );5792 );
5798 const protocolEvents = yield* Queue.bounded<EffectAcpProtocol.AcpProtocolLogEvent>(256);5793 const protocolEvents = yield* Queue.bounded<EffectAcpProtocol.AcpProtocolLogEvent>(256);
5799 const continuationRequests: Array<ProviderContinuationRequest> = [];5794 const continuationRequests: Array<ProviderContinuationRequests.ProviderContinuationRequest> =
5795 [];
5800 const promptSettled = yield* Deferred.make<void>();5796 const promptSettled = yield* Deferred.make<void>();
5801 const instanceId = ProviderInstanceId.make("acp-test");5797 const instanceId = ProviderInstanceId.make("acp-test");
5802 const childSessionId = "mock-child-session-post-settle";5798 const childSessionId = "mock-child-session-post-settle";
...@@ -5859,7 +5855,7 @@ describe("AcpAdapterV2", () => {...@@ -5859,7 +5855,7 @@ describe("AcpAdapterV2", () => {
5859 },5855 },
5860 });5856 });
5861 const threadId = ThreadId.make("thread-acp-carryover-child-session-post-settle");5857 const threadId = ThreadId.make("thread-acp-carryover-child-session-post-settle");
5862 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({5858 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
5863 runtimeMode: "full-access",5859 runtimeMode: "full-access",
5864 interactionMode: "default",5860 interactionMode: "default",
5865 cwd: process.cwd(),5861 cwd: process.cwd(),
...@@ -5879,7 +5875,7 @@ describe("AcpAdapterV2", () => {...@@ -5879,7 +5875,7 @@ describe("AcpAdapterV2", () => {
5879 );5875 );
5880 }5876 }
5881 const hasPendingBackgroundWork = runtime.hasPendingBackgroundWork;5877 const hasPendingBackgroundWork = runtime.hasPendingBackgroundWork;
5882 const events = yield* Queue.unbounded<ProviderAdapterV2Event>();5878 const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>();
5883 yield* runtime.events.pipe(5879 yield* runtime.events.pipe(
5884 Stream.runForEach((event) => Queue.offer(events, event)),5880 Stream.runForEach((event) => Queue.offer(events, event)),
5885 Effect.forkScoped,5881 Effect.forkScoped,
...@@ -6046,7 +6042,7 @@ describe("AcpAdapterV2", () => {...@@ -6046,7 +6042,7 @@ describe("AcpAdapterV2", () => {
6046 continuationRequests: { offer: () => Effect.void },6042 continuationRequests: { offer: () => Effect.void },
6047 });6043 });
6048 const threadId = ThreadId.make("thread-acp-carryover-subagent-finished");6044 const threadId = ThreadId.make("thread-acp-carryover-subagent-finished");
6049 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({6045 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
6050 runtimeMode: "full-access",6046 runtimeMode: "full-access",
6051 interactionMode: "default",6047 interactionMode: "default",
6052 cwd: process.cwd(),6048 cwd: process.cwd(),
...@@ -6062,7 +6058,7 @@ describe("AcpAdapterV2", () => {...@@ -6062,7 +6058,7 @@ describe("AcpAdapterV2", () => {
6062 return yield* Effect.die("post-settle continuation must expose hasPendingBackgroundWork");6058 return yield* Effect.die("post-settle continuation must expose hasPendingBackgroundWork");
6063 }6059 }
6064 const hasPendingBackgroundWork = runtime.hasPendingBackgroundWork;6060 const hasPendingBackgroundWork = runtime.hasPendingBackgroundWork;
6065 const events = yield* Queue.unbounded<ProviderAdapterV2Event>();6061 const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>();
6066 yield* runtime.events.pipe(6062 yield* runtime.events.pipe(
6067 Stream.runForEach((event) => Queue.offer(events, event)),6063 Stream.runForEach((event) => Queue.offer(events, event)),
6068 Effect.forkScoped,6064 Effect.forkScoped,
...@@ -6151,7 +6147,8 @@ describe("AcpAdapterV2", () => {...@@ -6151,7 +6147,8 @@ describe("AcpAdapterV2", () => {
6151 new URL("../../../scripts/acp-mock-agent.ts", import.meta.url),6147 new URL("../../../scripts/acp-mock-agent.ts", import.meta.url),
6152 );6148 );
6153 const protocolEvents = yield* Queue.bounded<EffectAcpProtocol.AcpProtocolLogEvent>(256);6149 const protocolEvents = yield* Queue.bounded<EffectAcpProtocol.AcpProtocolLogEvent>(256);
6154 const continuationRequests: Array<ProviderContinuationRequest> = [];6150 const continuationRequests: Array<ProviderContinuationRequests.ProviderContinuationRequest> =
6151 [];
6155 const instanceId = ProviderInstanceId.make("acp-test");6152 const instanceId = ProviderInstanceId.make("acp-test");
6156 const childSessionId = "019f44a6-4820-7402-925d-bc862ee711dd";6153 const childSessionId = "019f44a6-4820-7402-925d-bc862ee711dd";
6157 let subagentPhase: "spawn" | "complete" = "spawn";6154 let subagentPhase: "spawn" | "complete" = "spawn";
...@@ -6210,7 +6207,7 @@ describe("AcpAdapterV2", () => {...@@ -6210,7 +6207,7 @@ describe("AcpAdapterV2", () => {
6210 },6207 },
6211 });6208 });
6212 const threadId = ThreadId.make("thread-acp-completed-root-eager-carryover");6209 const threadId = ThreadId.make("thread-acp-completed-root-eager-carryover");
6213 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({6210 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
6214 runtimeMode: "full-access",6211 runtimeMode: "full-access",
6215 interactionMode: "default",6212 interactionMode: "default",
6216 cwd: process.cwd(),6213 cwd: process.cwd(),
...@@ -6230,7 +6227,7 @@ describe("AcpAdapterV2", () => {...@@ -6230,7 +6227,7 @@ describe("AcpAdapterV2", () => {
6230 );6227 );
6231 }6228 }
6232 const hasPendingBackgroundWork = runtime.hasPendingBackgroundWork;6229 const hasPendingBackgroundWork = runtime.hasPendingBackgroundWork;
6233 const events = yield* Queue.unbounded<ProviderAdapterV2Event>();6230 const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>();
6234 yield* runtime.events.pipe(6231 yield* runtime.events.pipe(
6235 Stream.runForEach((event) => Queue.offer(events, event)),6232 Stream.runForEach((event) => Queue.offer(events, event)),
6236 Effect.forkScoped,6233 Effect.forkScoped,
...@@ -6396,7 +6393,8 @@ describe("AcpAdapterV2", () => {...@@ -6396,7 +6393,8 @@ describe("AcpAdapterV2", () => {
6396 const mockAgentPath = yield* path.fromFileUrl(6393 const mockAgentPath = yield* path.fromFileUrl(
6397 new URL("../../../scripts/acp-mock-agent.ts", import.meta.url),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 const bufferedAssistantText = "BUFFERED_WAKE_AFTER_USER_ATTACH";6398 const bufferedAssistantText = "BUFFERED_WAKE_AFTER_USER_ATTACH";
6401 const instanceId = ProviderInstanceId.make("acp-test");6399 const instanceId = ProviderInstanceId.make("acp-test");
6402 let subagentPhase: "spawn" | "complete" = "spawn";6400 let subagentPhase: "spawn" | "complete" = "spawn";
...@@ -6458,7 +6456,7 @@ describe("AcpAdapterV2", () => {...@@ -6458,7 +6456,7 @@ describe("AcpAdapterV2", () => {
6458 },6456 },
6459 });6457 });
6460 const threadId = ThreadId.make("thread-acp-carryover-root-session-continuation");6458 const threadId = ThreadId.make("thread-acp-carryover-root-session-continuation");
6461 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({6459 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
6462 runtimeMode: "full-access",6460 runtimeMode: "full-access",
6463 interactionMode: "default",6461 interactionMode: "default",
6464 cwd: process.cwd(),6462 cwd: process.cwd(),
...@@ -6478,7 +6476,7 @@ describe("AcpAdapterV2", () => {...@@ -6478,7 +6476,7 @@ describe("AcpAdapterV2", () => {
6478 );6476 );
6479 }6477 }
6480 const hasPendingBackgroundWork = runtime.hasPendingBackgroundWork;6478 const hasPendingBackgroundWork = runtime.hasPendingBackgroundWork;
6481 const events = yield* Queue.unbounded<ProviderAdapterV2Event>();6479 const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>();
6482 yield* runtime.events.pipe(6480 yield* runtime.events.pipe(
6483 Stream.runForEach((event) => Queue.offer(events, event)),6481 Stream.runForEach((event) => Queue.offer(events, event)),
6484 Effect.forkScoped,6482 Effect.forkScoped,
...@@ -6678,7 +6676,8 @@ describe("AcpAdapterV2", () => {...@@ -6678,7 +6676,8 @@ describe("AcpAdapterV2", () => {
6678 const mockAgentPath = yield* path.fromFileUrl(6676 const mockAgentPath = yield* path.fromFileUrl(
6679 new URL("../../../scripts/acp-mock-agent.ts", import.meta.url),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 const promptWireReturned = yield* Deferred.make<void>();6681 const promptWireReturned = yield* Deferred.make<void>();
6683 const releasePromptCompletion = yield* Deferred.make<void>();6682 const releasePromptCompletion = yield* Deferred.make<void>();
6684 const instanceId = ProviderInstanceId.make("acp-test");6683 const instanceId = ProviderInstanceId.make("acp-test");
...@@ -6750,7 +6749,7 @@ describe("AcpAdapterV2", () => {...@@ -6750,7 +6749,7 @@ describe("AcpAdapterV2", () => {
6750 },6749 },
6751 });6750 });
6752 const threadId = ThreadId.make("thread-acp-carryover-already-handled-re-report");6751 const threadId = ThreadId.make("thread-acp-carryover-already-handled-re-report");
6753 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({6752 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
6754 runtimeMode: "full-access",6753 runtimeMode: "full-access",
6755 interactionMode: "default",6754 interactionMode: "default",
6756 cwd: process.cwd(),6755 cwd: process.cwd(),
...@@ -6770,7 +6769,7 @@ describe("AcpAdapterV2", () => {...@@ -6770,7 +6769,7 @@ describe("AcpAdapterV2", () => {
6770 );6769 );
6771 }6770 }
6772 const hasPendingBackgroundWork = runtime.hasPendingBackgroundWork;6771 const hasPendingBackgroundWork = runtime.hasPendingBackgroundWork;
6773 const events = yield* Queue.unbounded<ProviderAdapterV2Event>();6772 const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>();
6774 yield* runtime.events.pipe(6773 yield* runtime.events.pipe(
6775 Stream.runForEach((event) => Queue.offer(events, event)),6774 Stream.runForEach((event) => Queue.offer(events, event)),
6776 Effect.forkScoped,6775 Effect.forkScoped,
...@@ -6914,7 +6913,8 @@ describe("AcpAdapterV2", () => {...@@ -6914,7 +6913,8 @@ describe("AcpAdapterV2", () => {
6914 new URL("../../../scripts/acp-mock-agent.ts", import.meta.url),6913 new URL("../../../scripts/acp-mock-agent.ts", import.meta.url),
6915 );6914 );
6916 const protocolEvents = yield* Queue.bounded<EffectAcpProtocol.AcpProtocolLogEvent>(256);6915 const protocolEvents = yield* Queue.bounded<EffectAcpProtocol.AcpProtocolLogEvent>(256);
6917 const continuationRequests: Array<ProviderContinuationRequest> = [];6916 const continuationRequests: Array<ProviderContinuationRequests.ProviderContinuationRequest> =
6917 [];
6918 const promptSettled = yield* Deferred.make<void>();6918 const promptSettled = yield* Deferred.make<void>();
6919 const instanceId = ProviderInstanceId.make("acp-test");6919 const instanceId = ProviderInstanceId.make("acp-test");
6920 const childSessionId = "019f5470-bf92-7a90-afb3-5a6cea5b34a3";6920 const childSessionId = "019f5470-bf92-7a90-afb3-5a6cea5b34a3";
...@@ -6988,7 +6988,7 @@ describe("AcpAdapterV2", () => {...@@ -6988,7 +6988,7 @@ describe("AcpAdapterV2", () => {
6988 },6988 },
6989 });6989 });
6990 const threadId = ThreadId.make("thread-acp-carryover-root-end-notice");6990 const threadId = ThreadId.make("thread-acp-carryover-root-end-notice");
6991 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({6991 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
6992 runtimeMode: "full-access",6992 runtimeMode: "full-access",
6993 interactionMode: "default",6993 interactionMode: "default",
6994 cwd: process.cwd(),6994 cwd: process.cwd(),
...@@ -7006,7 +7006,7 @@ describe("AcpAdapterV2", () => {...@@ -7006,7 +7006,7 @@ describe("AcpAdapterV2", () => {
7006 );7006 );
7007 }7007 }
7008 const hasPendingBackgroundWork = runtime.hasPendingBackgroundWork;7008 const hasPendingBackgroundWork = runtime.hasPendingBackgroundWork;
7009 const events = yield* Queue.unbounded<ProviderAdapterV2Event>();7009 const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>();
7010 yield* runtime.events.pipe(7010 yield* runtime.events.pipe(
7011 Stream.runForEach((event) => Queue.offer(events, event)),7011 Stream.runForEach((event) => Queue.offer(events, event)),
7012 Effect.forkScoped,7012 Effect.forkScoped,
...@@ -7136,7 +7136,8 @@ describe("AcpAdapterV2", () => {...@@ -7136,7 +7136,8 @@ describe("AcpAdapterV2", () => {
7136 const mockAgentPath = yield* path.fromFileUrl(7136 const mockAgentPath = yield* path.fromFileUrl(
7137 new URL("../../../scripts/acp-mock-agent.ts", import.meta.url),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 const instanceId = ProviderInstanceId.make("acp-test");7141 const instanceId = ProviderInstanceId.make("acp-test");
7141 const childSessionId = "mock-child-session-pending-continuation";7142 const childSessionId = "mock-child-session-pending-continuation";
7142 const bufferedAssistantText = "POST_SETTLE_BUFFERED_ASSISTANT_TEXT";7143 const bufferedAssistantText = "POST_SETTLE_BUFFERED_ASSISTANT_TEXT";
...@@ -7199,7 +7200,7 @@ describe("AcpAdapterV2", () => {...@@ -7199,7 +7200,7 @@ describe("AcpAdapterV2", () => {
7199 },7200 },
7200 });7201 });
7201 const threadId = ThreadId.make("thread-acp-carryover-child-pending-continuation");7202 const threadId = ThreadId.make("thread-acp-carryover-child-pending-continuation");
7202 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({7203 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
7203 runtimeMode: "full-access",7204 runtimeMode: "full-access",
7204 interactionMode: "default",7205 interactionMode: "default",
7205 cwd: process.cwd(),7206 cwd: process.cwd(),
...@@ -7219,7 +7220,7 @@ describe("AcpAdapterV2", () => {...@@ -7219,7 +7220,7 @@ describe("AcpAdapterV2", () => {
7219 );7220 );
7220 }7221 }
7221 const hasPendingBackgroundWork = runtime.hasPendingBackgroundWork;7222 const hasPendingBackgroundWork = runtime.hasPendingBackgroundWork;
7222 const events = yield* Queue.unbounded<ProviderAdapterV2Event>();7223 const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>();
7223 yield* runtime.events.pipe(7224 yield* runtime.events.pipe(
7224 Stream.runForEach((event) => Queue.offer(events, event)),7225 Stream.runForEach((event) => Queue.offer(events, event)),
7225 Effect.forkScoped,7226 Effect.forkScoped,
...@@ -7401,7 +7402,8 @@ describe("AcpAdapterV2", () => {...@@ -7401,7 +7402,8 @@ describe("AcpAdapterV2", () => {
7401 const mockAgentPath = yield* path.fromFileUrl(7402 const mockAgentPath = yield* path.fromFileUrl(
7402 new URL("../../../scripts/acp-mock-agent.ts", import.meta.url),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 const instanceId = ProviderInstanceId.make("acp-test");7407 const instanceId = ProviderInstanceId.make("acp-test");
7406 const childSessionId = "mock-child-session-root-then-child";7408 const childSessionId = "mock-child-session-root-then-child";
7407 let subagentPhase: "spawn" | "complete" = "spawn";7409 let subagentPhase: "spawn" | "complete" = "spawn";
...@@ -7463,7 +7465,7 @@ describe("AcpAdapterV2", () => {...@@ -7463,7 +7465,7 @@ describe("AcpAdapterV2", () => {
7463 },7465 },
7464 });7466 });
7465 const threadId = ThreadId.make("thread-acp-carryover-root-then-child");7467 const threadId = ThreadId.make("thread-acp-carryover-root-then-child");
7466 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({7468 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
7467 runtimeMode: "full-access",7469 runtimeMode: "full-access",
7468 interactionMode: "default",7470 interactionMode: "default",
7469 cwd: process.cwd(),7471 cwd: process.cwd(),
...@@ -7483,7 +7485,7 @@ describe("AcpAdapterV2", () => {...@@ -7483,7 +7485,7 @@ describe("AcpAdapterV2", () => {
7483 );7485 );
7484 }7486 }
7485 const hasPendingBackgroundWork = runtime.hasPendingBackgroundWork;7487 const hasPendingBackgroundWork = runtime.hasPendingBackgroundWork;
7486 const events = yield* Queue.unbounded<ProviderAdapterV2Event>();7488 const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>();
7487 yield* runtime.events.pipe(7489 yield* runtime.events.pipe(
7488 Stream.runForEach((event) => Queue.offer(events, event)),7490 Stream.runForEach((event) => Queue.offer(events, event)),
7489 Effect.forkScoped,7491 Effect.forkScoped,
...@@ -7701,7 +7703,7 @@ describe("AcpAdapterV2", () => {...@@ -7701,7 +7703,7 @@ describe("AcpAdapterV2", () => {
7701 selfInvocation,7703 selfInvocation,
7702 });7704 });
7703 const threadId = ThreadId.make("thread-acp-settled-soft-steer");7705 const threadId = ThreadId.make("thread-acp-settled-soft-steer");
7704 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({7706 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
7705 runtimeMode: "full-access",7707 runtimeMode: "full-access",
7706 interactionMode: "default",7708 interactionMode: "default",
7707 cwd: process.cwd(),7709 cwd: process.cwd(),
...@@ -7713,7 +7715,7 @@ describe("AcpAdapterV2", () => {...@@ -7713,7 +7715,7 @@ describe("AcpAdapterV2", () => {
7713 modelSelection,7715 modelSelection,
7714 runtimePolicy,7716 runtimePolicy,
7715 });7717 });
7716 const events = yield* Queue.unbounded<ProviderAdapterV2Event>();7718 const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>();
7717 yield* runtime.events.pipe(7719 yield* runtime.events.pipe(
7718 Stream.runForEach((event) => Queue.offer(events, event)),7720 Stream.runForEach((event) => Queue.offer(events, event)),
7719 Effect.forkScoped,7721 Effect.forkScoped,
...@@ -7834,7 +7836,7 @@ describe("AcpAdapterV2", () => {...@@ -7834,7 +7836,7 @@ describe("AcpAdapterV2", () => {
7834 selfInvocation,7836 selfInvocation,
7835 });7837 });
7836 const threadId = ThreadId.make("thread-acp-unsettled-steer-stays-hard");7838 const threadId = ThreadId.make("thread-acp-unsettled-steer-stays-hard");
7837 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({7839 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
7838 runtimeMode: "full-access",7840 runtimeMode: "full-access",
7839 interactionMode: "default",7841 interactionMode: "default",
7840 cwd: process.cwd(),7842 cwd: process.cwd(),
...@@ -7892,7 +7894,8 @@ describe("AcpAdapterV2", () => {...@@ -7892,7 +7894,8 @@ describe("AcpAdapterV2", () => {
7892 new URL("../../../scripts/acp-mock-agent.ts", import.meta.url),7894 new URL("../../../scripts/acp-mock-agent.ts", import.meta.url),
7893 );7895 );
7894 const protocolEvents = yield* Queue.bounded<EffectAcpProtocol.AcpProtocolLogEvent>(256);7896 const protocolEvents = yield* Queue.bounded<EffectAcpProtocol.AcpProtocolLogEvent>(256);
7895 const continuationRequests: Array<ProviderContinuationRequest> = [];7897 const continuationRequests: Array<ProviderContinuationRequests.ProviderContinuationRequest> =
7898 [];
7896 const instanceId = ProviderInstanceId.make("acp-test");7899 const instanceId = ProviderInstanceId.make("acp-test");
7897 let cancelCalled = false;7900 let cancelCalled = false;
7898 let runtimeOrdinalSeen = 0;7901 let runtimeOrdinalSeen = 0;
...@@ -7940,7 +7943,7 @@ describe("AcpAdapterV2", () => {...@@ -7940,7 +7943,7 @@ describe("AcpAdapterV2", () => {
7940 },7943 },
7941 });7944 });
7942 const threadId = ThreadId.make("thread-acp-soft-mid-prompt-steer");7945 const threadId = ThreadId.make("thread-acp-soft-mid-prompt-steer");
7943 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({7946 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
7944 runtimeMode: "full-access",7947 runtimeMode: "full-access",
7945 interactionMode: "default",7948 interactionMode: "default",
7946 cwd: process.cwd(),7949 cwd: process.cwd(),
...@@ -7958,7 +7961,7 @@ describe("AcpAdapterV2", () => {...@@ -7958,7 +7961,7 @@ describe("AcpAdapterV2", () => {
7958 );7961 );
7959 }7962 }
7960 const hasPendingBackgroundWork = runtime.hasPendingBackgroundWork;7963 const hasPendingBackgroundWork = runtime.hasPendingBackgroundWork;
7961 const events = yield* Queue.unbounded<ProviderAdapterV2Event>();7964 const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>();
7962 yield* runtime.events.pipe(7965 yield* runtime.events.pipe(
7963 Stream.runForEach((event) => Queue.offer(events, event)),7966 Stream.runForEach((event) => Queue.offer(events, event)),
7964 Effect.forkScoped,7967 Effect.forkScoped,
...@@ -8115,7 +8118,7 @@ describe("AcpAdapterV2", () => {...@@ -8115,7 +8118,7 @@ describe("AcpAdapterV2", () => {
8115 continuationRequests: { offer: () => Effect.void },8118 continuationRequests: { offer: () => Effect.void },
8116 });8119 });
8117 const threadId = ThreadId.make("thread-acp-stop-quarantine-late-task");8120 const threadId = ThreadId.make("thread-acp-stop-quarantine-late-task");
8118 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({8121 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
8119 runtimeMode: "full-access",8122 runtimeMode: "full-access",
8120 interactionMode: "default",8123 interactionMode: "default",
8121 cwd: process.cwd(),8124 cwd: process.cwd(),
...@@ -8133,7 +8136,7 @@ describe("AcpAdapterV2", () => {...@@ -8133,7 +8136,7 @@ describe("AcpAdapterV2", () => {
8133 );8136 );
8134 }8137 }
8135 const hasPendingBackgroundWork = runtime.hasPendingBackgroundWork;8138 const hasPendingBackgroundWork = runtime.hasPendingBackgroundWork;
8136 const events = yield* Queue.unbounded<ProviderAdapterV2Event>();8139 const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>();
8137 yield* runtime.events.pipe(8140 yield* runtime.events.pipe(
8138 Stream.runForEach((event) => Queue.offer(events, event)),8141 Stream.runForEach((event) => Queue.offer(events, event)),
8139 Effect.forkScoped,8142 Effect.forkScoped,
...@@ -8255,7 +8258,7 @@ describe("AcpAdapterV2", () => {...@@ -8255,7 +8258,7 @@ describe("AcpAdapterV2", () => {
8255 continuationRequests: { offer: () => Effect.void },8258 continuationRequests: { offer: () => Effect.void },
8256 });8259 });
8257 const threadId = ThreadId.make("thread-acp-production-stop-hard-kill");8260 const threadId = ThreadId.make("thread-acp-production-stop-hard-kill");
8258 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({8261 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
8259 runtimeMode: "full-access",8262 runtimeMode: "full-access",
8260 interactionMode: "default",8263 interactionMode: "default",
8261 cwd: process.cwd(),8264 cwd: process.cwd(),
...@@ -8267,7 +8270,7 @@ describe("AcpAdapterV2", () => {...@@ -8267,7 +8270,7 @@ describe("AcpAdapterV2", () => {
8267 modelSelection,8270 modelSelection,
8268 runtimePolicy,8271 runtimePolicy,
8269 });8272 });
8270 const events = yield* Queue.unbounded<ProviderAdapterV2Event>();8273 const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>();
8271 yield* runtime.events.pipe(8274 yield* runtime.events.pipe(
8272 Stream.runForEach((event) => Queue.offer(events, event)),8275 Stream.runForEach((event) => Queue.offer(events, event)),
8273 Effect.forkScoped,8276 Effect.forkScoped,
...@@ -8417,7 +8420,7 @@ describe("AcpAdapterV2", () => {...@@ -8417,7 +8420,7 @@ describe("AcpAdapterV2", () => {
8417 selfInvocation,8420 selfInvocation,
8418 });8421 });
8419 const threadId = ThreadId.make("thread-acp-stop-after-soft-steer-orphan");8422 const threadId = ThreadId.make("thread-acp-stop-after-soft-steer-orphan");
8420 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({8423 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
8421 runtimeMode: "full-access",8424 runtimeMode: "full-access",
8422 interactionMode: "default",8425 interactionMode: "default",
8423 cwd: process.cwd(),8426 cwd: process.cwd(),
...@@ -8431,7 +8434,7 @@ describe("AcpAdapterV2", () => {...@@ -8431,7 +8434,7 @@ describe("AcpAdapterV2", () => {
8431 modelSelection,8434 modelSelection,
8432 runtimePolicy,8435 runtimePolicy,
8433 });8436 });
8434 const events = yield* Queue.unbounded<ProviderAdapterV2Event>();8437 const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>();
8435 yield* runtime.events.pipe(8438 yield* runtime.events.pipe(
8436 Stream.runForEach((event) => Queue.offer(events, event)),8439 Stream.runForEach((event) => Queue.offer(events, event)),
8437 Effect.forkScoped,8440 Effect.forkScoped,
...@@ -8700,7 +8703,7 @@ describe("AcpAdapterV2", () => {...@@ -8700,7 +8703,7 @@ describe("AcpAdapterV2", () => {
8700 continuationRequests: { offer: () => Effect.void },8703 continuationRequests: { offer: () => Effect.void },
8701 });8704 });
8702 const threadId = ThreadId.make("thread-acp-direct-stop-deferred-terminal");8705 const threadId = ThreadId.make("thread-acp-direct-stop-deferred-terminal");
8703 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({8706 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
8704 runtimeMode: "full-access",8707 runtimeMode: "full-access",
8705 interactionMode: "default",8708 interactionMode: "default",
8706 cwd: process.cwd(),8709 cwd: process.cwd(),
...@@ -8720,7 +8723,7 @@ describe("AcpAdapterV2", () => {...@@ -8720,7 +8723,7 @@ describe("AcpAdapterV2", () => {
8720 );8723 );
8721 }8724 }
8722 const hasPendingBackgroundWork = runtime.hasPendingBackgroundWork;8725 const hasPendingBackgroundWork = runtime.hasPendingBackgroundWork;
8723 const events = yield* Queue.unbounded<ProviderAdapterV2Event>();8726 const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>();
8724 yield* runtime.events.pipe(8727 yield* runtime.events.pipe(
8725 Stream.runForEach((event) => Queue.offer(events, event)),8728 Stream.runForEach((event) => Queue.offer(events, event)),
8726 Effect.forkScoped,8729 Effect.forkScoped,
...@@ -8904,7 +8907,7 @@ describe("AcpAdapterV2", () => {...@@ -8904,7 +8907,7 @@ describe("AcpAdapterV2", () => {
8904 selfInvocation,8907 selfInvocation,
8905 });8908 });
8906 const threadId = ThreadId.make("thread-acp-settled-soft-admission-race");8909 const threadId = ThreadId.make("thread-acp-settled-soft-admission-race");
8907 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({8910 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
8908 runtimeMode: "full-access",8911 runtimeMode: "full-access",
8909 interactionMode: "default",8912 interactionMode: "default",
8910 cwd: process.cwd(),8913 cwd: process.cwd(),
...@@ -8918,7 +8921,7 @@ describe("AcpAdapterV2", () => {...@@ -8918,7 +8921,7 @@ describe("AcpAdapterV2", () => {
8918 modelSelection,8921 modelSelection,
8919 runtimePolicy,8922 runtimePolicy,
8920 });8923 });
8921 const events = yield* Queue.unbounded<ProviderAdapterV2Event>();8924 const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>();
8922 yield* runtime.events.pipe(8925 yield* runtime.events.pipe(
8923 Stream.runForEach((event) => Queue.offer(events, event)),8926 Stream.runForEach((event) => Queue.offer(events, event)),
8924 Effect.forkScoped,8927 Effect.forkScoped,
...@@ -8999,7 +9002,8 @@ describe("AcpAdapterV2", () => {...@@ -8999,7 +9002,8 @@ describe("AcpAdapterV2", () => {
8999 new URL("../../../scripts/acp-mock-agent.ts", import.meta.url),9002 new URL("../../../scripts/acp-mock-agent.ts", import.meta.url),
9000 );9003 );
9001 const protocolEvents = yield* Queue.bounded<EffectAcpProtocol.AcpProtocolLogEvent>(256);9004 const protocolEvents = yield* Queue.bounded<EffectAcpProtocol.AcpProtocolLogEvent>(256);
9002 const continuationRequests: Array<ProviderContinuationRequest> = [];9005 const continuationRequests: Array<ProviderContinuationRequests.ProviderContinuationRequest> =
9006 [];
9003 const instanceId = ProviderInstanceId.make("acp-test");9007 const instanceId = ProviderInstanceId.make("acp-test");
9004 const adapter = yield* makeAcpAdapterV2({9008 const adapter = yield* makeAcpAdapterV2({
9005 instanceId,9009 instanceId,
...@@ -9037,7 +9041,7 @@ describe("AcpAdapterV2", () => {...@@ -9037,7 +9041,7 @@ describe("AcpAdapterV2", () => {
9037 },9041 },
9038 });9042 });
9039 const threadId = ThreadId.make("thread-acp-already-handled-wake-pin");9043 const threadId = ThreadId.make("thread-acp-already-handled-wake-pin");
9040 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({9044 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
9041 runtimeMode: "full-access",9045 runtimeMode: "full-access",
9042 interactionMode: "default",9046 interactionMode: "default",
9043 cwd: process.cwd(),9047 cwd: process.cwd(),
...@@ -9057,7 +9061,7 @@ describe("AcpAdapterV2", () => {...@@ -9057,7 +9061,7 @@ describe("AcpAdapterV2", () => {
9057 );9061 );
9058 }9062 }
9059 const hasPendingBackgroundWork = runtime.hasPendingBackgroundWork;9063 const hasPendingBackgroundWork = runtime.hasPendingBackgroundWork;
9060 const events = yield* Queue.unbounded<ProviderAdapterV2Event>();9064 const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>();
9061 yield* runtime.events.pipe(9065 yield* runtime.events.pipe(
9062 Stream.runForEach((event) => Queue.offer(events, event)),9066 Stream.runForEach((event) => Queue.offer(events, event)),
9063 Effect.forkScoped,9067 Effect.forkScoped,
...@@ -9123,7 +9127,8 @@ describe("AcpAdapterV2", () => {...@@ -9123,7 +9127,8 @@ describe("AcpAdapterV2", () => {
9123 const mockAgentPath = yield* path.fromFileUrl(9127 const mockAgentPath = yield* path.fromFileUrl(
9124 new URL("../../../scripts/acp-mock-agent.ts", import.meta.url),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 type RuntimeService = AcpSessionRuntime.AcpSessionRuntime["Service"];9132 type RuntimeService = AcpSessionRuntime.AcpSessionRuntime["Service"];
9128 let sessionUpdateHandler: Parameters<RuntimeService["handleSessionUpdate"]>[0] | undefined;9133 let sessionUpdateHandler: Parameters<RuntimeService["handleSessionUpdate"]>[0] | undefined;
9129 const instanceId = ProviderInstanceId.make("acp-test");9134 const instanceId = ProviderInstanceId.make("acp-test");
...@@ -9154,7 +9159,7 @@ describe("AcpAdapterV2", () => {...@@ -9154,7 +9159,7 @@ describe("AcpAdapterV2", () => {
9154 },9159 },
9155 });9160 });
9156 const threadId = ThreadId.make("thread-acp-sticky-continuation-dispatch");9161 const threadId = ThreadId.make("thread-acp-sticky-continuation-dispatch");
9157 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({9162 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
9158 runtimeMode: "full-access",9163 runtimeMode: "full-access",
9159 interactionMode: "default",9164 interactionMode: "default",
9160 cwd: process.cwd(),9165 cwd: process.cwd(),
...@@ -9168,7 +9173,7 @@ describe("AcpAdapterV2", () => {...@@ -9168,7 +9173,7 @@ describe("AcpAdapterV2", () => {
9168 modelSelection,9173 modelSelection,
9169 runtimePolicy,9174 runtimePolicy,
9170 });9175 });
9171 const events = yield* Queue.unbounded<ProviderAdapterV2Event>();9176 const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>();
9172 yield* runtime.events.pipe(9177 yield* runtime.events.pipe(
9173 Stream.runForEach((event) => Queue.offer(events, event)),9178 Stream.runForEach((event) => Queue.offer(events, event)),
9174 Effect.forkScoped,9179 Effect.forkScoped,
...@@ -9237,7 +9242,8 @@ describe("AcpAdapterV2", () => {...@@ -9237,7 +9242,8 @@ describe("AcpAdapterV2", () => {
9237 const mockAgentPath = yield* path.fromFileUrl(9242 const mockAgentPath = yield* path.fromFileUrl(
9238 new URL("../../../scripts/acp-mock-agent.ts", import.meta.url),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 const userPromptStarted = yield* Deferred.make<void>();9247 const userPromptStarted = yield* Deferred.make<void>();
9242 const releaseUserPrompt = yield* Deferred.make<void>();9248 const releaseUserPrompt = yield* Deferred.make<void>();
9243 const bufferedAssistantText = "BUFFERED_WAKE_QUEUED_AFTER_USER";9249 const bufferedAssistantText = "BUFFERED_WAKE_QUEUED_AFTER_USER";
...@@ -9284,7 +9290,7 @@ describe("AcpAdapterV2", () => {...@@ -9284,7 +9290,7 @@ describe("AcpAdapterV2", () => {
9284 },9290 },
9285 });9291 });
9286 const threadId = ThreadId.make("thread-acp-buffered-continuation-user-race");9292 const threadId = ThreadId.make("thread-acp-buffered-continuation-user-race");
9287 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({9293 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
9288 runtimeMode: "full-access",9294 runtimeMode: "full-access",
9289 interactionMode: "default",9295 interactionMode: "default",
9290 cwd: process.cwd(),9296 cwd: process.cwd(),
...@@ -9304,7 +9310,7 @@ describe("AcpAdapterV2", () => {...@@ -9304,7 +9310,7 @@ describe("AcpAdapterV2", () => {
9304 );9310 );
9305 }9311 }
9306 const hasPendingBackgroundWork = runtime.hasPendingBackgroundWork;9312 const hasPendingBackgroundWork = runtime.hasPendingBackgroundWork;
9307 const events = yield* Queue.unbounded<ProviderAdapterV2Event>();9313 const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>();
9308 yield* runtime.events.pipe(9314 yield* runtime.events.pipe(
9309 Stream.runForEach((event) => Queue.offer(events, event)),9315 Stream.runForEach((event) => Queue.offer(events, event)),
9310 Effect.forkScoped,9316 Effect.forkScoped,
...@@ -9480,7 +9486,7 @@ describe("AcpAdapterV2", () => {...@@ -9480,7 +9486,7 @@ describe("AcpAdapterV2", () => {
9480 selfInvocation,9486 selfInvocation,
9481 });9487 });
9482 const threadId = ThreadId.make("thread-acp-injected-report-hold");9488 const threadId = ThreadId.make("thread-acp-injected-report-hold");
9483 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({9489 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
9484 runtimeMode: "full-access",9490 runtimeMode: "full-access",
9485 interactionMode: "default",9491 interactionMode: "default",
9486 cwd: process.cwd(),9492 cwd: process.cwd(),
...@@ -9492,7 +9498,7 @@ describe("AcpAdapterV2", () => {...@@ -9492,7 +9498,7 @@ describe("AcpAdapterV2", () => {
9492 modelSelection,9498 modelSelection,
9493 runtimePolicy,9499 runtimePolicy,
9494 });9500 });
9495 const events = yield* Queue.unbounded<ProviderAdapterV2Event>();9501 const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>();
9496 yield* runtime.events.pipe(9502 yield* runtime.events.pipe(
9497 Stream.runForEach((event) => Queue.offer(events, event)),9503 Stream.runForEach((event) => Queue.offer(events, event)),
9498 Effect.forkScoped,9504 Effect.forkScoped,
...@@ -9515,7 +9521,7 @@ describe("AcpAdapterV2", () => {...@@ -9515,7 +9521,7 @@ describe("AcpAdapterV2", () => {
9515 // hydration are ingested: the hydrated monitor card carries the9521 // hydration are ingested: the hydrated monitor card carries the
9516 // fetched listing.9522 // fetched listing.
9517 let reportSeen = false;9523 let reportSeen = false;
9518 const trackReport = (event: ProviderAdapterV2Event): void => {9524 const trackReport = (event: ProviderAdapter.ProviderAdapterV2Event): void => {
9519 if (9525 if (
9520 event.type === "turn_item.updated" &&9526 event.type === "turn_item.updated" &&
9521 event.turnItem.type === "assistant_message" &&9527 event.turnItem.type === "assistant_message" &&
...@@ -9605,7 +9611,8 @@ describe("AcpAdapterV2", () => {...@@ -9605,7 +9611,8 @@ describe("AcpAdapterV2", () => {
9605 const triggerDir = yield* fileSystem.makeTempDirectoryScoped();9611 const triggerDir = yield* fileSystem.makeTempDirectoryScoped();
9606 const triggerPath = path.join(triggerDir, "report-trigger");9612 const triggerPath = path.join(triggerDir, "report-trigger");
9607 const protocolEvents = yield* Queue.bounded<EffectAcpProtocol.AcpProtocolLogEvent>(256);9613 const protocolEvents = yield* Queue.bounded<EffectAcpProtocol.AcpProtocolLogEvent>(256);
9608 const continuationRequests: Array<ProviderContinuationRequest> = [];9614 const continuationRequests: Array<ProviderContinuationRequests.ProviderContinuationRequest> =
9615 [];
9609 const capturedMutation: {9616 const capturedMutation: {
9610 current:9617 current:
9611 | ((mutation: {9618 | ((mutation: {
...@@ -9662,7 +9669,7 @@ describe("AcpAdapterV2", () => {...@@ -9662,7 +9669,7 @@ describe("AcpAdapterV2", () => {
9662 },9669 },
9663 });9670 });
9664 const threadId = ThreadId.make("thread-acp-settle-hold-ext-complete-no-wake");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 runtimeMode: "full-access",9673 runtimeMode: "full-access",
9667 interactionMode: "default",9674 interactionMode: "default",
9668 cwd: process.cwd(),9675 cwd: process.cwd(),
...@@ -9676,7 +9683,7 @@ describe("AcpAdapterV2", () => {...@@ -9676,7 +9683,7 @@ describe("AcpAdapterV2", () => {
9676 modelSelection,9683 modelSelection,
9677 runtimePolicy,9684 runtimePolicy,
9678 });9685 });
9679 const events = yield* Queue.unbounded<ProviderAdapterV2Event>();9686 const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>();
9680 yield* runtime.events.pipe(9687 yield* runtime.events.pipe(
9681 Stream.runForEach((event) => Queue.offer(events, event)),9688 Stream.runForEach((event) => Queue.offer(events, event)),
9682 Effect.forkScoped,9689 Effect.forkScoped,
...@@ -9696,7 +9703,7 @@ describe("AcpAdapterV2", () => {...@@ -9696,7 +9703,7 @@ describe("AcpAdapterV2", () => {
9696 });9703 });
96979704
9698 let reportSeen = false;9705 let reportSeen = false;
9699 const trackReport = (event: ProviderAdapterV2Event): void => {9706 const trackReport = (event: ProviderAdapter.ProviderAdapterV2Event): void => {
9700 if (9707 if (
9701 event.type === "turn_item.updated" &&9708 event.type === "turn_item.updated" &&
9702 event.turnItem.type === "assistant_message" &&9709 event.turnItem.type === "assistant_message" &&
...@@ -9799,7 +9806,8 @@ describe("AcpAdapterV2", () => {...@@ -9799,7 +9806,8 @@ describe("AcpAdapterV2", () => {
9799 new URL("../../../scripts/acp-mock-agent.ts", import.meta.url),9806 new URL("../../../scripts/acp-mock-agent.ts", import.meta.url),
9800 );9807 );
9801 const protocolEvents = yield* Queue.bounded<EffectAcpProtocol.AcpProtocolLogEvent>(256);9808 const protocolEvents = yield* Queue.bounded<EffectAcpProtocol.AcpProtocolLogEvent>(256);
9802 const continuationRequests: Array<ProviderContinuationRequest> = [];9809 const continuationRequests: Array<ProviderContinuationRequests.ProviderContinuationRequest> =
9810 [];
9803 type RuntimeService = AcpSessionRuntime.AcpSessionRuntime["Service"];9811 type RuntimeService = AcpSessionRuntime.AcpSessionRuntime["Service"];
9804 let sessionUpdateHandler: Parameters<RuntimeService["handleSessionUpdate"]>[0] | undefined;9812 let sessionUpdateHandler: Parameters<RuntimeService["handleSessionUpdate"]>[0] | undefined;
9805 const promptGate = yield* Deferred.make<EffectAcpSchema.PromptResponse>();9813 const promptGate = yield* Deferred.make<EffectAcpSchema.PromptResponse>();
...@@ -9854,7 +9862,7 @@ describe("AcpAdapterV2", () => {...@@ -9854,7 +9862,7 @@ describe("AcpAdapterV2", () => {
9854 },9862 },
9855 });9863 });
9856 const threadId = ThreadId.make("thread-acp-pre-settle-arm-cleared-by-report");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 runtimeMode: "full-access",9866 runtimeMode: "full-access",
9859 interactionMode: "default",9867 interactionMode: "default",
9860 cwd: process.cwd(),9868 cwd: process.cwd(),
...@@ -9868,7 +9876,7 @@ describe("AcpAdapterV2", () => {...@@ -9868,7 +9876,7 @@ describe("AcpAdapterV2", () => {
9868 modelSelection,9876 modelSelection,
9869 runtimePolicy,9877 runtimePolicy,
9870 });9878 });
9871 const events = yield* Queue.unbounded<ProviderAdapterV2Event>();9879 const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>();
9872 yield* runtime.events.pipe(9880 yield* runtime.events.pipe(
9873 Stream.runForEach((event) => Queue.offer(events, event)),9881 Stream.runForEach((event) => Queue.offer(events, event)),
9874 Effect.forkScoped,9882 Effect.forkScoped,
...@@ -9965,7 +9973,7 @@ describe("AcpAdapterV2", () => {...@@ -9965,7 +9973,7 @@ describe("AcpAdapterV2", () => {
9965 yield* Effect.yieldNow;9973 yield* Effect.yieldNow;
99669974
9967 let reportSeen = false;9975 let reportSeen = false;
9968 const trackReport = (event: ProviderAdapterV2Event): void => {9976 const trackReport = (event: ProviderAdapter.ProviderAdapterV2Event): void => {
9969 if (9977 if (
9970 event.type === "turn_item.updated" &&9978 event.type === "turn_item.updated" &&
9971 event.turnItem.type === "assistant_message" &&9979 event.turnItem.type === "assistant_message" &&
...@@ -10033,7 +10041,8 @@ describe("AcpAdapterV2", () => {...@@ -10033,7 +10041,8 @@ describe("AcpAdapterV2", () => {
10033 new URL("../../../scripts/acp-mock-agent.ts", import.meta.url),10041 new URL("../../../scripts/acp-mock-agent.ts", import.meta.url),
10034 );10042 );
10035 const protocolEvents = yield* Queue.bounded<EffectAcpProtocol.AcpProtocolLogEvent>(256);10043 const protocolEvents = yield* Queue.bounded<EffectAcpProtocol.AcpProtocolLogEvent>(256);
10036 const continuationRequests: Array<ProviderContinuationRequest> = [];10044 const continuationRequests: Array<ProviderContinuationRequests.ProviderContinuationRequest> =
10045 [];
10037 type RuntimeService = AcpSessionRuntime.AcpSessionRuntime["Service"];10046 type RuntimeService = AcpSessionRuntime.AcpSessionRuntime["Service"];
10038 let sessionUpdateHandler: Parameters<RuntimeService["handleSessionUpdate"]>[0] | undefined;10047 let sessionUpdateHandler: Parameters<RuntimeService["handleSessionUpdate"]>[0] | undefined;
10039 const promptGate = yield* Deferred.make<EffectAcpSchema.PromptResponse>();10048 const promptGate = yield* Deferred.make<EffectAcpSchema.PromptResponse>();
...@@ -10095,7 +10104,7 @@ describe("AcpAdapterV2", () => {...@@ -10095,7 +10104,7 @@ describe("AcpAdapterV2", () => {
10095 },10104 },
10096 });10105 });
10097 const threadId = ThreadId.make("thread-acp-staggered-midturn-keep-until-last");10106 const threadId = ThreadId.make("thread-acp-staggered-midturn-keep-until-last");
10098 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({10107 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
10099 runtimeMode: "full-access",10108 runtimeMode: "full-access",
10100 interactionMode: "default",10109 interactionMode: "default",
10101 cwd: process.cwd(),10110 cwd: process.cwd(),
...@@ -10109,7 +10118,7 @@ describe("AcpAdapterV2", () => {...@@ -10109,7 +10118,7 @@ describe("AcpAdapterV2", () => {
10109 modelSelection,10118 modelSelection,
10110 runtimePolicy,10119 runtimePolicy,
10111 });10120 });
10112 const events = yield* Queue.unbounded<ProviderAdapterV2Event>();10121 const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>();
10113 yield* runtime.events.pipe(10122 yield* runtime.events.pipe(
10114 Stream.runForEach((event) => Queue.offer(events, event)),10123 Stream.runForEach((event) => Queue.offer(events, event)),
10115 Effect.forkScoped,10124 Effect.forkScoped,
...@@ -10246,7 +10255,8 @@ describe("AcpAdapterV2", () => {...@@ -10246,7 +10255,8 @@ describe("AcpAdapterV2", () => {
10246 new URL("../../../scripts/acp-mock-agent.ts", import.meta.url),10255 new URL("../../../scripts/acp-mock-agent.ts", import.meta.url),
10247 );10256 );
10248 const protocolEvents = yield* Queue.bounded<EffectAcpProtocol.AcpProtocolLogEvent>(256);10257 const protocolEvents = yield* Queue.bounded<EffectAcpProtocol.AcpProtocolLogEvent>(256);
10249 const continuationRequests: Array<ProviderContinuationRequest> = [];10258 const continuationRequests: Array<ProviderContinuationRequests.ProviderContinuationRequest> =
10259 [];
10250 type RuntimeService = AcpSessionRuntime.AcpSessionRuntime["Service"];10260 type RuntimeService = AcpSessionRuntime.AcpSessionRuntime["Service"];
10251 let sessionUpdateHandler: Parameters<RuntimeService["handleSessionUpdate"]>[0] | undefined;10261 let sessionUpdateHandler: Parameters<RuntimeService["handleSessionUpdate"]>[0] | undefined;
10252 const promptGate = yield* Deferred.make<EffectAcpSchema.PromptResponse>();10262 const promptGate = yield* Deferred.make<EffectAcpSchema.PromptResponse>();
...@@ -10312,7 +10322,7 @@ describe("AcpAdapterV2", () => {...@@ -10312,7 +10322,7 @@ describe("AcpAdapterV2", () => {
10312 },10322 },
10313 });10323 });
10314 const threadId = ThreadId.make("thread-acp-staggered-midturn-clear-on-interrupt");10324 const threadId = ThreadId.make("thread-acp-staggered-midturn-clear-on-interrupt");
10315 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({10325 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
10316 runtimeMode: "full-access",10326 runtimeMode: "full-access",
10317 interactionMode: "default",10327 interactionMode: "default",
10318 cwd: process.cwd(),10328 cwd: process.cwd(),
...@@ -10326,7 +10336,7 @@ describe("AcpAdapterV2", () => {...@@ -10326,7 +10336,7 @@ describe("AcpAdapterV2", () => {
10326 modelSelection,10336 modelSelection,
10327 runtimePolicy,10337 runtimePolicy,
10328 });10338 });
10329 const events = yield* Queue.unbounded<ProviderAdapterV2Event>();10339 const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>();
10330 yield* runtime.events.pipe(10340 yield* runtime.events.pipe(
10331 Stream.runForEach((event) => Queue.offer(events, event)),10341 Stream.runForEach((event) => Queue.offer(events, event)),
10332 Effect.forkScoped,10342 Effect.forkScoped,
...@@ -10455,7 +10465,8 @@ describe("AcpAdapterV2", () => {...@@ -10455,7 +10465,8 @@ describe("AcpAdapterV2", () => {
10455 new URL("../../../scripts/acp-mock-agent.ts", import.meta.url),10465 new URL("../../../scripts/acp-mock-agent.ts", import.meta.url),
10456 );10466 );
10457 const protocolEvents = yield* Queue.bounded<EffectAcpProtocol.AcpProtocolLogEvent>(256);10467 const protocolEvents = yield* Queue.bounded<EffectAcpProtocol.AcpProtocolLogEvent>(256);
10458 const continuationRequests: Array<ProviderContinuationRequest> = [];10468 const continuationRequests: Array<ProviderContinuationRequests.ProviderContinuationRequest> =
10469 [];
10459 type RuntimeService = AcpSessionRuntime.AcpSessionRuntime["Service"];10470 type RuntimeService = AcpSessionRuntime.AcpSessionRuntime["Service"];
10460 let sessionUpdateHandler: Parameters<RuntimeService["handleSessionUpdate"]>[0] | undefined;10471 let sessionUpdateHandler: Parameters<RuntimeService["handleSessionUpdate"]>[0] | undefined;
10461 const promptGates: Array<Deferred.Deferred<EffectAcpSchema.PromptResponse, never>> = [];10472 const promptGates: Array<Deferred.Deferred<EffectAcpSchema.PromptResponse, never>> = [];
...@@ -10543,7 +10554,7 @@ describe("AcpAdapterV2", () => {...@@ -10543,7 +10554,7 @@ describe("AcpAdapterV2", () => {
10543 },10554 },
10544 });10555 });
10545 const threadId = ThreadId.make("thread-acp-multiturn-in-turn-monitor-no-wake");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 runtimeMode: "full-access",10558 runtimeMode: "full-access",
10548 interactionMode: "default",10559 interactionMode: "default",
10549 cwd: process.cwd(),10560 cwd: process.cwd(),
...@@ -10563,7 +10574,7 @@ describe("AcpAdapterV2", () => {...@@ -10563,7 +10574,7 @@ describe("AcpAdapterV2", () => {
10563 );10574 );
10564 }10575 }
10565 const hasPendingBackgroundWork = runtime.hasPendingBackgroundWork;10576 const hasPendingBackgroundWork = runtime.hasPendingBackgroundWork;
10566 const events = yield* Queue.unbounded<ProviderAdapterV2Event>();10577 const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>();
10567 yield* runtime.events.pipe(10578 yield* runtime.events.pipe(
10568 Stream.runForEach((event) => Queue.offer(events, event)),10579 Stream.runForEach((event) => Queue.offer(events, event)),
10569 Effect.forkScoped,10580 Effect.forkScoped,
...@@ -10820,7 +10831,8 @@ describe("AcpAdapterV2", () => {...@@ -10820,7 +10831,8 @@ describe("AcpAdapterV2", () => {
10820 const mockAgentPath = yield* path.fromFileUrl(10831 const mockAgentPath = yield* path.fromFileUrl(
10821 new URL("../../../scripts/acp-mock-agent.ts", import.meta.url),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 type RuntimeService = AcpSessionRuntime.AcpSessionRuntime["Service"];10836 type RuntimeService = AcpSessionRuntime.AcpSessionRuntime["Service"];
10825 let sessionUpdateHandler: Parameters<RuntimeService["handleSessionUpdate"]>[0] | undefined;10837 let sessionUpdateHandler: Parameters<RuntimeService["handleSessionUpdate"]>[0] | undefined;
10826 let applyMutation: AcpAdapterV2ExtensionContext["applyBackgroundTaskMutation"] | undefined;10838 let applyMutation: AcpAdapterV2ExtensionContext["applyBackgroundTaskMutation"] | undefined;
...@@ -10860,7 +10872,7 @@ describe("AcpAdapterV2", () => {...@@ -10860,7 +10872,7 @@ describe("AcpAdapterV2", () => {
10860 },10872 },
10861 });10873 });
10862 const threadId = ThreadId.make("thread-acp-wake-report-while-queued");10874 const threadId = ThreadId.make("thread-acp-wake-report-while-queued");
10863 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({10875 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
10864 runtimeMode: "full-access",10876 runtimeMode: "full-access",
10865 interactionMode: "default",10877 interactionMode: "default",
10866 cwd: process.cwd(),10878 cwd: process.cwd(),
...@@ -10872,7 +10884,7 @@ describe("AcpAdapterV2", () => {...@@ -10872,7 +10884,7 @@ describe("AcpAdapterV2", () => {
10872 modelSelection,10884 modelSelection,
10873 runtimePolicy,10885 runtimePolicy,
10874 });10886 });
10875 const events = yield* Queue.unbounded<ProviderAdapterV2Event>();10887 const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>();
10876 yield* runtime.events.pipe(10888 yield* runtime.events.pipe(
10877 Stream.runForEach((event) => Queue.offer(events, event)),10889 Stream.runForEach((event) => Queue.offer(events, event)),
10878 Effect.forkScoped,10890 Effect.forkScoped,
...@@ -10969,7 +10981,8 @@ describe("AcpAdapterV2", () => {...@@ -10969,7 +10981,8 @@ describe("AcpAdapterV2", () => {
10969 new URL("../../../scripts/acp-mock-agent.ts", import.meta.url),10981 new URL("../../../scripts/acp-mock-agent.ts", import.meta.url),
10970 );10982 );
10971 const protocolEvents = yield* Queue.bounded<EffectAcpProtocol.AcpProtocolLogEvent>(256);10983 const protocolEvents = yield* Queue.bounded<EffectAcpProtocol.AcpProtocolLogEvent>(256);
10972 const continuationRequests: Array<ProviderContinuationRequest> = [];10984 const continuationRequests: Array<ProviderContinuationRequests.ProviderContinuationRequest> =
10985 [];
10973 type RuntimeService = AcpSessionRuntime.AcpSessionRuntime["Service"];10986 type RuntimeService = AcpSessionRuntime.AcpSessionRuntime["Service"];
10974 let sessionUpdateHandler: Parameters<RuntimeService["handleSessionUpdate"]>[0] | undefined;10987 let sessionUpdateHandler: Parameters<RuntimeService["handleSessionUpdate"]>[0] | undefined;
10975 const promptGates: Array<Deferred.Deferred<EffectAcpSchema.PromptResponse, never>> = [];10988 const promptGates: Array<Deferred.Deferred<EffectAcpSchema.PromptResponse, never>> = [];
...@@ -11038,7 +11051,7 @@ describe("AcpAdapterV2", () => {...@@ -11038,7 +11051,7 @@ describe("AcpAdapterV2", () => {
11038 },11051 },
11039 });11052 });
11040 const threadId = ThreadId.make("thread-acp-mid-turn-dirty-wake-buffer");11053 const threadId = ThreadId.make("thread-acp-mid-turn-dirty-wake-buffer");
11041 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({11054 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
11042 runtimeMode: "full-access",11055 runtimeMode: "full-access",
11043 interactionMode: "default",11056 interactionMode: "default",
11044 cwd: process.cwd(),11057 cwd: process.cwd(),
...@@ -11058,7 +11071,7 @@ describe("AcpAdapterV2", () => {...@@ -11058,7 +11071,7 @@ describe("AcpAdapterV2", () => {
11058 );11071 );
11059 }11072 }
11060 const hasPendingBackgroundWork = runtime.hasPendingBackgroundWork;11073 const hasPendingBackgroundWork = runtime.hasPendingBackgroundWork;
11061 const events = yield* Queue.unbounded<ProviderAdapterV2Event>();11074 const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>();
11062 yield* runtime.events.pipe(11075 yield* runtime.events.pipe(
11063 Stream.runForEach((event) => Queue.offer(events, event)),11076 Stream.runForEach((event) => Queue.offer(events, event)),
11064 Effect.forkScoped,11077 Effect.forkScoped,
...@@ -11294,7 +11307,8 @@ describe("AcpAdapterV2", () => {...@@ -11294,7 +11307,8 @@ describe("AcpAdapterV2", () => {
11294 new URL("../../../scripts/acp-mock-agent.ts", import.meta.url),11307 new URL("../../../scripts/acp-mock-agent.ts", import.meta.url),
11295 );11308 );
11296 const protocolEvents = yield* Queue.bounded<EffectAcpProtocol.AcpProtocolLogEvent>(256);11309 const protocolEvents = yield* Queue.bounded<EffectAcpProtocol.AcpProtocolLogEvent>(256);
11297 const continuationRequests: Array<ProviderContinuationRequest> = [];11310 const continuationRequests: Array<ProviderContinuationRequests.ProviderContinuationRequest> =
11311 [];
11298 type RuntimeService = AcpSessionRuntime.AcpSessionRuntime["Service"];11312 type RuntimeService = AcpSessionRuntime.AcpSessionRuntime["Service"];
11299 let sessionUpdateHandler: Parameters<RuntimeService["handleSessionUpdate"]>[0] | undefined;11313 let sessionUpdateHandler: Parameters<RuntimeService["handleSessionUpdate"]>[0] | undefined;
11300 const promptGate = yield* Deferred.make<EffectAcpSchema.PromptResponse>();11314 const promptGate = yield* Deferred.make<EffectAcpSchema.PromptResponse>();
...@@ -11344,7 +11358,7 @@ describe("AcpAdapterV2", () => {...@@ -11344,7 +11358,7 @@ describe("AcpAdapterV2", () => {
11344 },11358 },
11345 });11359 });
11346 const threadId = ThreadId.make("thread-acp-mid-turn-unhandled-offers-after-finalize");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 runtimeMode: "full-access",11362 runtimeMode: "full-access",
11349 interactionMode: "default",11363 interactionMode: "default",
11350 cwd: process.cwd(),11364 cwd: process.cwd(),
...@@ -11358,7 +11372,7 @@ describe("AcpAdapterV2", () => {...@@ -11358,7 +11372,7 @@ describe("AcpAdapterV2", () => {
11358 modelSelection,11372 modelSelection,
11359 runtimePolicy,11373 runtimePolicy,
11360 });11374 });
11361 const events = yield* Queue.unbounded<ProviderAdapterV2Event>();11375 const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>();
11362 yield* runtime.events.pipe(11376 yield* runtime.events.pipe(
11363 Stream.runForEach((event) => Queue.offer(events, event)),11377 Stream.runForEach((event) => Queue.offer(events, event)),
11364 Effect.forkScoped,11378 Effect.forkScoped,
...@@ -11446,7 +11460,7 @@ describe("AcpAdapterV2", () => {...@@ -11446,7 +11460,7 @@ describe("AcpAdapterV2", () => {
11446 content: { type: "text", text: " \n\t " },11460 content: { type: "text", text: " \n\t " },
11447 },11461 },
11448 });11462 });
11449 const lateEvents: Array<ProviderAdapterV2Event> = [];11463 const lateEvents: Array<ProviderAdapter.ProviderAdapterV2Event> = [];
11450 let lateEvent = yield* Queue.poll(events);11464 let lateEvent = yield* Queue.poll(events);
11451 while (Option.isSome(lateEvent)) {11465 while (Option.isSome(lateEvent)) {
11452 lateEvents.push(lateEvent.value);11466 lateEvents.push(lateEvent.value);
...@@ -11475,7 +11489,8 @@ describe("AcpAdapterV2", () => {...@@ -11475,7 +11489,8 @@ describe("AcpAdapterV2", () => {
11475 new URL("../../../scripts/acp-mock-agent.ts", import.meta.url),11489 new URL("../../../scripts/acp-mock-agent.ts", import.meta.url),
11476 );11490 );
11477 const protocolEvents = yield* Queue.bounded<EffectAcpProtocol.AcpProtocolLogEvent>(256);11491 const protocolEvents = yield* Queue.bounded<EffectAcpProtocol.AcpProtocolLogEvent>(256);
11478 const continuationRequests: Array<ProviderContinuationRequest> = [];11492 const continuationRequests: Array<ProviderContinuationRequests.ProviderContinuationRequest> =
11493 [];
11479 type RuntimeService = AcpSessionRuntime.AcpSessionRuntime["Service"];11494 type RuntimeService = AcpSessionRuntime.AcpSessionRuntime["Service"];
11480 let sessionUpdateHandler: Parameters<RuntimeService["handleSessionUpdate"]>[0] | undefined;11495 let sessionUpdateHandler: Parameters<RuntimeService["handleSessionUpdate"]>[0] | undefined;
11481 const promptGate = yield* Deferred.make<EffectAcpSchema.PromptResponse>();11496 const promptGate = yield* Deferred.make<EffectAcpSchema.PromptResponse>();
...@@ -11529,7 +11544,7 @@ describe("AcpAdapterV2", () => {...@@ -11529,7 +11544,7 @@ describe("AcpAdapterV2", () => {
11529 },11544 },
11530 });11545 });
11531 const threadId = ThreadId.make("thread-acp-empty-drain-continuation-quiet");11546 const threadId = ThreadId.make("thread-acp-empty-drain-continuation-quiet");
11532 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({11547 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
11533 runtimeMode: "full-access",11548 runtimeMode: "full-access",
11534 interactionMode: "default",11549 interactionMode: "default",
11535 cwd: process.cwd(),11550 cwd: process.cwd(),
...@@ -11543,7 +11558,7 @@ describe("AcpAdapterV2", () => {...@@ -11543,7 +11558,7 @@ describe("AcpAdapterV2", () => {
11543 modelSelection,11558 modelSelection,
11544 runtimePolicy,11559 runtimePolicy,
11545 });11560 });
11546 const events = yield* Queue.unbounded<ProviderAdapterV2Event>();11561 const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>();
11547 yield* runtime.events.pipe(11562 yield* runtime.events.pipe(
11548 Stream.runForEach((event) => Queue.offer(events, event)),11563 Stream.runForEach((event) => Queue.offer(events, event)),
11549 Effect.forkScoped,11564 Effect.forkScoped,
...@@ -11713,7 +11728,8 @@ describe("AcpAdapterV2", () => {...@@ -11713,7 +11728,8 @@ describe("AcpAdapterV2", () => {
11713 new URL("../../../scripts/acp-mock-agent.ts", import.meta.url),11728 new URL("../../../scripts/acp-mock-agent.ts", import.meta.url),
11714 );11729 );
11715 const protocolEvents = yield* Queue.bounded<EffectAcpProtocol.AcpProtocolLogEvent>(256);11730 const protocolEvents = yield* Queue.bounded<EffectAcpProtocol.AcpProtocolLogEvent>(256);
11716 const continuationRequests: Array<ProviderContinuationRequest> = [];11731 const continuationRequests: Array<ProviderContinuationRequests.ProviderContinuationRequest> =
11732 [];
11717 type RuntimeService = AcpSessionRuntime.AcpSessionRuntime["Service"];11733 type RuntimeService = AcpSessionRuntime.AcpSessionRuntime["Service"];
11718 let sessionUpdateHandler: Parameters<RuntimeService["handleSessionUpdate"]>[0] | undefined;11734 let sessionUpdateHandler: Parameters<RuntimeService["handleSessionUpdate"]>[0] | undefined;
11719 const promptGate = yield* Deferred.make<EffectAcpSchema.PromptResponse>();11735 const promptGate = yield* Deferred.make<EffectAcpSchema.PromptResponse>();
...@@ -11765,7 +11781,7 @@ describe("AcpAdapterV2", () => {...@@ -11765,7 +11781,7 @@ describe("AcpAdapterV2", () => {
11765 },11781 },
11766 });11782 });
11767 const threadId = ThreadId.make("thread-acp-empty-drain-continuation-no-wedge");11783 const threadId = ThreadId.make("thread-acp-empty-drain-continuation-no-wedge");
11768 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({11784 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
11769 runtimeMode: "full-access",11785 runtimeMode: "full-access",
11770 interactionMode: "default",11786 interactionMode: "default",
11771 cwd: process.cwd(),11787 cwd: process.cwd(),
...@@ -11779,7 +11795,7 @@ describe("AcpAdapterV2", () => {...@@ -11779,7 +11795,7 @@ describe("AcpAdapterV2", () => {
11779 modelSelection,11795 modelSelection,
11780 runtimePolicy,11796 runtimePolicy,
11781 });11797 });
11782 const events = yield* Queue.unbounded<ProviderAdapterV2Event>();11798 const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>();
11783 yield* runtime.events.pipe(11799 yield* runtime.events.pipe(
11784 Stream.runForEach((event) => Queue.offer(events, event)),11800 Stream.runForEach((event) => Queue.offer(events, event)),
11785 Effect.forkScoped,11801 Effect.forkScoped,
...@@ -11913,7 +11929,7 @@ describe("AcpAdapterV2", () => {...@@ -11913,7 +11929,7 @@ describe("AcpAdapterV2", () => {
11913 selfInvocation,11929 selfInvocation,
11914 });11930 });
11915 const threadId = ThreadId.make("thread-acp-restart-after-interrupt");11931 const threadId = ThreadId.make("thread-acp-restart-after-interrupt");
11916 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({11932 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
11917 runtimeMode: "full-access",11933 runtimeMode: "full-access",
11918 interactionMode: "default",11934 interactionMode: "default",
11919 cwd: process.cwd(),11935 cwd: process.cwd(),
...@@ -12144,7 +12160,8 @@ describe("AcpAdapterV2", () => {...@@ -12144,7 +12160,8 @@ describe("AcpAdapterV2", () => {
12144 const residualCallbackResponseLogPath = path.join(residualCallbackDir, "responses.log");12160 const residualCallbackResponseLogPath = path.join(residualCallbackDir, "responses.log");
12145 const residualCallbackTriggerPath = path.join(residualCallbackDir, "trigger");12161 const residualCallbackTriggerPath = path.join(residualCallbackDir, "trigger");
12146 const protocolEvents = yield* Queue.bounded<EffectAcpProtocol.AcpProtocolLogEvent>(256);12162 const protocolEvents = yield* Queue.bounded<EffectAcpProtocol.AcpProtocolLogEvent>(256);
12147 const continuationRequests: Array<ProviderContinuationRequest> = [];12163 const continuationRequests: Array<ProviderContinuationRequests.ProviderContinuationRequest> =
12164 [];
12148 const teardownStarted = yield* Deferred.make<void>();12165 const teardownStarted = yield* Deferred.make<void>();
12149 const releaseTeardown = yield* Deferred.make<void>();12166 const releaseTeardown = yield* Deferred.make<void>();
12150 let terminatorCallCount = 0;12167 let terminatorCallCount = 0;
...@@ -12187,7 +12204,7 @@ describe("AcpAdapterV2", () => {...@@ -12187,7 +12204,7 @@ describe("AcpAdapterV2", () => {
12187 },12204 },
12188 });12205 });
12189 const threadId = ThreadId.make("thread-acp-failed-hard-teardown");12206 const threadId = ThreadId.make("thread-acp-failed-hard-teardown");
12190 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({12207 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
12191 runtimeMode: "full-access",12208 runtimeMode: "full-access",
12192 interactionMode: "default",12209 interactionMode: "default",
12193 cwd: process.cwd(),12210 cwd: process.cwd(),
...@@ -12202,7 +12219,7 @@ describe("AcpAdapterV2", () => {...@@ -12202,7 +12219,7 @@ describe("AcpAdapterV2", () => {
12202 runtimePolicy,12219 runtimePolicy,
12203 })12220 })
12204 .pipe(Effect.provideService(Scope.Scope, sessionScope));12221 .pipe(Effect.provideService(Scope.Scope, sessionScope));
12205 const adapterEvents = yield* Queue.unbounded<ProviderAdapterV2Event>();12222 const adapterEvents = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>();
12206 yield* runtime.events.pipe(12223 yield* runtime.events.pipe(
12207 Stream.runForEach((event) => Queue.offer(adapterEvents, event)),12224 Stream.runForEach((event) => Queue.offer(adapterEvents, event)),
12208 Effect.forkIn(sessionScope),12225 Effect.forkIn(sessionScope),
...@@ -12368,7 +12385,7 @@ describe("AcpAdapterV2", () => {...@@ -12368,7 +12385,7 @@ describe("AcpAdapterV2", () => {
12368 selfInvocation,12385 selfInvocation,
12369 });12386 });
12370 const threadId = ThreadId.make("thread-acp-missing-hard-teardown");12387 const threadId = ThreadId.make("thread-acp-missing-hard-teardown");
12371 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({12388 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
12372 runtimeMode: "full-access",12389 runtimeMode: "full-access",
12373 interactionMode: "default",12390 interactionMode: "default",
12374 cwd: process.cwd(),12391 cwd: process.cwd(),
...@@ -12464,7 +12481,7 @@ describe("AcpAdapterV2", () => {...@@ -12464,7 +12481,7 @@ describe("AcpAdapterV2", () => {
12464 selfInvocation,12481 selfInvocation,
12465 });12482 });
12466 const threadId = ThreadId.make("thread-acp-concurrent-hard-teardown");12483 const threadId = ThreadId.make("thread-acp-concurrent-hard-teardown");
12467 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({12484 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
12468 runtimeMode: "full-access",12485 runtimeMode: "full-access",
12469 interactionMode: "default",12486 interactionMode: "default",
12470 cwd: process.cwd(),12487 cwd: process.cwd(),
...@@ -12540,7 +12557,8 @@ describe("AcpAdapterV2", () => {...@@ -12540,7 +12557,8 @@ describe("AcpAdapterV2", () => {
12540 new URL("../../../scripts/acp-mock-agent.ts", import.meta.url),12557 new URL("../../../scripts/acp-mock-agent.ts", import.meta.url),
12541 );12558 );
12542 const protocolEvents = yield* Queue.bounded<EffectAcpProtocol.AcpProtocolLogEvent>(256);12559 const protocolEvents = yield* Queue.bounded<EffectAcpProtocol.AcpProtocolLogEvent>(256);
12543 const continuationRequests: Array<ProviderContinuationRequest> = [];12560 const continuationRequests: Array<ProviderContinuationRequests.ProviderContinuationRequest> =
12561 [];
12544 const responseLifecycle: Array<string> = [];12562 const responseLifecycle: Array<string> = [];
12545 type RuntimeService = AcpSessionRuntime.AcpSessionRuntime["Service"];12563 type RuntimeService = AcpSessionRuntime.AcpSessionRuntime["Service"];
12546 type HandlerRecord = {12564 type HandlerRecord = {
...@@ -12633,7 +12651,7 @@ describe("AcpAdapterV2", () => {...@@ -12633,7 +12651,7 @@ describe("AcpAdapterV2", () => {
12633 },12651 },
12634 });12652 });
12635 const threadId = ThreadId.make("thread-acp-successful-teardown-callback-quarantine");12653 const threadId = ThreadId.make("thread-acp-successful-teardown-callback-quarantine");
12636 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({12654 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
12637 runtimeMode: "full-access",12655 runtimeMode: "full-access",
12638 interactionMode: "default",12656 interactionMode: "default",
12639 cwd: process.cwd(),12657 cwd: process.cwd(),
...@@ -12647,7 +12665,7 @@ describe("AcpAdapterV2", () => {...@@ -12647,7 +12665,7 @@ describe("AcpAdapterV2", () => {
12647 modelSelection,12665 modelSelection,
12648 runtimePolicy,12666 runtimePolicy,
12649 });12667 });
12650 const adapterEvents = yield* Queue.unbounded<ProviderAdapterV2Event>();12668 const adapterEvents = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>();
12651 yield* runtime.events.pipe(12669 yield* runtime.events.pipe(
12652 Stream.runForEach((event) => Queue.offer(adapterEvents, event)),12670 Stream.runForEach((event) => Queue.offer(adapterEvents, event)),
12653 Effect.forkScoped,12671 Effect.forkScoped,
...@@ -12849,7 +12867,7 @@ describe("AcpAdapterV2", () => {...@@ -12849,7 +12867,7 @@ describe("AcpAdapterV2", () => {
12849 method: "_x.ai/ask_user_question",12867 method: "_x.ai/ask_user_question",
12850 },12868 },
12851 ).pipe(Effect.forkScoped);12869 ).pipe(Effect.forkScoped);
12852 let replacementRequest: ProviderAdapterV2Event | undefined;12870 let replacementRequest: ProviderAdapter.ProviderAdapterV2Event | undefined;
12853 while (replacementRequest === undefined) {12871 while (replacementRequest === undefined) {
12854 const event = yield* Queue.take(adapterEvents);12872 const event = yield* Queue.take(adapterEvents);
12855 if (event.type === "runtime_request.updated" && event.runtimeRequest.status === "pending") {12873 if (event.type === "runtime_request.updated" && event.runtimeRequest.status === "pending") {
...@@ -12916,7 +12934,7 @@ describe("AcpAdapterV2", () => {...@@ -12916,7 +12934,7 @@ describe("AcpAdapterV2", () => {
12916 selfInvocation,12934 selfInvocation,
12917 });12935 });
12918 const threadId = ThreadId.make("thread-acp-stale-deferred-cleanup");12936 const threadId = ThreadId.make("thread-acp-stale-deferred-cleanup");
12919 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({12937 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
12920 runtimeMode: "approval-required",12938 runtimeMode: "approval-required",
12921 interactionMode: "default",12939 interactionMode: "default",
12922 cwd: process.cwd(),12940 cwd: process.cwd(),
...@@ -12928,7 +12946,7 @@ describe("AcpAdapterV2", () => {...@@ -12928,7 +12946,7 @@ describe("AcpAdapterV2", () => {
12928 modelSelection,12946 modelSelection,
12929 runtimePolicy,12947 runtimePolicy,
12930 });12948 });
12931 const events = yield* Queue.unbounded<ProviderAdapterV2Event>();12949 const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>();
12932 yield* runtime.events.pipe(12950 yield* runtime.events.pipe(
12933 Stream.runForEach((event) => Queue.offer(events, event)),12951 Stream.runForEach((event) => Queue.offer(events, event)),
12934 Effect.forkScoped,12952 Effect.forkScoped,
...@@ -12975,7 +12993,7 @@ describe("AcpAdapterV2", () => {...@@ -12975,7 +12993,7 @@ describe("AcpAdapterV2", () => {
12975 yield* runtime.startTurn(12993 yield* runtime.startTurn(
12976 makeTurnInput({ threadId, providerThread, instanceId, runtimePolicy, now, ordinal: 2 }),12994 makeTurnInput({ threadId, providerThread, instanceId, runtimePolicy, now, ordinal: 2 }),
12977 );12995 );
12978 let replacementPending: ProviderAdapterV2Event | undefined;12996 let replacementPending: ProviderAdapter.ProviderAdapterV2Event | undefined;
12979 while (replacementPending === undefined) {12997 while (replacementPending === undefined) {
12980 const event = yield* Queue.take(events);12998 const event = yield* Queue.take(events);
12981 if (event.type === "runtime_request.updated" && event.runtimeRequest.status === "pending") {12999 if (event.type === "runtime_request.updated" && event.runtimeRequest.status === "pending") {
...@@ -13045,7 +13063,7 @@ describe("AcpAdapterV2", () => {...@@ -13045,7 +13063,7 @@ describe("AcpAdapterV2", () => {
13045 selfInvocation,13063 selfInvocation,
13046 });13064 });
13047 const threadId = ThreadId.make("thread-acp-concurrent-resume-teardown");13065 const threadId = ThreadId.make("thread-acp-concurrent-resume-teardown");
13048 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({13066 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
13049 runtimeMode: "full-access",13067 runtimeMode: "full-access",
13050 interactionMode: "default",13068 interactionMode: "default",
13051 cwd: process.cwd(),13069 cwd: process.cwd(),
...@@ -13192,7 +13210,7 @@ describe("AcpAdapterV2", () => {...@@ -13192,7 +13210,7 @@ describe("AcpAdapterV2", () => {
13192 continuationRequests: { offer: () => Effect.void },13210 continuationRequests: { offer: () => Effect.void },
13193 });13211 });
13194 const threadId = ThreadId.make("thread-acp-direct-stop-running-command");13212 const threadId = ThreadId.make("thread-acp-direct-stop-running-command");
13195 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({13213 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
13196 runtimeMode: "full-access",13214 runtimeMode: "full-access",
13197 interactionMode: "default",13215 interactionMode: "default",
13198 cwd: process.cwd(),13216 cwd: process.cwd(),
...@@ -13206,7 +13224,7 @@ describe("AcpAdapterV2", () => {...@@ -13206,7 +13224,7 @@ describe("AcpAdapterV2", () => {
13206 modelSelection,13224 modelSelection,
13207 runtimePolicy,13225 runtimePolicy,
13208 });13226 });
13209 const events = yield* Queue.unbounded<ProviderAdapterV2Event>();13227 const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>();
13210 yield* runtime.events.pipe(13228 yield* runtime.events.pipe(
13211 Stream.runForEach((event) => Queue.offer(events, event)),13229 Stream.runForEach((event) => Queue.offer(events, event)),
13212 Effect.forkScoped,13230 Effect.forkScoped,
...@@ -13458,7 +13476,7 @@ describe("AcpAdapterV2", () => {...@@ -13458,7 +13476,7 @@ describe("AcpAdapterV2", () => {
13458 selfInvocation,13476 selfInvocation,
13459 });13477 });
13460 const threadId = ThreadId.make("thread-acp-direct-stop-subagent-hold");13478 const threadId = ThreadId.make("thread-acp-direct-stop-subagent-hold");
13461 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({13479 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
13462 runtimeMode: "full-access",13480 runtimeMode: "full-access",
13463 interactionMode: "default",13481 interactionMode: "default",
13464 cwd: process.cwd(),13482 cwd: process.cwd(),
...@@ -13472,7 +13490,7 @@ describe("AcpAdapterV2", () => {...@@ -13472,7 +13490,7 @@ describe("AcpAdapterV2", () => {
13472 modelSelection,13490 modelSelection,
13473 runtimePolicy,13491 runtimePolicy,
13474 });13492 });
13475 const events = yield* Queue.unbounded<ProviderAdapterV2Event>();13493 const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>();
13476 yield* runtime.events.pipe(13494 yield* runtime.events.pipe(
13477 Stream.runForEach((event) => Queue.offer(events, event)),13495 Stream.runForEach((event) => Queue.offer(events, event)),
13478 Effect.forkScoped,13496 Effect.forkScoped,
...@@ -13602,7 +13620,7 @@ describe("AcpAdapterV2", () => {...@@ -13602,7 +13620,7 @@ describe("AcpAdapterV2", () => {
13602 selfInvocation,13620 selfInvocation,
13603 });13621 });
13604 const threadId = ThreadId.make("thread-acp-restart-active-in-process");13622 const threadId = ThreadId.make("thread-acp-restart-active-in-process");
13605 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({13623 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
13606 runtimeMode: "full-access",13624 runtimeMode: "full-access",
13607 interactionMode: "default",13625 interactionMode: "default",
13608 cwd: process.cwd(),13626 cwd: process.cwd(),
...@@ -13614,7 +13632,7 @@ describe("AcpAdapterV2", () => {...@@ -13614,7 +13632,7 @@ describe("AcpAdapterV2", () => {
13614 modelSelection,13632 modelSelection,
13615 runtimePolicy,13633 runtimePolicy,
13616 });13634 });
13617 const events = yield* Queue.unbounded<ProviderAdapterV2Event>();13635 const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>();
13618 yield* runtime.events.pipe(13636 yield* runtime.events.pipe(
13619 Stream.runForEach((event) => Queue.offer(events, event)),13637 Stream.runForEach((event) => Queue.offer(events, event)),
13620 Effect.forkScoped,13638 Effect.forkScoped,
apps/server/src/orchestration-v2/Adapters/AcpRegistryAdapterV2.test.ts+4-4
...@@ -29,7 +29,7 @@ import {...@@ -29,7 +29,7 @@ import {
29 makeAcpReplayCompletenessAssertion,29 makeAcpReplayCompletenessAssertion,
30 makeAcpReplayRuntime,30 makeAcpReplayRuntime,
31} from "./AcpAdapterV2.testkit.ts";31} from "./AcpAdapterV2.testkit.ts";
32import { ProviderAdapterV2RuntimePolicy } from "@t3tools/provider-core/server/ProviderAdapter";32import * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter";
33import { BUILT_IN_PROVIDER_ADAPTER_DRIVER_KINDS_V2 } from "../builtInProviderAdapterDrivers.ts";33import { BUILT_IN_PROVIDER_ADAPTER_DRIVER_KINDS_V2 } from "../builtInProviderAdapterDrivers.ts";
34import { AcpRegistryAdapterV2Driver } from "@t3tools/provider-acp-registry/server";34import { AcpRegistryAdapterV2Driver } from "@t3tools/provider-acp-registry/server";
35import {35import {
...@@ -216,7 +216,7 @@ describe("AcpRegistryAdapterV2", () => {...@@ -216,7 +216,7 @@ describe("AcpRegistryAdapterV2", () => {
216 model: "default",216 model: "default",
217 options: [{ id: ACP_SESSION_MODE_OPTION_ID, value: input.storedModePick }],217 options: [{ id: ACP_SESSION_MODE_OPTION_ID, value: input.storedModePick }],
218 },218 },
219 runtimePolicy: ProviderAdapterV2RuntimePolicy.make({219 runtimePolicy: ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
220 runtimeMode: "approval-required",220 runtimeMode: "approval-required",
221 interactionMode: "default",221 interactionMode: "default",
222 cwd: replayDir,222 cwd: replayDir,
...@@ -304,7 +304,7 @@ describe("AcpRegistryAdapterV2", () => {...@@ -304,7 +304,7 @@ describe("AcpRegistryAdapterV2", () => {
304 );304 );
305 }),305 }),
306 });306 });
307 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({307 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
308 runtimeMode: "full-access",308 runtimeMode: "full-access",
309 interactionMode: "default",309 interactionMode: "default",
310 cwd: process.cwd(),310 cwd: process.cwd(),
...@@ -413,7 +413,7 @@ describe("AcpRegistryAdapterV2", () => {...@@ -413,7 +413,7 @@ describe("AcpRegistryAdapterV2", () => {
413 },413 },
414 });414 });
415 const threadId = ThreadId.make("thread-acp-registry-fixture");415 const threadId = ThreadId.make("thread-acp-registry-fixture");
416 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({416 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
417 runtimeMode: "full-access",417 runtimeMode: "full-access",
418 interactionMode: "default",418 interactionMode: "default",
419 cwd: process.cwd(),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,7 +27,7 @@ import { layerTestProviderHost } from "@t3tools/provider-testing/host";
27import type * as AcpSessionRuntime from "@t3tools/provider-acp/server/AcpSessionRuntime";27import type * as AcpSessionRuntime from "@t3tools/provider-acp/server/AcpSessionRuntime";
28import { makeAntigravityAcpRuntime } from "../../provider/acp/AntigravityAcpSupport.ts";28import { makeAntigravityAcpRuntime } from "../../provider/acp/AntigravityAcpSupport.ts";
29import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator";29import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator";
30import { ProviderAdapterV2RuntimePolicy } from "@t3tools/provider-core/server/ProviderAdapter";30import * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter";
31import {31import {
32 makeAntigravityAcpAdapterFlavor,32 makeAntigravityAcpAdapterFlavor,
33 makeAntigravityAdapterV2,33 makeAntigravityAdapterV2,
...@@ -74,7 +74,7 @@ describe("AntigravityAdapterV2 flavor", () => {...@@ -74,7 +74,7 @@ describe("AntigravityAdapterV2 flavor", () => {
74 it("maps runtime modes to the agent's native permission modes", () => {74 it("maps runtime modes to the agent's native permission modes", () => {
75 const mode = (runtimeMode: "approval-required" | "auto-accept-edits" | "full-access") =>75 const mode = (runtimeMode: "approval-required" | "auto-accept-edits" | "full-access") =>
76 flavor.sessionModeForPolicy?.(76 flavor.sessionModeForPolicy?.(
77 ProviderAdapterV2RuntimePolicy.make({77 ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
78 runtimeMode,78 runtimeMode,
79 interactionMode: "default",79 interactionMode: "default",
80 cwd: "/workspace",80 cwd: "/workspace",
...@@ -190,7 +190,7 @@ describe("AntigravityAdapterV2 client file system", () => {...@@ -190,7 +190,7 @@ describe("AntigravityAdapterV2 client file system", () => {
190 yield* fileSystem.writeFileString(attachment, "pasted");190 yield* fileSystem.writeFileString(attachment, "pasted");
191191
192 const threadId = ThreadId.make("thread-antigravity-containment");192 const threadId = ThreadId.make("thread-antigravity-containment");
193 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({193 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
194 runtimeMode: "full-access",194 runtimeMode: "full-access",
195 interactionMode: "default",195 interactionMode: "default",
196 cwd: workspace,196 cwd: workspace,
...@@ -325,7 +325,7 @@ describe("AntigravityAdapterV2 workspace changes", () => {...@@ -325,7 +325,7 @@ describe("AntigravityAdapterV2 workspace changes", () => {
325 yield* fileSystem.writeFileString(path.join(workspaceA, "a.txt"), "from a");325 yield* fileSystem.writeFileString(path.join(workspaceA, "a.txt"), "from a");
326 yield* fileSystem.writeFileString(path.join(workspaceB, "b.txt"), "from b");326 yield* fileSystem.writeFileString(path.join(workspaceB, "b.txt"), "from b");
327 const policyFor = (cwd: string) =>327 const policyFor = (cwd: string) =>
328 ProviderAdapterV2RuntimePolicy.make({328 ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
329 runtimeMode: "full-access",329 runtimeMode: "full-access",
330 interactionMode: "default",330 interactionMode: "default",
331 cwd,331 cwd,
...@@ -476,7 +476,7 @@ describe("AntigravityAdapterV2 client file system under restrictive policies", (...@@ -476,7 +476,7 @@ describe("AntigravityAdapterV2 client file system under restrictive policies", (
476 });476 });
477 yield* fileSystem.writeFileString(path.join(workspace, "existing.ts"), "existing");477 yield* fileSystem.writeFileString(path.join(workspace, "existing.ts"), "existing");
478 const threadId = ThreadId.make(`thread-antigravity-restrictive-${policy.runtimeMode}`);478 const threadId = ThreadId.make(`thread-antigravity-restrictive-${policy.runtimeMode}`);
479 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({479 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
480 ...policy,480 ...policy,
481 interactionMode: "default",481 interactionMode: "default",
482 cwd: workspace,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,13 +61,9 @@ import { WorktreeToolkit } from "../../mcp/toolkits/worktree/tools.ts";
61import { ThreadToolkit } from "../../mcp/toolkits/thread/tools.ts";61import { ThreadToolkit } from "../../mcp/toolkits/thread/tools.ts";
62import { OrchestratorToolkit } from "../../mcp/toolkits/orchestrator/tools.ts";62import { OrchestratorToolkit } from "../../mcp/toolkits/orchestrator/tools.ts";
63import { ClaudeExecutableFileCheck } from "../../provider/Drivers/ClaudeExecutable.ts";63import { ClaudeExecutableFileCheck } from "../../provider/Drivers/ClaudeExecutable.ts";
64import type { EventNdjsonLogger } from "../../provider/EventNdjsonLogger.ts";64import type * as ProviderEventLoggers from "@t3tools/provider-core/server/ProviderEventLoggers";
65import {65import * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter";
66 ProviderAdapterV2RuntimePolicy,66import type * as ProviderContinuationRequests from "@t3tools/provider-core/server/ProviderContinuationRequests";
67 type ProviderAdapterV2Event,
68 type ProviderAdapterV2TurnInput,
69} from "@t3tools/provider-core/server/ProviderAdapter";
70import type { ProviderContinuationRequest } from "@t3tools/provider-core/server/continuationRequests";
71import { makeProviderFailure } from "@t3tools/provider-core/server/failure";67import { makeProviderFailure } from "@t3tools/provider-core/server/failure";
72import * as ClaudeAdapterV2 from "./ClaudeAdapterV2.ts";68import * as ClaudeAdapterV2 from "./ClaudeAdapterV2.ts";
73import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator";69import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator";
...@@ -81,7 +77,7 @@ const CLAUDE_TEST_MODEL_SELECTION = {...@@ -81,7 +77,7 @@ const CLAUDE_TEST_MODEL_SELECTION = {
81 model: "claude-sonnet-4-6",77 model: "claude-sonnet-4-6",
82 options: [{ id: "effort", value: "ultrathink" }],78 options: [{ id: "effort", value: "ultrathink" }],
83} satisfies ModelSelection;79} satisfies ModelSelection;
84const CLAUDE_TEST_RUNTIME_POLICY = ProviderAdapterV2RuntimePolicy.make({80const CLAUDE_TEST_RUNTIME_POLICY = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
85 runtimeMode: "full-access",81 runtimeMode: "full-access",
86 interactionMode: "default",82 interactionMode: "default",
87 cwd: "/workspace",83 cwd: "/workspace",
...@@ -127,14 +123,14 @@ function makeClaudeTestTurnInput(input: {...@@ -127,14 +123,14 @@ function makeClaudeTestTurnInput(input: {
127 readonly now: DateTime.Utc;123 readonly now: DateTime.Utc;
128 readonly attemptId: RunAttemptId;124 readonly attemptId: RunAttemptId;
129 readonly text: string;125 readonly text: string;
130 readonly attachments: ProviderAdapterV2TurnInput["message"]["attachments"];126 readonly attachments: ProviderAdapter.ProviderAdapterV2TurnInput["message"]["attachments"];
131 readonly providerTurnOrdinal?: number;127 readonly providerTurnOrdinal?: number;
132 readonly nativeThreadHasTurns?: boolean;128 readonly nativeThreadHasTurns?: boolean;
133 readonly messageCreatedBy?: ProviderAdapterV2TurnInput["message"]["createdBy"];129 readonly messageCreatedBy?: ProviderAdapter.ProviderAdapterV2TurnInput["message"]["createdBy"];
134 readonly messageCreationSource?: ProviderAdapterV2TurnInput["message"]["creationSource"];130 readonly messageCreationSource?: ProviderAdapter.ProviderAdapterV2TurnInput["message"]["creationSource"];
135 readonly modelSelection?: ModelSelection;131 readonly modelSelection?: ModelSelection;
136 readonly runtimePolicy?: ProviderAdapterV2RuntimePolicy;132 readonly runtimePolicy?: ProviderAdapter.ProviderAdapterV2RuntimePolicy;
137}): ProviderAdapterV2TurnInput {133}): ProviderAdapter.ProviderAdapterV2TurnInput {
138 return {134 return {
139 appThread: makeClaudeTestAppThread(input),135 appThread: makeClaudeTestAppThread(input),
140 threadId: input.threadId,136 threadId: input.threadId,
...@@ -298,7 +294,7 @@ describe("ClaudeAdapterV2 runtime query policy", () => {...@@ -298,7 +294,7 @@ describe("ClaudeAdapterV2 runtime query policy", () => {
298294
299 it("maps canonical read-only never policy to Claude dontAsk with read-only tools", () => {295 it("maps canonical read-only never policy to Claude dontAsk with read-only tools", () => {
300 const queryPolicy = ClaudeAdapterV2.claudeRuntimeQueryPolicyForRuntimePolicy(296 const queryPolicy = ClaudeAdapterV2.claudeRuntimeQueryPolicyForRuntimePolicy(
301 ProviderAdapterV2RuntimePolicy.make({297 ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
302 runtimeMode: "full-access",298 runtimeMode: "full-access",
303 interactionMode: "default",299 interactionMode: "default",
304 cwd: "/workspace",300 cwd: "/workspace",
...@@ -321,7 +317,7 @@ describe("ClaudeAdapterV2 runtime query policy", () => {...@@ -321,7 +317,7 @@ describe("ClaudeAdapterV2 runtime query policy", () => {
321317
322 it("maps canonical read-only on-request policy to Claude default with callbacks", () => {318 it("maps canonical read-only on-request policy to Claude default with callbacks", () => {
323 const queryPolicy = ClaudeAdapterV2.claudeRuntimeQueryPolicyForRuntimePolicy(319 const queryPolicy = ClaudeAdapterV2.claudeRuntimeQueryPolicyForRuntimePolicy(
324 ProviderAdapterV2RuntimePolicy.make({320 ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
325 runtimeMode: "full-access",321 runtimeMode: "full-access",
326 interactionMode: "default",322 interactionMode: "default",
327 cwd: "/workspace",323 cwd: "/workspace",
...@@ -344,7 +340,7 @@ describe("ClaudeAdapterV2 runtime query policy", () => {...@@ -344,7 +340,7 @@ describe("ClaudeAdapterV2 runtime query policy", () => {
344340
345 it("does not auto-allow reads for canonical restricted read-only never policy", () => {341 it("does not auto-allow reads for canonical restricted read-only never policy", () => {
346 const queryPolicy = ClaudeAdapterV2.claudeRuntimeQueryPolicyForRuntimePolicy(342 const queryPolicy = ClaudeAdapterV2.claudeRuntimeQueryPolicyForRuntimePolicy(
347 ProviderAdapterV2RuntimePolicy.make({343 ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
348 runtimeMode: "full-access",344 runtimeMode: "full-access",
349 interactionMode: "default",345 interactionMode: "default",
350 cwd: "/workspace",346 cwd: "/workspace",
...@@ -370,7 +366,7 @@ describe("ClaudeAdapterV2 runtime query policy", () => {...@@ -370,7 +366,7 @@ describe("ClaudeAdapterV2 runtime query policy", () => {
370366
371 it("maps default full-access policy to Claude bypass permissions", () => {367 it("maps default full-access policy to Claude bypass permissions", () => {
372 const queryPolicy = ClaudeAdapterV2.claudeRuntimeQueryPolicyForRuntimePolicy(368 const queryPolicy = ClaudeAdapterV2.claudeRuntimeQueryPolicyForRuntimePolicy(
373 ProviderAdapterV2RuntimePolicy.make({369 ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
374 runtimeMode: "full-access",370 runtimeMode: "full-access",
375 interactionMode: "default",371 interactionMode: "default",
376 cwd: "/workspace",372 cwd: "/workspace",
...@@ -386,7 +382,7 @@ describe("ClaudeAdapterV2 runtime query policy", () => {...@@ -386,7 +382,7 @@ describe("ClaudeAdapterV2 runtime query policy", () => {
386382
387 it("maps Auto runtime mode to Claude's AI-reviewed permission mode", () => {383 it("maps Auto runtime mode to Claude's AI-reviewed permission mode", () => {
388 const queryPolicy = ClaudeAdapterV2.claudeRuntimeQueryPolicyForRuntimePolicy(384 const queryPolicy = ClaudeAdapterV2.claudeRuntimeQueryPolicyForRuntimePolicy(
389 ProviderAdapterV2RuntimePolicy.make({385 ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
390 runtimeMode: "auto",386 runtimeMode: "auto",
391 interactionMode: "default",387 interactionMode: "default",
392 cwd: "/workspace",388 cwd: "/workspace",
...@@ -401,7 +397,7 @@ describe("ClaudeAdapterV2 runtime query policy", () => {...@@ -401,7 +397,7 @@ describe("ClaudeAdapterV2 runtime query policy", () => {
401397
402 it("keeps approval-required mode interactive with danger-full-access sandboxing", () => {398 it("keeps approval-required mode interactive with danger-full-access sandboxing", () => {
403 const queryPolicy = ClaudeAdapterV2.claudeRuntimeQueryPolicyForRuntimePolicy(399 const queryPolicy = ClaudeAdapterV2.claudeRuntimeQueryPolicyForRuntimePolicy(
404 ProviderAdapterV2RuntimePolicy.make({400 ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
405 runtimeMode: "approval-required",401 runtimeMode: "approval-required",
406 interactionMode: "default",402 interactionMode: "default",
407 cwd: "/workspace",403 cwd: "/workspace",
...@@ -419,7 +415,7 @@ describe("ClaudeAdapterV2 runtime query policy", () => {...@@ -419,7 +415,7 @@ describe("ClaudeAdapterV2 runtime query policy", () => {
419415
420 it("installs the permission callback for approval-required plan mode", () => {416 it("installs the permission callback for approval-required plan mode", () => {
421 const queryPolicy = ClaudeAdapterV2.claudeRuntimeQueryPolicyForRuntimePolicy(417 const queryPolicy = ClaudeAdapterV2.claudeRuntimeQueryPolicyForRuntimePolicy(
422 ProviderAdapterV2RuntimePolicy.make({418 ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
423 runtimeMode: "approval-required",419 runtimeMode: "approval-required",
424 interactionMode: "plan",420 interactionMode: "plan",
425 cwd: "/workspace",421 cwd: "/workspace",
...@@ -434,7 +430,7 @@ describe("ClaudeAdapterV2 runtime query policy", () => {...@@ -434,7 +430,7 @@ describe("ClaudeAdapterV2 runtime query policy", () => {
434430
435 it("honors never approvals for approval-required workspace-write policy", () => {431 it("honors never approvals for approval-required workspace-write policy", () => {
436 const queryPolicy = ClaudeAdapterV2.claudeRuntimeQueryPolicyForRuntimePolicy(432 const queryPolicy = ClaudeAdapterV2.claudeRuntimeQueryPolicyForRuntimePolicy(
437 ProviderAdapterV2RuntimePolicy.make({433 ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
438 runtimeMode: "approval-required",434 runtimeMode: "approval-required",
439 interactionMode: "default",435 interactionMode: "default",
440 cwd: "/workspace",436 cwd: "/workspace",
...@@ -453,7 +449,7 @@ describe("ClaudeAdapterV2 runtime query policy", () => {...@@ -453,7 +449,7 @@ describe("ClaudeAdapterV2 runtime query policy", () => {
453449
454 it("honors never approvals for externally sandboxed policy", () => {450 it("honors never approvals for externally sandboxed policy", () => {
455 const queryPolicy = ClaudeAdapterV2.claudeRuntimeQueryPolicyForRuntimePolicy(451 const queryPolicy = ClaudeAdapterV2.claudeRuntimeQueryPolicyForRuntimePolicy(
456 ProviderAdapterV2RuntimePolicy.make({452 ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
457 runtimeMode: "approval-required",453 runtimeMode: "approval-required",
458 interactionMode: "default",454 interactionMode: "default",
459 cwd: "/workspace",455 cwd: "/workspace",
...@@ -595,7 +591,7 @@ describe("ClaudeAdapterV2 MCP query overrides", () => {...@@ -595,7 +591,7 @@ describe("ClaudeAdapterV2 MCP query overrides", () => {
595 const threadId = ThreadId.make("thread-claude-mcp-query-key");591 const threadId = ThreadId.make("thread-claude-mcp-query-key");
596 withMcpSession(threadId, () => {592 withMcpSession(threadId, () => {
597 const queryPolicy = ClaudeAdapterV2.claudeRuntimeQueryPolicyForRuntimePolicy(593 const queryPolicy = ClaudeAdapterV2.claudeRuntimeQueryPolicyForRuntimePolicy(
598 ProviderAdapterV2RuntimePolicy.make({594 ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
599 runtimeMode: "full-access",595 runtimeMode: "full-access",
600 interactionMode: "default",596 interactionMode: "default",
601 cwd: "/workspace",597 cwd: "/workspace",
...@@ -627,7 +623,7 @@ describe("ClaudeAdapterV2 MCP query overrides", () => {...@@ -627,7 +623,7 @@ describe("ClaudeAdapterV2 MCP query overrides", () => {
627 const threadId = ThreadId.make("thread-claude-mcp-credential-rotation");623 const threadId = ThreadId.make("thread-claude-mcp-credential-rotation");
628 withMcpSession(threadId, () => {624 withMcpSession(threadId, () => {
629 const queryPolicy = ClaudeAdapterV2.claudeRuntimeQueryPolicyForRuntimePolicy(625 const queryPolicy = ClaudeAdapterV2.claudeRuntimeQueryPolicyForRuntimePolicy(
630 ProviderAdapterV2RuntimePolicy.make({626 ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
631 runtimeMode: "approval-required",627 runtimeMode: "approval-required",
632 interactionMode: "default",628 interactionMode: "default",
633 cwd: "/workspace",629 cwd: "/workspace",
...@@ -750,7 +746,7 @@ describe("ClaudeAdapterV2 native protocol logging", () => {...@@ -750,7 +746,7 @@ describe("ClaudeAdapterV2 native protocol logging", () => {
750 readonly event: unknown;746 readonly event: unknown;
751 readonly threadId: ThreadId | null;747 readonly threadId: ThreadId | null;
752 }> = [];748 }> = [];
753 const logger: EventNdjsonLogger = {749 const logger: ProviderEventLoggers.EventNdjsonLogger = {
754 filePath: "/tmp/events.log",750 filePath: "/tmp/events.log",
755 write: (event, threadId) =>751 write: (event, threadId) =>
756 Effect.sync(() => {752 Effect.sync(() => {
...@@ -993,7 +989,7 @@ describe("ClaudeAdapterV2 Auto-accept edits", () => {...@@ -993,7 +989,7 @@ describe("ClaudeAdapterV2 Auto-accept edits", () => {
993 assertComplete: Effect.void,989 assertComplete: Effect.void,
994 },990 },
995 });991 });
996 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({992 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
997 runtimeMode: "auto-accept-edits",993 runtimeMode: "auto-accept-edits",
998 interactionMode: "default",994 interactionMode: "default",
999 cwd: "/workspace",995 cwd: "/workspace",
...@@ -1701,7 +1697,7 @@ describe("ClaudeAdapterV2 native fork", () => {...@@ -1701,7 +1697,7 @@ describe("ClaudeAdapterV2 native fork", () => {
1701 instanceId: ProviderInstanceId.make(ClaudeAdapterV2.CLAUDE_PROVIDER),1697 instanceId: ProviderInstanceId.make(ClaudeAdapterV2.CLAUDE_PROVIDER),
1702 model: "claude-sonnet-4-6",1698 model: "claude-sonnet-4-6",
1703 },1699 },
1704 runtimePolicy: ProviderAdapterV2RuntimePolicy.make({1700 runtimePolicy: ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
1705 runtimeMode: "full-access",1701 runtimeMode: "full-access",
1706 interactionMode: "default",1702 interactionMode: "default",
1707 cwd: "/workspace",1703 cwd: "/workspace",
...@@ -1713,7 +1709,7 @@ describe("ClaudeAdapterV2 native fork", () => {...@@ -1713,7 +1709,7 @@ describe("ClaudeAdapterV2 native fork", () => {
1713 instanceId: ProviderInstanceId.make(ClaudeAdapterV2.CLAUDE_PROVIDER),1709 instanceId: ProviderInstanceId.make(ClaudeAdapterV2.CLAUDE_PROVIDER),
1714 model: "claude-sonnet-4-6",1710 model: "claude-sonnet-4-6",
1715 },1711 },
1716 runtimePolicy: ProviderAdapterV2RuntimePolicy.make({1712 runtimePolicy: ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
1717 runtimeMode: "full-access",1713 runtimeMode: "full-access",
1718 interactionMode: "default",1714 interactionMode: "default",
1719 cwd: "/workspace",1715 cwd: "/workspace",
...@@ -1808,7 +1804,7 @@ describe("ClaudeAdapterV2 native fork", () => {...@@ -1808,7 +1804,7 @@ describe("ClaudeAdapterV2 native fork", () => {
1808 instanceId: ProviderInstanceId.make(ClaudeAdapterV2.CLAUDE_PROVIDER),1804 instanceId: ProviderInstanceId.make(ClaudeAdapterV2.CLAUDE_PROVIDER),
1809 model: "claude-sonnet-4-6",1805 model: "claude-sonnet-4-6",
1810 },1806 },
1811 runtimePolicy: ProviderAdapterV2RuntimePolicy.make({1807 runtimePolicy: ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
1812 runtimeMode: "full-access",1808 runtimeMode: "full-access",
1813 interactionMode: "default",1809 interactionMode: "default",
1814 cwd: "/workspace",1810 cwd: "/workspace",
...@@ -2120,11 +2116,16 @@ describe("ClaudeAdapterV2 background wake turns", () => {...@@ -2120,11 +2116,16 @@ describe("ClaudeAdapterV2 background wake turns", () => {
2120 });2116 });
2121 const offeredMessages: Array<SDKUserMessage> = [];2117 const offeredMessages: Array<SDKUserMessage> = [];
2122 const permissionModeChanges: Array<string> = [];2118 const permissionModeChanges: Array<string> = [];
2123 const continuationRequests: Array<ProviderContinuationRequest> = [];2119 const continuationRequests: Array<ProviderContinuationRequests.ProviderContinuationRequest> =
2120 [];
2124 const terminalReceipts =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 const systemNoticeReceipts =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 let openedOptions: ClaudeAdapterV2.ClaudeAgentSdkQueryOptions | undefined;2129 let openedOptions: ClaudeAdapterV2.ClaudeAgentSdkQueryOptions | undefined;
2129 const adapter = ClaudeAdapterV2.makeClaudeAdapterV2({2130 const adapter = ClaudeAdapterV2.makeClaudeAdapterV2({
2130 instanceId: ClaudeAdapterV2.CLAUDE_DEFAULT_INSTANCE_ID,2131 instanceId: ClaudeAdapterV2.CLAUDE_DEFAULT_INSTANCE_ID,
...@@ -2210,7 +2211,7 @@ describe("ClaudeAdapterV2 background wake turns", () => {...@@ -2210,7 +2211,7 @@ describe("ClaudeAdapterV2 background wake turns", () => {
2210 modelSelection: CLAUDE_TEST_MODEL_SELECTION,2211 modelSelection: CLAUDE_TEST_MODEL_SELECTION,
2211 runtimePolicy: CLAUDE_TEST_RUNTIME_POLICY,2212 runtimePolicy: CLAUDE_TEST_RUNTIME_POLICY,
2212 });2213 });
2213 const events: Array<ProviderAdapterV2Event> = [];2214 const events: Array<ProviderAdapter.ProviderAdapterV2Event> = [];
2214 yield* runtime.events.pipe(2215 yield* runtime.events.pipe(
2215 Stream.runForEach((event) =>2216 Stream.runForEach((event) =>
2216 Effect.gen(function* () {2217 Effect.gen(function* () {
...@@ -2231,7 +2232,9 @@ describe("ClaudeAdapterV2 background wake turns", () => {...@@ -2231,7 +2232,9 @@ describe("ClaudeAdapterV2 background wake turns", () => {
2231 const hasPendingBackgroundWork = runtime.hasPendingBackgroundWork;2232 const hasPendingBackgroundWork = runtime.hasPendingBackgroundWork;
2232 const terminalEvents = () =>2233 const terminalEvents = () =>
2233 events.filter(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 event.type === "turn.terminal",2238 event.type === "turn.terminal",
2236 );2239 );
2237 return {2240 return {
...@@ -2817,7 +2820,7 @@ describe("ClaudeAdapterV2 background wake turns", () => {...@@ -2817,7 +2820,7 @@ describe("ClaudeAdapterV2 background wake turns", () => {
2817 attemptId: RunAttemptId.make("attempt-claude-auth-failure"),2820 attemptId: RunAttemptId.make("attempt-claude-auth-failure"),
2818 text: "Continue.",2821 text: "Continue.",
2819 attachments: [],2822 attachments: [],
2820 runtimePolicy: ProviderAdapterV2RuntimePolicy.make({2823 runtimePolicy: ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
2821 runtimeMode: "full-access",2824 runtimeMode: "full-access",
2822 interactionMode: "default",2825 interactionMode: "default",
2823 cwd,2826 cwd,
...@@ -3633,10 +3636,16 @@ describe("ClaudeAdapterV2 background wake turns", () => {...@@ -3633,10 +3636,16 @@ describe("ClaudeAdapterV2 background wake turns", () => {
3633 ),3636 ),
3634 );3637 );
36353638
3636 const providerThreadRosterEvents = (events: ReadonlyArray<ProviderAdapterV2Event>) =>3639 const providerThreadRosterEvents = (
3640 events: ReadonlyArray<ProviderAdapter.ProviderAdapterV2Event>,
3641 ) =>
3637 events.filter(3642 events.filter(
3638 (event): event is Extract<ProviderAdapterV2Event, { type: "provider_thread.updated" }> =>3643 (
3639 event.type === "provider_thread.updated",3644 event,
3645 ): event is Extract<
3646 ProviderAdapter.ProviderAdapterV2Event,
3647 { type: "provider_thread.updated" }
3648 > => event.type === "provider_thread.updated",
3640 );3649 );
36413650
3642 it.effect(3651 it.effect(
...@@ -3932,8 +3941,9 @@ describe("ClaudeAdapterV2 background wake turns", () => {...@@ -3932,8 +3941,9 @@ describe("ClaudeAdapterV2 background wake turns", () => {
3932 });3941 });
3933 const processQueues: Array<Queue.Queue<SDKMessage>> = [];3942 const processQueues: Array<Queue.Queue<SDKMessage>> = [];
3934 const firstCloseRequested = yield* Deferred.make<void>();3943 const firstCloseRequested = yield* Deferred.make<void>();
3935 const events: Array<ProviderAdapterV2Event> = [];3944 const events: Array<ProviderAdapter.ProviderAdapterV2Event> = [];
3936 const continuationRequests: Array<ProviderContinuationRequest> = [];3945 const continuationRequests: Array<ProviderContinuationRequests.ProviderContinuationRequest> =
3946 [];
3937 const adapter = ClaudeAdapterV2.makeClaudeAdapterV2({3947 const adapter = ClaudeAdapterV2.makeClaudeAdapterV2({
3938 instanceId: ClaudeAdapterV2.CLAUDE_DEFAULT_INSTANCE_ID,3948 instanceId: ClaudeAdapterV2.CLAUDE_DEFAULT_INSTANCE_ID,
3939 settings: DEFAULT_CLAUDE_SETTINGS,3949 settings: DEFAULT_CLAUDE_SETTINGS,
...@@ -4179,7 +4189,7 @@ describe("ClaudeAdapterV2 background wake turns", () => {...@@ -4179,7 +4189,7 @@ describe("ClaudeAdapterV2 background wake turns", () => {
4179 readonly nativeThreadId: string;4189 readonly nativeThreadId: string;
4180 readonly queue: Queue.Queue<SDKMessage>;4190 readonly queue: Queue.Queue<SDKMessage>;
4181 }> = [];4191 }> = [];
4182 const events: Array<ProviderAdapterV2Event> = [];4192 const events: Array<ProviderAdapter.ProviderAdapterV2Event> = [];
4183 const adapter = ClaudeAdapterV2.makeClaudeAdapterV2({4193 const adapter = ClaudeAdapterV2.makeClaudeAdapterV2({
4184 instanceId: ClaudeAdapterV2.CLAUDE_DEFAULT_INSTANCE_ID,4194 instanceId: ClaudeAdapterV2.CLAUDE_DEFAULT_INSTANCE_ID,
4185 settings: DEFAULT_CLAUDE_SETTINGS,4195 settings: DEFAULT_CLAUDE_SETTINGS,
...@@ -4430,7 +4440,7 @@ describe("ClaudeAdapterV2 background wake turns", () => {...@@ -4430,7 +4440,7 @@ describe("ClaudeAdapterV2 background wake turns", () => {
4430 Effect.gen(function* () {4440 Effect.gen(function* () {
4431 const harness = yield* makeWakeHarness;4441 const harness = yield* makeWakeHarness;
4432 const now = yield* DateTime.now;4442 const now = yield* DateTime.now;
4433 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({4443 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
4434 runtimeMode,4444 runtimeMode,
4435 interactionMode: "default",4445 interactionMode: "default",
4436 cwd: "/workspace",4446 cwd: "/workspace",
...@@ -4731,7 +4741,7 @@ describe("ClaudeAdapterV2 background wake turns", () => {...@@ -4731,7 +4741,7 @@ describe("ClaudeAdapterV2 background wake turns", () => {
4731 Effect.gen(function* () {4741 Effect.gen(function* () {
4732 const harness = yield* makeWakeHarness;4742 const harness = yield* makeWakeHarness;
4733 const now = yield* DateTime.now;4743 const now = yield* DateTime.now;
4734 const approvalPolicy = ProviderAdapterV2RuntimePolicy.make({4744 const approvalPolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
4735 runtimeMode: "approval-required",4745 runtimeMode: "approval-required",
4736 interactionMode: "default",4746 interactionMode: "default",
4737 cwd: "/workspace",4747 cwd: "/workspace",
...@@ -5972,7 +5982,7 @@ describe("ClaudeAdapterV2 background wake turns", () => {...@@ -5972,7 +5982,7 @@ describe("ClaudeAdapterV2 background wake turns", () => {
5972 session_id: WAKE_NATIVE_SESSION,5982 session_id: WAKE_NATIVE_SESSION,
5973 });5983 });
5974 const subagentRouting = (5984 const subagentRouting = (
5975 events: ReadonlyArray<ProviderAdapterV2Event>,5985 events: ReadonlyArray<ProviderAdapter.ProviderAdapterV2Event>,
5976 nativeToolIds: ReadonlyArray<string>,5986 nativeToolIds: ReadonlyArray<string>,
5977 ) => {5987 ) => {
5978 const childThreadId =5988 const childThreadId =
...@@ -6090,7 +6100,7 @@ describe("ClaudeAdapterV2 background wake turns", () => {...@@ -6090,7 +6100,7 @@ describe("ClaudeAdapterV2 background wake turns", () => {
6090 const userTurn = (6100 const userTurn = (
6091 attempt: string,6101 attempt: string,
6092 providerTurnOrdinal: number,6102 providerTurnOrdinal: number,
6093 attachments: ProviderAdapterV2TurnInput["message"]["attachments"] = [],6103 attachments: ProviderAdapter.ProviderAdapterV2TurnInput["message"]["attachments"] = [],
6094 ) =>6104 ) =>
6095 harness.runtime.startTurn(6105 harness.runtime.startTurn(
6096 makeClaudeTestTurnInput({6106 makeClaudeTestTurnInput({
...@@ -7838,8 +7848,12 @@ describe("ClaudeAdapterV2 background wake turns", () => {...@@ -7838,8 +7848,12 @@ describe("ClaudeAdapterV2 background wake turns", () => {
7838 const now = yield* DateTime.now;7848 const now = yield* DateTime.now;
7839 const subagentEvents = () =>7849 const subagentEvents = () =>
7840 harness.events.filter(7850 harness.events.filter(
7841 (event): event is Extract<ProviderAdapterV2Event, { type: "subagent.updated" }> =>7851 (
7842 event.type === "subagent.updated",7852 event,
7853 ): event is Extract<
7854 ProviderAdapter.ProviderAdapterV2Event,
7855 { type: "subagent.updated" }
7856 > => event.type === "subagent.updated",
7843 );7857 );
78447858
7845 yield* harness.runtime.startTurn(7859 yield* harness.runtime.startTurn(
...@@ -7942,8 +7956,12 @@ describe("ClaudeAdapterV2 background wake turns", () => {...@@ -7942,8 +7956,12 @@ describe("ClaudeAdapterV2 background wake turns", () => {
7942 const now = yield* DateTime.now;7956 const now = yield* DateTime.now;
7943 const subagentEvents = () =>7957 const subagentEvents = () =>
7944 harness.events.filter(7958 harness.events.filter(
7945 (event): event is Extract<ProviderAdapterV2Event, { type: "subagent.updated" }> =>7959 (
7946 event.type === "subagent.updated",7960 event,
7961 ): event is Extract<
7962 ProviderAdapter.ProviderAdapterV2Event,
7963 { type: "subagent.updated" }
7964 > => event.type === "subagent.updated",
7947 );7965 );
79487966
7949 yield* harness.runtime.startTurn(7967 yield* harness.runtime.startTurn(
...@@ -8059,8 +8077,12 @@ describe("ClaudeAdapterV2 background wake turns", () => {...@@ -8059,8 +8077,12 @@ describe("ClaudeAdapterV2 background wake turns", () => {
8059 const now = yield* DateTime.now;8077 const now = yield* DateTime.now;
8060 const subagentEvents = () =>8078 const subagentEvents = () =>
8061 harness.events.filter(8079 harness.events.filter(
8062 (event): event is Extract<ProviderAdapterV2Event, { type: "subagent.updated" }> =>8080 (
8063 event.type === "subagent.updated",8081 event,
8082 ): event is Extract<
8083 ProviderAdapter.ProviderAdapterV2Event,
8084 { type: "subagent.updated" }
8085 > => event.type === "subagent.updated",
8064 );8086 );
80658087
8066 yield* harness.runtime.startTurn(8088 yield* harness.runtime.startTurn(
...@@ -8249,7 +8271,9 @@ describe("ClaudeAdapterV2 background wake turns", () => {...@@ -8249,7 +8271,9 @@ describe("ClaudeAdapterV2 background wake turns", () => {
8249 // already pre-opened by the wake buffer before the drain replay.8271 // already pre-opened by the wake buffer before the drain replay.
8250 const nodeStatuses = harness.events8272 const nodeStatuses = harness.events
8251 .filter(8273 .filter(
8252 (event): event is Extract<ProviderAdapterV2Event, { type: "node.updated" }> =>8274 (
8275 event,
8276 ): event is Extract<ProviderAdapter.ProviderAdapterV2Event, { type: "node.updated" }> =>
8253 event.type === "node.updated" &&8277 event.type === "node.updated" &&
8254 event.node.kind === "subagent" &&8278 event.node.kind === "subagent" &&
8255 event.node.nativeItemRef?.nativeId === SUBAGENT_TASK_ID,8279 event.node.nativeItemRef?.nativeId === SUBAGENT_TASK_ID,
...@@ -8288,8 +8312,9 @@ describe("ClaudeAdapterV2 background wake turns", () => {...@@ -8288,8 +8312,9 @@ describe("ClaudeAdapterV2 background wake turns", () => {
8288 prefix: "t3-claude-v2-process-reset-",8312 prefix: "t3-claude-v2-process-reset-",
8289 });8313 });
8290 const processQueues: Array<Queue.Queue<SDKMessage>> = [];8314 const processQueues: Array<Queue.Queue<SDKMessage>> = [];
8291 const events: Array<ProviderAdapterV2Event> = [];8315 const events: Array<ProviderAdapter.ProviderAdapterV2Event> = [];
8292 const continuationRequests: Array<ProviderContinuationRequest> = [];8316 const continuationRequests: Array<ProviderContinuationRequests.ProviderContinuationRequest> =
8317 [];
8293 const adapter = ClaudeAdapterV2.makeClaudeAdapterV2({8318 const adapter = ClaudeAdapterV2.makeClaudeAdapterV2({
8294 instanceId: ClaudeAdapterV2.CLAUDE_DEFAULT_INSTANCE_ID,8319 instanceId: ClaudeAdapterV2.CLAUDE_DEFAULT_INSTANCE_ID,
8295 settings: DEFAULT_CLAUDE_SETTINGS,8320 settings: DEFAULT_CLAUDE_SETTINGS,
...@@ -8472,8 +8497,9 @@ describe("ClaudeAdapterV2 background wake turns", () => {...@@ -8472,8 +8497,9 @@ describe("ClaudeAdapterV2 background wake turns", () => {
8472 prefix: "t3-claude-v2-buffer-replace-",8497 prefix: "t3-claude-v2-buffer-replace-",
8473 });8498 });
8474 const processQueues: Array<Queue.Queue<SDKMessage>> = [];8499 const processQueues: Array<Queue.Queue<SDKMessage>> = [];
8475 const events: Array<ProviderAdapterV2Event> = [];8500 const events: Array<ProviderAdapter.ProviderAdapterV2Event> = [];
8476 const continuationRequests: Array<ProviderContinuationRequest> = [];8501 const continuationRequests: Array<ProviderContinuationRequests.ProviderContinuationRequest> =
8502 [];
8477 const adapter = ClaudeAdapterV2.makeClaudeAdapterV2({8503 const adapter = ClaudeAdapterV2.makeClaudeAdapterV2({
8478 instanceId: ClaudeAdapterV2.CLAUDE_DEFAULT_INSTANCE_ID,8504 instanceId: ClaudeAdapterV2.CLAUDE_DEFAULT_INSTANCE_ID,
8479 settings: DEFAULT_CLAUDE_SETTINGS,8505 settings: DEFAULT_CLAUDE_SETTINGS,
...@@ -8708,8 +8734,9 @@ describe("ClaudeAdapterV2 background wake turns", () => {...@@ -8708,8 +8734,9 @@ describe("ClaudeAdapterV2 background wake turns", () => {
8708 prefix: "t3-claude-v2-subagent-buffer-replace-",8734 prefix: "t3-claude-v2-subagent-buffer-replace-",
8709 });8735 });
8710 const processQueues: Array<Queue.Queue<SDKMessage>> = [];8736 const processQueues: Array<Queue.Queue<SDKMessage>> = [];
8711 const events: Array<ProviderAdapterV2Event> = [];8737 const events: Array<ProviderAdapter.ProviderAdapterV2Event> = [];
8712 const continuationRequests: Array<ProviderContinuationRequest> = [];8738 const continuationRequests: Array<ProviderContinuationRequests.ProviderContinuationRequest> =
8739 [];
8713 const adapter = ClaudeAdapterV2.makeClaudeAdapterV2({8740 const adapter = ClaudeAdapterV2.makeClaudeAdapterV2({
8714 instanceId: ClaudeAdapterV2.CLAUDE_DEFAULT_INSTANCE_ID,8741 instanceId: ClaudeAdapterV2.CLAUDE_DEFAULT_INSTANCE_ID,
8715 settings: DEFAULT_CLAUDE_SETTINGS,8742 settings: DEFAULT_CLAUDE_SETTINGS,
...@@ -8775,8 +8802,12 @@ describe("ClaudeAdapterV2 background wake turns", () => {...@@ -8775,8 +8802,12 @@ describe("ClaudeAdapterV2 background wake turns", () => {
8775 const hasPendingBackgroundWork = runtime.hasPendingBackgroundWork;8802 const hasPendingBackgroundWork = runtime.hasPendingBackgroundWork;
8776 const subagentEvents = () =>8803 const subagentEvents = () =>
8777 events.filter(8804 events.filter(
8778 (event): event is Extract<ProviderAdapterV2Event, { type: "subagent.updated" }> =>8805 (
8779 event.type === "subagent.updated",8806 event,
8807 ): event is Extract<
8808 ProviderAdapter.ProviderAdapterV2Event,
8809 { type: "subagent.updated" }
8810 > => event.type === "subagent.updated",
8780 );8811 );
8781 const now = yield* DateTime.now;8812 const now = yield* DateTime.now;
87828813
...@@ -8880,7 +8911,9 @@ describe("ClaudeAdapterV2 background wake turns", () => {...@@ -8880,7 +8911,9 @@ describe("ClaudeAdapterV2 background wake turns", () => {
8880 assert.equal(finalSubagent?.result, SUBAGENT_SUMMARY);8911 assert.equal(finalSubagent?.result, SUBAGENT_SUMMARY);
8881 assert.equal(finalSubagent?.runId, subagentEvents()[0]?.subagent.runId);8912 assert.equal(finalSubagent?.runId, subagentEvents()[0]?.subagent.runId);
8882 const subagentNodeEvents = events.filter(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 event.type === "node.updated" &&8917 event.type === "node.updated" &&
8885 event.node.kind === "subagent" &&8918 event.node.kind === "subagent" &&
8886 event.node.nativeItemRef?.nativeId === SUBAGENT_TASK_ID,8919 event.node.nativeItemRef?.nativeId === SUBAGENT_TASK_ID,
...@@ -8902,7 +8935,7 @@ describe("ClaudeAdapterV2 background wake turns", () => {...@@ -8902,7 +8935,7 @@ describe("ClaudeAdapterV2 background wake turns", () => {
8902 prefix: "t3-claude-v2-model-change-running-subagent-",8935 prefix: "t3-claude-v2-model-change-running-subagent-",
8903 });8936 });
8904 const processQueues: Array<Queue.Queue<SDKMessage>> = [];8937 const processQueues: Array<Queue.Queue<SDKMessage>> = [];
8905 const events: Array<ProviderAdapterV2Event> = [];8938 const events: Array<ProviderAdapter.ProviderAdapterV2Event> = [];
8906 const adapter = ClaudeAdapterV2.makeClaudeAdapterV2({8939 const adapter = ClaudeAdapterV2.makeClaudeAdapterV2({
8907 instanceId: ClaudeAdapterV2.CLAUDE_DEFAULT_INSTANCE_ID,8940 instanceId: ClaudeAdapterV2.CLAUDE_DEFAULT_INSTANCE_ID,
8908 settings: DEFAULT_CLAUDE_SETTINGS,8941 settings: DEFAULT_CLAUDE_SETTINGS,
...@@ -9069,7 +9102,7 @@ describe("ClaudeAdapterV2 background wake turns", () => {...@@ -9069,7 +9102,7 @@ describe("ClaudeAdapterV2 background wake turns", () => {
9069 });9102 });
9070 let openCount = 0;9103 let openCount = 0;
9071 const processQueues: Array<Queue.Queue<SDKMessage>> = [];9104 const processQueues: Array<Queue.Queue<SDKMessage>> = [];
9072 const events: Array<ProviderAdapterV2Event> = [];9105 const events: Array<ProviderAdapter.ProviderAdapterV2Event> = [];
9073 const adapter = ClaudeAdapterV2.makeClaudeAdapterV2({9106 const adapter = ClaudeAdapterV2.makeClaudeAdapterV2({
9074 instanceId: ClaudeAdapterV2.CLAUDE_DEFAULT_INSTANCE_ID,9107 instanceId: ClaudeAdapterV2.CLAUDE_DEFAULT_INSTANCE_ID,
9075 settings: DEFAULT_CLAUDE_SETTINGS,9108 settings: DEFAULT_CLAUDE_SETTINGS,
...@@ -9198,8 +9231,9 @@ describe("ClaudeAdapterV2 background wake turns", () => {...@@ -9198,8 +9231,9 @@ describe("ClaudeAdapterV2 background wake turns", () => {
9198 });9231 });
9199 let openCount = 0;9232 let openCount = 0;
9200 const processQueues: Array<Queue.Queue<SDKMessage>> = [];9233 const processQueues: Array<Queue.Queue<SDKMessage>> = [];
9201 const events: Array<ProviderAdapterV2Event> = [];9234 const events: Array<ProviderAdapter.ProviderAdapterV2Event> = [];
9202 const continuationRequests: Array<ProviderContinuationRequest> = [];9235 const continuationRequests: Array<ProviderContinuationRequests.ProviderContinuationRequest> =
9236 [];
9203 const adapter = ClaudeAdapterV2.makeClaudeAdapterV2({9237 const adapter = ClaudeAdapterV2.makeClaudeAdapterV2({
9204 instanceId: ClaudeAdapterV2.CLAUDE_DEFAULT_INSTANCE_ID,9238 instanceId: ClaudeAdapterV2.CLAUDE_DEFAULT_INSTANCE_ID,
9205 settings: DEFAULT_CLAUDE_SETTINGS,9239 settings: DEFAULT_CLAUDE_SETTINGS,
...@@ -9394,7 +9428,7 @@ describe("ClaudeAdapterV2 background wake turns", () => {...@@ -9394,7 +9428,7 @@ describe("ClaudeAdapterV2 background wake turns", () => {
9394 const attachmentsDir = yield* fileSystem.makeTempDirectoryScoped({9428 const attachmentsDir = yield* fileSystem.makeTempDirectoryScoped({
9395 prefix: "t3-claude-v2-first-open-fail-",9429 prefix: "t3-claude-v2-first-open-fail-",
9396 });9430 });
9397 const events: Array<ProviderAdapterV2Event> = [];9431 const events: Array<ProviderAdapter.ProviderAdapterV2Event> = [];
9398 const adapter = ClaudeAdapterV2.makeClaudeAdapterV2({9432 const adapter = ClaudeAdapterV2.makeClaudeAdapterV2({
9399 instanceId: ClaudeAdapterV2.CLAUDE_DEFAULT_INSTANCE_ID,9433 instanceId: ClaudeAdapterV2.CLAUDE_DEFAULT_INSTANCE_ID,
9400 settings: DEFAULT_CLAUDE_SETTINGS,9434 settings: DEFAULT_CLAUDE_SETTINGS,
...@@ -9496,7 +9530,7 @@ describe("ClaudeAdapterV2 background wake turns", () => {...@@ -9496,7 +9530,7 @@ describe("ClaudeAdapterV2 background wake turns", () => {
9496 uuid,9530 uuid,
9497 session_id: WAKE_NATIVE_SESSION,9531 session_id: WAKE_NATIVE_SESSION,
9498 });9532 });
9499 const goalStatuses = (events: ReadonlyArray<ProviderAdapterV2Event>) =>9533 const goalStatuses = (events: ReadonlyArray<ProviderAdapter.ProviderAdapterV2Event>) =>
9500 events.flatMap((event) =>9534 events.flatMap((event) =>
9501 event.type === "provider_thread.updated" && event.providerThread.goal != null9535 event.type === "provider_thread.updated" && event.providerThread.goal != null
9502 ? [event.providerThread.goal]9536 ? [event.providerThread.goal]
apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts+7-11
...@@ -101,10 +101,9 @@ import {...@@ -101,10 +101,9 @@ import {
101} from "../../provider/ClaudeModelCatalog.ts";101} from "../../provider/ClaudeModelCatalog.ts";
102import {102import {
103 boundProviderEventForLogging,103 boundProviderEventForLogging,
104 type EventNdjsonLogger,
105 shouldPersistProviderEvent,104 shouldPersistProviderEvent,
106} from "../../provider/EventNdjsonLogger.ts";105} from "../../provider/EventNdjsonLogger.ts";
107import * as ProviderEventLoggers from "../../provider/ProviderEventLoggers.ts";106import * as ProviderEventLoggers from "@t3tools/provider-core/server/ProviderEventLoggers";
108import {107import {
109 claudeRateLimitEventToUpdate,108 claudeRateLimitEventToUpdate,
110 type ClaudeScopedLimitNames,109 type ClaudeScopedLimitNames,
...@@ -135,7 +134,7 @@ import {...@@ -135,7 +134,7 @@ import {
135 type BackgroundWorkReport,134 type BackgroundWorkReport,
136 backgroundWorkNotification,135 backgroundWorkNotification,
137} from "@t3tools/provider-core/server/notification";136} from "@t3tools/provider-core/server/notification";
138import * as ProviderContinuationRequests from "@t3tools/provider-core/server/continuationRequests";137import * as ProviderContinuationRequests from "@t3tools/provider-core/server/ProviderContinuationRequests";
139import {138import {
140 makeSubagentChildThread,139 makeSubagentChildThread,
141 makeSubagentConversationArtifacts,140 makeSubagentConversationArtifacts,
...@@ -570,7 +569,7 @@ export function loggedClaudeQueryOptions(...@@ -570,7 +569,7 @@ export function loggedClaudeQueryOptions(
570}569}
571570
572export function makeClaudeAgentSdkProtocolLogger(input: {571export function makeClaudeAgentSdkProtocolLogger(input: {
573 readonly nativeEventLogger: EventNdjsonLogger | undefined;572 readonly nativeEventLogger: ProviderEventLoggers.EventNdjsonLogger | undefined;
574 readonly threadId: ThreadId;573 readonly threadId: ThreadId;
575 readonly providerSessionId: OrchestrationV2ProviderSession["id"];574 readonly providerSessionId: OrchestrationV2ProviderSession["id"];
576}): ClaudeAgentSdkProtocolLogger | undefined {575}): ClaudeAgentSdkProtocolLogger | undefined {
...@@ -1118,7 +1117,7 @@ function resultTextFromSdkMessage(...@@ -1118,7 +1117,7 @@ function resultTextFromSdkMessage(
1118}1117}
11191118
1120function makeProviderThread(input: {1119function makeProviderThread(input: {
1121 readonly idAllocator: IdAllocator.IdAllocatorV2Shape;1120 readonly idAllocator: IdAllocator.IdAllocatorV2["Service"];
1122 readonly providerInstanceId: ProviderInstanceId;1121 readonly providerInstanceId: ProviderInstanceId;
1123 readonly appThreadId: OrchestrationV2ProviderThread["appThreadId"];1122 readonly appThreadId: OrchestrationV2ProviderThread["appThreadId"];
1124 readonly ownerNodeId?: OrchestrationV2ProviderThread["ownerNodeId"];1123 readonly ownerNodeId?: OrchestrationV2ProviderThread["ownerNodeId"];
...@@ -2581,7 +2580,7 @@ function providerFailureFromApiRetry(message: SDKAPIRetryMessage): Orchestration...@@ -2581,7 +2580,7 @@ function providerFailureFromApiRetry(message: SDKAPIRetryMessage): Orchestration
2581}2580}
25822581
2583function buildAssistantArtifacts(input: {2582function buildAssistantArtifacts(input: {
2584 readonly idAllocator: IdAllocator.IdAllocatorV2Shape;2583 readonly idAllocator: IdAllocator.IdAllocatorV2["Service"];
2585 readonly turnInput: ProviderAdapter.ProviderAdapterV2TurnInput;2584 readonly turnInput: ProviderAdapter.ProviderAdapterV2TurnInput;
2586 readonly providerTurnId: OrchestrationV2ProviderTurn["id"];2585 readonly providerTurnId: OrchestrationV2ProviderTurn["id"];
2587 readonly nativeItemId: string;2586 readonly nativeItemId: string;
...@@ -3015,7 +3014,7 @@ export interface ClaudeAdapterV2Options {...@@ -3015,7 +3014,7 @@ export interface ClaudeAdapterV2Options {
3015 readonly fileSystem: FileSystem.FileSystem;3014 readonly fileSystem: FileSystem.FileSystem;
3016 readonly path: Path.Path;3015 readonly path: Path.Path;
3017 readonly crypto: Crypto.Crypto;3016 readonly crypto: Crypto.Crypto;
3018 readonly idAllocator: IdAllocator.IdAllocatorV2Shape;3017 readonly idAllocator: IdAllocator.IdAllocatorV2["Service"];
3019 readonly queryRunner: ClaudeAgentSdkQueryRunnerShape;3018 readonly queryRunner: ClaudeAgentSdkQueryRunnerShape;
3020 readonly scopedLimitNames?: Ref.Ref<ClaudeScopedLimitNames>;3019 readonly scopedLimitNames?: Ref.Ref<ClaudeScopedLimitNames>;
3021 readonly onUsageLimits?: ServerProviderShape["applyUsageLimits"];3020 readonly onUsageLimits?: ServerProviderShape["applyUsageLimits"];
...@@ -3029,7 +3028,7 @@ export interface ClaudeAdapterV2Options {...@@ -3029,7 +3028,7 @@ export interface ClaudeAdapterV2Options {
30293028
3030export function makeClaudeAdapterV2(3029export function makeClaudeAdapterV2(
3031 adapterOptions: ClaudeAdapterV2Options,3030 adapterOptions: ClaudeAdapterV2Options,
3032): ProviderAdapter.ProviderAdapterV2Shape {3031): ProviderAdapter.ProviderAdapterV2["Service"] {
3033 const { attachmentsDir, fileSystem, path, crypto, idAllocator, queryRunner } = adapterOptions;3032 const { attachmentsDir, fileSystem, path, crypto, idAllocator, queryRunner } = adapterOptions;
3034 const continuationRequests = adapterOptions.continuationRequests ?? {3033 const continuationRequests = adapterOptions.continuationRequests ?? {
3035 offer: () => Effect.void,3034 offer: () => Effect.void,
...@@ -3356,7 +3355,6 @@ export function makeClaudeAdapterV2(...@@ -3356,7 +3355,6 @@ export function makeClaudeAdapterV2(
3356 type: "turn_item.updated",3355 type: "turn_item.updated",
3357 driver: CLAUDE_PROVIDER,3356 driver: CLAUDE_PROVIDER,
3358 turnItem: makeProviderRetryTurnItem({3357 turnItem: makeProviderRetryTurnItem({
3359 idAllocator,
3360 driver: CLAUDE_PROVIDER,3358 driver: CLAUDE_PROVIDER,
3361 threadId: context.input.threadId,3359 threadId: context.input.threadId,
3362 runId: context.input.runId,3360 runId: context.input.runId,
...@@ -5083,7 +5081,6 @@ export function makeClaudeAdapterV2(...@@ -5083,7 +5081,6 @@ export function makeClaudeAdapterV2(
5083 type: "turn_item.updated",5081 type: "turn_item.updated",
5084 driver: CLAUDE_PROVIDER,5082 driver: CLAUDE_PROVIDER,
5085 turnItem: makeProviderRetryTurnItem({5083 turnItem: makeProviderRetryTurnItem({
5086 idAllocator,
5087 driver: CLAUDE_PROVIDER,5084 driver: CLAUDE_PROVIDER,
5088 threadId: input.context.input.threadId,5085 threadId: input.context.input.threadId,
5089 runId: input.context.input.runId,5086 runId: input.context.input.runId,
...@@ -6089,7 +6086,6 @@ export function makeClaudeAdapterV2(...@@ -6089,7 +6086,6 @@ export function makeClaudeAdapterV2(
6089 type: "turn_item.updated",6086 type: "turn_item.updated",
6090 driver: CLAUDE_PROVIDER,6087 driver: CLAUDE_PROVIDER,
6091 turnItem: makeProviderRetryTurnItem({6088 turnItem: makeProviderRetryTurnItem({
6092 idAllocator,
6093 driver: CLAUDE_PROVIDER,6089 driver: CLAUDE_PROVIDER,
6094 threadId: context.input.threadId,6090 threadId: context.input.threadId,
6095 runId: context.input.runId,6091 runId: context.input.runId,
apps/server/src/orchestration-v2/Adapters/CodexAdapterV2.test.ts+108-57
...@@ -1,7 +1,6 @@...@@ -1,7 +1,6 @@
1import * as NodeOS from "node:os";1import * as NodeOS from "node:os";
22
3import { historyResponseItems } from "@t3tools/provider-core/server/handoffBudget";3import { historyResponseItems } from "@t3tools/provider-core/server/handoffBudget";
4import type { ProviderAdapterV2HistoricalContext } from "@t3tools/provider-core/server/ProviderAdapter";
5import {4import {
6 makeProviderTextDeltaCoalescer,5 makeProviderTextDeltaCoalescer,
7 type ProviderTextDeltaUpdate,6 type ProviderTextDeltaUpdate,
...@@ -56,21 +55,13 @@ import { ChildProcess, ChildProcessSpawner } from "effect/process";...@@ -56,21 +55,13 @@ import { ChildProcess, ChildProcessSpawner } from "effect/process";
56import packageJson from "../../../package.json" with { type: "json" };55import packageJson from "../../../package.json" with { type: "json" };
57import * as ServerConfig from "../../config.ts";56import * as ServerConfig from "../../config.ts";
58import * as McpProviderSession from "@t3tools/provider-core/server/mcpSession";57import * as McpProviderSession from "@t3tools/provider-core/server/mcpSession";
59import type { EventNdjsonLogger } from "../../provider/EventNdjsonLogger.ts";58import * as ProviderEventLoggers from "@t3tools/provider-core/server/ProviderEventLoggers";
60import * as ProviderEventLoggers from "../../provider/ProviderEventLoggers.ts";
61import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator";59import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator";
62import * as EffectWorker from "../EffectWorker.ts";60import * as EffectWorker from "../EffectWorker.ts";
63import * as Orchestrator from "../Orchestrator.ts";61import * as Orchestrator from "../Orchestrator.ts";
64import * as ProviderReplayHarness from "../testkit/ProviderReplayHarness.ts";62import * as ProviderReplayHarness from "../testkit/ProviderReplayHarness.ts";
65import {63import * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter";
66 ProviderAdapterForkThreadError,64import type * as ProviderContinuationRequests from "@t3tools/provider-core/server/ProviderContinuationRequests";
67 ProviderAdapterOpenSessionError,
68 ProviderAdapterRollbackThreadError,
69 ProviderAdapterV2RuntimePolicy,
70 type ProviderAdapterV2Event,
71 type ProviderAdapterV2TurnInput,
72} from "@t3tools/provider-core/server/ProviderAdapter";
73import type { ProviderContinuationRequest } from "@t3tools/provider-core/server/continuationRequests";
74import * as CodexAdapterV2 from "./CodexAdapterV2.ts";65import * as CodexAdapterV2 from "./CodexAdapterV2.ts";
75import { makeReplayServerConfig, withCodexReplayChildMetadata } from "./CodexAdapterV2.testkit.ts";66import { makeReplayServerConfig, withCodexReplayChildMetadata } from "./CodexAdapterV2.testkit.ts";
76import * as CodexAdapterV2Testkit from "./CodexAdapterV2.testkit.ts";67import * as CodexAdapterV2Testkit from "./CodexAdapterV2.testkit.ts";
...@@ -767,7 +758,7 @@ describe("CodexAdapterV2 process spawning", () => {...@@ -767,7 +758,7 @@ describe("CodexAdapterV2 process spawning", () => {
767 instanceId: CodexAdapterV2.CODEX_DEFAULT_INSTANCE_ID,758 instanceId: CodexAdapterV2.CODEX_DEFAULT_INSTANCE_ID,
768 threadId: ThreadId.make("thread-launch-args"),759 threadId: ThreadId.make("thread-launch-args"),
769 providerSessionId: ProviderSessionId.make("provider-session-launch-args"),760 providerSessionId: ProviderSessionId.make("provider-session-launch-args"),
770 runtimePolicy: ProviderAdapterV2RuntimePolicy.make({761 runtimePolicy: ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
771 runtimeMode: "full-access",762 runtimeMode: "full-access",
772 interactionMode: "default",763 interactionMode: "default",
773 cwd: "/workspace",764 cwd: "/workspace",
...@@ -1036,7 +1027,7 @@ describe("CodexAdapterV2 native protocol logging", () => {...@@ -1036,7 +1027,7 @@ describe("CodexAdapterV2 native protocol logging", () => {
1036 readonly event: unknown;1027 readonly event: unknown;
1037 readonly threadId: ThreadId | null;1028 readonly threadId: ThreadId | null;
1038 }> = [];1029 }> = [];
1039 const logger: EventNdjsonLogger = {1030 const logger: ProviderEventLoggers.EventNdjsonLogger = {
1040 filePath: "/tmp/events.log",1031 filePath: "/tmp/events.log",
1041 write: (event, threadId) =>1032 write: (event, threadId) =>
1042 Effect.sync(() => {1033 Effect.sync(() => {
...@@ -1410,7 +1401,7 @@ describe("CodexAdapterV2 fork boundary", () => {...@@ -1410,7 +1401,7 @@ describe("CodexAdapterV2 fork boundary", () => {
1410 }),1401 }),
1411 );1402 );
14121403
1413 assert.instanceOf(error, ProviderAdapterForkThreadError);1404 assert.instanceOf(error, ProviderAdapter.ProviderAdapterForkThreadError);
1414 assert.include(String(error.cause), "provider-turn-missing");1405 assert.include(String(error.cause), "provider-turn-missing");
1415 }),1406 }),
1416 );1407 );
...@@ -1478,7 +1469,7 @@ const CODEX_TEST_MODEL_SELECTION = {...@@ -1478,7 +1469,7 @@ const CODEX_TEST_MODEL_SELECTION = {
1478 instanceId: CodexAdapterV2.CODEX_DEFAULT_INSTANCE_ID,1469 instanceId: CodexAdapterV2.CODEX_DEFAULT_INSTANCE_ID,
1479 model: "gpt-5.4",1470 model: "gpt-5.4",
1480} satisfies ModelSelection;1471} satisfies ModelSelection;
1481const CODEX_TEST_RUNTIME_POLICY = ProviderAdapterV2RuntimePolicy.make({1472const CODEX_TEST_RUNTIME_POLICY = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
1482 runtimeMode: "full-access",1473 runtimeMode: "full-access",
1483 interactionMode: "default",1474 interactionMode: "default",
1484 cwd: "/workspace",1475 cwd: "/workspace",
...@@ -1524,7 +1515,7 @@ function makeCodexTestTurnInput(input: {...@@ -1524,7 +1515,7 @@ function makeCodexTestTurnInput(input: {
1524 readonly now: DateTime.Utc;1515 readonly now: DateTime.Utc;
1525 readonly attemptId: RunAttemptId;1516 readonly attemptId: RunAttemptId;
1526 readonly text: string;1517 readonly text: string;
1527}): ProviderAdapterV2TurnInput {1518}): ProviderAdapter.ProviderAdapterV2TurnInput {
1528 return {1519 return {
1529 appThread: makeCodexTestAppThread(input),1520 appThread: makeCodexTestAppThread(input),
1530 threadId: input.threadId,1521 threadId: input.threadId,
...@@ -1747,7 +1738,7 @@ describe("CodexAdapterV2 session initialize", () => {...@@ -1747,7 +1738,7 @@ describe("CodexAdapterV2 session initialize", () => {
1747 ),1738 ),
1748 Effect.mapError(1739 Effect.mapError(
1749 (cause) =>1740 (cause) =>
1750 new ProviderAdapterOpenSessionError({1741 new ProviderAdapter.ProviderAdapterOpenSessionError({
1751 driver: CodexAdapterV2.CODEX_DRIVER_KIND,1742 driver: CodexAdapterV2.CODEX_DRIVER_KIND,
1752 providerSessionId: openInput.providerSessionId,1743 providerSessionId: openInput.providerSessionId,
1753 cause,1744 cause,
...@@ -1907,7 +1898,8 @@ describe("CodexAdapterV2 post-settle continuation", () => {...@@ -1907,7 +1898,8 @@ describe("CodexAdapterV2 post-settle continuation", () => {
19071898
1908 const makeCodexReplayHarness = (1899 const makeCodexReplayHarness = (
1909 transcript: CodexReplay.CodexAppServerReplayTranscript,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 onRequest: (method: string, params: unknown) => Effect.Effect<void> = () => Effect.void,1903 onRequest: (method: string, params: unknown) => Effect.Effect<void> = () => Effect.void,
1912 readChildMetadata?: Parameters<typeof withCodexReplayChildMetadata>[2],1904 readChildMetadata?: Parameters<typeof withCodexReplayChildMetadata>[2],
1913 ) =>1905 ) =>
...@@ -1915,13 +1907,14 @@ describe("CodexAdapterV2 post-settle continuation", () => {...@@ -1915,13 +1907,14 @@ describe("CodexAdapterV2 post-settle continuation", () => {
1915 const fileSystem = yield* FileSystem.FileSystem;1907 const fileSystem = yield* FileSystem.FileSystem;
1916 const idAllocator = yield* IdAllocator.IdAllocatorV2;1908 const idAllocator = yield* IdAllocator.IdAllocatorV2;
1917 const serverConfig = yield* makeReplayServerConfig(transcript.scenario).pipe(Effect.orDie);1909 const serverConfig = yield* makeReplayServerConfig(transcript.scenario).pipe(Effect.orDie);
1918 const continuationRequests: Array<ProviderContinuationRequest> = [];1910 const continuationRequests: Array<ProviderContinuationRequests.ProviderContinuationRequest> =
1911 [];
1919 const clientFactory: CodexAdapterV2.CodexAppServerClientFactoryShape = {1912 const clientFactory: CodexAdapterV2.CodexAppServerClientFactoryShape = {
1920 open: (openInput) =>1913 open: (openInput) =>
1921 Layer.build(CodexReplay.layerReplay(transcript)).pipe(1914 Layer.build(CodexReplay.layerReplay(transcript)).pipe(
1922 Effect.mapError(1915 Effect.mapError(
1923 (cause) =>1916 (cause) =>
1924 new ProviderAdapterOpenSessionError({1917 new ProviderAdapter.ProviderAdapterOpenSessionError({
1925 driver: CodexAdapterV2.CODEX_DRIVER_KIND,1918 driver: CodexAdapterV2.CODEX_DRIVER_KIND,
1926 providerSessionId: openInput.providerSessionId,1919 providerSessionId: openInput.providerSessionId,
1927 cause,1920 cause,
...@@ -1975,7 +1968,7 @@ describe("CodexAdapterV2 post-settle continuation", () => {...@@ -1975,7 +1968,7 @@ describe("CodexAdapterV2 post-settle continuation", () => {
1975 modelSelection: CODEX_TEST_MODEL_SELECTION,1968 modelSelection: CODEX_TEST_MODEL_SELECTION,
1976 runtimePolicy: CODEX_TEST_RUNTIME_POLICY,1969 runtimePolicy: CODEX_TEST_RUNTIME_POLICY,
1977 });1970 });
1978 const events: Array<ProviderAdapterV2Event> = [];1971 const events: Array<ProviderAdapter.ProviderAdapterV2Event> = [];
1979 const firstTerminal = yield* Deferred.make<void>();1972 const firstTerminal = yield* Deferred.make<void>();
1980 yield* runtime.events.pipe(1973 yield* runtime.events.pipe(
1981 Stream.runForEach((event) =>1974 Stream.runForEach((event) =>
...@@ -1998,13 +1991,19 @@ describe("CodexAdapterV2 post-settle continuation", () => {...@@ -1998,13 +1991,19 @@ describe("CodexAdapterV2 post-settle continuation", () => {
1998 const hasPendingBackgroundWork = runtime.hasPendingBackgroundWork;1991 const hasPendingBackgroundWork = runtime.hasPendingBackgroundWork;
1999 const terminalEvents = () =>1992 const terminalEvents = () =>
2000 events.filter(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 event.type === "turn.terminal",1997 event.type === "turn.terminal",
2003 );1998 );
2004 const subagentUpdates = () =>1999 const subagentUpdates = () =>
2005 events.filter(2000 events.filter(
2006 (event): event is Extract<ProviderAdapterV2Event, { type: "subagent.updated" }> =>2001 (
2007 event.type === "subagent.updated",2002 event,
2003 ): event is Extract<
2004 ProviderAdapter.ProviderAdapterV2Event,
2005 { type: "subagent.updated" }
2006 > => event.type === "subagent.updated",
2008 );2007 );
2009 return {2008 return {
2010 runtime,2009 runtime,
...@@ -2026,7 +2025,7 @@ describe("CodexAdapterV2 post-settle continuation", () => {...@@ -2026,7 +2025,7 @@ describe("CodexAdapterV2 post-settle continuation", () => {
2026 Effect.gen(function* () {2025 Effect.gen(function* () {
2027 const nativeThreadId = `inject-${response}`;2026 const nativeThreadId = `inject-${response}`;
2028 const prompt = "Only the current request";2027 const prompt = "Only the current request";
2029 const history: ProviderAdapterV2HistoricalContext = {2028 const history: ProviderAdapter.ProviderAdapterV2HistoricalContext = {
2030 context: "Historical conversation",2029 context: "Historical conversation",
2031 messages: (["user", "assistant"] as const).map((role) => ({2030 messages: (["user", "assistant"] as const).map((role) => ({
2032 role,2031 role,
...@@ -2528,12 +2527,14 @@ describe("CodexAdapterV2 post-settle continuation", () => {...@@ -2528,12 +2527,14 @@ describe("CodexAdapterV2 post-settle continuation", () => {
2528 }).pipe(Effect.scoped, Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))),2527 }).pipe(Effect.scoped, Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))),
2529 );2528 );
25302529
2531 const assistantMessages = (events: ReadonlyArray<ProviderAdapterV2Event>) =>2530 const assistantMessages = (events: ReadonlyArray<ProviderAdapter.ProviderAdapterV2Event>) =>
2532 events.filter(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 event.type === "message.updated" && event.message.role === "assistant",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 events.flatMap((event) =>2538 events.flatMap((event) =>
2538 event.type === "turn_item.updated" && event.turnItem.type === "assistant_message"2539 event.type === "turn_item.updated" && event.turnItem.type === "assistant_message"
2539 ? [event.turnItem]2540 ? [event.turnItem]
...@@ -4914,8 +4915,12 @@ describe("CodexAdapterV2 post-settle continuation", () => {...@@ -4914,8 +4915,12 @@ describe("CodexAdapterV2 post-settle continuation", () => {
4914 );4915 );
49154916
4916 const providerTurnId = harness.events.find(4917 const providerTurnId = harness.events.find(
4917 (event): event is Extract<ProviderAdapterV2Event, { type: "provider_turn.updated" }> =>4918 (
4918 event.type === "provider_turn.updated",4919 event,
4920 ): event is Extract<
4921 ProviderAdapter.ProviderAdapterV2Event,
4922 { type: "provider_turn.updated" }
4923 > => event.type === "provider_turn.updated",
4919 )?.providerTurn.id;4924 )?.providerTurn.id;
4920 assert.isDefined(providerTurnId);4925 assert.isDefined(providerTurnId);
49214926
...@@ -4931,7 +4936,7 @@ describe("CodexAdapterV2 post-settle continuation", () => {...@@ -4931,7 +4936,7 @@ describe("CodexAdapterV2 post-settle continuation", () => {
4931 assert.isAtLeast(terminalIndex, 0);4936 assert.isAtLeast(terminalIndex, 0);
49324937
4933 let lastCommandBeforeTerminal:4938 let lastCommandBeforeTerminal:
4934 | Extract<ProviderAdapterV2Event, { type: "turn_item.updated" }>4939 | Extract<ProviderAdapter.ProviderAdapterV2Event, { type: "turn_item.updated" }>
4935 | undefined;4940 | undefined;
4936 for (let index = 0; index < terminalIndex; index++) {4941 for (let index = 0; index < terminalIndex; index++) {
4937 const event = harness.events[index];4942 const event = harness.events[index];
...@@ -4997,8 +5002,12 @@ describe("CodexAdapterV2 post-settle continuation", () => {...@@ -4997,8 +5002,12 @@ describe("CodexAdapterV2 post-settle continuation", () => {
4997 );5002 );
49985003
4999 const commandUpdates = harness.events.filter(5004 const commandUpdates = harness.events.filter(
5000 (event): event is Extract<ProviderAdapterV2Event, { type: "turn_item.updated" }> =>5005 (
5001 event.type === "turn_item.updated" && event.turnItem.type === "command_execution",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 assert.isAtLeast(commandUpdates.length, 2, "start + interrupt terminalization");5012 assert.isAtLeast(commandUpdates.length, 2, "start + interrupt terminalization");
5004 assert.equal(commandUpdates[commandUpdates.length - 1]?.turnItem.status, "interrupted");5013 assert.equal(commandUpdates[commandUpdates.length - 1]?.turnItem.status, "interrupted");
...@@ -5142,7 +5151,7 @@ describe("CodexAdapterV2 post-settle continuation", () => {...@@ -5142,7 +5151,7 @@ describe("CodexAdapterV2 post-settle continuation", () => {
5142 });5151 });
51435152
5144 const assertChildProviderTerminalBeforeRoot = (5153 const assertChildProviderTerminalBeforeRoot = (
5145 events: ReadonlyArray<ProviderAdapterV2Event>,5154 events: ReadonlyArray<ProviderAdapter.ProviderAdapterV2Event>,
5146 rootThreadId: ThreadId,5155 rootThreadId: ThreadId,
5147 ) => {5156 ) => {
5148 const terminalIndex = events.findIndex((event) => event.type === "turn.terminal");5157 const terminalIndex = events.findIndex((event) => event.type === "turn.terminal");
...@@ -5199,8 +5208,12 @@ describe("CodexAdapterV2 post-settle continuation", () => {...@@ -5199,8 +5208,12 @@ describe("CodexAdapterV2 post-settle continuation", () => {
5199 "running child command item",5208 "running child command item",
5200 );5209 );
5201 const providerTurnId = harness.events.find(5210 const providerTurnId = harness.events.find(
5202 (event): event is Extract<ProviderAdapterV2Event, { type: "provider_turn.updated" }> =>5211 (
5203 event.type === "provider_turn.updated" && event.threadId === harness.threadId,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 )?.providerTurn.id;5217 )?.providerTurn.id;
5205 assert.isDefined(providerTurnId);5218 assert.isDefined(providerTurnId);
52065219
...@@ -5212,7 +5225,12 @@ describe("CodexAdapterV2 post-settle continuation", () => {...@@ -5212,7 +5225,12 @@ describe("CodexAdapterV2 post-settle continuation", () => {
5212 yield* awaitUntil(() => harness.terminalEvents().length === 1, "interrupted root terminal");5225 yield* awaitUntil(() => harness.terminalEvents().length === 1, "interrupted root terminal");
5213 assert.equal(harness.terminalEvents()[0]?.status, "interrupted");5226 assert.equal(harness.terminalEvents()[0]?.status, "interrupted");
5214 const childCommandUpdates = harness.events.filter(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 event.type === "turn_item.updated" &&5234 event.type === "turn_item.updated" &&
5217 event.turnItem.type === "command_execution" &&5235 event.turnItem.type === "command_execution" &&
5218 event.turnItem.nativeItemRef?.nativeId === INTERRUPT_CHILD_COMMAND_ITEM,5236 event.turnItem.nativeItemRef?.nativeId === INTERRUPT_CHILD_COMMAND_ITEM,
...@@ -5289,8 +5307,12 @@ describe("CodexAdapterV2 post-settle continuation", () => {...@@ -5289,8 +5307,12 @@ describe("CodexAdapterV2 post-settle continuation", () => {
5289 "running child command item",5307 "running child command item",
5290 );5308 );
5291 const providerTurnId = harness.events.find(5309 const providerTurnId = harness.events.find(
5292 (event): event is Extract<ProviderAdapterV2Event, { type: "provider_turn.updated" }> =>5310 (
5293 event.type === "provider_turn.updated" && event.threadId === harness.threadId,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 )?.providerTurn.id;5316 )?.providerTurn.id;
5295 assert.isDefined(providerTurnId);5317 assert.isDefined(providerTurnId);
52965318
...@@ -5361,8 +5383,12 @@ describe("CodexAdapterV2 post-settle continuation", () => {...@@ -5361,8 +5383,12 @@ describe("CodexAdapterV2 post-settle continuation", () => {
5361 "running child command item",5383 "running child command item",
5362 );5384 );
5363 const providerTurnId = harness.events.find(5385 const providerTurnId = harness.events.find(
5364 (event): event is Extract<ProviderAdapterV2Event, { type: "provider_turn.updated" }> =>5386 (
5365 event.type === "provider_turn.updated" && event.threadId === harness.threadId,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 )?.providerTurn.id;5392 )?.providerTurn.id;
5367 assert.isDefined(providerTurnId);5393 assert.isDefined(providerTurnId);
53685394
...@@ -5460,8 +5486,12 @@ describe("CodexAdapterV2 post-settle continuation", () => {...@@ -5460,8 +5486,12 @@ describe("CodexAdapterV2 post-settle continuation", () => {
5460 "running child command item",5486 "running child command item",
5461 );5487 );
5462 const providerTurnId = harness.events.find(5488 const providerTurnId = harness.events.find(
5463 (event): event is Extract<ProviderAdapterV2Event, { type: "provider_turn.updated" }> =>5489 (
5464 event.type === "provider_turn.updated" && event.threadId === harness.threadId,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 )?.providerTurn.id;5495 )?.providerTurn.id;
5466 assert.isDefined(providerTurnId);5496 assert.isDefined(providerTurnId);
54675497
...@@ -5481,7 +5511,12 @@ describe("CodexAdapterV2 post-settle continuation", () => {...@@ -5481,7 +5511,12 @@ describe("CodexAdapterV2 post-settle continuation", () => {
54815511
5482 yield* awaitUntil(() => harness.terminalEvents().length === 1, "interrupted root terminal");5512 yield* awaitUntil(() => harness.terminalEvents().length === 1, "interrupted root terminal");
5483 const lateChildUpdates = harness.events.filter(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 event.type === "provider_turn.updated" &&5520 event.type === "provider_turn.updated" &&
5486 event.providerTurn.nativeTurnRef?.nativeId === INTERRUPT_LATE_CHILD_NATIVE_TURN,5521 event.providerTurn.nativeTurnRef?.nativeId === INTERRUPT_LATE_CHILD_NATIVE_TURN,
5487 );5522 );
...@@ -5589,8 +5624,12 @@ describe("CodexAdapterV2 post-settle continuation", () => {...@@ -5589,8 +5624,12 @@ describe("CodexAdapterV2 post-settle continuation", () => {
5589 "running child command item",5624 "running child command item",
5590 );5625 );
5591 const providerTurnId = harness.events.find(5626 const providerTurnId = harness.events.find(
5592 (event): event is Extract<ProviderAdapterV2Event, { type: "provider_turn.updated" }> =>5627 (
5593 event.type === "provider_turn.updated" && event.threadId === harness.threadId,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 )?.providerTurn.id;5633 )?.providerTurn.id;
5595 assert.isDefined(providerTurnId);5634 assert.isDefined(providerTurnId);
55965635
...@@ -5835,8 +5874,12 @@ describe("CodexAdapterV2 post-settle continuation", () => {...@@ -5835,8 +5874,12 @@ describe("CodexAdapterV2 post-settle continuation", () => {
5835 "running command item",5874 "running command item",
5836 );5875 );
5837 const providerTurnId = harness.events.find(5876 const providerTurnId = harness.events.find(
5838 (event): event is Extract<ProviderAdapterV2Event, { type: "provider_turn.updated" }> =>5877 (
5839 event.type === "provider_turn.updated",5878 event,
5879 ): event is Extract<
5880 ProviderAdapter.ProviderAdapterV2Event,
5881 { type: "provider_turn.updated" }
5882 > => event.type === "provider_turn.updated",
5840 )?.providerTurn.id;5883 )?.providerTurn.id;
5841 assert.isDefined(providerTurnId);5884 assert.isDefined(providerTurnId);
58425885
...@@ -5868,8 +5911,12 @@ describe("CodexAdapterV2 post-settle continuation", () => {...@@ -5868,8 +5911,12 @@ describe("CodexAdapterV2 post-settle continuation", () => {
5868 assert.lengthOf(terminalProviderTurnsBeforeLateEvents, 1);5911 assert.lengthOf(terminalProviderTurnsBeforeLateEvents, 1);
58695912
5870 const commandUpdatesBeforeLateEvents = harness.events.filter(5913 const commandUpdatesBeforeLateEvents = harness.events.filter(
5871 (event): event is Extract<ProviderAdapterV2Event, { type: "turn_item.updated" }> =>5914 (
5872 event.type === "turn_item.updated" && event.turnItem.type === "command_execution",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 const terminalCommands = commandUpdatesBeforeLateEvents.filter(5921 const terminalCommands = commandUpdatesBeforeLateEvents.filter(
5875 (event) =>5922 (event) =>
...@@ -5993,8 +6040,12 @@ describe("CodexAdapterV2 post-settle continuation", () => {...@@ -5993,8 +6040,12 @@ describe("CodexAdapterV2 post-settle continuation", () => {
5993 "running command item",6040 "running command item",
5994 );6041 );
5995 const providerTurnId = harness.events.find(6042 const providerTurnId = harness.events.find(
5996 (event): event is Extract<ProviderAdapterV2Event, { type: "provider_turn.updated" }> =>6043 (
5997 event.type === "provider_turn.updated",6044 event,
6045 ): event is Extract<
6046 ProviderAdapter.ProviderAdapterV2Event,
6047 { type: "provider_turn.updated" }
6048 > => event.type === "provider_turn.updated",
5998 )?.providerTurn.id;6049 )?.providerTurn.id;
5999 assert.isDefined(providerTurnId);6050 assert.isDefined(providerTurnId);
60006051
...@@ -7663,7 +7714,7 @@ describe("CodexAdapterV2 post-settle continuation", () => {...@@ -7663,7 +7714,7 @@ describe("CodexAdapterV2 post-settle continuation", () => {
7663 }),7714 }),
7664 );7715 );
76657716
7666 assert.instanceOf(error, ProviderAdapterRollbackThreadError);7717 assert.instanceOf(error, ProviderAdapter.ProviderAdapterRollbackThreadError);
7667 assert.include(7718 assert.include(
7668 errorCauseChainText(error),7719 errorCauseChainText(error),
7669 "legacy",7720 "legacy",
...@@ -7904,7 +7955,7 @@ describe("CodexAdapterV2 post-settle continuation", () => {...@@ -7904,7 +7955,7 @@ describe("CodexAdapterV2 post-settle continuation", () => {
7904 }),7955 }),
7905 );7956 );
79067957
7907 assert.instanceOf(error, ProviderAdapterForkThreadError);7958 assert.instanceOf(error, ProviderAdapter.ProviderAdapterForkThreadError);
7908 assert.include(7959 assert.include(
7909 errorCauseChainText(error),7960 errorCauseChainText(error),
7910 "legacy",7961 "legacy",
...@@ -7969,7 +8020,7 @@ describe("CodexAdapterV2 post-settle continuation", () => {...@@ -7969,7 +8020,7 @@ describe("CodexAdapterV2 post-settle continuation", () => {
7969 }),8020 }),
7970 );8021 );
79718022
7972 assert.instanceOf(error, ProviderAdapterForkThreadError);8023 assert.instanceOf(error, ProviderAdapter.ProviderAdapterForkThreadError);
7973 assert.include(errorCauseChainText(error), "fork exploded");8024 assert.include(errorCauseChainText(error), "fork exploded");
7974 }).pipe(Effect.scoped, Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))),8025 }).pipe(Effect.scoped, Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))),
7975 );8026 );
...@@ -8033,7 +8084,7 @@ describe("CodexAdapterV2 post-settle continuation", () => {...@@ -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 events.flatMap((event) =>8088 events.flatMap((event) =>
8038 event.type === "provider_thread.updated" ? [event.providerThread.goal?.status ?? null] : [],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,7 +15,7 @@ import * as Schema from "effect/Schema";
1515
16import * as ServerConfig from "../../config.ts";16import * as ServerConfig from "../../config.ts";
17import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator";17import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator";
18import { ProviderAdapterOpenSessionError } from "@t3tools/provider-core/server/ProviderAdapter";18import * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter";
19import { ProviderAdapterDriverCreateError } from "@t3tools/provider-core/server/adapterDriver";19import { ProviderAdapterDriverCreateError } from "@t3tools/provider-core/server/adapterDriver";
20import * as ProviderAdapterRegistry from "../ProviderAdapterRegistry.ts";20import * as ProviderAdapterRegistry from "../ProviderAdapterRegistry.ts";
21import type { OrchestratorV2ProviderReplayHarness } from "../testkit/ProviderReplayHarness.ts";21import type { OrchestratorV2ProviderReplayHarness } from "../testkit/ProviderReplayHarness.ts";
...@@ -195,7 +195,7 @@ export function layer(input: {...@@ -195,7 +195,7 @@ export function layer(input: {
195 const context = yield* Layer.build(layerReplay).pipe(195 const context = yield* Layer.build(layerReplay).pipe(
196 Effect.mapError(196 Effect.mapError(
197 (cause) =>197 (cause) =>
198 new ProviderAdapterOpenSessionError({198 new ProviderAdapter.ProviderAdapterOpenSessionError({
199 driver: CodexAdapterV2.CODEX_DRIVER_KIND,199 driver: CodexAdapterV2.CODEX_DRIVER_KIND,
200 providerSessionId: openInput.providerSessionId,200 providerSessionId: openInput.providerSessionId,
201 cause,201 cause,
apps/server/src/orchestration-v2/Adapters/CodexAdapterV2.ts+102-106
...@@ -102,10 +102,9 @@ import {...@@ -102,10 +102,9 @@ import {
102} from "../../provider/Drivers/CodexHomeLayout.ts";102} from "../../provider/Drivers/CodexHomeLayout.ts";
103import {103import {
104 boundProviderEventForLogging,104 boundProviderEventForLogging,
105 type EventNdjsonLogger,
106 shouldPersistProviderEvent,105 shouldPersistProviderEvent,
107} from "../../provider/EventNdjsonLogger.ts";106} from "../../provider/EventNdjsonLogger.ts";
108import { ProviderEventLoggers } from "../../provider/ProviderEventLoggers.ts";107import * as ProviderEventLoggers from "@t3tools/provider-core/server/ProviderEventLoggers";
109import { codexAppServerArgs, resolveCodexLaunchArgs } from "../../provider/codexLaunchArgs.ts";108import { codexAppServerArgs, resolveCodexLaunchArgs } from "../../provider/codexLaunchArgs.ts";
110import { mergeProviderInstanceEnvironment } from "@t3tools/provider-core/server/instanceEnvironment";109import { mergeProviderInstanceEnvironment } from "@t3tools/provider-core/server/instanceEnvironment";
111import * as McpProviderSession from "@t3tools/provider-core/server/mcpSession";110import * as McpProviderSession from "@t3tools/provider-core/server/mcpSession";
...@@ -121,11 +120,7 @@ import {...@@ -121,11 +120,7 @@ import {
121 type ProviderAdapterDriver,120 type ProviderAdapterDriver,
122 type ProviderAdapterDriverCreateInput,121 type ProviderAdapterDriverCreateInput,
123} from "@t3tools/provider-core/server/adapterDriver";122} from "@t3tools/provider-core/server/adapterDriver";
124import { IdAllocatorV2, type IdAllocatorV2Shape } from "@t3tools/provider-core/server/IdAllocator";123import * as ProviderContinuationRequests from "@t3tools/provider-core/server/ProviderContinuationRequests";
125import {
126 type ProviderContinuationRequest,
127 ProviderContinuationRequests,
128} from "@t3tools/provider-core/server/continuationRequests";
129import { backgroundWorkNotification } from "@t3tools/provider-core/server/notification";124import { backgroundWorkNotification } from "@t3tools/provider-core/server/notification";
130import {125import {
131 makeProviderFailure,126 makeProviderFailure,
...@@ -137,35 +132,13 @@ import {...@@ -137,35 +132,13 @@ import {
137 isProviderNativeImageAttachment,132 isProviderNativeImageAttachment,
138 providerMessageTextWithAttachmentPaths,133 providerMessageTextWithAttachmentPaths,
139} from "@t3tools/provider-core/server/attachmentPrompt";134} from "@t3tools/provider-core/server/attachmentPrompt";
140import {
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";
164import {135import {
165 makeSubagentChildThread,136 makeSubagentChildThread,
166 makeSubagentConversationArtifacts,137 makeSubagentConversationArtifacts,
167 subagentThreadTitle,138 subagentThreadTitle,
168} from "@t3tools/provider-core/server/subagentProjection";139} from "@t3tools/provider-core/server/subagentProjection";
140import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator";
141import * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter";
169142
170const CODEX_PROVIDER = ProviderDriverKind.make("codex");143const CODEX_PROVIDER = ProviderDriverKind.make("codex");
171export const CODEX_DRIVER_KIND = CODEX_PROVIDER;144export const CODEX_DRIVER_KIND = CODEX_PROVIDER;
...@@ -341,8 +314,11 @@ export const CodexProviderCapabilitiesV2 = {...@@ -341,8 +314,11 @@ export const CodexProviderCapabilitiesV2 = {
341 },314 },
342} satisfies OrchestrationV2ProviderCapabilities;315} satisfies OrchestrationV2ProviderCapabilities;
343316
344function toProtocolError(detail: string, payload?: unknown): ProviderAdapterProtocolError {317function toProtocolError(
345 return new ProviderAdapterProtocolError({318 detail: string,
319 payload?: unknown,
320): ProviderAdapter.ProviderAdapterProtocolError {
321 return new ProviderAdapter.ProviderAdapterProtocolError({
346 driver: CODEX_PROVIDER,322 driver: CODEX_PROVIDER,
347 detail,323 detail,
348 ...(payload === undefined ? {} : { payload }),324 ...(payload === undefined ? {} : { payload }),
...@@ -385,7 +361,7 @@ function mapCodexTurnStatus(...@@ -385,7 +361,7 @@ function mapCodexTurnStatus(
385361
386function providerTurnStatusToTerminal(362function providerTurnStatusToTerminal(
387 status: OrchestrationV2ProviderTurn["status"],363 status: OrchestrationV2ProviderTurn["status"],
388): Extract<ProviderAdapterV2Event, { type: "turn.terminal" }>["status"] {364): Extract<ProviderAdapter.ProviderAdapterV2Event, { type: "turn.terminal" }>["status"] {
389 switch (status) {365 switch (status) {
390 case "completed":366 case "completed":
391 return "completed";367 return "completed";
...@@ -677,7 +653,7 @@ const decodeCodexTurnStartParamsWithCollaborationMode = Schema.decodeUnknownEffe...@@ -677,7 +653,7 @@ const decodeCodexTurnStartParamsWithCollaborationMode = Schema.decodeUnknownEffe
677 CodexTurnStartParamsWithCollaborationMode,653 CodexTurnStartParamsWithCollaborationMode,
678);654);
679const isProviderAdapterRuntimeRequestResponseError = Schema.is(655const isProviderAdapterRuntimeRequestResponseError = Schema.is(
680 ProviderAdapterRuntimeRequestResponseError,656 ProviderAdapter.ProviderAdapterRuntimeRequestResponseError,
681);657);
682658
683function codexRuntimeModeTurnDefaults(runtimeMode: RuntimeMode): {659function codexRuntimeModeTurnDefaults(runtimeMode: RuntimeMode): {
...@@ -724,7 +700,7 @@ function codexRuntimeModeTurnDefaults(runtimeMode: RuntimeMode): {...@@ -724,7 +700,7 @@ function codexRuntimeModeTurnDefaults(runtimeMode: RuntimeMode): {
724export function buildCodexTurnStartParams(input: {700export function buildCodexTurnStartParams(input: {
725 readonly nativeThreadId: string;701 readonly nativeThreadId: string;
726 readonly codexInput: ReadonlyArray<CodexSchema.V2TurnStartParams__UserInput>;702 readonly codexInput: ReadonlyArray<CodexSchema.V2TurnStartParams__UserInput>;
727 readonly runtimePolicy: ProviderAdapterV2RuntimePolicy;703 readonly runtimePolicy: ProviderAdapter.ProviderAdapterV2RuntimePolicy;
728 readonly modelSelection: ModelSelection;704 readonly modelSelection: ModelSelection;
729 readonly hasT3Mcp?: boolean;705 readonly hasT3Mcp?: boolean;
730 readonly browserToolsAvailable?: boolean;706 readonly browserToolsAvailable?: boolean;
...@@ -732,7 +708,7 @@ export function buildCodexTurnStartParams(input: {...@@ -732,7 +708,7 @@ export function buildCodexTurnStartParams(input: {
732 /** ChatGPT token sharing does not accept service tiers. */708 /** ChatGPT token sharing does not accept service tiers. */
733 readonly omitServiceTier?: boolean;709 readonly omitServiceTier?: boolean;
734 /** What the thread's MCP Apps want the agent to know (`ui/update-model-context`). */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 return Effect.gen(function* () {713 return Effect.gen(function* () {
738 const runtimeModeDefaults = codexRuntimeModeTurnDefaults(input.runtimePolicy.runtimeMode);714 const runtimeModeDefaults = codexRuntimeModeTurnDefaults(input.runtimePolicy.runtimeMode);
...@@ -851,7 +827,7 @@ function getNativeThreadId(providerThread: OrchestrationV2ProviderThread) {...@@ -851,7 +827,7 @@ function getNativeThreadId(providerThread: OrchestrationV2ProviderThread) {
851827
852function providerThreadFromCodexThread(input: {828function providerThreadFromCodexThread(input: {
853 readonly appThreadId: ThreadId | null;829 readonly appThreadId: ThreadId | null;
854 readonly idAllocator: IdAllocatorV2Shape;830 readonly idAllocator: IdAllocator.IdAllocatorV2["Service"];
855 readonly ownerNodeId: OrchestrationV2ProviderThread["ownerNodeId"];831 readonly ownerNodeId: OrchestrationV2ProviderThread["ownerNodeId"];
856 readonly providerSessionId: OrchestrationV2ProviderThread["providerSessionId"];832 readonly providerSessionId: OrchestrationV2ProviderThread["providerSessionId"];
857 readonly providerInstanceId: ProviderInstanceId;833 readonly providerInstanceId: ProviderInstanceId;
...@@ -983,7 +959,7 @@ const countTerminalTurnsAfterBoundary = (...@@ -983,7 +959,7 @@ const countTerminalTurnsAfterBoundary = (
983};959};
984960
985const resolveCodexForkRollbackTurnCount = Effect.fn("CodexAdapterV2.resolveForkRollbackTurnCount")(961const resolveCodexForkRollbackTurnCount = Effect.fn("CodexAdapterV2.resolveForkRollbackTurnCount")(
986 function* (input: ProviderAdapterV2ForkThreadInput) {962 function* (input: ProviderAdapter.ProviderAdapterV2ForkThreadInput) {
987 if (input.providerTurnId === undefined || input.sourceProviderTurns === undefined) {963 if (input.providerTurnId === undefined || input.sourceProviderTurns === undefined) {
988 return 0;964 return 0;
989 }965 }
...@@ -993,7 +969,7 @@ const resolveCodexForkRollbackTurnCount = Effect.fn("CodexAdapterV2.resolveForkR...@@ -993,7 +969,7 @@ const resolveCodexForkRollbackTurnCount = Effect.fn("CodexAdapterV2.resolveForkR
993 input.providerTurnId,969 input.providerTurnId,
994 );970 );
995 if (rollbackTurnCount === null) {971 if (rollbackTurnCount === null) {
996 return yield* new ProviderAdapterForkThreadError({972 return yield* new ProviderAdapter.ProviderAdapterForkThreadError({
997 driver: CODEX_PROVIDER,973 driver: CODEX_PROVIDER,
998 providerThreadId: input.sourceProviderThread.id,974 providerThreadId: input.sourceProviderThread.id,
999 cause: `Cannot fork Codex thread from provider turn ${input.providerTurnId}: source turn was not found in provider thread ${input.sourceProviderThread.id}.`,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,7 +988,7 @@ const resolveCodexForkRollbackTurnCount = Effect.fn("CodexAdapterV2.resolveForkR
1012 * only for source turns that predate native turn references.988 * only for source turns that predate native turn references.
1013 */989 */
1014export const resolveCodexForkBoundary = Effect.fn("CodexAdapterV2.resolveForkBoundary")(function* (990export const resolveCodexForkBoundary = Effect.fn("CodexAdapterV2.resolveForkBoundary")(function* (
1015 input: ProviderAdapterV2ForkThreadInput,991 input: ProviderAdapter.ProviderAdapterV2ForkThreadInput,
1016) {992) {
1017 const rollbackTurnCount = yield* resolveCodexForkRollbackTurnCount(input);993 const rollbackTurnCount = yield* resolveCodexForkRollbackTurnCount(input);
1018 if (input.providerTurnId === undefined || input.sourceProviderTurns === undefined) {994 if (input.providerTurnId === undefined || input.sourceProviderTurns === undefined) {
...@@ -1066,14 +1042,14 @@ const readCodexThreadHistoryMetadata = Effect.fn("CodexAdapterV2.readThreadHisto...@@ -1066,14 +1042,14 @@ const readCodexThreadHistoryMetadata = Effect.fn("CodexAdapterV2.readThreadHisto
1066);1042);
10671043
1068export const resolveCodexRollbackTurnCount = Effect.fn("CodexAdapterV2.resolveRollbackTurnCount")(1044export const resolveCodexRollbackTurnCount = Effect.fn("CodexAdapterV2.resolveRollbackTurnCount")(
1069 function* (input: ProviderAdapterV2RollbackThreadInput) {1045 function* (input: ProviderAdapter.ProviderAdapterV2RollbackThreadInput) {
1070 const providerTurns = input.providerThreadTurns;1046 const providerTurns = input.providerThreadTurns;
1071 switch (input.target.type) {1047 switch (input.target.type) {
1072 case "thread_start":1048 case "thread_start":
1073 return providerTurns.filter(isTerminalProviderTurn).length;1049 return providerTurns.filter(isTerminalProviderTurn).length;
1074 case "provider_turn": {1050 case "provider_turn": {
1075 if (input.target.providerTurn.providerThreadId !== input.providerThread.id) {1051 if (input.target.providerTurn.providerThreadId !== input.providerThread.id) {
1076 return yield* new ProviderAdapterRollbackThreadError({1052 return yield* new ProviderAdapter.ProviderAdapterRollbackThreadError({
1077 driver: CODEX_PROVIDER,1053 driver: CODEX_PROVIDER,
1078 providerThreadId: input.providerThread.id,1054 providerThreadId: input.providerThread.id,
1079 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}.`,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,7 +1061,7 @@ export const resolveCodexRollbackTurnCount = Effect.fn("CodexAdapterV2.resolveRo
1085 input.target.providerTurn.id,1061 input.target.providerTurn.id,
1086 );1062 );
1087 if (rollbackTurnCount === null) {1063 if (rollbackTurnCount === null) {
1088 return yield* new ProviderAdapterRollbackThreadError({1064 return yield* new ProviderAdapter.ProviderAdapterRollbackThreadError({
1089 driver: CODEX_PROVIDER,1065 driver: CODEX_PROVIDER,
1090 providerThreadId: input.providerThread.id,1066 providerThreadId: input.providerThread.id,
1091 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.`,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,10 +1105,10 @@ interface ActiveCodexTurnContext {
1129 readonly failure: OrchestrationV2ProviderFailure;1105 readonly failure: OrchestrationV2ProviderFailure;
1130 };1106 };
1131 readonly nativeStartReady?: Deferred.Deferred<void>;1107 readonly nativeStartReady?: Deferred.Deferred<void>;
1132 readonly input: ProviderAdapterV2TurnInput;1108 readonly input: ProviderAdapter.ProviderAdapterV2TurnInput;
1133 readonly projectionAppThread: OrchestrationV2AppThread;1109 readonly projectionAppThread: OrchestrationV2AppThread;
1134 readonly projectionThreadId: ThreadId;1110 readonly projectionThreadId: ThreadId;
1135 readonly projectionRunId: ProviderAdapterV2TurnInput["runId"] | null;1111 readonly projectionRunId: ProviderAdapter.ProviderAdapterV2TurnInput["runId"] | null;
1136 readonly nativeTurnId: string;1112 readonly nativeTurnId: string;
1137 readonly providerThread: OrchestrationV2ProviderThread;1113 readonly providerThread: OrchestrationV2ProviderThread;
1138 readonly providerTurnId: ProviderTurnId;1114 readonly providerTurnId: ProviderTurnId;
...@@ -1177,7 +1153,10 @@ function isPersistentCodexDynamicTool(item: CodexDynamicToolItem): boolean {...@@ -1177,7 +1153,10 @@ function isPersistentCodexDynamicTool(item: CodexDynamicToolItem): boolean {
1177 return Reflect.get(input, "persistent") === true;1153 return Reflect.get(input, "persistent") === true;
1178}1154}
11791155
1180type CodexRootTerminalEvent = Extract<ProviderAdapterV2Event, { readonly type: "turn.terminal" }>;1156type CodexRootTerminalEvent = Extract<
1157 ProviderAdapter.ProviderAdapterV2Event,
1158 { readonly type: "turn.terminal" }
1159>;
11811160
1182/** A completed root turn whose run stays open for the goal turn Codex starts next. */1161/** A completed root turn whose run stays open for the goal turn Codex starts next. */
1183interface CodexGoalHold {1162interface CodexGoalHold {
...@@ -1294,12 +1273,12 @@ export interface CodexAppServerClientFactoryShape {...@@ -1294,12 +1273,12 @@ export interface CodexAppServerClientFactoryShape {
1294 readonly instanceId: ProviderInstanceId;1273 readonly instanceId: ProviderInstanceId;
1295 readonly threadId: ThreadId;1274 readonly threadId: ThreadId;
1296 readonly providerSessionId: OrchestrationV2ProviderSession["id"];1275 readonly providerSessionId: OrchestrationV2ProviderSession["id"];
1297 readonly runtimePolicy: ProviderAdapterV2RuntimePolicy;1276 readonly runtimePolicy: ProviderAdapter.ProviderAdapterV2RuntimePolicy;
1298 readonly settings: CodexSettings;1277 readonly settings: CodexSettings;
1299 readonly environment: NodeJS.ProcessEnv;1278 readonly environment: NodeJS.ProcessEnv;
1300 }) => Effect.Effect<1279 }) => Effect.Effect<
1301 CodexClient.CodexAppServerClient["Service"],1280 CodexClient.CodexAppServerClient["Service"],
1302 ProviderAdapterOpenSessionError,1281 ProviderAdapter.ProviderAdapterOpenSessionError,
1303 Scope.Scope1282 Scope.Scope
1304 >;1283 >;
1305}1284}
...@@ -1319,7 +1298,7 @@ export const CODEX_THREAD_CONFIG = { "tools.update_plan.enabled": true } as cons...@@ -1319,7 +1298,7 @@ export const CODEX_THREAD_CONFIG = { "tools.update_plan.enabled": true } as cons
1319export function codexThreadRuntimeParams(input: {1298export function codexThreadRuntimeParams(input: {
1320 readonly threadId: ThreadId | null;1299 readonly threadId: ThreadId | null;
1321 readonly modelSelection?: { readonly model: string };1300 readonly modelSelection?: { readonly model: string };
1322 readonly runtimePolicy?: ProviderAdapterV2RuntimePolicy;1301 readonly runtimePolicy?: ProviderAdapter.ProviderAdapterV2RuntimePolicy;
1323}): {1302}): {
1324 readonly cwd?: string;1303 readonly cwd?: string;
1325 readonly model?: string;1304 readonly model?: string;
...@@ -1417,7 +1396,7 @@ const layerCodexAppServerClientFactoryCommand = (...@@ -1417,7 +1396,7 @@ const layerCodexAppServerClientFactoryCommand = (
1417 Effect.provideService(Scope.Scope, scope),1396 Effect.provideService(Scope.Scope, scope),
1418 Effect.mapError(1397 Effect.mapError(
1419 (cause) =>1398 (cause) =>
1420 new ProviderAdapterOpenSessionError({1399 new ProviderAdapter.ProviderAdapterOpenSessionError({
1421 driver: CODEX_PROVIDER,1400 driver: CODEX_PROVIDER,
1422 providerSessionId: input.providerSessionId,1401 providerSessionId: input.providerSessionId,
1423 cause,1402 cause,
...@@ -1434,7 +1413,7 @@ const layerCodexAppServerClientFactoryCommand = (...@@ -1434,7 +1413,7 @@ const layerCodexAppServerClientFactoryCommand = (
1434 );1413 );
14351414
1436export function makeCodexAppServerProtocolLogger(input: {1415export function makeCodexAppServerProtocolLogger(input: {
1437 readonly nativeEventLogger: EventNdjsonLogger | undefined;1416 readonly nativeEventLogger: ProviderEventLoggers.EventNdjsonLogger | undefined;
1438 readonly threadId: ThreadId;1417 readonly threadId: ThreadId;
1439 readonly providerSessionId: OrchestrationV2ProviderSession["id"];1418 readonly providerSessionId: OrchestrationV2ProviderSession["id"];
1440}): CodexClient.CodexAppServerClientOptions["logger"] | undefined {1419}): CodexClient.CodexAppServerClientOptions["logger"] | undefined {
...@@ -1510,12 +1489,12 @@ function isSensitiveCodexProtocolKey(key: string): boolean {...@@ -1510,12 +1489,12 @@ function isSensitiveCodexProtocolKey(key: string): boolean {
1510export const layerAppServerClientFactory: Layer.Layer<1489export const layerAppServerClientFactory: Layer.Layer<
1511 CodexAppServerClientFactory,1490 CodexAppServerClientFactory,
1512 never,1491 never,
1513 ChildProcessSpawner.ChildProcessSpawner | ProviderEventLoggers1492 ChildProcessSpawner.ChildProcessSpawner | ProviderEventLoggers.ProviderEventLoggers
1514> = Layer.effect(1493> = Layer.effect(
1515 CodexAppServerClientFactory,1494 CodexAppServerClientFactory,
1516 Effect.gen(function* () {1495 Effect.gen(function* () {
1517 const spawner = yield* ChildProcessSpawner.ChildProcessSpawner;1496 const spawner = yield* ChildProcessSpawner.ChildProcessSpawner;
1518 const { native: nativeEventLogger } = yield* ProviderEventLoggers;1497 const { native: nativeEventLogger } = yield* ProviderEventLoggers.ProviderEventLoggers;
15191498
1520 return CodexAppServerClientFactory.of({1499 return CodexAppServerClientFactory.of({
1521 open: (input) =>1500 open: (input) =>
...@@ -1536,7 +1515,7 @@ export const layerAppServerClientFactory: Layer.Layer<...@@ -1536,7 +1515,7 @@ export const layerAppServerClientFactory: Layer.Layer<
1536 Effect.provideService(Scope.Scope, scope),1515 Effect.provideService(Scope.Scope, scope),
1537 Effect.mapError(1516 Effect.mapError(
1538 (cause) =>1517 (cause) =>
1539 new ProviderAdapterOpenSessionError({1518 new ProviderAdapter.ProviderAdapterOpenSessionError({
1540 driver: CODEX_PROVIDER,1519 driver: CODEX_PROVIDER,
1541 providerSessionId: input.providerSessionId,1520 providerSessionId: input.providerSessionId,
1542 cause,1521 cause,
...@@ -1569,7 +1548,7 @@ export type CodexAdapterV2DriverEnv =...@@ -1569,7 +1548,7 @@ export type CodexAdapterV2DriverEnv =
1569 | CodexAppServerClientFactory1548 | CodexAppServerClientFactory
1570 | Crypto.Crypto1549 | Crypto.Crypto
1571 | FileSystem.FileSystem1550 | FileSystem.FileSystem
1572 | IdAllocatorV21551 | IdAllocator.IdAllocatorV2
1573 | Path.Path1552 | Path.Path
1574 | ServerConfig;1553 | ServerConfig;
15751554
...@@ -1579,11 +1558,11 @@ export const createCodexAdapterV2 = (...@@ -1579,11 +1558,11 @@ export const createCodexAdapterV2 = (
1579) =>1558) =>
1580 Effect.gen(function* () {1559 Effect.gen(function* () {
1581 const clientFactory = yield* CodexAppServerClientFactory;1560 const clientFactory = yield* CodexAppServerClientFactory;
1582 const continuationRequests = yield* ProviderContinuationRequests;1561 const continuationRequests = yield* ProviderContinuationRequests.ProviderContinuationRequests;
1583 const crypto = yield* Crypto.Crypto;1562 const crypto = yield* Crypto.Crypto;
1584 const fileSystem = yield* FileSystem.FileSystem;1563 const fileSystem = yield* FileSystem.FileSystem;
1585 const hostEnvironment = yield* HostProcessEnvironment;1564 const hostEnvironment = yield* HostProcessEnvironment;
1586 const idAllocator = yield* IdAllocatorV2;1565 const idAllocator = yield* IdAllocator.IdAllocatorV2;
1587 const serverConfig = yield* ServerConfig;1566 const serverConfig = yield* ServerConfig;
1588 const homeLayout = yield* resolveCodexHomeLayout(config);1567 const homeLayout = yield* resolveCodexHomeLayout(config);
15891568
...@@ -1628,18 +1607,22 @@ export const CodexAdapterV2Driver: ProviderAdapterDriver<CodexSettings, CodexAda...@@ -1628,18 +1607,22 @@ export const CodexAdapterV2Driver: ProviderAdapterDriver<CodexSettings, CodexAda
1628};1607};
16291608
1630const layer: Layer.Layer<1609const layer: Layer.Layer<
1631 ProviderAdapterV2,1610 ProviderAdapter.ProviderAdapterV2,
1632 never,1611 never,
1633 CodexAppServerClientFactory | Crypto.Crypto | FileSystem.FileSystem | IdAllocatorV2 | ServerConfig1612 | CodexAppServerClientFactory
1613 | Crypto.Crypto
1614 | FileSystem.FileSystem
1615 | IdAllocator.IdAllocatorV2
1616 | ServerConfig
1634> = Layer.effect(1617> = Layer.effect(
1635 ProviderAdapterV2,1618 ProviderAdapter.ProviderAdapterV2,
1636 Effect.gen(function* () {1619 Effect.gen(function* () {
1637 const clientFactory = yield* CodexAppServerClientFactory;1620 const clientFactory = yield* CodexAppServerClientFactory;
1638 const continuationRequests = yield* ProviderContinuationRequests;1621 const continuationRequests = yield* ProviderContinuationRequests.ProviderContinuationRequests;
1639 const crypto = yield* Crypto.Crypto;1622 const crypto = yield* Crypto.Crypto;
1640 const fileSystem = yield* FileSystem.FileSystem;1623 const fileSystem = yield* FileSystem.FileSystem;
1641 const hostEnvironment = yield* HostProcessEnvironment;1624 const hostEnvironment = yield* HostProcessEnvironment;
1642 const idAllocator = yield* IdAllocatorV2;1625 const idAllocator = yield* IdAllocator.IdAllocatorV2;
1643 const serverConfig = yield* ServerConfig;1626 const serverConfig = yield* ServerConfig;
16441627
1645 return makeCodexAdapterV2({1628 return makeCodexAdapterV2({
...@@ -1670,7 +1653,7 @@ export interface CodexAdapterV2Options {...@@ -1670,7 +1653,7 @@ export interface CodexAdapterV2Options {
1670 readonly resolveRuntime?: Effect.Effect<CodexEffectiveRuntime, ProviderSetupError, Scope.Scope>;1653 readonly resolveRuntime?: Effect.Effect<CodexEffectiveRuntime, ProviderSetupError, Scope.Scope>;
1671 readonly crypto: Crypto.Crypto;1654 readonly crypto: Crypto.Crypto;
1672 readonly fileSystem: FileSystem.FileSystem;1655 readonly fileSystem: FileSystem.FileSystem;
1673 readonly idAllocator: IdAllocatorV2Shape;1656 readonly idAllocator: IdAllocator.IdAllocatorV2["Service"];
1674 readonly serverConfig: ServerConfig["Service"];1657 readonly serverConfig: ServerConfig["Service"];
1675 /**1658 /**
1676 * Sink for post-settle background command completions so the orchestrator1659 * Sink for post-settle background command completions so the orchestrator
...@@ -1678,15 +1661,19 @@ export interface CodexAdapterV2Options {...@@ -1678,15 +1661,19 @@ export interface CodexAdapterV2Options {
1678 * projection-only handling for late item completions.1661 * projection-only handling for late item completions.
1679 */1662 */
1680 readonly continuationRequests?: {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}
16841669
1685export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): ProviderAdapterV2Shape {1670export function makeCodexAdapterV2(
1671 adapterOptions: CodexAdapterV2Options,
1672): ProviderAdapter.ProviderAdapterV2["Service"] {
1686 const { clientFactory, crypto, fileSystem, idAllocator, serverConfig } = adapterOptions;1673 const { clientFactory, crypto, fileSystem, idAllocator, serverConfig } = adapterOptions;
1687 const continuationRequests = adapterOptions.continuationRequests;1674 const continuationRequests = adapterOptions.continuationRequests;
16881675
1689 return ProviderAdapterV2.of({1676 return ProviderAdapter.ProviderAdapterV2.of({
1690 instanceId: adapterOptions.instanceId,1677 instanceId: adapterOptions.instanceId,
1691 driver: CODEX_PROVIDER,1678 driver: CODEX_PROVIDER,
1692 getCapabilities: () => Effect.succeed(CodexProviderCapabilitiesV2),1679 getCapabilities: () => Effect.succeed(CodexProviderCapabilitiesV2),
...@@ -1700,7 +1687,7 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi...@@ -1700,7 +1687,7 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi
1700 : yield* adapterOptions.resolveRuntime.pipe(1687 : yield* adapterOptions.resolveRuntime.pipe(
1701 Effect.mapError(1688 Effect.mapError(
1702 (cause) =>1689 (cause) =>
1703 new ProviderAdapterOpenSessionError({1690 new ProviderAdapter.ProviderAdapterOpenSessionError({
1704 driver: CODEX_PROVIDER,1691 driver: CODEX_PROVIDER,
1705 providerSessionId: input.providerSessionId,1692 providerSessionId: input.providerSessionId,
1706 cause,1693 cause,
...@@ -1800,7 +1787,7 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi...@@ -1800,7 +1787,7 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi
1800 model: input.modelSelection.model,1787 model: input.modelSelection.model,
1801 now,1788 now,
1802 });1789 });
1803 const events = yield* Queue.unbounded<ProviderAdapterV2Event>();1790 const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>();
1804 const rateLimitSnapshot = yield* Ref.make<CodexRateLimitSnapshot | undefined>(undefined);1791 const rateLimitSnapshot = yield* Ref.make<CodexRateLimitSnapshot | undefined>(undefined);
1805 const limitedTurnItems = yield* Ref.make(1792 const limitedTurnItems = yield* Ref.make(
1806 new Map<ProviderThreadId, Extract<OrchestrationV2TurnItem, { type: "error" }>>(),1793 new Map<ProviderThreadId, Extract<OrchestrationV2TurnItem, { type: "error" }>>(),
...@@ -1833,7 +1820,9 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi...@@ -1833,7 +1820,9 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi
1833 context.nativeTurnId,1820 context.nativeTurnId,
1834 ).hasSubagents = true;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 const turnWaiters = yield* Ref.make(new Map<string, Deferred.Deferred<void, never>>());1826 const turnWaiters = yield* Ref.make(new Map<string, Deferred.Deferred<void, never>>());
1838 const subagentThreads = yield* Ref.make(new Map<string, CodexSubagentThreadContext>());1827 const subagentThreads = yield* Ref.make(new Map<string, CodexSubagentThreadContext>());
1839 const subagentSelections = new Map<string, Omit<ModelSelection, "instanceId">>();1828 const subagentSelections = new Map<string, Omit<ModelSelection, "instanceId">>();
...@@ -1893,7 +1882,7 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi...@@ -1893,7 +1882,7 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi
1893 const latestGoalTurnId = (providerTurnId: ProviderTurnId) =>1882 const latestGoalTurnId = (providerTurnId: ProviderTurnId) =>
1894 goalRuns.get(providerTurnId)?.at(-1)?.providerTurnId ?? providerTurnId;1883 goalRuns.get(providerTurnId)?.at(-1)?.providerTurnId ?? providerTurnId;
18951884
1896 const emitProviderEvent = (event: ProviderAdapterV2Event) =>1885 const emitProviderEvent = (event: ProviderAdapter.ProviderAdapterV2Event) =>
1897 Queue.offer(events, event).pipe(Effect.asVoid);1886 Queue.offer(events, event).pipe(Effect.asVoid);
18981887
1899 /** Writes the thread's current native goal onto its root provider thread. */1888 /** Writes the thread's current native goal onto its root provider thread. */
...@@ -1942,7 +1931,6 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi...@@ -1942,7 +1931,6 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi
1942 type: "turn_item.updated",1931 type: "turn_item.updated",
1943 driver: CODEX_PROVIDER,1932 driver: CODEX_PROVIDER,
1944 turnItem: makeProviderRetryTurnItem({1933 turnItem: makeProviderRetryTurnItem({
1945 idAllocator,
1946 driver: CODEX_PROVIDER,1934 driver: CODEX_PROVIDER,
1947 threadId: context.projectionThreadId,1935 threadId: context.projectionThreadId,
1948 runId: context.projectionRunId,1936 runId: context.projectionRunId,
...@@ -1960,7 +1948,7 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi...@@ -1960,7 +1948,7 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi
1960 });1948 });
19611949
1962 const registerRootTurn = (input: {1950 const registerRootTurn = (input: {
1963 readonly turnInput: ProviderAdapterV2TurnInput;1951 readonly turnInput: ProviderAdapter.ProviderAdapterV2TurnInput;
1964 readonly nativeTurnId: string;1952 readonly nativeTurnId: string;
1965 readonly startedAt: DateTime.Utc;1953 readonly startedAt: DateTime.Utc;
1966 readonly waitForNativeStart?: boolean;1954 readonly waitForNativeStart?: boolean;
...@@ -3143,7 +3131,11 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi...@@ -3143,7 +3131,11 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi
3143 });3131 });
31443132
3145 const toCodexInput = (3133 const toCodexInput = (
3146 turnInput: Pick<ProviderAdapterV2TurnInput | ProviderAdapterV2SteerInput, "message">,3134 turnInput: Pick<
3135 | ProviderAdapter.ProviderAdapterV2TurnInput
3136 | ProviderAdapter.ProviderAdapterV2SteerInput,
3137 "message"
3138 >,
3147 ) =>3139 ) =>
3148 Effect.gen(function* () {3140 Effect.gen(function* () {
3149 const inputItems: Array<CodexSchema.V2TurnStartParams__UserInput> = [];3141 const inputItems: Array<CodexSchema.V2TurnStartParams__UserInput> = [];
...@@ -4539,7 +4531,6 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi...@@ -4539,7 +4531,6 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi
4539 type: "turn_item.updated",4531 type: "turn_item.updated",
4540 driver: CODEX_PROVIDER,4532 driver: CODEX_PROVIDER,
4541 turnItem: makeProviderRetryTurnItem({4533 turnItem: makeProviderRetryTurnItem({
4542 idAllocator,
4543 driver: CODEX_PROVIDER,4534 driver: CODEX_PROVIDER,
4544 threadId: context.projectionThreadId,4535 threadId: context.projectionThreadId,
4545 runId: context.projectionRunId,4536 runId: context.projectionRunId,
...@@ -5584,7 +5575,6 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi...@@ -5584,7 +5575,6 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi
5584 }5575 }
5585 if (current.status === "failed" && current.failure.class === "usage_limit") {5576 if (current.status === "failed" && current.failure.class === "usage_limit") {
5586 const item = makeProviderFailureTurnItem({5577 const item = makeProviderFailureTurnItem({
5587 idAllocator,
5588 driver: CODEX_PROVIDER,5578 driver: CODEX_PROVIDER,
5589 threadId: context.input.threadId,5579 threadId: context.input.threadId,
5590 runId: context.input.runId,5580 runId: context.input.runId,
...@@ -5735,7 +5725,6 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi...@@ -5735,7 +5725,6 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi
5735 type: "turn_item.updated",5725 type: "turn_item.updated",
5736 driver: CODEX_PROVIDER,5726 driver: CODEX_PROVIDER,
5737 turnItem: makeProviderRetryTurnItem({5727 turnItem: makeProviderRetryTurnItem({
5738 idAllocator,
5739 driver: CODEX_PROVIDER,5728 driver: CODEX_PROVIDER,
5740 threadId: input.context.projectionThreadId,5729 threadId: input.context.projectionThreadId,
5741 runId: input.context.projectionRunId,5730 runId: input.context.projectionRunId,
...@@ -6000,7 +5989,7 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi...@@ -6000,7 +5989,7 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi
6000 * assistant text, as Claude's own `/goal` output is.5989 * assistant text, as Claude's own `/goal` output is.
6001 */5990 */
6002 const completeGoalCommandTurn = Effect.fnUntraced(function* (5991 const completeGoalCommandTurn = Effect.fnUntraced(function* (
6003 turnInput: ProviderAdapterV2TurnInput,5992 turnInput: ProviderAdapter.ProviderAdapterV2TurnInput,
6004 reply: string,5993 reply: string,
6005 ) {5994 ) {
6006 const now = yield* DateTime.now;5995 const now = yield* DateTime.now;
...@@ -6104,7 +6093,7 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi...@@ -6104,7 +6093,7 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi
6104 * Stop also reaches.6093 * Stop also reaches.
6105 */6094 */
6106 const resolveGoalStopTarget = Effect.fnUntraced(function* (6095 const resolveGoalStopTarget = Effect.fnUntraced(function* (
6107 turnInput: ProviderAdapterV2InterruptInput,6096 turnInput: ProviderAdapter.ProviderAdapterV2InterruptInput,
6108 ) {6097 ) {
6109 const goalThreadId = turnInput.providerThread.nativeThreadRef?.nativeId;6098 const goalThreadId = turnInput.providerThread.nativeThreadRef?.nativeId;
6110 if (goalThreadId == null) {6099 if (goalThreadId == null) {
...@@ -6172,7 +6161,7 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi...@@ -6172,7 +6161,7 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi
61726161
6173 /** Starts a native turn with this run's full turn configuration. */6162 /** Starts a native turn with this run's full turn configuration. */
6174 const startNativeTurn = (6163 const startNativeTurn = (
6175 turnInput: ProviderAdapterV2TurnInput,6164 turnInput: ProviderAdapter.ProviderAdapterV2TurnInput,
6176 codexInput: ReadonlyArray<CodexSchema.V2TurnStartParams__UserInput>,6165 codexInput: ReadonlyArray<CodexSchema.V2TurnStartParams__UserInput>,
6177 ) =>6166 ) =>
6178 Effect.gen(function* () {6167 Effect.gen(function* () {
...@@ -6227,7 +6216,10 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi...@@ -6227,7 +6216,10 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi
6227 * turn Codex starts on its own reuses the last turn's settings. Codex6216 * turn Codex starts on its own reuses the last turn's settings. Codex
6228 * continues later goal turns itself. Other commands settle with a reply.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 Effect.gen(function* () {6223 Effect.gen(function* () {
6232 const threadId = yield* getNativeThreadId(turnInput.providerThread);6224 const threadId = yield* getNativeThreadId(turnInput.providerThread);
6233 // Goal notifications during the command belong to this run's snapshot.6225 // Goal notifications during the command belong to this run's snapshot.
...@@ -6326,7 +6318,7 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi...@@ -6326,7 +6318,7 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi
6326 }).pipe(6318 }).pipe(
6327 Effect.mapError(6319 Effect.mapError(
6328 (cause) =>6320 (cause) =>
6329 new ProviderAdapterTurnStartError({6321 new ProviderAdapter.ProviderAdapterTurnStartError({
6330 driver: CODEX_PROVIDER,6322 driver: CODEX_PROVIDER,
6331 threadId: turnInput.threadId,6323 threadId: turnInput.threadId,
6332 providerThreadId: turnInput.providerThread.id,6324 providerThreadId: turnInput.providerThread.id,
...@@ -6339,8 +6331,12 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi...@@ -6339,8 +6331,12 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi
6339 // MCP Apps reach their server through Codex's own MCP client, so any6331 // MCP Apps reach their server through Codex's own MCP client, so any
6340 // server the user configured for Codex works without T3 connecting to it.6332 // server the user configured for Codex works without T3 connecting to it.
6341 const mcpAppsError = (detail: string) => (cause: unknown) =>6333 const mcpAppsError = (detail: string) => (cause: unknown) =>
6342 new ProviderAdapterProtocolError({ driver: CODEX_PROVIDER, detail, payload: cause });6334 new ProviderAdapter.ProviderAdapterProtocolError({
6343 const mcpApps: ProviderAdapterV2McpApps = {6335 driver: CODEX_PROVIDER,
6336 detail,
6337 payload: cause,
6338 });
6339 const mcpApps: ProviderAdapter.ProviderAdapterV2McpApps = {
6344 listTools: (toolsInput) =>6340 listTools: (toolsInput) =>
6345 Effect.gen(function* () {6341 Effect.gen(function* () {
6346 const threadId = yield* getNativeThreadId(toolsInput.providerThread);6342 const threadId = yield* getNativeThreadId(toolsInput.providerThread);
...@@ -6394,7 +6390,7 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi...@@ -6394,7 +6390,7 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi
6394 }).pipe(Effect.mapError(mcpAppsError("Codex MCP resource read failed."))),6390 }).pipe(Effect.mapError(mcpAppsError("Codex MCP resource read failed."))),
6395 };6391 };
63966392
6397 const runtime: ProviderAdapterV2SessionRuntime = {6393 const runtime: ProviderAdapter.ProviderAdapterV2SessionRuntime = {
6398 instanceId: adapterOptions.instanceId,6394 instanceId: adapterOptions.instanceId,
6399 driver: CODEX_PROVIDER,6395 driver: CODEX_PROVIDER,
6400 providerSessionId: input.providerSessionId,6396 providerSessionId: input.providerSessionId,
...@@ -6471,7 +6467,7 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi...@@ -6471,7 +6467,7 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi
6471 ),6467 ),
6472 Effect.mapError(6468 Effect.mapError(
6473 (cause) =>6469 (cause) =>
6474 new ProviderAdapterEnsureThreadError({6470 new ProviderAdapter.ProviderAdapterEnsureThreadError({
6475 driver: CODEX_PROVIDER,6471 driver: CODEX_PROVIDER,
6476 threadId: threadInput.threadId,6472 threadId: threadInput.threadId,
6477 cause: normalizeCodexCause(cause),6473 cause: normalizeCodexCause(cause),
...@@ -6535,7 +6531,7 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi...@@ -6535,7 +6531,7 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi
6535 }).pipe(6531 }).pipe(
6536 Effect.mapError(6532 Effect.mapError(
6537 (cause) =>6533 (cause) =>
6538 new ProviderAdapterResumeThreadError({6534 new ProviderAdapter.ProviderAdapterResumeThreadError({
6539 driver: CODEX_PROVIDER,6535 driver: CODEX_PROVIDER,
6540 providerSessionId: input.providerSessionId,6536 providerSessionId: input.providerSessionId,
6541 providerThreadId: threadInput.providerThread.id,6537 providerThreadId: threadInput.providerThread.id,
...@@ -6563,7 +6559,7 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi...@@ -6563,7 +6559,7 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi
6563 }).pipe(6559 }).pipe(
6564 Effect.mapError(6560 Effect.mapError(
6565 (cause) =>6561 (cause) =>
6566 new ProviderAdapterTurnStartError({6562 new ProviderAdapter.ProviderAdapterTurnStartError({
6567 driver: CODEX_PROVIDER,6563 driver: CODEX_PROVIDER,
6568 threadId: turnInput.threadId,6564 threadId: turnInput.threadId,
6569 providerThreadId: turnInput.providerThread.id,6565 providerThreadId: turnInput.providerThread.id,
...@@ -6593,7 +6589,7 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi...@@ -6593,7 +6589,7 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi
6593 }).pipe(6589 }).pipe(
6594 Effect.mapError(6590 Effect.mapError(
6595 (cause) =>6591 (cause) =>
6596 new ProviderAdapterProtocolError({6592 new ProviderAdapter.ProviderAdapterProtocolError({
6597 driver: CODEX_PROVIDER,6593 driver: CODEX_PROVIDER,
6598 detail: "Failed to inject historical context",6594 detail: "Failed to inject historical context",
6599 cause,6595 cause,
...@@ -6617,7 +6613,7 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi...@@ -6617,7 +6613,7 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi
6617 }).pipe(6613 }).pipe(
6618 Effect.mapError(6614 Effect.mapError(
6619 (cause) =>6615 (cause) =>
6620 new ProviderAdapterTurnStartError({6616 new ProviderAdapter.ProviderAdapterTurnStartError({
6621 driver: CODEX_PROVIDER,6617 driver: CODEX_PROVIDER,
6622 threadId: turnInput.threadId,6618 threadId: turnInput.threadId,
6623 providerThreadId: turnInput.providerThread.id,6619 providerThreadId: turnInput.providerThread.id,
...@@ -6655,7 +6651,7 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi...@@ -6655,7 +6651,7 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi
6655 }).pipe(6651 }).pipe(
6656 Effect.mapError(6652 Effect.mapError(
6657 (cause) =>6653 (cause) =>
6658 new ProviderAdapterSteerRunError({6654 new ProviderAdapter.ProviderAdapterSteerRunError({
6659 driver: CODEX_PROVIDER,6655 driver: CODEX_PROVIDER,
6660 providerThreadId: turnInput.providerThread.id,6656 providerThreadId: turnInput.providerThread.id,
6661 providerTurnId: turnInput.providerTurnId,6657 providerTurnId: turnInput.providerTurnId,
...@@ -6674,7 +6670,7 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi...@@ -6674,7 +6670,7 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi
6674 Effect.mapError((cause) =>6670 Effect.mapError((cause) =>
6675 cause._tag === "ProviderAdapterProtocolError"6671 cause._tag === "ProviderAdapterProtocolError"
6676 ? cause6672 ? cause
6677 : new ProviderAdapterProtocolError({6673 : new ProviderAdapter.ProviderAdapterProtocolError({
6678 driver: CODEX_PROVIDER,6674 driver: CODEX_PROVIDER,
6679 detail: `Failed to unload Codex thread for provider thread ${unloadInput.providerThread.id}`,6675 detail: `Failed to unload Codex thread for provider thread ${unloadInput.providerThread.id}`,
6680 cause: normalizeCodexCause(cause),6676 cause: normalizeCodexCause(cause),
...@@ -7061,7 +7057,7 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi...@@ -7061,7 +7057,7 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi
7061 }).pipe(7057 }).pipe(
7062 Effect.mapError(7058 Effect.mapError(
7063 (cause) =>7059 (cause) =>
7064 new ProviderAdapterInterruptError({7060 new ProviderAdapter.ProviderAdapterInterruptError({
7065 driver: CODEX_PROVIDER,7061 driver: CODEX_PROVIDER,
7066 providerThreadId: requestedInput.providerThread.id,7062 providerThreadId: requestedInput.providerThread.id,
7067 providerTurnId: requestedInput.providerTurnId,7063 providerTurnId: requestedInput.providerTurnId,
...@@ -7075,7 +7071,7 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi...@@ -7075,7 +7071,7 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi
7075 String(requestInput.requestId),7071 String(requestInput.requestId),
7076 );7072 );
7077 if (pending === undefined) {7073 if (pending === undefined) {
7078 return yield* new ProviderAdapterRuntimeRequestResponseError({7074 return yield* new ProviderAdapter.ProviderAdapterRuntimeRequestResponseError({
7079 driver: CODEX_PROVIDER,7075 driver: CODEX_PROVIDER,
7080 requestId: requestInput.requestId,7076 requestId: requestInput.requestId,
7081 cause: toProtocolError(7077 cause: toProtocolError(
...@@ -7085,7 +7081,7 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi...@@ -7085,7 +7081,7 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi
7085 }7081 }
7086 if (pending.type === "user_input") {7082 if (pending.type === "user_input") {
7087 if (requestInput.answers === undefined) {7083 if (requestInput.answers === undefined) {
7088 return yield* new ProviderAdapterRuntimeRequestResponseError({7084 return yield* new ProviderAdapter.ProviderAdapterRuntimeRequestResponseError({
7089 driver: CODEX_PROVIDER,7085 driver: CODEX_PROVIDER,
7090 requestId: requestInput.requestId,7086 requestId: requestInput.requestId,
7091 cause: toProtocolError(7087 cause: toProtocolError(
...@@ -7097,7 +7093,7 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi...@@ -7097,7 +7093,7 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi
7097 return;7093 return;
7098 }7094 }
7099 if (requestInput.decision === undefined) {7095 if (requestInput.decision === undefined) {
7100 return yield* new ProviderAdapterRuntimeRequestResponseError({7096 return yield* new ProviderAdapter.ProviderAdapterRuntimeRequestResponseError({
7101 driver: CODEX_PROVIDER,7097 driver: CODEX_PROVIDER,
7102 requestId: requestInput.requestId,7098 requestId: requestInput.requestId,
7103 cause: toProtocolError(7099 cause: toProtocolError(
...@@ -7110,7 +7106,7 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi...@@ -7110,7 +7106,7 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi
7110 Effect.mapError((cause) =>7106 Effect.mapError((cause) =>
7111 isProviderAdapterRuntimeRequestResponseError(cause)7107 isProviderAdapterRuntimeRequestResponseError(cause)
7112 ? cause7108 ? cause
7113 : new ProviderAdapterRuntimeRequestResponseError({7109 : new ProviderAdapter.ProviderAdapterRuntimeRequestResponseError({
7114 driver: CODEX_PROVIDER,7110 driver: CODEX_PROVIDER,
7115 requestId: requestInput.requestId,7111 requestId: requestInput.requestId,
7116 cause,7112 cause,
...@@ -7134,7 +7130,7 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi...@@ -7134,7 +7130,7 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi
7134 }).pipe(7130 }).pipe(
7135 Effect.mapError(7131 Effect.mapError(
7136 (cause) =>7132 (cause) =>
7137 new ProviderAdapterProtocolError({7133 new ProviderAdapter.ProviderAdapterProtocolError({
7138 driver: CODEX_PROVIDER,7134 driver: CODEX_PROVIDER,
7139 detail: "Failed to upload Codex thread feedback.",7135 detail: "Failed to upload Codex thread feedback.",
7140 payload: cause,7136 payload: cause,
...@@ -7167,7 +7163,7 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi...@@ -7167,7 +7163,7 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi
7167 }).pipe(7163 }).pipe(
7168 Effect.mapError(7164 Effect.mapError(
7169 (cause) =>7165 (cause) =>
7170 new ProviderAdapterReadThreadSnapshotError({7166 new ProviderAdapter.ProviderAdapterReadThreadSnapshotError({
7171 driver: CODEX_PROVIDER,7167 driver: CODEX_PROVIDER,
7172 providerThreadId: threadInput.providerThread.id,7168 providerThreadId: threadInput.providerThread.id,
7173 cause,7169 cause,
...@@ -7200,7 +7196,7 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi...@@ -7200,7 +7196,7 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi
7200 Effect.andThen(readCodexThreadHistoryMetadata(client.raw, threadId)),7196 Effect.andThen(readCodexThreadHistoryMetadata(client.raw, threadId)),
7201 );7197 );
7202 if (historyMode !== "paginated") {7198 if (historyMode !== "paginated") {
7203 return yield* new ProviderAdapterRollbackThreadError({7199 return yield* new ProviderAdapter.ProviderAdapterRollbackThreadError({
7204 driver: CODEX_PROVIDER,7200 driver: CODEX_PROVIDER,
7205 providerThreadId: threadInput.providerThread.id,7201 providerThreadId: threadInput.providerThread.id,
7206 cause: `Cannot roll back Codex thread ${threadId}: the thread uses legacy history, which Codex 0.156 cannot revert.`,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,7 +7240,7 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi
7244 }).pipe(7240 }).pipe(
7245 Effect.mapError(7241 Effect.mapError(
7246 (cause) =>7242 (cause) =>
7247 new ProviderAdapterRollbackThreadError({7243 new ProviderAdapter.ProviderAdapterRollbackThreadError({
7248 driver: CODEX_PROVIDER,7244 driver: CODEX_PROVIDER,
7249 providerThreadId: threadInput.providerThread.id,7245 providerThreadId: threadInput.providerThread.id,
7250 cause: normalizeCodexCause(cause),7246 cause: normalizeCodexCause(cause),
...@@ -7275,7 +7271,7 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi...@@ -7275,7 +7271,7 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi
7275 ),7271 ),
7276 Effect.mapError(7272 Effect.mapError(
7277 (cause) =>7273 (cause) =>
7278 new ProviderAdapterForkThreadError({7274 new ProviderAdapter.ProviderAdapterForkThreadError({
7279 driver: CODEX_PROVIDER,7275 driver: CODEX_PROVIDER,
7280 providerThreadId: threadInput.sourceProviderThread.id,7276 providerThreadId: threadInput.sourceProviderThread.id,
7281 cause: normalizeCodexCause(cause),7277 cause: normalizeCodexCause(cause),
...@@ -7291,7 +7287,7 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi...@@ -7291,7 +7287,7 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi
7291 Effect.andThen(readCodexThreadHistoryMetadata(client.raw, response.thread.id)),7287 Effect.andThen(readCodexThreadHistoryMetadata(client.raw, response.thread.id)),
7292 );7288 );
7293 if (historyMode !== "paginated") {7289 if (historyMode !== "paginated") {
7294 return yield* new ProviderAdapterForkThreadError({7290 return yield* new ProviderAdapter.ProviderAdapterForkThreadError({
7295 driver: CODEX_PROVIDER,7291 driver: CODEX_PROVIDER,
7296 providerThreadId: threadInput.sourceProviderThread.id,7292 providerThreadId: threadInput.sourceProviderThread.id,
7297 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.`,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,7 +7299,7 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi
7303 ),7299 ),
7304 Effect.mapError(7300 Effect.mapError(
7305 (cause) =>7301 (cause) =>
7306 new ProviderAdapterForkThreadError({7302 new ProviderAdapter.ProviderAdapterForkThreadError({
7307 driver: CODEX_PROVIDER,7303 driver: CODEX_PROVIDER,
7308 providerThreadId: threadInput.sourceProviderThread.id,7304 providerThreadId: threadInput.sourceProviderThread.id,
7309 cause: normalizeCodexCause(cause),7305 cause: normalizeCodexCause(cause),
...@@ -7328,7 +7324,7 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi...@@ -7328,7 +7324,7 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi
7328 }).pipe(7324 }).pipe(
7329 Effect.mapError(7325 Effect.mapError(
7330 (cause) =>7326 (cause) =>
7331 new ProviderAdapterForkThreadError({7327 new ProviderAdapter.ProviderAdapterForkThreadError({
7332 driver: CODEX_PROVIDER,7328 driver: CODEX_PROVIDER,
7333 providerThreadId: threadInput.sourceProviderThread.id,7329 providerThreadId: threadInput.sourceProviderThread.id,
7334 cause: normalizeCodexCause(cause),7330 cause: normalizeCodexCause(cause),
...@@ -7340,7 +7336,7 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi...@@ -7340,7 +7336,7 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi
7340 }).pipe(7336 }).pipe(
7341 Effect.mapError(7337 Effect.mapError(
7342 (cause) =>7338 (cause) =>
7343 new ProviderAdapterOpenSessionError({7339 new ProviderAdapter.ProviderAdapterOpenSessionError({
7344 driver: CODEX_PROVIDER,7340 driver: CODEX_PROVIDER,
7345 providerSessionId: input.providerSessionId,7341 providerSessionId: input.providerSessionId,
7346 cause,7342 cause,
apps/server/src/orchestration-v2/Adapters/CursorAdapterV2.testkit.ts+2-2
...@@ -28,7 +28,7 @@ import {...@@ -28,7 +28,7 @@ import {
28 cursorSdkModelSelection,28 cursorSdkModelSelection,
29 makeCursorAgentOptions,29 makeCursorAgentOptions,
30} from "@t3tools/provider-cursor/testing";30} from "@t3tools/provider-cursor/testing";
31import type { ProviderAdapterV2RuntimePolicy } from "@t3tools/provider-core/server/ProviderAdapter";31import type * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter";
32import type { RuntimePolicyV2Override } from "../RuntimePolicy.ts";32import type { RuntimePolicyV2Override } from "../RuntimePolicy.ts";
3333
34const CursorAgentSdkReplayTranscript = Schema.Struct({34const CursorAgentSdkReplayTranscript = Schema.Struct({
...@@ -625,7 +625,7 @@ function recordingRuntimePolicy(input: {...@@ -625,7 +625,7 @@ function recordingRuntimePolicy(input: {
625 readonly cwd: string;625 readonly cwd: string;
626 readonly interactionMode: "default" | "plan";626 readonly interactionMode: "default" | "plan";
627 readonly override?: Pick<RuntimePolicyV2Override, "approvalPolicy" | "sandboxPolicy">;627 readonly override?: Pick<RuntimePolicyV2Override, "approvalPolicy" | "sandboxPolicy">;
628}): ProviderAdapterV2RuntimePolicy {628}): ProviderAdapter.ProviderAdapterV2RuntimePolicy {
629 return {629 return {
630 runtimeMode: "full-access",630 runtimeMode: "full-access",
631 interactionMode: input.interactionMode,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,7 +26,7 @@ import { buildInitialGrokProviderSnapshot } from "@t3tools/provider-grok/testing
26import type { ProviderInstance } from "@t3tools/provider-core/server/driver";26import type { ProviderInstance } from "@t3tools/provider-core/server/driver";
27import * as ProviderInstanceRegistry from "../../provider/ProviderInstanceRegistry.ts";27import * as ProviderInstanceRegistry from "../../provider/ProviderInstanceRegistry.ts";
28import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator";28import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator";
29import { ProviderAdapterV2RuntimePolicy } from "@t3tools/provider-core/server/ProviderAdapter";29import * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter";
30import * as RuntimePolicy from "../RuntimePolicy.ts";30import * as RuntimePolicy from "../RuntimePolicy.ts";
31import { acpPermissionDisposition } from "@t3tools/provider-acp/server/clientPolicy";31import { acpPermissionDisposition } from "@t3tools/provider-acp/server/clientPolicy";
32import {32import {
...@@ -68,7 +68,7 @@ function runtimePolicy(input: {...@@ -68,7 +68,7 @@ function runtimePolicy(input: {
68 readonly approvalPolicy?: unknown;68 readonly approvalPolicy?: unknown;
69 readonly sandboxPolicy?: unknown;69 readonly sandboxPolicy?: unknown;
70}) {70}) {
71 return ProviderAdapterV2RuntimePolicy.make({71 return ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
72 runtimeMode: input.runtimeMode,72 runtimeMode: input.runtimeMode,
73 interactionMode: "default",73 interactionMode: "default",
74 cwd: "/workspace",74 cwd: "/workspace",
...@@ -314,7 +314,7 @@ describe("Grok launch permission mode", () => {...@@ -314,7 +314,7 @@ describe("Grok launch permission mode", () => {
314 // Opens a session through the adapter's own Grok runtime factory and returns314 // Opens a session through the adapter's own Grok runtime factory and returns
315 // the argv it tried to launch. The spawn fails after recording, so no315 // the argv it tried to launch. The spawn fails after recording, so no
316 // process starts.316 // process starts.
317 const launchArgs = (runtimePolicy: ProviderAdapterV2RuntimePolicy) =>317 const launchArgs = (runtimePolicy: ProviderAdapter.ProviderAdapterV2RuntimePolicy) =>
318 Effect.gen(function* () {318 Effect.gen(function* () {
319 const launches: Array<ReadonlyArray<string>> = [];319 const launches: Array<ReadonlyArray<string>> = [];
320 const childProcessSpawner = ChildProcessSpawner.make((command) => {320 const childProcessSpawner = ChildProcessSpawner.make((command) => {
...@@ -352,9 +352,9 @@ describe("Grok launch permission mode", () => {...@@ -352,9 +352,9 @@ describe("Grok launch permission mode", () => {
352352
353 const policy = (353 const policy = (
354 runtimeMode: RuntimeMode,354 runtimeMode: RuntimeMode,
355 override: Partial<ProviderAdapterV2RuntimePolicy> = {},355 override: Partial<ProviderAdapter.ProviderAdapterV2RuntimePolicy> = {},
356 ) =>356 ) =>
357 ProviderAdapterV2RuntimePolicy.make({357 ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
358 runtimeMode,358 runtimeMode,
359 interactionMode: "default",359 interactionMode: "default",
360 cwd: process.cwd(),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,7 +13,7 @@ import { layerTestProviderHost } from "@t3tools/provider-testing/host";
13import { GROK_ACP_CANCEL_META, GROK_ACP_INITIALIZE_META } from "@t3tools/provider-grok/testing";13import { GROK_ACP_CANCEL_META, GROK_ACP_INITIALIZE_META } from "@t3tools/provider-grok/testing";
14import { makeXAiPromptCompletionRuntime } from "@t3tools/provider-grok/testing";14import { makeXAiPromptCompletionRuntime } from "@t3tools/provider-grok/testing";
15import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator";15import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator";
16import * as ProviderContinuationRequests from "@t3tools/provider-core/server/continuationRequests";16import * as ProviderContinuationRequests from "@t3tools/provider-core/server/ProviderContinuationRequests";
17import * as ProviderAdapterRegistry from "../ProviderAdapterRegistry.ts";17import * as ProviderAdapterRegistry from "../ProviderAdapterRegistry.ts";
18import type { ProviderReplayGate } from "@t3tools/provider-testing/replayGate";18import type { ProviderReplayGate } from "@t3tools/provider-testing/replayGate";
19import type { OrchestratorV2ProviderReplayHarness } from "../testkit/ProviderReplayHarness.ts";19import type { OrchestratorV2ProviderReplayHarness } from "../testkit/ProviderReplayHarness.ts";
apps/server/src/orchestration-v2/Adapters/MuseAdapterV2.testkit.ts+1-1
...@@ -36,7 +36,7 @@ import {...@@ -36,7 +36,7 @@ import {
36} from "@t3tools/provider-muse/testing";36} from "@t3tools/provider-muse/testing";
37import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator";37import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator";
38import * as ProviderAdapterRegistry from "../ProviderAdapterRegistry.ts";38import * as ProviderAdapterRegistry from "../ProviderAdapterRegistry.ts";
39import * as ProviderContinuationRequests from "@t3tools/provider-core/server/continuationRequests";39import * as ProviderContinuationRequests from "@t3tools/provider-core/server/ProviderContinuationRequests";
40import type { OrchestratorV2ProviderReplayHarness } from "../testkit/ProviderReplayHarness.ts";40import type { OrchestratorV2ProviderReplayHarness } from "../testkit/ProviderReplayHarness.ts";
41import { makeMuseAdapterV2 } from "@t3tools/provider-muse/server";41import { makeMuseAdapterV2 } from "@t3tools/provider-muse/server";
4242
apps/server/src/orchestration-v2/Adapters/OpenCode2AdapterV2.test.ts+36-24
...@@ -38,14 +38,10 @@ import * as Exit from "effect/Exit";...@@ -38,14 +38,10 @@ import * as Exit from "effect/Exit";
38import { TestClock } from "effect/testing";38import { TestClock } from "effect/testing";
39import { describe } from "vite-plus/test";39import { describe } from "vite-plus/test";
4040
41import type {41import type * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter";
42 ProviderAdapterV2Event,
43 ProviderAdapterV2SessionRuntime,
44} from "@t3tools/provider-core/server/ProviderAdapter";
45import * as McpProviderSession from "@t3tools/provider-core/server/mcpSession";42import * as McpProviderSession from "@t3tools/provider-core/server/mcpSession";
46import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator";43import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator";
47import type { ProviderContinuationRequest } from "@t3tools/provider-core/server/continuationRequests";44import * as ProviderContinuationRequests from "@t3tools/provider-core/server/ProviderContinuationRequests";
48import * as ProviderContinuationRequests from "@t3tools/provider-core/server/continuationRequests";
49import {45import {
50 OPENCODE_2_STILL_STOPPING,46 OPENCODE_2_STILL_STOPPING,
51 OPENCODE_PROVIDER,47 OPENCODE_PROVIDER,
...@@ -347,21 +343,27 @@ const resumed = (...@@ -347,21 +343,27 @@ const resumed = (
347 return { runtime, thread };343 return { runtime, thread };
348 });344 });
349345
350const requestOf = (runtime: ProviderAdapterV2SessionRuntime) =>346const requestOf = (runtime: ProviderAdapter.ProviderAdapterV2SessionRuntime) =>
351 runtime.events.pipe(347 runtime.events.pipe(
352 Stream.filter(348 Stream.filter(
353 (event): event is Extract<ProviderAdapterV2Event, { type: "runtime_request.updated" }> =>349 (
354 event.type === "runtime_request.updated",350 event,
351 ): event is Extract<
352 ProviderAdapter.ProviderAdapterV2Event,
353 { type: "runtime_request.updated" }
354 > => event.type === "runtime_request.updated",
355 ),355 ),
356 Stream.map((event) => event.runtimeRequest),356 Stream.map((event) => event.runtimeRequest),
357 Stream.runHead,357 Stream.runHead,
358 Effect.map(Option.getOrUndefined),358 Effect.map(Option.getOrUndefined),
359 );359 );
360360
361const terminalOf = (runtime: ProviderAdapterV2SessionRuntime) =>361const terminalOf = (runtime: ProviderAdapter.ProviderAdapterV2SessionRuntime) =>
362 runtime.events.pipe(362 runtime.events.pipe(
363 Stream.filter(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 event.type === "turn.terminal",367 event.type === "turn.terminal",
366 ),368 ),
367 Stream.runHead,369 Stream.runHead,
...@@ -637,7 +639,7 @@ describe("OpenCode2 adapter", () => {...@@ -637,7 +639,7 @@ describe("OpenCode2 adapter", () => {
637 * The single reader of the runtime's events: resolves `attached` once the639 * The single reader of the runtime's events: resolves `attached` once the
638 * running background child has its thread, then returns the turn's terminal.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 Effect.gen(function* () {643 Effect.gen(function* () {
642 const attached = yield* Deferred.make<void>();644 const attached = yield* Deferred.make<void>();
643 const terminal = yield* runtime.events.pipe(645 const terminal = yield* runtime.events.pipe(
...@@ -647,7 +649,9 @@ describe("OpenCode2 adapter", () => {...@@ -647,7 +649,9 @@ describe("OpenCode2 adapter", () => {
647 : Effect.void,649 : Effect.void,
648 ),650 ),
649 Stream.filter(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 event.type === "turn.terminal",655 event.type === "turn.terminal",
652 ),656 ),
653 Stream.runHead,657 Stream.runHead,
...@@ -767,7 +771,9 @@ describe("OpenCode2 adapter", () => {...@@ -767,7 +771,9 @@ describe("OpenCode2 adapter", () => {
767 : Effect.void,771 : Effect.void,
768 ),772 ),
769 Stream.filter(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 event.type === "turn.terminal",777 event.type === "turn.terminal",
772 ),778 ),
773 Stream.runHead,779 Stream.runHead,
...@@ -809,7 +815,9 @@ describe("OpenCode2 adapter", () => {...@@ -809,7 +815,9 @@ describe("OpenCode2 adapter", () => {
809 const firstEnded = yield* Deferred.make<void>();815 const firstEnded = yield* Deferred.make<void>();
810 const ended = yield* runtime.events.pipe(816 const ended = yield* runtime.events.pipe(
811 Stream.filter(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 event.type === "turn.terminal",821 event.type === "turn.terminal",
814 ),822 ),
815 Stream.tap(() => Deferred.succeed(firstEnded, undefined)),823 Stream.tap(() => Deferred.succeed(firstEnded, undefined)),
...@@ -1110,7 +1118,7 @@ describe("OpenCode2 adapter", () => {...@@ -1110,7 +1118,7 @@ describe("OpenCode2 adapter", () => {
1110 event("session.execution.succeeded", { sessionID: SESSION }),1118 event("session.execution.succeeded", { sessionID: SESSION }),
1111 ];1119 ];
1112 /** The runtime's events, with the background subagent's statuses and the middle session's turns. */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 Effect.gen(function* () {1122 Effect.gen(function* () {
1115 const deep: Array<string> = [];1123 const deep: Array<string> = [];
1116 const middleTurns = new Map<string, string>();1124 const middleTurns = new Map<string, string>();
...@@ -1325,7 +1333,7 @@ describe("OpenCode2 adapter", () => {...@@ -1325,7 +1333,7 @@ describe("OpenCode2 adapter", () => {
1325 attemptId: RunAttemptId.make("attempt:opencode2-adapter:2"),1333 attemptId: RunAttemptId.make("attempt:opencode2-adapter:2"),
1326 });1334 });
1327 const stopFirstTurn = (1335 const stopFirstTurn = (
1328 runtime: ProviderAdapterV2SessionRuntime,1336 runtime: ProviderAdapter.ProviderAdapterV2SessionRuntime,
1329 thread: OrchestrationV2ProviderThread,1337 thread: OrchestrationV2ProviderThread,
1330 ) =>1338 ) =>
1331 Effect.gen(function* () {1339 Effect.gen(function* () {
...@@ -1336,10 +1344,12 @@ describe("OpenCode2 adapter", () => {...@@ -1336,10 +1344,12 @@ describe("OpenCode2 adapter", () => {
1336 yield* TestClock.adjust("11 seconds");1344 yield* TestClock.adjust("11 seconds");
1337 yield* Fiber.join(interrupt);1345 yield* Fiber.join(interrupt);
1338 });1346 });
1339 const terminals = (runtime: ProviderAdapterV2SessionRuntime, count: number) =>1347 const terminals = (runtime: ProviderAdapter.ProviderAdapterV2SessionRuntime, count: number) =>
1340 runtime.events.pipe(1348 runtime.events.pipe(
1341 Stream.filter(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 event.type === "turn.terminal",1353 event.type === "turn.terminal",
1344 ),1354 ),
1345 Stream.take(count),1355 Stream.take(count),
...@@ -2113,7 +2123,9 @@ describe("OpenCode2 adapter", () => {...@@ -2113,7 +2123,9 @@ describe("OpenCode2 adapter", () => {
2113 }),2123 }),
2114 ),2124 ),
2115 Stream.filter(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 event.type === "turn.terminal",2129 event.type === "turn.terminal",
2118 ),2130 ),
2119 Stream.tap(() => Deferred.succeed(firstEnded, undefined)),2131 Stream.tap(() => Deferred.succeed(firstEnded, undefined)),
...@@ -2331,7 +2343,7 @@ describe("OpenCode2 adapter", () => {...@@ -2331,7 +2343,7 @@ describe("OpenCode2 adapter", () => {
2331 replyData("session.form.list", forms),2343 replyData("session.form.list", forms),
2332 ];2344 ];
23332345
2334 const turnItems = (collected: ReadonlyArray<ProviderAdapterV2Event>) =>2346 const turnItems = (collected: ReadonlyArray<ProviderAdapter.ProviderAdapterV2Event>) =>
2335 collected.flatMap((event) =>2347 collected.flatMap((event) =>
2336 event.type === "turn_item.updated" ? [`${event.turnItem.type}:${event.turnItem.status}`] : [],2348 event.type === "turn_item.updated" ? [`${event.turnItem.type}:${event.turnItem.status}`] : [],
2337 );2349 );
...@@ -3043,7 +3055,7 @@ describe("OpenCode2 adapter", () => {...@@ -3043,7 +3055,7 @@ describe("OpenCode2 adapter", () => {
3043 Effect.gen(function* () {3055 Effect.gen(function* () {
3044 const kept = "msg_t3_turn_run-attempt:kept:1";3056 const kept = "msg_t3_turn_run-attempt:kept:1";
3045 const dropped = "msg_t3_turn_run-attempt:dropped:1";3057 const dropped = "msg_t3_turn_run-attempt:dropped:1";
3046 const offers: Array<ProviderContinuationRequest> = [];3058 const offers: Array<ProviderContinuationRequests.ProviderContinuationRequest> = [];
3047 const { runtime, thread } = yield* resumed([3059 const { runtime, thread } = yield* resumed([
3048 out("message.list", "<any>"),3060 out("message.list", "<any>"),
3049 reply("message.list", {3061 reply("message.list", {
...@@ -3727,7 +3739,7 @@ describe("OpenCode2 adapter", () => {...@@ -3727,7 +3739,7 @@ describe("OpenCode2 adapter", () => {
3727 */3739 */
3728 const continued = (after: ReadonlyArray<ProviderReplayEntry>) =>3740 const continued = (after: ReadonlyArray<ProviderReplayEntry>) =>
3729 Effect.gen(function* () {3741 Effect.gen(function* () {
3730 const offers: Array<ProviderContinuationRequest> = [];3742 const offers: Array<ProviderContinuationRequests.ProviderContinuationRequest> = [];
3731 const { runtime, thread } = yield* resumed([3743 const { runtime, thread } = yield* resumed([
3732 ...backgroundLaunch(CHILD),3744 ...backgroundLaunch(CHILD),
3733 event("session.execution.succeeded", { sessionID: SESSION }),3745 event("session.execution.succeeded", { sessionID: SESSION }),
...@@ -3867,7 +3879,7 @@ describe("OpenCode2 adapter", () => {...@@ -3867,7 +3879,7 @@ describe("OpenCode2 adapter", () => {
3867 Effect.gen(function* () {3879 Effect.gen(function* () {
3868 const reportText = `<subagent sessionID="${CHILD}" state="completed" description="Sleep">\nCHILD_OK\n</subagent>`;3880 const reportText = `<subagent sessionID="${CHILD}" state="completed" description="Sleep">\nCHILD_OK\n</subagent>`;
3869 const steerId = `msg_t3_steer_${SESSION}:message:opencode2-adapter:steer`;3881 const steerId = `msg_t3_steer_${SESSION}:message:opencode2-adapter:steer`;
3870 const offers: Array<ProviderContinuationRequest> = [];3882 const offers: Array<ProviderContinuationRequests.ProviderContinuationRequest> = [];
3871 const { runtime, thread } = yield* resumed([3883 const { runtime, thread } = yield* resumed([
3872 ...backgroundLaunch(CHILD),3884 ...backgroundLaunch(CHILD),
3873 event("session.execution.succeeded", { sessionID: SESSION }),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,7 +9,7 @@ import * as Schema from "effect/Schema";
9import { OpenCodeAdapterV2Driver } from "@t3tools/provider-opencode/server";9import { OpenCodeAdapterV2Driver } from "@t3tools/provider-opencode/server";
10import * as OpenCodeRuntime from "@t3tools/provider-opencode/server/OpenCodeRuntime";10import * as OpenCodeRuntime from "@t3tools/provider-opencode/server/OpenCodeRuntime";
11import { layerTestProviderHost } from "@t3tools/provider-testing/host";11import { layerTestProviderHost } from "@t3tools/provider-testing/host";
12import * as ProviderEventLoggers from "../../provider/ProviderEventLoggers.ts";12import * as ProviderEventLoggers from "@t3tools/provider-core/server/ProviderEventLoggers";
13import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator";13import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator";
14import { ProviderAdapterDriverCreateError } from "@t3tools/provider-core/server/adapterDriver";14import { ProviderAdapterDriverCreateError } from "@t3tools/provider-core/server/adapterDriver";
15import * as ProviderAdapterRegistry from "../ProviderAdapterRegistry.ts";15import * 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,16 +24,11 @@ import { CodexProviderCapabilitiesV2 } from "./Adapters/CodexAdapterV2.ts";
24import * as EffectWorker from "./EffectWorker.ts";24import * as EffectWorker from "./EffectWorker.ts";
25import * as EventSink from "./EventSink.ts";25import * as EventSink from "./EventSink.ts";
26import * as Orchestrator from "./Orchestrator.ts";26import * as Orchestrator from "./Orchestrator.ts";
27import type {
28 ProviderAdapterV2Event,
29 ProviderAdapterV2InterruptInput,
30 ProviderAdapterV2Shape,
31 ProviderAdapterV2TurnInput,
32} from "@t3tools/provider-core/server/ProviderAdapter";
33import * as ProviderAdapterRegistry from "./ProviderAdapterRegistry.ts";27import * as ProviderAdapterRegistry from "./ProviderAdapterRegistry.ts";
34import * as ProviderSessionManager from "./ProviderSessionManager.ts";28import * as ProviderSessionManager from "./ProviderSessionManager.ts";
35import * as ProviderReplayHarness from "./testkit/ProviderReplayHarness.ts";29import * as ProviderReplayHarness from "./testkit/ProviderReplayHarness.ts";
36import { checkpointWorkspace } from "@t3tools/provider-testing/replayWorkspace";30import { checkpointWorkspace } from "@t3tools/provider-testing/replayWorkspace";
31import type * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter";
3732
38const driver = ProviderDriverKind.make("codex");33const driver = ProviderDriverKind.make("codex");
39const instanceId = ProviderInstanceId.make("codex");34const instanceId = ProviderInstanceId.make("codex");
...@@ -61,10 +56,10 @@ const stopEarlierBackgroundWork = ({...@@ -61,10 +56,10 @@ const stopEarlierBackgroundWork = ({
61 Effect.scoped(56 Effect.scoped(
62 Effect.gen(function* () {57 Effect.gen(function* () {
63 const cwd = yield* checkpointWorkspace("background-work-stop");58 const cwd = yield* checkpointWorkspace("background-work-stop");
64 const events = yield* Queue.unbounded<ProviderAdapterV2Event>();59 const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>();
65 const started: ProviderAdapterV2TurnInput[] = [];60 const started: ProviderAdapter.ProviderAdapterV2TurnInput[] = [];
66 const interrupts: ProviderAdapterV2InterruptInput[] = [];61 const interrupts: ProviderAdapter.ProviderAdapterV2InterruptInput[] = [];
67 const adapter: ProviderAdapterV2Shape = {62 const adapter: ProviderAdapter.ProviderAdapterV2["Service"] = {
68 instanceId,63 instanceId,
69 driver,64 driver,
70 getCapabilities: () => Effect.succeed(CodexProviderCapabilitiesV2),65 getCapabilities: () => Effect.succeed(CodexProviderCapabilitiesV2),
apps/server/src/orchestration-v2/CheckpointCaptureService.ts+1-1
...@@ -252,7 +252,7 @@ export const layer: Layer.Layer<...@@ -252,7 +252,7 @@ export const layer: Layer.Layer<
252);252);
253253
254function makeCheckpointTurnItem(input: {254function makeCheckpointTurnItem(input: {
255 readonly idAllocator: IdAllocator.IdAllocatorV2Shape;255 readonly idAllocator: IdAllocator.IdAllocatorV2["Service"];
256 readonly run: OrchestrationV2Run;256 readonly run: OrchestrationV2Run;
257 readonly rootNode: OrchestrationV2ExecutionNode;257 readonly rootNode: OrchestrationV2ExecutionNode;
258 readonly providerThread: OrchestrationV2ProviderThread;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,7 +26,7 @@ import * as EventSink from "./EventSink.ts";
26import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator";26import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator";
27import * as ProjectionStore from "./ProjectionStore.ts";27import * as ProjectionStore from "./ProjectionStore.ts";
28import * as ProjectStore from "./ProjectStore.ts";28import * as ProjectStore from "./ProjectStore.ts";
29import type { ProviderAdapterV2RollbackThreadInput } from "@t3tools/provider-core/server/ProviderAdapter";29import type * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter";
30import * as ProviderSessionManager from "./ProviderSessionManager.ts";30import * as ProviderSessionManager from "./ProviderSessionManager.ts";
31import * as RuntimePolicy from "./RuntimePolicy.ts";31import * as RuntimePolicy from "./RuntimePolicy.ts";
3232
...@@ -457,7 +457,7 @@ it.effect.each([...@@ -457,7 +457,7 @@ it.effect.each([
457 Layer.mock(ProviderSessionManager.ProviderSessionManagerV2)({457 Layer.mock(ProviderSessionManager.ProviderSessionManagerV2)({
458 open: () =>458 open: () =>
459 Effect.succeed({459 Effect.succeed({
460 rollbackThread: (input: ProviderAdapterV2RollbackThreadInput) =>460 rollbackThread: (input: ProviderAdapter.ProviderAdapterV2RollbackThreadInput) =>
461 Effect.gen(function* () {461 Effect.gen(function* () {
462 const count = yield* resolveCodexRollbackTurnCount(input);462 const count = yield* resolveCodexRollbackTurnCount(input);
463 assert.equal(count, 2 - targetOrdinal);463 assert.equal(count, 2 - targetOrdinal);
apps/server/src/orchestration-v2/CheckpointRollbackService.ts+5-5
...@@ -20,10 +20,10 @@ import {...@@ -20,10 +20,10 @@ import {
20} from "./CheckpointRestoreSafety.ts";20} from "./CheckpointRestoreSafety.ts";
21import { CheckpointServiceV2 } from "./CheckpointService.ts";21import { CheckpointServiceV2 } from "./CheckpointService.ts";
22import { EventSinkV2 } from "./EventSink.ts";22import { EventSinkV2 } from "./EventSink.ts";
23import { IdAllocatorV2 } from "@t3tools/provider-core/server/IdAllocator";23import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator";
24import { ProjectionStoreV2 } from "./ProjectionStore.ts";24import { ProjectionStoreV2 } from "./ProjectionStore.ts";
25import * as ProjectStore from "./ProjectStore.ts";25import * as ProjectStore from "./ProjectStore.ts";
26import type { ProviderAdapterV2RollbackTarget } from "@t3tools/provider-core/server/ProviderAdapter";26import type * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter";
27import { ProviderSessionManagerV2 } from "./ProviderSessionManager.ts";27import { ProviderSessionManagerV2 } from "./ProviderSessionManager.ts";
28import { RuntimePolicyV2 } from "./RuntimePolicy.ts";28import { RuntimePolicyV2 } from "./RuntimePolicy.ts";
29import * as ThreadCommandExecutor from "./ThreadCommandExecutor.ts";29import * as ThreadCommandExecutor from "./ThreadCommandExecutor.ts";
...@@ -85,7 +85,7 @@ export const layer: Layer.Layer<...@@ -85,7 +85,7 @@ export const layer: Layer.Layer<
85 never,85 never,
86 | CheckpointServiceV286 | CheckpointServiceV2
87 | EventSinkV287 | EventSinkV2
88 | IdAllocatorV288 | IdAllocator.IdAllocatorV2
89 | ProjectionStoreV289 | ProjectionStoreV2
90 | ProviderSessionManagerV290 | ProviderSessionManagerV2
91 | RuntimePolicyV291 | RuntimePolicyV2
...@@ -98,7 +98,7 @@ export const layer: Layer.Layer<...@@ -98,7 +98,7 @@ export const layer: Layer.Layer<
98 Effect.gen(function* () {98 Effect.gen(function* () {
99 const checkpoints = yield* CheckpointServiceV2;99 const checkpoints = yield* CheckpointServiceV2;
100 const eventSink = yield* EventSinkV2;100 const eventSink = yield* EventSinkV2;
101 const ids = yield* IdAllocatorV2;101 const ids = yield* IdAllocator.IdAllocatorV2;
102 const projections = yield* ProjectionStoreV2;102 const projections = yield* ProjectionStoreV2;
103 const sessions = yield* ProviderSessionManagerV2;103 const sessions = yield* ProviderSessionManagerV2;
104 const runtimePolicy = yield* RuntimePolicyV2;104 const runtimePolicy = yield* RuntimePolicyV2;
...@@ -225,7 +225,7 @@ export const layer: Layer.Layer<...@@ -225,7 +225,7 @@ export const layer: Layer.Layer<
225 turn.providerThreadId === providerThread.id &&225 turn.providerThreadId === providerThread.id &&
226 (turn.runAttemptId === null || !rolledBackAttemptIds.has(turn.runAttemptId)),226 (turn.runAttemptId === null || !rolledBackAttemptIds.has(turn.runAttemptId)),
227 );227 );
228 const rollbackTarget: ProviderAdapterV2RollbackTarget =228 const rollbackTarget: ProviderAdapter.ProviderAdapterV2RollbackTarget =
229 targetOrdinal === 0229 targetOrdinal === 0
230 ? {230 ? {
231 type: "thread_start",231 type: "thread_start",
apps/server/src/orchestration-v2/CheckpointService.ts+2-2
...@@ -172,7 +172,7 @@ export const checkpointRefForScopeOrdinal = Effect.fn("checkpointRefForScopeOrdi...@@ -172,7 +172,7 @@ export const checkpointRefForScopeOrdinal = Effect.fn("checkpointRefForScopeOrdi
172);172);
173173
174function checkpointIdForScopeOrdinal(174function checkpointIdForScopeOrdinal(
175 idAllocator: IdAllocator.IdAllocatorV2Shape,175 idAllocator: IdAllocator.IdAllocatorV2["Service"],
176 input: {176 input: {
177 readonly scopeId: CheckpointScopeId;177 readonly scopeId: CheckpointScopeId;
178 readonly ordinalWithinScope: number;178 readonly ordinalWithinScope: number;
...@@ -185,7 +185,7 @@ function checkpointIdForScopeOrdinal(...@@ -185,7 +185,7 @@ function checkpointIdForScopeOrdinal(
185}185}
186186
187function makeRootRunScope(input: {187function makeRootRunScope(input: {
188 readonly idAllocator: IdAllocator.IdAllocatorV2Shape;188 readonly idAllocator: IdAllocator.IdAllocatorV2["Service"];
189 readonly threadId: ThreadId;189 readonly threadId: ThreadId;
190 readonly runId: RunId;190 readonly runId: RunId;
191 readonly rootNodeId: NodeId;191 readonly rootNodeId: NodeId;
apps/server/src/orchestration-v2/ContextHandoffBudget.test.ts+3-3
...@@ -1,4 +1,4 @@...@@ -1,4 +1,4 @@
1import type { ProviderAdapterV2HistoricalContext } from "@t3tools/provider-core/server/ProviderAdapter";1import type * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter";
2import { assert, describe, it } from "@effect/vitest";2import { assert, describe, it } from "@effect/vitest";
3import {3import {
4 ContextHandoffId,4 ContextHandoffId,
...@@ -501,7 +501,7 @@ describe("handoff delivery", () => {...@@ -501,7 +501,7 @@ describe("handoff delivery", () => {
501 providerThread,501 providerThread,
502 budget: 16_000,502 budget: 16_000,
503 alreadyDeliveredItemIds: new Set<string>(),503 alreadyDeliveredItemIds: new Set<string>(),
504 inject: (value: ProviderAdapterV2HistoricalContext) =>504 inject: (value: ProviderAdapter.ProviderAdapterV2HistoricalContext) =>
505 Effect.sync(() => {505 Effect.sync(() => {
506 history.push(...historyResponseItems(value.messages, value.context));506 history.push(...historyResponseItems(value.messages, value.context));
507 return true;507 return true;
...@@ -588,7 +588,7 @@ describe("handoff delivery", () => {...@@ -588,7 +588,7 @@ describe("handoff delivery", () => {
588 ...handoff,588 ...handoff,
589 id: ContextHandoffId.make(`handoff:retry:${index}`),589 id: ContextHandoffId.make(`handoff:retry:${index}`),
590 }));590 }));
591 let captured: ProviderAdapterV2HistoricalContext | undefined;591 let captured: ProviderAdapter.ProviderAdapterV2HistoricalContext | undefined;
592 const result = yield* deliverContextHandoffs({592 const result = yield* deliverContextHandoffs({
593 handoffs: many,593 handoffs: many,
594 providerThread,594 providerThread,
apps/server/src/orchestration-v2/ContextHandoffDelivery.ts+2-2
...@@ -2,7 +2,7 @@ import type {...@@ -2,7 +2,7 @@ import type {
2 OrchestrationV2ContextHandoff,2 OrchestrationV2ContextHandoff,
3 OrchestrationV2ProviderThread,3 OrchestrationV2ProviderThread,
4} from "@t3tools/contracts";4} from "@t3tools/contracts";
5import type { ProviderAdapterV2HistoricalContext } from "@t3tools/provider-core/server/ProviderAdapter";5import type * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter";
6import { ContextHandoffBudgetError } from "@t3tools/provider-core/server/failure";6import { ContextHandoffBudgetError } from "@t3tools/provider-core/server/failure";
7import * as Effect from "effect/Effect";7import * as Effect from "effect/Effect";
8import * as Schema from "effect/Schema";8import * as Schema from "effect/Schema";
...@@ -25,7 +25,7 @@ export const deliverContextHandoffs = Effect.fn("orchestrationV2.deliverContextH...@@ -25,7 +25,7 @@ export const deliverContextHandoffs = Effect.fn("orchestrationV2.deliverContextH
25 readonly deferInline?: boolean;25 readonly deferInline?: boolean;
26 readonly alreadyDeliveredItemIds: ReadonlySet<string>;26 readonly alreadyDeliveredItemIds: ReadonlySet<string>;
27 readonly inject?: (27 readonly inject?: (
28 history: ProviderAdapterV2HistoricalContext,28 history: ProviderAdapter.ProviderAdapterV2HistoricalContext,
29 ) => Effect.Effect<boolean, InjectError>;29 ) => Effect.Effect<boolean, InjectError>;
30 readonly persist: (handoff: OrchestrationV2ContextHandoff) => Effect.Effect<void, PersistError>;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,7 +29,7 @@ import * as CodexInstallation from "../provider/CodexInstallation.ts";
29import * as ServerEnvironment from "../environment/ServerEnvironment.ts";29import * as ServerEnvironment from "../environment/ServerEnvironment.ts";
30import * as ModelManifest from "../provider/ModelManifest.ts";30import * as ModelManifest from "../provider/ModelManifest.ts";
31import * as ProviderInstanceRegistryHydration from "../provider/ProviderInstanceRegistryHydration.ts";31import * as ProviderInstanceRegistryHydration from "../provider/ProviderInstanceRegistryHydration.ts";
32import * as ProviderEventLoggers from "../provider/ProviderEventLoggers.ts";32import * as ProviderEventLoggers from "@t3tools/provider-core/server/ProviderEventLoggers";
33import * as OpenCodeRuntime from "@t3tools/provider-opencode/server/OpenCodeRuntime";33import * as OpenCodeRuntime from "@t3tools/provider-opencode/server/OpenCodeRuntime";
34import * as OpenCodeServerLedger from "@t3tools/provider-opencode/server/OpenCodeServerLedger";34import * as OpenCodeServerLedger from "@t3tools/provider-opencode/server/OpenCodeServerLedger";
35import * as ServerSettings from "../serverSettings.ts";35import * 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,10 +36,10 @@ import * as WorkspacePaths from "../workspace/WorkspacePaths.ts";
36import { CodexProviderCapabilitiesV2 } from "./Adapters/CodexAdapterV2.ts";36import { CodexProviderCapabilitiesV2 } from "./Adapters/CodexAdapterV2.ts";
37import * as EventSink from "./EventSink.ts";37import * as EventSink from "./EventSink.ts";
38import * as Orchestrator from "./Orchestrator.ts";38import * as Orchestrator from "./Orchestrator.ts";
39import type { ProviderAdapterV2Shape } from "@t3tools/provider-core/server/ProviderAdapter";
40import { continueRestartedRun } from "./RestartContinuation.ts";39import { continueRestartedRun } from "./RestartContinuation.ts";
41import * as RuntimeLayer from "./runtimeLayer.ts";40import * as RuntimeLayer from "./runtimeLayer.ts";
42import * as ProviderTurnStartServiceTestkit from "./ProviderTurnStartService.testkit.ts";41import * as ProviderTurnStartServiceTestkit from "./ProviderTurnStartService.testkit.ts";
42import type * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter";
4343
44const layerPlatformTest = Layer.merge(44const layerPlatformTest = Layer.merge(
45 NodeServices.layer,45 NodeServices.layer,
...@@ -74,7 +74,7 @@ const orchestrationAdapter = {...@@ -74,7 +74,7 @@ const orchestrationAdapter = {
74 getCapabilities: () => Effect.succeed(CodexProviderCapabilitiesV2),74 getCapabilities: () => Effect.succeed(CodexProviderCapabilitiesV2),
75 planSelectionTransition: () => Effect.succeed({ type: "apply_on_next_turn" }),75 planSelectionTransition: () => Effect.succeed({ type: "apply_on_next_turn" }),
76 openSession: () => Effect.die("sessions are not used by delegated completion tests"),76 openSession: () => Effect.die("sessions are not used by delegated completion tests"),
77} as ProviderAdapterV2Shape;77} as ProviderAdapter.ProviderAdapterV2["Service"];
78const providerInstance = {78const providerInstance = {
79 instanceId: modelSelection.instanceId,79 instanceId: modelSelection.instanceId,
80 driverKind: driver,80 driverKind: driver,
apps/server/src/orchestration-v2/DispatchModeLimit.test.ts+2-2
...@@ -13,9 +13,9 @@ import { CodexProviderCapabilitiesV2 } from "./Adapters/CodexAdapterV2.ts";...@@ -13,9 +13,9 @@ import { CodexProviderCapabilitiesV2 } from "./Adapters/CodexAdapterV2.ts";
13import { DispatchModeLimit } from "./DispatchModeLimit.ts";13import { DispatchModeLimit } from "./DispatchModeLimit.ts";
14import * as Orchestrator from "./Orchestrator.ts";14import * as Orchestrator from "./Orchestrator.ts";
15import * as ProjectionStore from "./ProjectionStore.ts";15import * as ProjectionStore from "./ProjectionStore.ts";
16import type { ProviderAdapterV2Shape } from "@t3tools/provider-core/server/ProviderAdapter";
17import * as ProviderAdapterRegistry from "./ProviderAdapterRegistry.ts";16import * as ProviderAdapterRegistry from "./ProviderAdapterRegistry.ts";
18import * as ProviderReplayHarness from "./testkit/ProviderReplayHarness.ts";17import * as ProviderReplayHarness from "./testkit/ProviderReplayHarness.ts";
18import type * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter";
1919
20const instanceId = ProviderInstanceId.make("codex");20const instanceId = ProviderInstanceId.make("codex");
21const adapter = {21const adapter = {
...@@ -24,7 +24,7 @@ const adapter = {...@@ -24,7 +24,7 @@ const adapter = {
24 getCapabilities: () => Effect.succeed(CodexProviderCapabilitiesV2),24 getCapabilities: () => Effect.succeed(CodexProviderCapabilitiesV2),
25 planSelectionTransition: () => Effect.succeed({ type: "apply_on_next_turn" as const }),25 planSelectionTransition: () => Effect.succeed({ type: "apply_on_next_turn" as const }),
26 openSession: () => Effect.die("No provider process needed for metadata commands"),26 openSession: () => Effect.die("No provider process needed for metadata commands"),
27} as ProviderAdapterV2Shape;27} as ProviderAdapter.ProviderAdapterV2["Service"];
28const layerDatabase = SqlitePersistence.layerMemory;28const layerDatabase = SqlitePersistence.layerMemory;
29const layerTest = Layer.mergeAll(29const layerTest = Layer.mergeAll(
30 layerDatabase,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,7 +29,7 @@ import * as CodexInstallation from "../provider/CodexInstallation.ts";
29import * as ServerEnvironment from "../environment/ServerEnvironment.ts";29import * as ServerEnvironment from "../environment/ServerEnvironment.ts";
30import * as ModelManifest from "../provider/ModelManifest.ts";30import * as ModelManifest from "../provider/ModelManifest.ts";
31import * as ProviderInstanceRegistryHydration from "../provider/ProviderInstanceRegistryHydration.ts";31import * as ProviderInstanceRegistryHydration from "../provider/ProviderInstanceRegistryHydration.ts";
32import * as ProviderEventLoggers from "../provider/ProviderEventLoggers.ts";32import * as ProviderEventLoggers from "@t3tools/provider-core/server/ProviderEventLoggers";
33import * as OpenCodeRuntime from "@t3tools/provider-opencode/server/OpenCodeRuntime";33import * as OpenCodeRuntime from "@t3tools/provider-opencode/server/OpenCodeRuntime";
34import * as OpenCodeServerLedger from "@t3tools/provider-opencode/server/OpenCodeServerLedger";34import * as OpenCodeServerLedger from "@t3tools/provider-opencode/server/OpenCodeServerLedger";
35import * as ServerSettings from "../serverSettings.ts";35import * 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,7 +54,7 @@ import * as CodexInstallation from "../provider/CodexInstallation.ts";
54import * as ServerEnvironment from "../environment/ServerEnvironment.ts";54import * as ServerEnvironment from "../environment/ServerEnvironment.ts";
55import * as ModelManifest from "../provider/ModelManifest.ts";55import * as ModelManifest from "../provider/ModelManifest.ts";
56import * as ProviderInstanceRegistryHydration from "../provider/ProviderInstanceRegistryHydration.ts";56import * as ProviderInstanceRegistryHydration from "../provider/ProviderInstanceRegistryHydration.ts";
57import * as ProviderEventLoggers from "../provider/ProviderEventLoggers.ts";57import * as ProviderEventLoggers from "@t3tools/provider-core/server/ProviderEventLoggers";
58import * as OpenCode2Client from "@t3tools/provider-opencode/server/v2/OpenCode2Client";58import * as OpenCode2Client from "@t3tools/provider-opencode/server/v2/OpenCode2Client";
59import * as OpenCodeRuntime from "@t3tools/provider-opencode/server/OpenCodeRuntime";59import * as OpenCodeRuntime from "@t3tools/provider-opencode/server/OpenCodeRuntime";
60import * as OpenCodeServerLedger from "@t3tools/provider-opencode/server/OpenCodeServerLedger";60import * as OpenCodeServerLedger from "@t3tools/provider-opencode/server/OpenCodeServerLedger";
...@@ -67,7 +67,7 @@ import * as ProviderInstanceRegistry from "../provider/ProviderInstanceRegistry....@@ -67,7 +67,7 @@ import * as ProviderInstanceRegistry from "../provider/ProviderInstanceRegistry.
67import * as ProviderTurnStartServiceTestkit from "./ProviderTurnStartService.testkit.ts";67import * as ProviderTurnStartServiceTestkit from "./ProviderTurnStartService.testkit.ts";
68import * as RuntimeLayer from "./runtimeLayer.ts";68import * as RuntimeLayer from "./runtimeLayer.ts";
69import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator";69import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator";
70import * as ProviderContinuationRequests from "@t3tools/provider-core/server/continuationRequests";70import * as ProviderContinuationRequests from "@t3tools/provider-core/server/ProviderContinuationRequests";
71import * as ProviderContinuationService from "./ProviderContinuationService.ts";71import * as ProviderContinuationService from "./ProviderContinuationService.ts";
72import * as ThreadManagementService from "./ThreadManagementService.ts";72import * as ThreadManagementService from "./ThreadManagementService.ts";
73import * as McpSessionRegistry from "../mcp/McpSessionRegistry.ts";73import * 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,9 +25,9 @@ import * as SqlitePersistence from "../persistence/Sqlite.ts";
25import { CodexProviderCapabilitiesV2 } from "./Adapters/CodexAdapterV2.ts";25import { CodexProviderCapabilitiesV2 } from "./Adapters/CodexAdapterV2.ts";
26import * as Orchestrator from "./Orchestrator.ts";26import * as Orchestrator from "./Orchestrator.ts";
27import * as ProjectionStore from "./ProjectionStore.ts";27import * as ProjectionStore from "./ProjectionStore.ts";
28import type { ProviderAdapterV2Shape } from "@t3tools/provider-core/server/ProviderAdapter";
29import * as ProviderAdapterRegistry from "./ProviderAdapterRegistry.ts";28import * as ProviderAdapterRegistry from "./ProviderAdapterRegistry.ts";
30import * as ProviderReplayHarness from "./testkit/ProviderReplayHarness.ts";29import * as ProviderReplayHarness from "./testkit/ProviderReplayHarness.ts";
30import type * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter";
3131
32const instanceId = ProviderInstanceId.make("codex");32const instanceId = ProviderInstanceId.make("codex");
33const modelSelection = { instanceId, model: "gpt-5.1-codex" };33const modelSelection = { instanceId, model: "gpt-5.1-codex" };
...@@ -37,7 +37,7 @@ const adapter = {...@@ -37,7 +37,7 @@ const adapter = {
37 getCapabilities: () => Effect.succeed(CodexProviderCapabilitiesV2),37 getCapabilities: () => Effect.succeed(CodexProviderCapabilitiesV2),
38 planSelectionTransition: () => Effect.succeed({ type: "apply_on_next_turn" as const }),38 planSelectionTransition: () => Effect.succeed({ type: "apply_on_next_turn" as const }),
39 openSession: () => Effect.die("No provider process needed for metadata controls"),39 openSession: () => Effect.die("No provider process needed for metadata controls"),
40} as ProviderAdapterV2Shape;40} as ProviderAdapter.ProviderAdapterV2["Service"];
41const layerDatabase = SqlitePersistence.layerMemory;41const layerDatabase = SqlitePersistence.layerMemory;
42const layerTest = Layer.mergeAll(42const layerTest = Layer.mergeAll(
43 layerDatabase,43 layerDatabase,
apps/server/src/orchestration-v2/Orchestrator.ts+7-7
...@@ -91,7 +91,7 @@ import { isUndeliveredMailboxSteer } from "./NotificationMailbox.ts";...@@ -91,7 +91,7 @@ import { isUndeliveredMailboxSteer } from "./NotificationMailbox.ts";
91import { EventSinkV2 } from "./EventSink.ts";91import { EventSinkV2 } from "./EventSink.ts";
92import * as EffectOutbox from "./EffectOutbox.ts";92import * as EffectOutbox from "./EffectOutbox.ts";
93import type { OrchestrationEffectRequestV2, PendingOrchestrationEffectV2 } from "./EffectOutbox.ts";93import type { OrchestrationEffectRequestV2, PendingOrchestrationEffectV2 } from "./EffectOutbox.ts";
94import { IdAllocatorV2 } from "@t3tools/provider-core/server/IdAllocator";94import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator";
95import * as ThreadCommandExecutor from "./ThreadCommandExecutor.ts";95import * as ThreadCommandExecutor from "./ThreadCommandExecutor.ts";
96import { DispatchModeLimit, exceededDispatchModeLimit } from "./DispatchModeLimit.ts";96import { DispatchModeLimit, exceededDispatchModeLimit } from "./DispatchModeLimit.ts";
97import {97import {
...@@ -107,9 +107,8 @@ import {...@@ -107,9 +107,8 @@ import {
107 type ProjectionCheckpointContext,107 type ProjectionCheckpointContext,
108 type ShellSnapshotOptions,108 type ShellSnapshotOptions,
109} from "./ProjectionStore.ts";109} from "./ProjectionStore.ts";
110import type { ProviderAdapterV2Shape } from "@t3tools/provider-core/server/ProviderAdapter";
111import { ProviderAdapterRegistryV2 } from "./ProviderAdapterRegistry.ts";110import { ProviderAdapterRegistryV2 } from "./ProviderAdapterRegistry.ts";
112import { ProviderContinuationRequests } from "@t3tools/provider-core/server/continuationRequests";111import * as ProviderContinuationRequests from "@t3tools/provider-core/server/ProviderContinuationRequests";
113import { makeProviderFailure } from "@t3tools/provider-core/server/failure";112import { makeProviderFailure } from "@t3tools/provider-core/server/failure";
114import * as RunExecutionService from "./RunExecutionService.ts";113import * as RunExecutionService from "./RunExecutionService.ts";
115import { ProviderSessionManagerV2 } from "./ProviderSessionManager.ts";114import { ProviderSessionManagerV2 } from "./ProviderSessionManager.ts";
...@@ -128,6 +127,7 @@ import {...@@ -128,6 +127,7 @@ import {
128 ThreadForkServiceV2,127 ThreadForkServiceV2,
129} from "./ThreadForkService.ts";128} from "./ThreadForkService.ts";
130import { planThreadDeletion } from "./ThreadDeletion.ts";129import { planThreadDeletion } from "./ThreadDeletion.ts";
130import type * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter";
131131
132export class OrchestratorDispatchError extends Schema.TaggedError<OrchestratorDispatchError>()(132export class OrchestratorDispatchError extends Schema.TaggedError<OrchestratorDispatchError>()(
133 "OrchestratorDispatchError",133 "OrchestratorDispatchError",
...@@ -800,7 +800,7 @@ const makeOrchestrator = Effect.fn("orchestrationV2.Orchestrator.layer")(functio...@@ -800,7 +800,7 @@ const makeOrchestrator = Effect.fn("orchestrationV2.Orchestrator.layer")(functio
800 const contextHandoffService = yield* ContextHandoffServiceV2;800 const contextHandoffService = yield* ContextHandoffServiceV2;
801 const eventSink = yield* EventSinkV2;801 const eventSink = yield* EventSinkV2;
802 const commandReceipts = yield* CommandReceiptStoreV2;802 const commandReceipts = yield* CommandReceiptStoreV2;
803 const idAllocator = yield* IdAllocatorV2;803 const idAllocator = yield* IdAllocator.IdAllocatorV2;
804 const projects = yield* ProjectStore.ProjectStoreV2;804 const projects = yield* ProjectStore.ProjectStoreV2;
805 const projectionStore = yield* ProjectionStoreV2;805 const projectionStore = yield* ProjectionStoreV2;
806 const effectOutbox = yield* EffectOutbox.EffectOutboxV2;806 const effectOutbox = yield* EffectOutbox.EffectOutboxV2;
...@@ -823,7 +823,7 @@ const makeOrchestrator = Effect.fn("orchestrationV2.Orchestrator.layer")(functio...@@ -823,7 +823,7 @@ const makeOrchestrator = Effect.fn("orchestrationV2.Orchestrator.layer")(functio
823 const fileSystem = yield* FileSystem.FileSystem;823 const fileSystem = yield* FileSystem.FileSystem;
824 const path = yield* Path.Path;824 const path = yield* Path.Path;
825 const providerAdapters = yield* ProviderAdapterRegistryV2;825 const providerAdapters = yield* ProviderAdapterRegistryV2;
826 const continuationRequests = yield* ProviderContinuationRequests;826 const continuationRequests = yield* ProviderContinuationRequests.ProviderContinuationRequests;
827 const providerSessions = yield* ProviderSessionManagerV2;827 const providerSessions = yield* ProviderSessionManagerV2;
828 const providerSwitchService = yield* ProviderSwitchServiceV2;828 const providerSwitchService = yield* ProviderSwitchServiceV2;
829 const runtimePolicy = yield* RuntimePolicyV2;829 const runtimePolicy = yield* RuntimePolicyV2;
...@@ -857,7 +857,7 @@ const makeOrchestrator = Effect.fn("orchestrationV2.Orchestrator.layer")(functio...@@ -857,7 +857,7 @@ const makeOrchestrator = Effect.fn("orchestrationV2.Orchestrator.layer")(functio
857 );857 );
858858
859 const providerSessionIdFor = (input: {859 const providerSessionIdFor = (input: {
860 readonly adapter: ProviderAdapterV2Shape;860 readonly adapter: ProviderAdapter.ProviderAdapterV2["Service"];
861 readonly providerInstanceId: ProviderInstanceId;861 readonly providerInstanceId: ProviderInstanceId;
862 readonly threadId: ThreadId;862 readonly threadId: ThreadId;
863 }) =>863 }) =>
...@@ -10955,7 +10955,7 @@ export const layer: Layer.Layer<...@@ -10955,7 +10955,7 @@ export const layer: Layer.Layer<
10955 | ContextHandoffServiceV210955 | ContextHandoffServiceV2
10956 | EffectOutbox.EffectOutboxV210956 | EffectOutbox.EffectOutboxV2
10957 | EventSinkV210957 | EventSinkV2
10958 | IdAllocatorV210958 | IdAllocator.IdAllocatorV2
10959 | ProjectStore.ProjectStoreV210959 | ProjectStore.ProjectStoreV2
10960 | ProviderAdapterRegistryV210960 | ProviderAdapterRegistryV2
10961 | ProviderSessionManagerV210961 | ProviderSessionManagerV2
apps/server/src/orchestration-v2/ProviderAdapterRegistry.test.ts+14-11
...@@ -18,36 +18,35 @@ import * as Schema from "effect/Schema";...@@ -18,36 +18,35 @@ import * as Schema from "effect/Schema";
18import type * as Scope from "effect/Scope";18import type * as Scope from "effect/Scope";
19import * as Stream from "effect/Stream";19import * as Stream from "effect/Stream";
2020
21import * as ProviderAuthFlow from "@t3tools/provider-core/server/ProviderAuthFlow";21import * as ProviderAuthFlow from "@t3tools/provider-core/server/providerAuthFlow";
22import type { ProviderAuthController } from "../provider/ProviderAuthService.ts";22import type { ProviderAuthController } from "../provider/ProviderAuthService.ts";
23import type { ProviderInstance } from "@t3tools/provider-core/server/driver";23import type { ProviderInstance } from "@t3tools/provider-core/server/driver";
24import * as ProviderInstanceRegistry from "../provider/ProviderInstanceRegistry.ts";24import * as ProviderInstanceRegistry from "../provider/ProviderInstanceRegistry.ts";
25import {
26 ProviderAdapterOpenSessionError,
27 type ProviderAdapterV2Shape,
28} from "@t3tools/provider-core/server/ProviderAdapter";
29import {25import {
30 ProviderAdapterDriverCreateError,26 ProviderAdapterDriverCreateError,
31 type ProviderAdapterDriver,27 type ProviderAdapterDriver,
32} from "@t3tools/provider-core/server/adapterDriver";28} from "@t3tools/provider-core/server/adapterDriver";
33import * as ProviderAdapterRegistry from "./ProviderAdapterRegistry.ts";29import * as ProviderAdapterRegistry from "./ProviderAdapterRegistry.ts";
30import * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter";
3431
35const driver = ProviderDriverKind.make("codex");32const driver = ProviderDriverKind.make("codex");
36const personalId = ProviderInstanceId.make("codex_personal");33const personalId = ProviderInstanceId.make("codex_personal");
37const workId = ProviderInstanceId.make("codex_work");34const workId = ProviderInstanceId.make("codex_work");
3835
39const makeAdapter = (instanceId: ProviderInstanceId): ProviderAdapterV2Shape =>36const makeAdapter = (
37 instanceId: ProviderInstanceId,
38): ProviderAdapter.ProviderAdapterV2["Service"] =>
40 ({39 ({
41 instanceId,40 instanceId,
42 driver,41 driver,
43 getCapabilities: () => Effect.die("capabilities are not used by this registry test"),42 getCapabilities: () => Effect.die("capabilities are not used by this registry test"),
44 planSelectionTransition: () => Effect.succeed({ type: "apply_on_next_turn" }),43 planSelectionTransition: () => Effect.succeed({ type: "apply_on_next_turn" }),
45 openSession: () => Effect.die("sessions are not used by this registry test"),44 openSession: () => Effect.die("sessions are not used by this registry test"),
46 }) as ProviderAdapterV2Shape;45 }) as ProviderAdapter.ProviderAdapterV2["Service"];
4746
48const makeInstance = (47const makeInstance = (
49 instanceId: ProviderInstanceId,48 instanceId: ProviderInstanceId,
50 orchestrationAdapter: ProviderAdapterV2Shape,49 orchestrationAdapter: ProviderAdapter.ProviderAdapterV2["Service"],
51): ProviderInstance => ({50): ProviderInstance => ({
52 instanceId,51 instanceId,
53 driverKind: driver,52 driverKind: driver,
...@@ -101,7 +100,11 @@ const lifecycleConfigMap: ProviderInstanceConfigMap = {...@@ -101,7 +100,11 @@ const lifecycleConfigMap: ProviderInstanceConfigMap = {
101const lifecycleAdapter = makeAdapter(lifecycleInstanceId);100const lifecycleAdapter = makeAdapter(lifecycleInstanceId);
102101
103const makeLifecycleDriver = (102const makeLifecycleDriver = (
104 create: Effect.Effect<ProviderAdapterV2Shape, ProviderAdapterDriverCreateError, Scope.Scope>,103 create: Effect.Effect<
104 ProviderAdapter.ProviderAdapterV2["Service"],
105 ProviderAdapterDriverCreateError,
106 Scope.Scope
107 >,
105): ProviderAdapterDriver<Record<string, never>> => ({108): ProviderAdapterDriver<Record<string, never>> => ({
106 driverKind: lifecycleDriver,109 driverKind: lifecycleDriver,
107 configSchema: Schema.Struct({}),110 configSchema: Schema.Struct({}),
...@@ -263,7 +266,7 @@ it.effect(...@@ -263,7 +266,7 @@ it.effect(
263 },266 },
264 })267 })
265 .pipe(Effect.flip);268 .pipe(Effect.flip);
266 assert.instanceOf(error, ProviderAdapterOpenSessionError);269 assert.instanceOf(error, ProviderAdapter.ProviderAdapterOpenSessionError);
267 assert.instanceOf(error.cause, ProviderSetupError);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,7 +290,7 @@ it.effect("interrupts admitted session startup when a shared peer signs out", ()
287 authenticate: () => Effect.void,290 authenticate: () => Effect.void,
288 logout: Effect.void,291 logout: Effect.void,
289 });292 });
290 const adapter: ProviderAdapterV2Shape = {293 const adapter: ProviderAdapter.ProviderAdapterV2["Service"] = {
291 ...workAdapter,294 ...workAdapter,
292 openSession: () =>295 openSession: () =>
293 Effect.gen(function* () {296 Effect.gen(function* () {
apps/server/src/orchestration-v2/ProviderAdapterRegistry.ts+8-8
...@@ -48,11 +48,11 @@ export type ProviderAdapterRegistryV2Error = typeof ProviderAdapterRegistryV2Err...@@ -48,11 +48,11 @@ export type ProviderAdapterRegistryV2Error = typeof ProviderAdapterRegistryV2Err
48export interface ProviderAdapterRegistryV2Shape {48export interface ProviderAdapterRegistryV2Shape {
49 readonly get: (49 readonly get: (
50 instanceId: ProviderInstanceId,50 instanceId: ProviderInstanceId,
51 ) => Effect.Effect<ProviderAdapter.ProviderAdapterV2Shape, ProviderAdapterRegistryV2Error>;51 ) => Effect.Effect<ProviderAdapter.ProviderAdapterV2["Service"], ProviderAdapterRegistryV2Error>;
52 readonly list: () => Effect.Effect<ReadonlyArray<ProviderInstanceId>>;52 readonly list: () => Effect.Effect<ReadonlyArray<ProviderInstanceId>>;
53 readonly getMetadata?: (instanceId: ProviderInstanceId) => Effect.Effect<53 readonly getMetadata?: (instanceId: ProviderInstanceId) => Effect.Effect<
54 {54 {
55 readonly driver: ProviderAdapter.ProviderAdapterV2Shape["driver"];55 readonly driver: ProviderAdapter.ProviderAdapterV2["Service"]["driver"];
56 readonly continuationKey: string;56 readonly continuationKey: string;
57 readonly enabled: boolean;57 readonly enabled: boolean;
58 readonly capabilities: OrchestrationV2ProviderCapabilities;58 readonly capabilities: OrchestrationV2ProviderCapabilities;
...@@ -134,7 +134,7 @@ export const layerFromProviderInstanceRegistry: Layer.Layer<...@@ -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 list: () =>140 list: () =>
...@@ -169,7 +169,7 @@ export const ProviderAdapterRegistryBuildError = Schema.Union([ProviderAdapterDr...@@ -169,7 +169,7 @@ export const ProviderAdapterRegistryBuildError = Schema.Union([ProviderAdapterDr
169export type ProviderAdapterRegistryBuildError = typeof ProviderAdapterRegistryBuildError.Type;169export type ProviderAdapterRegistryBuildError = typeof ProviderAdapterRegistryBuildError.Type;
170170
171function makeRegistry(171function makeRegistry(
172 adapters: ReadonlyArray<ProviderAdapter.ProviderAdapterV2Shape>,172 adapters: ReadonlyArray<ProviderAdapter.ProviderAdapterV2["Service"]>,
173): ProviderAdapterRegistryV2Shape {173): ProviderAdapterRegistryV2Shape {
174 return {174 return {
175 get: (instanceId) =>175 get: (instanceId) =>
...@@ -185,7 +185,7 @@ function makeRegistry(...@@ -185,7 +185,7 @@ function makeRegistry(
185}185}
186186
187export function layerFromAdapters(187export function layerFromAdapters(
188 adapters: ReadonlyArray<ProviderAdapter.ProviderAdapterV2Shape>,188 adapters: ReadonlyArray<ProviderAdapter.ProviderAdapterV2["Service"]>,
189): Layer.Layer<ProviderAdapterRegistryV2> {189): Layer.Layer<ProviderAdapterRegistryV2> {
190 return Layer.succeed(190 return Layer.succeed(
191 ProviderAdapterRegistryV2,191 ProviderAdapterRegistryV2,
...@@ -194,7 +194,7 @@ export function layerFromAdapters(...@@ -194,7 +194,7 @@ export function layerFromAdapters(
194}194}
195195
196export function layerFromAdaptersEffect<R, E>(196export function layerFromAdaptersEffect<R, E>(
197 adapters: Effect.Effect<ReadonlyArray<ProviderAdapter.ProviderAdapterV2Shape>, E, R>,197 adapters: Effect.Effect<ReadonlyArray<ProviderAdapter.ProviderAdapterV2["Service"]>, E, R>,
198): Layer.Layer<ProviderAdapterRegistryV2, E, R> {198): Layer.Layer<ProviderAdapterRegistryV2, E, R> {
199 return Layer.effect(199 return Layer.effect(
200 ProviderAdapterRegistryV2,200 ProviderAdapterRegistryV2,
...@@ -203,7 +203,7 @@ export function layerFromAdaptersEffect<R, E>(...@@ -203,7 +203,7 @@ export function layerFromAdaptersEffect<R, E>(
203}203}
204204
205export function layerSingle(205export function layerSingle(
206 adapter: ProviderAdapter.ProviderAdapterV2Shape,206 adapter: ProviderAdapter.ProviderAdapterV2["Service"],
207): Layer.Layer<ProviderAdapterRegistryV2> {207): Layer.Layer<ProviderAdapterRegistryV2> {
208 return layerFromAdapters([adapter]);208 return layerFromAdapters([adapter]);
209}209}
...@@ -217,7 +217,7 @@ const decodedConfigEnabled = (config: unknown): boolean | undefined => {...@@ -217,7 +217,7 @@ const decodedConfigEnabled = (config: unknown): boolean | undefined => {
217};217};
218218
219interface LiveAdapterEntry {219interface LiveAdapterEntry {
220 readonly adapter: ProviderAdapter.ProviderAdapterV2Shape;220 readonly adapter: ProviderAdapter.ProviderAdapterV2["Service"];
221 readonly scope: Scope.Closeable;221 readonly scope: Scope.Closeable;
222 readonly entry: ProviderInstanceConfig;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,7 +19,7 @@ import * as Semaphore from "effect/Semaphore";
19import * as TestClock from "effect/testing/TestClock";19import * as TestClock from "effect/testing/TestClock";
2020
21import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator";21import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator";
22import * as ProviderContinuationRequests from "@t3tools/provider-core/server/continuationRequests";22import * as ProviderContinuationRequests from "@t3tools/provider-core/server/ProviderContinuationRequests";
23import * as ProviderContinuationService from "./ProviderContinuationService.ts";23import * as ProviderContinuationService from "./ProviderContinuationService.ts";
24import * as ThreadManagementService from "./ThreadManagementService.ts";24import * as ThreadManagementService from "./ThreadManagementService.ts";
2525
apps/server/src/orchestration-v2/ProviderContinuationService.ts+1-1
...@@ -4,7 +4,7 @@ import * as Layer from "effect/Layer";...@@ -4,7 +4,7 @@ import * as Layer from "effect/Layer";
4import * as Ref from "effect/Ref";4import * as Ref from "effect/Ref";
55
6import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator";6import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator";
7import * as ProviderContinuationRequests from "@t3tools/provider-core/server/continuationRequests";7import * as ProviderContinuationRequests from "@t3tools/provider-core/server/ProviderContinuationRequests";
8import * as ThreadManagementService from "./ThreadManagementService.ts";8import * as ThreadManagementService from "./ThreadManagementService.ts";
9import { isUndeliveredMailboxSteer } from "./NotificationMailbox.ts";9import { isUndeliveredMailboxSteer } from "./NotificationMailbox.ts";
1010
apps/server/src/orchestration-v2/ProviderEventIngestor.ts+4-5
...@@ -31,7 +31,7 @@ import * as AnalyticsService from "../telemetry/AnalyticsService.ts";...@@ -31,7 +31,7 @@ import * as AnalyticsService from "../telemetry/AnalyticsService.ts";
31import * as EventSink from "./EventSink.ts";31import * as EventSink from "./EventSink.ts";
32import * as ProjectionStore from "./ProjectionStore.ts";32import * as ProjectionStore from "./ProjectionStore.ts";
33import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator";33import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator";
34import { ProviderAdapterV2Event } from "@t3tools/provider-core/server/ProviderAdapter";34import * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter";
35import { makeProviderFailureTurnItem } from "@t3tools/provider-core/server/failure";35import { makeProviderFailureTurnItem } from "@t3tools/provider-core/server/failure";
36import * as ThreadCommandExecutor from "./ThreadCommandExecutor.ts";36import * as ThreadCommandExecutor from "./ThreadCommandExecutor.ts";
37import { stripUnservedToolOutputImageBytes } from "./toolOutputImageBytes.ts";37import { stripUnservedToolOutputImageBytes } from "./toolOutputImageBytes.ts";
...@@ -41,7 +41,7 @@ export class ProviderEventNormalizeError extends Schema.TaggedError<ProviderEven...@@ -41,7 +41,7 @@ export class ProviderEventNormalizeError extends Schema.TaggedError<ProviderEven
41 {41 {
42 providerSessionId: ProviderSessionId,42 providerSessionId: ProviderSessionId,
43 threadId: ThreadId,43 threadId: ThreadId,
44 providerEvent: ProviderAdapterV2Event,44 providerEvent: ProviderAdapter.ProviderAdapterV2Event,
45 cause: Schema.optional(Schema.Defect()),45 cause: Schema.optional(Schema.Defect()),
46 },46 },
47) {47) {
...@@ -93,7 +93,7 @@ export const layerAnalytics = Layer.effect(...@@ -93,7 +93,7 @@ export const layerAnalytics = Layer.effect(
93);93);
9494
95function providerTurnAnalyticsProperties(input: {95function providerTurnAnalyticsProperties(input: {
96 readonly driver: ProviderAdapterV2Event["driver"];96 readonly driver: ProviderAdapter.ProviderAdapterV2Event["driver"];
97 readonly providerTurn: OrchestrationV2ProviderTurn;97 readonly providerTurn: OrchestrationV2ProviderTurn;
98 readonly context?: ProviderTurnAnalyticsContext;98 readonly context?: ProviderTurnAnalyticsContext;
99}): Readonly<Record<string, unknown>> {99}): Readonly<Record<string, unknown>> {
...@@ -257,7 +257,7 @@ export interface ProviderEventIngestInput {...@@ -257,7 +257,7 @@ export interface ProviderEventIngestInput {
257 readonly runId?: RunId;257 readonly runId?: RunId;
258 readonly nodeId?: NodeId;258 readonly nodeId?: NodeId;
259 readonly rawEventId?: RawEventId;259 readonly rawEventId?: RawEventId;
260 readonly event: ProviderAdapterV2Event;260 readonly event: ProviderAdapter.ProviderAdapterV2Event;
261 readonly analyticsContext?: ProviderTurnAnalyticsContext;261 readonly analyticsContext?: ProviderTurnAnalyticsContext;
262}262}
263263
...@@ -657,7 +657,6 @@ export const layer: Layer.Layer<...@@ -657,7 +657,6 @@ export const layer: Layer.Layer<
657 yield* makeDomainEvent(input, {657 yield* makeDomainEvent(input, {
658 type: "turn-item.updated",658 type: "turn-item.updated",
659 payload: makeProviderFailureTurnItem({659 payload: makeProviderFailureTurnItem({
660 idAllocator,
661 driver: input.event.driver,660 driver: input.event.driver,
662 threadId: input.threadId,661 threadId: input.threadId,
663 runId: input.runId ?? null,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,18 +44,11 @@ import * as EventSink from "./EventSink.ts";
44import * as EventStore from "./EventStore.ts";44import * as EventStore from "./EventStore.ts";
45import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator";45import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator";
46import * as ProjectionStore from "./ProjectionStore.ts";46import * as ProjectionStore from "./ProjectionStore.ts";
47import {
48 ProviderAdapterEventStreamError,
49 type ProviderAdapterV2Event,
50 ProviderAdapterProtocolError,
51 type ProviderAdapterV2RuntimePolicy,
52 type ProviderAdapterV2SessionRuntime,
53 type ProviderAdapterV2Shape,
54} from "@t3tools/provider-core/server/ProviderAdapter";
55import * as ProviderAdapterRegistry from "./ProviderAdapterRegistry.ts";47import * as ProviderAdapterRegistry from "./ProviderAdapterRegistry.ts";
56import * as ProviderEventIngestor from "./ProviderEventIngestor.ts";48import * as ProviderEventIngestor from "./ProviderEventIngestor.ts";
57import * as ThreadCommandExecutor from "./ThreadCommandExecutor.ts";49import * as ThreadCommandExecutor from "./ThreadCommandExecutor.ts";
58import * as ProviderSessionManager from "./ProviderSessionManager.ts";50import * as ProviderSessionManager from "./ProviderSessionManager.ts";
51import * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter";
5952
60const layerTestDatabase = SqlitePersistence.layerMemory;53const layerTestDatabase = SqlitePersistence.layerMemory;
61const layerTestStores = Layer.merge(EventStore.layer, ProjectionStore.layer).pipe(54const layerTestStores = Layer.merge(EventStore.layer, ProjectionStore.layer).pipe(
...@@ -167,7 +160,10 @@ interface TestProviderRuntimeState {...@@ -167,7 +160,10 @@ interface TestProviderRuntimeState {
167 readonly interruptCount: number;160 readonly interruptCount: number;
168 readonly resumeCount: number;161 readonly resumeCount: number;
169 readonly unloadedNativeThreadIds: ReadonlyArray<string>;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}
172168
173const emptyState: TestProviderRuntimeState = {169const emptyState: TestProviderRuntimeState = {
...@@ -189,7 +185,7 @@ const runtimePolicy = {...@@ -189,7 +185,7 @@ const runtimePolicy = {
189 runtimeMode: "full-access",185 runtimeMode: "full-access",
190 interactionMode: "default",186 interactionMode: "default",
191 cwd: process.cwd(),187 cwd: process.cwd(),
192} satisfies ProviderAdapterV2RuntimePolicy;188} satisfies ProviderAdapter.ProviderAdapterV2RuntimePolicy;
193189
194function makeProviderSession(input: {190function makeProviderSession(input: {
195 readonly providerSessionId: ProviderSessionId;191 readonly providerSessionId: ProviderSessionId;
...@@ -211,7 +207,7 @@ function makeProviderSession(input: {...@@ -211,7 +207,7 @@ function makeProviderSession(input: {
211}207}
212208
213function makeThreadCreatedEvent(input: {209function makeThreadCreatedEvent(input: {
214 readonly idAllocator: IdAllocator.IdAllocatorV2Shape;210 readonly idAllocator: IdAllocator.IdAllocatorV2["Service"];
215 readonly threadId: ThreadId;211 readonly threadId: ThreadId;
216 readonly now: DateTime.Utc;212 readonly now: DateTime.Utc;
217 readonly projectId?: ProjectId;213 readonly projectId?: ProjectId;
...@@ -264,7 +260,7 @@ function makeThreadCreatedEvent(input: {...@@ -264,7 +260,7 @@ function makeThreadCreatedEvent(input: {
264}260}
265261
266function makeProviderThread(input: {262function makeProviderThread(input: {
267 readonly idAllocator: IdAllocator.IdAllocatorV2Shape;263 readonly idAllocator: IdAllocator.IdAllocatorV2["Service"];
268 readonly threadId: ThreadId;264 readonly threadId: ThreadId;
269 readonly providerSessionId: ProviderSessionId;265 readonly providerSessionId: ProviderSessionId;
270 readonly now: DateTime.Utc;266 readonly now: DateTime.Utc;
...@@ -299,7 +295,7 @@ function makeProviderThread(input: {...@@ -299,7 +295,7 @@ function makeProviderThread(input: {
299295
300function unimplemented(detail: string) {296function unimplemented(detail: string) {
301 return Effect.fail(297 return Effect.fail(
302 new ProviderAdapterProtocolError({298 new ProviderAdapter.ProviderAdapterProtocolError({
303 driver: CODEX_DRIVER,299 driver: CODEX_DRIVER,
304 detail,300 detail,
305 }),301 }),
...@@ -328,7 +324,7 @@ function makeProviderAdapter(...@@ -328,7 +324,7 @@ function makeProviderAdapter(
328 /** Completed when a hanging scope close reaches its wedged finalizer. */324 /** Completed when a hanging scope close reaches its wedged finalizer. */
329 readonly scopeCloseReached?: Deferred.Deferred<void>;325 readonly scopeCloseReached?: Deferred.Deferred<void>;
330 } = {},326 } = {},
331): ProviderAdapterV2Shape {327): ProviderAdapter.ProviderAdapterV2["Service"] {
332 const countClose = Effect.addFinalizer(() =>328 const countClose = Effect.addFinalizer(() =>
333 Ref.update(state, (current) => ({329 Ref.update(state, (current) => ({
334 ...current,330 ...current,
...@@ -371,7 +367,7 @@ function makeProviderAdapter(...@@ -371,7 +367,7 @@ function makeProviderAdapter(
371 ]);367 ]);
372 }368 }
373 const now = yield* DateTime.now;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 const session = makeProviderSession({371 const session = makeProviderSession({
376 providerSessionId: input.providerSessionId,372 providerSessionId: input.providerSessionId,
377 now,373 now,
...@@ -398,7 +394,7 @@ function makeProviderAdapter(...@@ -398,7 +394,7 @@ function makeProviderAdapter(
398 providerSession: session,394 providerSession: session,
399 events: options.failEventStream395 events: options.failEventStream
400 ? Stream.fail(396 ? Stream.fail(
401 new ProviderAdapterEventStreamError({397 new ProviderAdapter.ProviderAdapterEventStreamError({
402 driver: CODEX_DRIVER,398 driver: CODEX_DRIVER,
403 providerSessionId: input.providerSessionId,399 providerSessionId: input.providerSessionId,
404 cause: "process exited",400 cause: "process exited",
...@@ -442,7 +438,7 @@ function makeProviderAdapter(...@@ -442,7 +438,7 @@ function makeProviderAdapter(
442 readThreadSnapshot: () => unimplemented("readThreadSnapshot unused in test"),438 readThreadSnapshot: () => unimplemented("readThreadSnapshot unused in test"),
443 rollbackThread: () => unimplemented("rollbackThread unused in test"),439 rollbackThread: () => unimplemented("rollbackThread unused in test"),
444 forkThread: () => unimplemented("forkThread unused in test"),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,7 +670,7 @@ function runBrowserAccessScenario(input: {
674}670}
675671
676function makePendingRuntimeRequestEvents(input: {672function makePendingRuntimeRequestEvents(input: {
677 readonly idAllocator: IdAllocator.IdAllocatorV2Shape;673 readonly idAllocator: IdAllocator.IdAllocatorV2["Service"];
678 readonly threadId: ThreadId;674 readonly threadId: ThreadId;
679 readonly providerSessionId: ProviderSessionId;675 readonly providerSessionId: ProviderSessionId;
680 readonly providerThread: OrchestrationV2ProviderThread;676 readonly providerThread: OrchestrationV2ProviderThread;
...@@ -795,7 +791,7 @@ function makePendingRuntimeRequestEvents(input: {...@@ -795,7 +791,7 @@ function makePendingRuntimeRequestEvents(input: {
795 driver: CODEX_DRIVER,791 driver: CODEX_DRIVER,
796 turnItem,792 turnItem,
797 },793 },
798 ] satisfies ReadonlyArray<ProviderAdapterV2Event>;794 ] satisfies ReadonlyArray<ProviderAdapter.ProviderAdapterV2Event>;
799 return { events, providerEvents, requestId, nodeId };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,14 +46,7 @@ import * as McpSessionRegistry from "../mcp/McpSessionRegistry.ts";
46import * as EventSink from "./EventSink.ts";46import * as EventSink from "./EventSink.ts";
47import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator";47import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator";
48import * as ProviderEventIngestor from "./ProviderEventIngestor.ts";48import * as ProviderEventIngestor from "./ProviderEventIngestor.ts";
49import {49import * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter";
50 ProviderAdapterEventStreamError,
51 ProviderAdapterV2RuntimePolicy,
52 type ProviderAdapterV2Error,
53 type ProviderAdapterV2Event,
54 type ProviderAdapterV2EventSubscription,
55 type ProviderAdapterV2SessionRuntime,
56} from "@t3tools/provider-core/server/ProviderAdapter";
57import * as ProviderAdapterRegistry from "./ProviderAdapterRegistry.ts";50import * as ProviderAdapterRegistry from "./ProviderAdapterRegistry.ts";
58import * as ProjectionStore from "./ProjectionStore.ts";51import * as ProjectionStore from "./ProjectionStore.ts";
5952
...@@ -161,14 +154,20 @@ export interface ProviderSessionManagerV2Shape {...@@ -161,14 +154,20 @@ export interface ProviderSessionManagerV2Shape {
161 readonly threadId: ThreadId;154 readonly threadId: ThreadId;
162 readonly providerSessionId: ProviderSessionId;155 readonly providerSessionId: ProviderSessionId;
163 readonly modelSelection: ModelSelection;156 readonly modelSelection: ModelSelection;
164 readonly runtimePolicy: ProviderAdapterV2RuntimePolicy;157 readonly runtimePolicy: ProviderAdapter.ProviderAdapterV2RuntimePolicy;
165 readonly resumeFromSession?: OrchestrationV2ProviderSession;158 readonly resumeFromSession?: OrchestrationV2ProviderSession;
166 readonly initialNativeThreadId?: string;159 readonly initialNativeThreadId?: string;
167 readonly initialProviderItemIdentityVersion?: 2;160 readonly initialProviderItemIdentityVersion?: 2;
168 }) => Effect.Effect<ProviderAdapterV2SessionRuntime, ProviderSessionManagerV2Error>;161 }) => Effect.Effect<
162 ProviderAdapter.ProviderAdapterV2SessionRuntime,
163 ProviderSessionManagerV2Error
164 >;
169 readonly get: (165 readonly get: (
170 providerSessionId: ProviderSessionId,166 providerSessionId: ProviderSessionId,
171 ) => Effect.Effect<Option.Option<ProviderAdapterV2SessionRuntime>, ProviderSessionManagerV2Error>;167 ) => Effect.Effect<
168 Option.Option<ProviderAdapter.ProviderAdapterV2SessionRuntime>,
169 ProviderSessionManagerV2Error
170 >;
172 readonly close: (171 readonly close: (
173 providerSessionId: ProviderSessionId,172 providerSessionId: ProviderSessionId,
174 ) => Effect.Effect<void, ProviderSessionManagerV2Error>;173 ) => Effect.Effect<void, ProviderSessionManagerV2Error>;
...@@ -211,8 +210,8 @@ interface LiveSessionEntry {...@@ -211,8 +210,8 @@ interface LiveSessionEntry {
211 */210 */
212 readonly mcpCredentialIdByThread: ReadonlyMap<ThreadId, string>;211 readonly mcpCredentialIdByThread: ReadonlyMap<ThreadId, string>;
213 readonly supportsMultipleProviderThreads: boolean;212 readonly supportsMultipleProviderThreads: boolean;
214 readonly runtime: ProviderAdapterV2SessionRuntime;213 readonly runtime: ProviderAdapter.ProviderAdapterV2SessionRuntime;
215 readonly exposedRuntime: ProviderAdapterV2SessionRuntime;214 readonly exposedRuntime: ProviderAdapter.ProviderAdapterV2SessionRuntime;
216 readonly eventSubscribers: Ref.Ref<215 readonly eventSubscribers: Ref.Ref<
217 ReadonlyMap<number, Queue.Queue<ProviderSessionEventSignal, Cause.Done>>216 ReadonlyMap<number, Queue.Queue<ProviderSessionEventSignal, Cause.Done>>
218 >;217 >;
...@@ -244,10 +243,10 @@ interface IdleThreadUnload {...@@ -244,10 +243,10 @@ interface IdleThreadUnload {
244}243}
245244
246type ProviderSessionEventSignal =245type ProviderSessionEventSignal =
247 | { readonly type: "event"; readonly event: ProviderAdapterV2Event }246 | { readonly type: "event"; readonly event: ProviderAdapter.ProviderAdapterV2Event }
248 | {247 | {
249 readonly type: "failure";248 readonly type: "failure";
250 readonly cause: Cause.Cause<ProviderAdapterV2Error>;249 readonly cause: Cause.Cause<ProviderAdapter.ProviderAdapterV2Error>;
251 };250 };
252251
253export interface ProviderSessionManagerV2LayerOptions {252export interface ProviderSessionManagerV2LayerOptions {
...@@ -279,7 +278,9 @@ function sessionKey(providerSessionId: ProviderSessionId): string {...@@ -279,7 +278,9 @@ function sessionKey(providerSessionId: ProviderSessionId): string {
279 * Their node and transcript item are runless too, so they bypass the normal278 * Their node and transcript item are runless too, so they bypass the normal
280 * per-run subscriber and are persisted by the session event pump.279 * per-run subscriber and are persisted by the session event pump.
281 */280 */
282function sessionScopedRuntimeRequestThreadId(event: ProviderAdapterV2Event): ThreadId | undefined {281function sessionScopedRuntimeRequestThreadId(
282 event: ProviderAdapter.ProviderAdapterV2Event,
283): ThreadId | undefined {
283 switch (event.type) {284 switch (event.type) {
284 case "runtime_request.updated":285 case "runtime_request.updated":
285 return event.runtimeRequest.providerTurnId === null ? event.threadId : undefined;286 return event.runtimeRequest.providerTurnId === null ? event.threadId : undefined;
...@@ -298,7 +299,9 @@ function sessionScopedRuntimeRequestThreadId(event: ProviderAdapterV2Event): Thr...@@ -298,7 +299,9 @@ function sessionScopedRuntimeRequestThreadId(event: ProviderAdapterV2Event): Thr
298}299}
299300
300function providerThreadRuntimeKey(301function providerThreadRuntimeKey(
301 providerThread: Parameters<ProviderAdapterV2SessionRuntime["resumeThread"]>[0]["providerThread"],302 providerThread: Parameters<
303 ProviderAdapter.ProviderAdapterV2SessionRuntime["resumeThread"]
304 >[0]["providerThread"],
302): string {305): string {
303 const nativeThreadRef = providerThread.nativeThreadRef;306 const nativeThreadRef = providerThread.nativeThreadRef;
304 return nativeThreadRef === null307 return nativeThreadRef === null
...@@ -308,10 +311,10 @@ function providerThreadRuntimeKey(...@@ -308,10 +311,10 @@ function providerThreadRuntimeKey(
308311
309function providerThreadLoadKey(input: {312function providerThreadLoadKey(input: {
310 readonly providerThread: Parameters<313 readonly providerThread: Parameters<
311 ProviderAdapterV2SessionRuntime["resumeThread"]314 ProviderAdapter.ProviderAdapterV2SessionRuntime["resumeThread"]
312 >[0]["providerThread"];315 >[0]["providerThread"];
313 readonly modelSelection?: ModelSelection;316 readonly modelSelection?: ModelSelection;
314 readonly runtimePolicy?: ProviderAdapterV2RuntimePolicy;317 readonly runtimePolicy?: ProviderAdapter.ProviderAdapterV2RuntimePolicy;
315}): string {318}): string {
316 return JSON.stringify({319 return JSON.stringify({
317 providerThread: providerThreadRuntimeKey(input.providerThread),320 providerThread: providerThreadRuntimeKey(input.providerThread),
...@@ -575,7 +578,7 @@ export const layerWithOptions = (...@@ -575,7 +578,7 @@ export const layerWithOptions = (
575578
576 const failSubscribers = (entry: LiveSessionEntry, detail: string) =>579 const failSubscribers = (entry: LiveSessionEntry, detail: string) =>
577 Effect.gen(function* () {580 Effect.gen(function* () {
578 const error = new ProviderAdapterEventStreamError({581 const error = new ProviderAdapter.ProviderAdapterEventStreamError({
579 driver: entry.runtime.driver,582 driver: entry.runtime.driver,
580 providerSessionId: entry.runtime.providerSessionId,583 providerSessionId: entry.runtime.providerSessionId,
581 cause: detail,584 cause: detail,
...@@ -617,7 +620,7 @@ export const layerWithOptions = (...@@ -617,7 +620,7 @@ export const layerWithOptions = (
617 fiber === null ? Effect.void : Fiber.interrupt(fiber).pipe(Effect.ignore);620 fiber === null ? Effect.void : Fiber.interrupt(fiber).pipe(Effect.ignore);
618621
619 const writeProviderSessionEvents = (input: {622 const writeProviderSessionEvents = (input: {
620 readonly runtime: ProviderAdapterV2SessionRuntime;623 readonly runtime: ProviderAdapter.ProviderAdapterV2SessionRuntime;
621 readonly threadIds: Iterable<ThreadId>;624 readonly threadIds: Iterable<ThreadId>;
622 readonly type: "provider-session.attached" | "provider-session.updated";625 readonly type: "provider-session.attached" | "provider-session.updated";
623 readonly payload: OrchestrationV2ProviderSession;626 readonly payload: OrchestrationV2ProviderSession;
...@@ -1226,7 +1229,7 @@ export const layerWithOptions = (...@@ -1226,7 +1229,7 @@ export const layerWithOptions = (
1226 const removeThreadAttachment = (input: {1229 const removeThreadAttachment = (input: {
1227 readonly providerSessionId: ProviderSessionId;1230 readonly providerSessionId: ProviderSessionId;
1228 readonly threadId: ThreadId;1231 readonly threadId: ThreadId;
1229 readonly runtime: ProviderAdapterV2SessionRuntime;1232 readonly runtime: ProviderAdapter.ProviderAdapterV2SessionRuntime;
1230 }) =>1233 }) =>
1231 Ref.update(sessions, (current) => {1234 Ref.update(sessions, (current) => {
1232 const key = sessionKey(input.providerSessionId);1235 const key = sessionKey(input.providerSessionId);
...@@ -1289,7 +1292,7 @@ export const layerWithOptions = (...@@ -1289,7 +1292,7 @@ export const layerWithOptions = (
1289 readonly providerInstanceId: ProviderInstanceId;1292 readonly providerInstanceId: ProviderInstanceId;
1290 }) =>1293 }) =>
1291 Effect.suspend(() => {1294 Effect.suspend(() => {
1292 let attachedTo: ProviderAdapterV2SessionRuntime | undefined;1295 let attachedTo: ProviderAdapter.ProviderAdapterV2SessionRuntime | undefined;
1293 let preparedForCleanup: PreparedMcpCredential | undefined;1296 let preparedForCleanup: PreparedMcpCredential | undefined;
1294 let reservationDropped = false;1297 let reservationDropped = false;
1295 const dropReservation = () => {1298 const dropReservation = () => {
...@@ -1631,7 +1634,7 @@ export const layerWithOptions = (...@@ -1631,7 +1634,7 @@ export const layerWithOptions = (
1631 subscribers: Ref.Ref<1634 subscribers: Ref.Ref<
1632 ReadonlyMap<number, Queue.Queue<ProviderSessionEventSignal, Cause.Done>>1635 ReadonlyMap<number, Queue.Queue<ProviderSessionEventSignal, Cause.Done>>
1633 >,1636 >,
1634 ): Effect.Effect<ProviderAdapterV2EventSubscription> =>1637 ): Effect.Effect<ProviderAdapter.ProviderAdapterV2EventSubscription> =>
1635 Effect.gen(function* () {1638 Effect.gen(function* () {
1636 const queue = yield* Queue.unbounded<ProviderSessionEventSignal, Cause.Done>();1639 const queue = yield* Queue.unbounded<ProviderSessionEventSignal, Cause.Done>();
1637 const subscriberId = yield* Ref.getAndUpdate(nextSubscriberId, (value) => value + 1);1640 const subscriberId = yield* Ref.getAndUpdate(nextSubscriberId, (value) => value + 1);
...@@ -1662,15 +1665,15 @@ export const layerWithOptions = (...@@ -1662,15 +1665,15 @@ export const layerWithOptions = (
1662 ),1665 ),
1663 Stream.ensuring(close),1666 Stream.ensuring(close),
1664 );1667 );
1665 return { events, close } satisfies ProviderAdapterV2EventSubscription;1668 return { events, close } satisfies ProviderAdapter.ProviderAdapterV2EventSubscription;
1666 });1669 });
16671670
1668 const decorateRuntime = (1671 const decorateRuntime = (
1669 runtime: ProviderAdapterV2SessionRuntime,1672 runtime: ProviderAdapter.ProviderAdapterV2SessionRuntime,
1670 eventSubscribers: Ref.Ref<1673 eventSubscribers: Ref.Ref<
1671 ReadonlyMap<number, Queue.Queue<ProviderSessionEventSignal, Cause.Done>>1674 ReadonlyMap<number, Queue.Queue<ProviderSessionEventSignal, Cause.Done>>
1672 >,1675 >,
1673 ): ProviderAdapterV2SessionRuntime => {1676 ): ProviderAdapter.ProviderAdapterV2SessionRuntime => {
1674 const providerSessionId = runtime.providerSessionId;1677 const providerSessionId = runtime.providerSessionId;
1675 const subscribeEvents = makeEventSubscription(eventSubscribers);1678 const subscribeEvents = makeEventSubscription(eventSubscribers);
1676 // Every provider's turn operations pass through here, so this is where they are1679 // Every provider's turn operations pass through here, so this is where they are
...@@ -1852,7 +1855,10 @@ export const layerWithOptions = (...@@ -1852,7 +1855,10 @@ export const layerWithOptions = (
18521855
1853 const persistProviderSessionUpdate = (1856 const persistProviderSessionUpdate = (
1854 entry: LiveSessionEntry,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 Effect.gen(function* () {1863 Effect.gen(function* () {
1858 const current = (yield* Ref.get(sessions)).get(1864 const current = (yield* Ref.get(sessions)).get(
...@@ -1925,7 +1931,7 @@ export const layerWithOptions = (...@@ -1925,7 +1931,7 @@ export const layerWithOptions = (
1925 .pipe(1931 .pipe(
1926 Effect.mapError(1932 Effect.mapError(
1927 (cause) =>1933 (cause) =>
1928 new ProviderAdapterEventStreamError({1934 new ProviderAdapter.ProviderAdapterEventStreamError({
1929 driver: entry.runtime.driver,1935 driver: entry.runtime.driver,
1930 providerSessionId: entry.runtime.providerSessionId,1936 providerSessionId: entry.runtime.providerSessionId,
1931 cause,1937 cause,
...@@ -1962,7 +1968,7 @@ export const layerWithOptions = (...@@ -1962,7 +1968,7 @@ export const layerWithOptions = (
1962 const cause = Exit.isFailure(exit)1968 const cause = Exit.isFailure(exit)
1963 ? exit.cause1969 ? exit.cause
1964 : Cause.fail(1970 : Cause.fail(
1965 new ProviderAdapterEventStreamError({1971 new ProviderAdapter.ProviderAdapterEventStreamError({
1966 driver: entry.runtime.driver,1972 driver: entry.runtime.driver,
1967 providerSessionId: entry.runtime.providerSessionId,1973 providerSessionId: entry.runtime.providerSessionId,
1968 cause: "Provider event stream ended unexpectedly.",1974 cause: "Provider event stream ended unexpectedly.",
...@@ -2212,7 +2218,7 @@ export const layerWithOptions = (...@@ -2212,7 +2218,7 @@ export const layerWithOptions = (
2212 Effect.gen(function* () {2218 Effect.gen(function* () {
2213 const entry = (yield* Ref.get(sessions)).get(sessionKey(providerSessionId));2219 const entry = (yield* Ref.get(sessions)).get(sessionKey(providerSessionId));
2214 if (entry === undefined) {2220 if (entry === undefined) {
2215 return Option.none<ProviderAdapterV2SessionRuntime>();2221 return Option.none<ProviderAdapter.ProviderAdapterV2SessionRuntime>();
2216 }2222 }
2217 yield* touchActivity(providerSessionId);2223 yield* touchActivity(providerSessionId);
2218 return Option.some(entry.exposedRuntime);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,10 +12,10 @@ import * as Effect from "effect/Effect";
12import * as Layer from "effect/Layer";12import * as Layer from "effect/Layer";
1313
14import { CodexProviderCapabilitiesV2 } from "./Adapters/CodexAdapterV2.ts";14import { CodexProviderCapabilitiesV2 } from "./Adapters/CodexAdapterV2.ts";
15import type { ProviderAdapterV2Shape } from "@t3tools/provider-core/server/ProviderAdapter";
16import * as ProviderAdapterRegistry from "./ProviderAdapterRegistry.ts";15import * as ProviderAdapterRegistry from "./ProviderAdapterRegistry.ts";
17import { acpSelectionTransition } from "@t3tools/provider-core/server/selectionTransition";16import { acpSelectionTransition } from "@t3tools/provider-core/server/selectionTransition";
18import * as ProviderSwitch from "./ProviderSwitchService.ts";17import * as ProviderSwitch from "./ProviderSwitchService.ts";
18import type * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter";
1919
20const driver = ProviderDriverKind.make("codex");20const driver = ProviderDriverKind.make("codex");
21const currentInstanceId = ProviderInstanceId.make("codex_primary");21const currentInstanceId = ProviderInstanceId.make("codex_primary");
...@@ -67,10 +67,12 @@ function deadSessionRecord(...@@ -67,10 +67,12 @@ function deadSessionRecord(
6767
68function layerTest(68function layerTest(
69 metadata: Readonly<Record<string, { continuationKey: string }>>,69 metadata: Readonly<Record<string, { continuationKey: string }>>,
70 planSelectionTransition: ProviderAdapterV2Shape["planSelectionTransition"] = () =>70 planSelectionTransition: ProviderAdapter.ProviderAdapterV2["Service"]["planSelectionTransition"] = () =>
71 Effect.succeed({ type: "restart_session" }),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 instanceId,76 instanceId,
75 driver,77 driver,
76 getCapabilities: () => Effect.succeed(capabilitiesWithoutModelSwitch),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,7 +24,7 @@ import * as Stream from "effect/Stream";
2424
25import { CodexProviderCapabilitiesV2 } from "./Adapters/CodexAdapterV2.ts";25import { CodexProviderCapabilitiesV2 } from "./Adapters/CodexAdapterV2.ts";
26import * as ProjectionStore from "./ProjectionStore.ts";26import * as ProjectionStore from "./ProjectionStore.ts";
27import type { ProviderAdapterV2SessionRuntime } from "@t3tools/provider-core/server/ProviderAdapter";27import type * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter";
28import * as ProviderSessionManager from "./ProviderSessionManager.ts";28import * as ProviderSessionManager from "./ProviderSessionManager.ts";
29import * as ProviderTurnControlService from "./ProviderTurnControlService.ts";29import * as ProviderTurnControlService from "./ProviderTurnControlService.ts";
3030
...@@ -177,7 +177,7 @@ it.effect(...@@ -177,7 +177,7 @@ it.effect(
177 updatedAt: now,177 updatedAt: now,
178 lastError: null,178 lastError: null,
179 };179 };
180 const runtime: ProviderAdapterV2SessionRuntime = {180 const runtime: ProviderAdapter.ProviderAdapterV2SessionRuntime = {
181 instanceId: providerInstanceId,181 instanceId: providerInstanceId,
182 driver,182 driver,
183 providerSessionId: oldSessionId,183 providerSessionId: oldSessionId,
apps/server/src/orchestration-v2/ProviderTurnStartService.test.ts+3-3
...@@ -32,7 +32,7 @@ import * as EventSink from "./EventSink.ts";...@@ -32,7 +32,7 @@ import * as EventSink from "./EventSink.ts";
32import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator";32import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator";
33import { CodexProviderCapabilitiesV2 } from "./Adapters/CodexAdapterV2.ts";33import { CodexProviderCapabilitiesV2 } from "./Adapters/CodexAdapterV2.ts";
34import * as ProjectionStore from "./ProjectionStore.ts";34import * as ProjectionStore from "./ProjectionStore.ts";
35import { ProviderAdapterEventStreamError } from "@t3tools/provider-core/server/ProviderAdapter";35import * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter";
36import * as ProviderSessionManager from "./ProviderSessionManager.ts";36import * as ProviderSessionManager from "./ProviderSessionManager.ts";
37import * as ProviderTurnStart from "./ProviderTurnStartService.ts";37import * as ProviderTurnStart from "./ProviderTurnStartService.ts";
38import * as RunExecutionService from "./RunExecutionService.ts";38import * as RunExecutionService from "./RunExecutionService.ts";
...@@ -372,7 +372,7 @@ function makeLocalCommandHarness(input: {...@@ -372,7 +372,7 @@ function makeLocalCommandHarness(input: {
372 }).pipe(372 }).pipe(
373 Effect.andThen(373 Effect.andThen(
374 Effect.fail(374 Effect.fail(
375 new ProviderAdapterEventStreamError({375 new ProviderAdapter.ProviderAdapterEventStreamError({
376 driver: providerThread.driver,376 driver: providerThread.driver,
377 providerSessionId,377 providerSessionId,
378 cause: input.ensureThreadFailure,378 cause: input.ensureThreadFailure,
...@@ -385,7 +385,7 @@ function makeLocalCommandHarness(input: {...@@ -385,7 +385,7 @@ function makeLocalCommandHarness(input: {
385 driver: providerThread.driver,385 driver: providerThread.driver,
386 resumeThread: () =>386 resumeThread: () =>
387 Effect.fail(387 Effect.fail(
388 new ProviderAdapterEventStreamError({388 new ProviderAdapter.ProviderAdapterEventStreamError({
389 driver: providerThread.driver,389 driver: providerThread.driver,
390 providerSessionId,390 providerSessionId,
391 cause: "native thread is gone",391 cause: "native thread is gone",
apps/server/src/orchestration-v2/ProviderTurnStartService.ts+9-14
...@@ -37,12 +37,7 @@ import {...@@ -37,12 +37,7 @@ import {
37 latestNativeContextUsage,37 latestNativeContextUsage,
38} from "@t3tools/provider-core/server/handoffBudget";38} from "@t3tools/provider-core/server/handoffBudget";
39import { deliverContextHandoffs } from "./ContextHandoffDelivery.ts";39import { deliverContextHandoffs } from "./ContextHandoffDelivery.ts";
40import {40import * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter";
41 ProviderAdapterTurnStartError,
42 type ProviderAdapterV2Error,
43 type ProviderAdapterV2HistoricalContext,
44 type ProviderAdapterV2SessionRuntime,
45} from "@t3tools/provider-core/server/ProviderAdapter";
46import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator";41import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator";
47import * as ProjectionStore from "./ProjectionStore.ts";42import * as ProjectionStore from "./ProjectionStore.ts";
48import * as ProviderSessionManager from "./ProviderSessionManager.ts";43import * as ProviderSessionManager from "./ProviderSessionManager.ts";
...@@ -184,15 +179,15 @@ export const layer: Layer.Layer<...@@ -184,15 +179,15 @@ export const layer: Layer.Layer<
184 };179 };
185180
186 const makeDeliverySession = (181 const makeDeliverySession = (
187 session: ProviderAdapterV2SessionRuntime,182 session: ProviderAdapter.ProviderAdapterV2SessionRuntime,
188 startWithHandoffs: (183 startWithHandoffs: (
189 input: Parameters<ProviderAdapterV2SessionRuntime["startTurn"]>[0],184 input: Parameters<ProviderAdapter.ProviderAdapterV2SessionRuntime["startTurn"]>[0],
190 compact?: boolean,185 compact?: boolean,
191 ) => ReturnType<ProviderAdapterV2SessionRuntime["startTurn"]>,186 ) => ReturnType<ProviderAdapter.ProviderAdapterV2SessionRuntime["startTurn"]>,
192 ) => {187 ) => {
193 let deliver: typeof startWithHandoffs | undefined = startWithHandoffs;188 let deliver: typeof startWithHandoffs | undefined = startWithHandoffs;
194 const start = (189 const start = (
195 input: Parameters<ProviderAdapterV2SessionRuntime["startTurn"]>[0],190 input: Parameters<ProviderAdapter.ProviderAdapterV2SessionRuntime["startTurn"]>[0],
196 compact = false,191 compact = false,
197 ) =>192 ) =>
198 Effect.suspend(() => {193 Effect.suspend(() => {
...@@ -595,7 +590,7 @@ export const layer: Layer.Layer<...@@ -595,7 +590,7 @@ export const layer: Layer.Layer<
595 // Only the provider's own thread load fails the run on the last attempt;590 // Only the provider's own thread load fails the run on the last attempt;
596 // store, id and handoff failures around it keep their typed errors.591 // store, id and handoff failures around it keep their typed errors.
597 const loadFromProvider = (592 const loadFromProvider = (
598 load: Effect.Effect<OrchestrationV2ProviderThread, ProviderAdapterV2Error>,593 load: Effect.Effect<OrchestrationV2ProviderThread, ProviderAdapter.ProviderAdapterV2Error>,
599 ) =>594 ) =>
600 Effect.gen(function* () {595 Effect.gen(function* () {
601 const loaded = yield* Effect.result(load);596 const loaded = yield* Effect.result(load);
...@@ -672,7 +667,7 @@ export const layer: Layer.Layer<...@@ -672,7 +667,7 @@ export const layer: Layer.Layer<
672 const resumed = yield* Effect.result(667 const resumed = yield* Effect.result(
673 uncertainDelivery668 uncertainDelivery
674 ? Effect.fail(669 ? Effect.fail(
675 new ProviderAdapterTurnStartError({670 new ProviderAdapter.ProviderAdapterTurnStartError({
676 driver: session.driver,671 driver: session.driver,
677 threadId: projection.thread.id,672 threadId: projection.thread.id,
678 providerThreadId: providerThread.id,673 providerThreadId: providerThread.id,
...@@ -1143,7 +1138,7 @@ export const layer: Layer.Layer<...@@ -1143,7 +1138,7 @@ export const layer: Layer.Layer<
1143 ...(session.injectHistory === undefined1138 ...(session.injectHistory === undefined
1144 ? {}1139 ? {}
1145 : {1140 : {
1146 inject: (history: ProviderAdapterV2HistoricalContext) =>1141 inject: (history: ProviderAdapter.ProviderAdapterV2HistoricalContext) =>
1147 session.injectHistory!({1142 session.injectHistory!({
1148 providerThread: runningProviderThread,1143 providerThread: runningProviderThread,
1149 ...history,1144 ...history,
...@@ -1211,7 +1206,7 @@ export const layer: Layer.Layer<...@@ -1211,7 +1206,7 @@ export const layer: Layer.Layer<
1211 Effect.mapError((cause) =>1206 Effect.mapError((cause) =>
1212 cause._tag === "ProviderAdapterTurnStartError"1207 cause._tag === "ProviderAdapterTurnStartError"
1213 ? cause1208 ? cause
1214 : new ProviderAdapterTurnStartError({1209 : new ProviderAdapter.ProviderAdapterTurnStartError({
1215 driver: session.driver,1210 driver: session.driver,
1216 threadId: projection.thread.id,1211 threadId: projection.thread.id,
1217 providerThreadId: providerThread.id,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,14 +43,7 @@ import * as CheckpointService from "./CheckpointService.ts";
43import type { PendingOrchestrationEffectV2 } from "./EffectOutbox.ts";43import type { PendingOrchestrationEffectV2 } from "./EffectOutbox.ts";
44import * as EventSink from "./EventSink.ts";44import * as EventSink from "./EventSink.ts";
45import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator";45import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator";
46import {46import * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter";
47 ProviderAdapterEventStreamError,
48 ProviderAdapterTurnStartError,
49 type ProviderAdapterV2Error,
50 type ProviderAdapterV2Event,
51 type ProviderAdapterV2SessionRuntime,
52 type ProviderAdapterV2TurnInput,
53} from "@t3tools/provider-core/server/ProviderAdapter";
54import * as ProjectionStore from "./ProjectionStore.ts";47import * as ProjectionStore from "./ProjectionStore.ts";
55import * as ProviderEventIngestor from "./ProviderEventIngestor.ts";48import * as ProviderEventIngestor from "./ProviderEventIngestor.ts";
56import * as McpAppModelContext from "../mcpApps/McpAppModelContext.ts";49import * as McpAppModelContext from "../mcpApps/McpAppModelContext.ts";
...@@ -95,7 +88,7 @@ it.effect("routes shared-runtime events only to their owning root run", () =>...@@ -95,7 +88,7 @@ it.effect("routes shared-runtime events only to their owning root run", () =>
95 providerThreadId: ProviderThreadId.make("provider-thread:shared-runtime:second"),88 providerThreadId: ProviderThreadId.make("provider-thread:shared-runtime:second"),
96 };89 };
97 const firstTurnId = ProviderTurnId.make("provider-turn:shared-runtime:first");90 const firstTurnId = ProviderTurnId.make("provider-turn:shared-runtime:first");
98 const turnEvent: ProviderAdapterV2Event = {91 const turnEvent: ProviderAdapter.ProviderAdapterV2Event = {
99 type: "provider_turn.updated",92 type: "provider_turn.updated",
100 driver,93 driver,
101 threadId: first.threadId,94 threadId: first.threadId,
...@@ -111,7 +104,7 @@ it.effect("routes shared-runtime events only to their owning root run", () =>...@@ -111,7 +104,7 @@ it.effect("routes shared-runtime events only to their owning root run", () =>
111 completedAt: null,104 completedAt: null,
112 },105 },
113 };106 };
114 const messageEvent: ProviderAdapterV2Event = {107 const messageEvent: ProviderAdapter.ProviderAdapterV2Event = {
115 type: "message.updated",108 type: "message.updated",
116 driver,109 driver,
117 message: {110 message: {
...@@ -129,7 +122,7 @@ it.effect("routes shared-runtime events only to their owning root run", () =>...@@ -129,7 +122,7 @@ it.effect("routes shared-runtime events only to their owning root run", () =>
129 updatedAt: now,122 updatedAt: now,
130 },123 },
131 };124 };
132 const terminalEvent: ProviderAdapterV2Event = {125 const terminalEvent: ProviderAdapter.ProviderAdapterV2Event = {
133 type: "turn.terminal",126 type: "turn.terminal",
134 driver,127 driver,
135 providerThreadId: first.providerThreadId,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,7 +174,7 @@ it("leaves a child thread created after the root turn ended to the run that is l
181 attemptId: RunAttemptId.make("attempt:late-child"),174 attemptId: RunAttemptId.make("attempt:late-child"),
182 providerThreadId: ProviderThreadId.make("provider-thread:late-child"),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 type: "app_thread.created",179 type: "app_thread.created",
187 driver,180 driver,
...@@ -193,7 +186,7 @@ it("leaves a child thread created after the root turn ended to the run that is l...@@ -193,7 +186,7 @@ it("leaves a child thread created after the root turn ended to the run that is l
193 rootThreadId: threadId,186 rootThreadId: threadId,
194 },187 },
195 },188 },
196 }) as ProviderAdapterV2Event;189 }) as ProviderAdapter.ProviderAdapterV2Event;
197 const earlyChild = ThreadId.make("thread:late-child:early");190 const earlyChild = ThreadId.make("thread:late-child:early");
198 const lateChild = ThreadId.make("thread:late-child:late");191 const lateChild = ThreadId.make("thread:late-child:late");
199192
...@@ -246,7 +239,7 @@ it("does not route a superseded attempt through a reused provider thread", () =>...@@ -246,7 +239,7 @@ it("does not route a superseded attempt through a reused provider thread", () =>
246 ...oldAttempt,239 ...oldAttempt,
247 attemptId: RunAttemptId.make("attempt:shared-runtime:restart:new"),240 attemptId: RunAttemptId.make("attempt:shared-runtime:restart:new"),
248 };241 };
249 const oldTurnEvent: ProviderAdapterV2Event = {242 const oldTurnEvent: ProviderAdapter.ProviderAdapterV2Event = {
250 type: "provider_turn.updated",243 type: "provider_turn.updated",
251 driver,244 driver,
252 threadId,245 threadId,
...@@ -299,33 +292,33 @@ it("routes only exact same-thread background items inherited from settled runs",...@@ -299,33 +292,33 @@ it("routes only exact same-thread background items inherited from settled runs",
299 type: "subagent",292 type: "subagent",
300 status: "running",293 status: "running",
301 },294 },
302 } as Extract<ProviderAdapterV2Event, { type: "turn_item.updated" }>;295 } as Extract<ProviderAdapter.ProviderAdapterV2Event, { type: "turn_item.updated" }>;
303 const unrelatedRunItem = {296 const unrelatedRunItem = {
304 ...inheritedRunning,297 ...inheritedRunning,
305 turnItem: {298 turnItem: {
306 ...inheritedRunning.turnItem,299 ...inheritedRunning.turnItem,
307 runId: RunId.make("run:inherited-background-routing:unrelated"),300 runId: RunId.make("run:inherited-background-routing:unrelated"),
308 },301 },
309 } as ProviderAdapterV2Event;302 } as ProviderAdapter.ProviderAdapterV2Event;
310 const unlistedPriorItem = {303 const unlistedPriorItem = {
311 ...inheritedRunning,304 ...inheritedRunning,
312 turnItem: {305 turnItem: {
313 ...inheritedRunning.turnItem,306 ...inheritedRunning.turnItem,
314 id: TurnItemId.make("turn-item:inherited-background-routing:unrelated"),307 id: TurnItemId.make("turn-item:inherited-background-routing:unrelated"),
315 },308 },
316 } as ProviderAdapterV2Event;309 } as ProviderAdapter.ProviderAdapterV2Event;
317 const unrelatedThreadItem = {310 const unrelatedThreadItem = {
318 ...inheritedRunning,311 ...inheritedRunning,
319 turnItem: { ...inheritedRunning.turnItem, threadId: otherThreadId },312 turnItem: { ...inheritedRunning.turnItem, threadId: otherThreadId },
320 } as ProviderAdapterV2Event;313 } as ProviderAdapter.ProviderAdapterV2Event;
321 const ordinaryItem = {314 const ordinaryItem = {
322 ...inheritedRunning,315 ...inheritedRunning,
323 turnItem: { ...inheritedRunning.turnItem, type: "reasoning" as const },316 turnItem: { ...inheritedRunning.turnItem, type: "reasoning" as const },
324 } as ProviderAdapterV2Event;317 } as ProviderAdapter.ProviderAdapterV2Event;
325 const inheritedTerminal = {318 const inheritedTerminal = {
326 ...inheritedRunning,319 ...inheritedRunning,
327 turnItem: { ...inheritedRunning.turnItem, status: "completed" as const },320 turnItem: { ...inheritedRunning.turnItem, status: "completed" as const },
328 } as ProviderAdapterV2Event;321 } as ProviderAdapter.ProviderAdapterV2Event;
329322
330 const [runningAccepted, afterRunning] = RunExecutionService.routeProviderEvent(323 const [runningAccepted, afterRunning] = RunExecutionService.routeProviderEvent(
331 inheritedRunning,324 inheritedRunning,
...@@ -515,7 +508,7 @@ it("does not carry interrupted or still-running child ownership into later attem...@@ -515,7 +508,7 @@ it("does not carry interrupted or still-running child ownership into later attem
515 startedAt: null,508 startedAt: null,
516 completedAt: null,509 completedAt: null,
517 },510 },
518 } satisfies ProviderAdapterV2Event;511 } satisfies ProviderAdapter.ProviderAdapterV2Event;
519512
520 assert.isFalse(RunExecutionService.routeProviderEvent(lateChildNode, identity, state)[0]);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,7 +543,7 @@ it.effect("rechecks run ownership immediately before calling the provider", () =
550 const session = {543 const session = {
551 events: Stream.never,544 events: Stream.never,
552 startTurn: () => Ref.update(providerStarts, (count) => count + 1),545 startTurn: () => Ref.update(providerStarts, (count) => count + 1),
553 } as unknown as ProviderAdapterV2SessionRuntime;546 } as unknown as ProviderAdapter.ProviderAdapterV2SessionRuntime;
554547
555 yield* runExecution.startRootRun({548 yield* runExecution.startRootRun({
556 commandId: CommandId.make("command:run-execution-start-guard"),549 commandId: CommandId.make("command:run-execution-start-guard"),
...@@ -597,14 +590,15 @@ it.effect("rechecks run ownership immediately before calling the provider", () =...@@ -597,14 +590,15 @@ it.effect("rechecks run ownership immediately before calling the provider", () =
597it.effect("passes the thread's MCP app context to the provider under a safe key", () =>590it.effect("passes the thread's MCP app context to the provider under a safe key", () =>
598 Effect.gen(function* () {591 Effect.gen(function* () {
599 const runExecution = yield* RunExecutionService.RunExecutionServiceV2;592 const runExecution = yield* RunExecutionService.RunExecutionServiceV2;
600 const started = yield* Deferred.make<ProviderAdapterV2TurnInput>();593 const started = yield* Deferred.make<ProviderAdapter.ProviderAdapterV2TurnInput>();
601 const threadId = ThreadId.make("thread:run-execution-app-context");594 const threadId = ThreadId.make("thread:run-execution-app-context");
602 const providerInstanceId = ProviderInstanceId.make("codex");595 const providerInstanceId = ProviderInstanceId.make("codex");
603 const attemptId = RunAttemptId.make("attempt:run-execution-app-context");596 const attemptId = RunAttemptId.make("attempt:run-execution-app-context");
604 const session = {597 const session = {
605 events: Stream.never,598 events: Stream.never,
606 startTurn: (input: ProviderAdapterV2TurnInput) => Deferred.succeed(started, input),599 startTurn: (input: ProviderAdapter.ProviderAdapterV2TurnInput) =>
607 } as unknown as ProviderAdapterV2SessionRuntime;600 Deferred.succeed(started, input),
601 } as unknown as ProviderAdapter.ProviderAdapterV2SessionRuntime;
608602
609 yield* runExecution.startRootRun({603 yield* runExecution.startRootRun({
610 commandId: CommandId.make("command:run-execution-app-context"),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,7 +710,7 @@ it.effect("fails the run when its ownership check cannot be read before calling
716 session: {710 session: {
717 events: Stream.never,711 events: Stream.never,
718 startTurn: () => Ref.update(providerStarts, (count) => count + 1),712 startTurn: () => Ref.update(providerStarts, (count) => count + 1),
719 } as unknown as ProviderAdapterV2SessionRuntime,713 } as unknown as ProviderAdapter.ProviderAdapterV2SessionRuntime,
720 run: { id: runId, threadId, ordinal: 1, providerInstanceId } as OrchestrationV2Run,714 run: { id: runId, threadId, ordinal: 1, providerInstanceId } as OrchestrationV2Run,
721 rootNode: {715 rootNode: {
722 id: NodeId.make("node:run-execution-start-guard-read"),716 id: NodeId.make("node:run-execution-start-guard-read"),
...@@ -821,7 +815,7 @@ it.effect(...@@ -821,7 +815,7 @@ it.effect(
821 Effect.sync(() => {815 Effect.sync(() => {
822 calls.push("compact");816 calls.push("compact");
823 }),817 }),
824 } as unknown as ProviderAdapterV2SessionRuntime,818 } as unknown as ProviderAdapter.ProviderAdapterV2SessionRuntime,
825 run: {819 run: {
826 id: RunId.make(`run:compact-routing:${index}`),820 id: RunId.make(`run:compact-routing:${index}`),
827 threadId,821 threadId,
...@@ -888,7 +882,7 @@ it.effect("refreshes MCP credential liveness before calling the provider", () =>...@@ -888,7 +882,7 @@ it.effect("refreshes MCP credential liveness before calling the provider", () =>
888 session: {882 session: {
889 events: Stream.never,883 events: Stream.never,
890 startTurn: () => Ref.update(order, (entries) => [...entries, "start-turn"]),884 startTurn: () => Ref.update(order, (entries) => [...entries, "start-turn"]),
891 } as unknown as ProviderAdapterV2SessionRuntime,885 } as unknown as ProviderAdapter.ProviderAdapterV2SessionRuntime,
892 run: {886 run: {
893 id: RunId.make("run:run-execution-mcp-liveness"),887 id: RunId.make("run:run-execution-mcp-liveness"),
894 threadId,888 threadId,
...@@ -1000,7 +994,7 @@ it.effect("starts the provider when checkpoint baseline capture fails", () =>...@@ -1000,7 +994,7 @@ it.effect("starts the provider when checkpoint baseline capture fails", () =>
1000 session: {994 session: {
1001 events: Stream.never,995 events: Stream.never,
1002 startTurn: () => Ref.update(providerStarts, (count) => count + 1),996 startTurn: () => Ref.update(providerStarts, (count) => count + 1),
1003 } as unknown as ProviderAdapterV2SessionRuntime,997 } as unknown as ProviderAdapter.ProviderAdapterV2SessionRuntime,
1004 run: {998 run: {
1005 id: runId,999 id: runId,
1006 threadId,1000 threadId,
...@@ -1137,7 +1131,7 @@ it.effect.each(["failure", "interruption", "stale-attempt", "start-guard"] as co...@@ -1137,7 +1131,7 @@ it.effect.each(["failure", "interruption", "stale-attempt", "start-guard"] as co
1137 session: {1131 session: {
1138 events: Stream.never,1132 events: Stream.never,
1139 startTurn: () => Ref.update(providerStarts, (count) => count + 1),1133 startTurn: () => Ref.update(providerStarts, (count) => count + 1),
1140 } as unknown as ProviderAdapterV2SessionRuntime,1134 } as unknown as ProviderAdapter.ProviderAdapterV2SessionRuntime,
1141 run: {1135 run: {
1142 id: runId,1136 id: runId,
1143 threadId,1137 threadId,
...@@ -1288,7 +1282,7 @@ it.effect("keeps ingesting owned child events after the root turn terminalizes",...@@ -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 type: "app_thread.created",1287 type: "app_thread.created",
1294 driver,1288 driver,
...@@ -1300,7 +1294,7 @@ it.effect("keeps ingesting owned child events after the root turn terminalizes",...@@ -1300,7 +1294,7 @@ it.effect("keeps ingesting owned child events after the root turn terminalizes",
1300 rootThreadId: threadId,1294 rootThreadId: threadId,
1301 },1295 },
1302 },1296 },
1303 } as ProviderAdapterV2Event,1297 } as ProviderAdapter.ProviderAdapterV2Event,
1304 {1298 {
1305 type: "provider_thread.updated",1299 type: "provider_thread.updated",
1306 driver,1300 driver,
...@@ -1308,7 +1302,7 @@ it.effect("keeps ingesting owned child events after the root turn terminalizes",...@@ -1308,7 +1302,7 @@ it.effect("keeps ingesting owned child events after the root turn terminalizes",
1308 id: childProviderThreadId,1302 id: childProviderThreadId,
1309 appThreadId: childThreadId,1303 appThreadId: childThreadId,
1310 },1304 },
1311 } as ProviderAdapterV2Event,1305 } as ProviderAdapter.ProviderAdapterV2Event,
1312 {1306 {
1313 type: "subagent.updated",1307 type: "subagent.updated",
1314 driver,1308 driver,
...@@ -1318,7 +1312,7 @@ it.effect("keeps ingesting owned child events after the root turn terminalizes",...@@ -1318,7 +1312,7 @@ it.effect("keeps ingesting owned child events after the root turn terminalizes",
1318 runId,1312 runId,
1319 status: "running",1313 status: "running",
1320 },1314 },
1321 } as ProviderAdapterV2Event,1315 } as ProviderAdapter.ProviderAdapterV2Event,
1322 {1316 {
1323 type: "provider_turn.updated",1317 type: "provider_turn.updated",
1324 driver,1318 driver,
...@@ -1330,7 +1324,7 @@ it.effect("keeps ingesting owned child events after the root turn terminalizes",...@@ -1330,7 +1324,7 @@ it.effect("keeps ingesting owned child events after the root turn terminalizes",
1330 runAttemptId: null,1324 runAttemptId: null,
1331 status: "running",1325 status: "running",
1332 },1326 },
1333 } as ProviderAdapterV2Event,1327 } as ProviderAdapter.ProviderAdapterV2Event,
1334 {1328 {
1335 type: "turn.terminal",1329 type: "turn.terminal",
1336 driver,1330 driver,
...@@ -1353,7 +1347,7 @@ it.effect("keeps ingesting owned child events after the root turn terminalizes",...@@ -1353,7 +1347,7 @@ it.effect("keeps ingesting owned child events after the root turn terminalizes",
1353 text: "Hello.",1347 text: "Hello.",
1354 streaming: false,1348 streaming: false,
1355 },1349 },
1356 } as ProviderAdapterV2Event,1350 } as ProviderAdapter.ProviderAdapterV2Event,
1357 {1351 {
1358 type: "provider_turn.updated",1352 type: "provider_turn.updated",
1359 driver,1353 driver,
...@@ -1365,7 +1359,7 @@ it.effect("keeps ingesting owned child events after the root turn terminalizes",...@@ -1365,7 +1359,7 @@ it.effect("keeps ingesting owned child events after the root turn terminalizes",
1365 runAttemptId: null,1359 runAttemptId: null,
1366 status: "completed",1360 status: "completed",
1367 },1361 },
1368 } as ProviderAdapterV2Event,1362 } as ProviderAdapter.ProviderAdapterV2Event,
1369 {1363 {
1370 type: "subagent.updated",1364 type: "subagent.updated",
1371 driver,1365 driver,
...@@ -1375,7 +1369,7 @@ it.effect("keeps ingesting owned child events after the root turn terminalizes",...@@ -1375,7 +1369,7 @@ it.effect("keeps ingesting owned child events after the root turn terminalizes",
1375 runId,1369 runId,
1376 status: "completed",1370 status: "completed",
1377 },1371 },
1378 } as ProviderAdapterV2Event,1372 } as ProviderAdapter.ProviderAdapterV2Event,
1379 ];1373 ];
13801374
1381 yield* Effect.gen(function* () {1375 yield* Effect.gen(function* () {
...@@ -1387,7 +1381,7 @@ it.effect("keeps ingesting owned child events after the root turn terminalizes",...@@ -1387,7 +1381,7 @@ it.effect("keeps ingesting owned child events after the root turn terminalizes",
1387 session: {1381 session: {
1388 events: Stream.fromIterable(events),1382 events: Stream.fromIterable(events),
1389 startTurn: () => Effect.void,1383 startTurn: () => Effect.void,
1390 } as unknown as ProviderAdapterV2SessionRuntime,1384 } as unknown as ProviderAdapter.ProviderAdapterV2SessionRuntime,
1391 run: {1385 run: {
1392 id: runId,1386 id: runId,
1393 threadId,1387 threadId,
...@@ -1788,7 +1782,7 @@ it.effect(...@@ -1788,7 +1782,7 @@ it.effect(
1788 ],1782 ],
1789 updatedAt: now,1783 updatedAt: now,
1790 },1784 },
1791 } as ProviderAdapterV2Event,1785 } as ProviderAdapter.ProviderAdapterV2Event,
1792 rootTerminalEvent(ids, "completed"),1786 rootTerminalEvent(ids, "completed"),
1793 {1787 {
1794 type: "provider_thread.updated",1788 type: "provider_thread.updated",
...@@ -1799,12 +1793,12 @@ it.effect(...@@ -1799,12 +1793,12 @@ it.effect(
1799 pendingBackgroundTasks: [],1793 pendingBackgroundTasks: [],
1800 updatedAt: now,1794 updatedAt: now,
1801 },1795 },
1802 } as ProviderAdapterV2Event,1796 } as ProviderAdapter.ProviderAdapterV2Event,
1803 ]),1797 ]),
1804 close: Deferred.succeed(ingestionDone, undefined),1798 close: Deferred.succeed(ingestionDone, undefined),
1805 }),1799 }),
1806 startTurn: () => Effect.void,1800 startTurn: () => Effect.void,
1807 } as unknown as ProviderAdapterV2SessionRuntime,1801 } as unknown as ProviderAdapter.ProviderAdapterV2SessionRuntime,
1808 run: {1802 run: {
1809 id: ids.runId,1803 id: ids.runId,
1810 threadId: ids.threadId,1804 threadId: ids.threadId,
...@@ -2000,7 +1994,7 @@ it.effect("drops late root provider-thread writes from a superseded attempt", ()...@@ -2000,7 +1994,7 @@ it.effect("drops late root provider-thread writes from a superseded attempt", ()
2000 ],1994 ],
2001 updatedAt: now,1995 updatedAt: now,
2002 },1996 },
2003 } as ProviderAdapterV2Event,1997 } as ProviderAdapter.ProviderAdapterV2Event,
2004 rootTerminalEvent(ids, "completed"),1998 rootTerminalEvent(ids, "completed"),
2005 // Late snapshot after a replacement attempt claimed the same1999 // Late snapshot after a replacement attempt claimed the same
2006 // run ordinal. Without attempt gating this would clobber it.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,12 +2010,12 @@ it.effect("drops late root provider-thread writes from a superseded attempt", ()
2016 ],2010 ],
2017 updatedAt: now,2011 updatedAt: now,
2018 },2012 },
2019 } as ProviderAdapterV2Event,2013 } as ProviderAdapter.ProviderAdapterV2Event,
2020 ]),2014 ]),
2021 close: Deferred.succeed(ingestionDone, undefined),2015 close: Deferred.succeed(ingestionDone, undefined),
2022 }),2016 }),
2023 startTurn: () => Effect.void,2017 startTurn: () => Effect.void,
2024 } as unknown as ProviderAdapterV2SessionRuntime,2018 } as unknown as ProviderAdapter.ProviderAdapterV2SessionRuntime,
2025 run: {2019 run: {
2026 id: ids.runId,2020 id: ids.runId,
2027 threadId: ids.threadId,2021 threadId: ids.threadId,
...@@ -2197,7 +2191,7 @@ it.effect(...@@ -2197,7 +2191,7 @@ it.effect(
2197 ],2191 ],
2198 updatedAt: now,2192 updatedAt: now,
2199 },2193 },
2200 } as ProviderAdapterV2Event,2194 } as ProviderAdapter.ProviderAdapterV2Event,
2201 // Late completion still writable (turn_item writes are not2195 // Late completion still writable (turn_item writes are not
2202 // ownership-gated); stream must stay open for it.2196 // ownership-gated); stream must stay open for it.
2203 backgroundTurnItemEvent(ids, "command_execution", "completed", 2),2197 backgroundTurnItemEvent(ids, "command_execution", "completed", 2),
...@@ -2205,7 +2199,7 @@ it.effect(...@@ -2205,7 +2199,7 @@ it.effect(
2205 close: Deferred.succeed(ingestionDone, undefined),2199 close: Deferred.succeed(ingestionDone, undefined),
2206 }),2200 }),
2207 startTurn: () => Effect.void,2201 startTurn: () => Effect.void,
2208 } as unknown as ProviderAdapterV2SessionRuntime,2202 } as unknown as ProviderAdapter.ProviderAdapterV2SessionRuntime,
2209 run: {2203 run: {
2210 id: ids.runId,2204 id: ids.runId,
2211 threadId: ids.threadId,2205 threadId: ids.threadId,
...@@ -2371,12 +2365,12 @@ it.effect(...@@ -2371,12 +2365,12 @@ it.effect(
2371 ],2365 ],
2372 updatedAt: now,2366 updatedAt: now,
2373 },2367 },
2374 } as ProviderAdapterV2Event,2368 } as ProviderAdapter.ProviderAdapterV2Event,
2375 ]),2369 ]),
2376 close: Deferred.succeed(ingestionDone, undefined),2370 close: Deferred.succeed(ingestionDone, undefined),
2377 }),2371 }),
2378 startTurn: () => Effect.void,2372 startTurn: () => Effect.void,
2379 } as unknown as ProviderAdapterV2SessionRuntime,2373 } as unknown as ProviderAdapter.ProviderAdapterV2SessionRuntime,
2380 run: {2374 run: {
2381 id: ids.runId,2375 id: ids.runId,
2382 threadId: ids.threadId,2376 threadId: ids.threadId,
...@@ -2439,7 +2433,7 @@ it.effect(...@@ -2439,7 +2433,7 @@ it.effect(
2439 );2433 );
2440 const providerInstanceId = ProviderInstanceId.make("codex");2434 const providerInstanceId = ProviderInstanceId.make("codex");
2441 const written = yield* Ref.make<ReadonlyArray<OrchestrationV2DomainEvent>>([]);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 const ingestionDone = yield* Deferred.make<void>();2437 const ingestionDone = yield* Deferred.make<void>();
2444 const layerTest = RunExecutionService.layer.pipe(2438 const layerTest = RunExecutionService.layer.pipe(
2445 Layer.provide(2439 Layer.provide(
...@@ -2609,11 +2603,11 @@ it.effect(...@@ -2609,11 +2603,11 @@ it.effect(
2609 completedAt: runningChildNode.startedAt,2603 completedAt: runningChildNode.startedAt,
2610 },2604 },
2611 },2605 },
2612 ] satisfies ReadonlyArray<ProviderAdapterV2Event>),2606 ] satisfies ReadonlyArray<ProviderAdapter.ProviderAdapterV2Event>),
2613 close: Deferred.succeed(ingestionDone, undefined),2607 close: Deferred.succeed(ingestionDone, undefined),
2614 }),2608 }),
2615 startTurn: () => Effect.void,2609 startTurn: () => Effect.void,
2616 } as unknown as ProviderAdapterV2SessionRuntime,2610 } as unknown as ProviderAdapter.ProviderAdapterV2SessionRuntime,
2617 run: {2611 run: {
2618 id: ids.runId,2612 id: ids.runId,
2619 threadId: ids.threadId,2613 threadId: ids.threadId,
...@@ -2940,11 +2934,11 @@ it.effect(...@@ -2940,11 +2934,11 @@ it.effect(
2940 },2934 },
2941 },2935 },
2942 rootTerminalEvent(ids, "interrupted"),2936 rootTerminalEvent(ids, "interrupted"),
2943 ] satisfies ReadonlyArray<ProviderAdapterV2Event>),2937 ] satisfies ReadonlyArray<ProviderAdapter.ProviderAdapterV2Event>),
2944 close: Deferred.succeed(ingestionDone, undefined),2938 close: Deferred.succeed(ingestionDone, undefined),
2945 }),2939 }),
2946 startTurn: () => Effect.void,2940 startTurn: () => Effect.void,
2947 } as unknown as ProviderAdapterV2SessionRuntime,2941 } as unknown as ProviderAdapter.ProviderAdapterV2SessionRuntime,
2948 run: {2942 run: {
2949 id: ids.runId,2943 id: ids.runId,
2950 threadId: ids.threadId,2944 threadId: ids.threadId,
...@@ -3439,7 +3433,7 @@ it.effect("refreshes pull requests after a provider stream exits with an error",...@@ -3439,7 +3433,7 @@ it.effect("refreshes pull requests after a provider stream exits with an error",
3439 shouldFinalizeRun: () => Effect.succeed(true),3433 shouldFinalizeRun: () => Effect.succeed(true),
3440 events: () =>3434 events: () =>
3441 Stream.fail(3435 Stream.fail(
3442 new ProviderAdapterEventStreamError({3436 new ProviderAdapter.ProviderAdapterEventStreamError({
3443 driver,3437 driver,
3444 providerSessionId: ProviderSessionId.make("session:exited"),3438 providerSessionId: ProviderSessionId.make("session:exited"),
3445 cause: "provider process exited",3439 cause: "provider process exited",
...@@ -3468,7 +3462,7 @@ it.effect("refreshes pull requests only once when startup failure closes its eve...@@ -3468,7 +3462,7 @@ it.effect("refreshes pull requests only once when startup failure closes its eve
3468 Deferred.await(ingestionStarted).pipe(3462 Deferred.await(ingestionStarted).pipe(
3469 Effect.andThen(3463 Effect.andThen(
3470 Effect.fail(3464 Effect.fail(
3471 new ProviderAdapterTurnStartError({3465 new ProviderAdapter.ProviderAdapterTurnStartError({
3472 driver,3466 driver,
3473 threadId: input.threadId,3467 threadId: input.threadId,
3474 providerThreadId: input.providerThread.id,3468 providerThreadId: input.providerThread.id,
...@@ -3506,8 +3500,11 @@ function captureRootRunTermination(input: {...@@ -3506,8 +3500,11 @@ function captureRootRunTermination(input: {
3506 readonly seedOpenSubagent?: boolean;3500 readonly seedOpenSubagent?: boolean;
3507 readonly events?: (3501 readonly events?: (
3508 ids: BackgroundScenarioIds,3502 ids: BackgroundScenarioIds,
3509 ) => Stream.Stream<ProviderAdapterV2Event, ProviderAdapterV2Error>;3503 ) => Stream.Stream<
3510 readonly startTurn?: ProviderAdapterV2SessionRuntime["startTurn"];3504 ProviderAdapter.ProviderAdapterV2Event,
3505 ProviderAdapter.ProviderAdapterV2Error
3506 >;
3507 readonly startTurn?: ProviderAdapter.ProviderAdapterV2SessionRuntime["startTurn"];
3511 readonly refreshAfterTurn?: Effect.Effect<void>;3508 readonly refreshAfterTurn?: Effect.Effect<void>;
3512}) {3509}) {
3513 return Effect.gen(function* () {3510 return Effect.gen(function* () {
...@@ -3622,11 +3619,11 @@ function captureRootRunTermination(input: {...@@ -3622,11 +3619,11 @@ function captureRootRunTermination(input: {
3622 ]3619 ]
3623 : []),3620 : []),
3624 rootTerminalEvent(ids, "interrupted"),3621 rootTerminalEvent(ids, "interrupted"),
3625 ] satisfies ReadonlyArray<ProviderAdapterV2Event>),3622 ] satisfies ReadonlyArray<ProviderAdapter.ProviderAdapterV2Event>),
3626 close: Deferred.succeed(ingestionDone, undefined),3623 close: Deferred.succeed(ingestionDone, undefined),
3627 }),3624 }),
3628 startTurn: input.startTurn ?? (() => Effect.void),3625 startTurn: input.startTurn ?? (() => Effect.void),
3629 } as unknown as ProviderAdapterV2SessionRuntime,3626 } as unknown as ProviderAdapter.ProviderAdapterV2SessionRuntime,
3630 run: {3627 run: {
3631 id: ids.runId,3628 id: ids.runId,
3632 threadId: ids.threadId,3629 threadId: ids.threadId,
...@@ -3716,7 +3713,9 @@ function backgroundScenarioIds(key: string): BackgroundScenarioIds {...@@ -3716,7 +3713,9 @@ function backgroundScenarioIds(key: string): BackgroundScenarioIds {
3716 };3713 };
3717}3714}
37183715
3719function childThreadCreatedEvent(ids: BackgroundScenarioIds): ProviderAdapterV2Event {3716function childThreadCreatedEvent(
3717 ids: BackgroundScenarioIds,
3718): ProviderAdapter.ProviderAdapterV2Event {
3720 return {3719 return {
3721 type: "app_thread.created",3720 type: "app_thread.created",
3722 driver,3721 driver,
...@@ -3728,14 +3727,14 @@ function childThreadCreatedEvent(ids: BackgroundScenarioIds): ProviderAdapterV2E...@@ -3728,14 +3727,14 @@ function childThreadCreatedEvent(ids: BackgroundScenarioIds): ProviderAdapterV2E
3728 rootThreadId: ids.threadId,3727 rootThreadId: ids.threadId,
3729 },3728 },
3730 },3729 },
3731 } as ProviderAdapterV2Event;3730 } as ProviderAdapter.ProviderAdapterV2Event;
3732}3731}
37333732
3734function childBackgroundTurnItemEvent(3733function childBackgroundTurnItemEvent(
3735 ids: BackgroundScenarioIds,3734 ids: BackgroundScenarioIds,
3736 status: "running" | "completed" | "idle",3735 status: "running" | "completed" | "idle",
3737 ordinal: number,3736 ordinal: number,
3738): ProviderAdapterV2Event {3737): ProviderAdapter.ProviderAdapterV2Event {
3739 return {3738 return {
3740 type: "turn_item.updated",3739 type: "turn_item.updated",
3741 driver,3740 driver,
...@@ -3748,7 +3747,7 @@ function childBackgroundTurnItemEvent(...@@ -3748,7 +3747,7 @@ function childBackgroundTurnItemEvent(
3748 type: "command_execution",3747 type: "command_execution",
3749 status,3748 status,
3750 },3749 },
3751 } as ProviderAdapterV2Event;3750 } as ProviderAdapter.ProviderAdapterV2Event;
3752}3751}
37533752
3754function backgroundTurnItemEvent(3753function backgroundTurnItemEvent(
...@@ -3757,7 +3756,7 @@ function backgroundTurnItemEvent(...@@ -3757,7 +3756,7 @@ function backgroundTurnItemEvent(
3757 status: "running" | "completed" | "idle",3756 status: "running" | "completed" | "idle",
3758 ordinal: number,3757 ordinal: number,
3759 itemId?: TurnItemId,3758 itemId?: TurnItemId,
3760): ProviderAdapterV2Event {3759): ProviderAdapter.ProviderAdapterV2Event {
3761 return {3760 return {
3762 type: "turn_item.updated",3761 type: "turn_item.updated",
3763 driver,3762 driver,
...@@ -3770,7 +3769,7 @@ function backgroundTurnItemEvent(...@@ -3770,7 +3769,7 @@ function backgroundTurnItemEvent(
3770 type,3769 type,
3771 status,3770 status,
3772 },3771 },
3773 } as ProviderAdapterV2Event;3772 } as ProviderAdapter.ProviderAdapterV2Event;
3774}3773}
37753774
3776function backgroundTurnItemEventForRun(3775function backgroundTurnItemEventForRun(
...@@ -3779,7 +3778,7 @@ function backgroundTurnItemEventForRun(...@@ -3779,7 +3778,7 @@ function backgroundTurnItemEventForRun(
3779 type: "command_execution" | "dynamic_tool" | "subagent",3778 type: "command_execution" | "dynamic_tool" | "subagent",
3780 status: "running" | "completed" | "idle",3779 status: "running" | "completed" | "idle",
3781 ordinal: number,3780 ordinal: number,
3782): ProviderAdapterV2Event {3781): ProviderAdapter.ProviderAdapterV2Event {
3783 const event = backgroundTurnItemEvent(ids, type, status, ordinal);3782 const event = backgroundTurnItemEvent(ids, type, status, ordinal);
3784 if (event.type !== "turn_item.updated") {3783 if (event.type !== "turn_item.updated") {
3785 return event;3784 return event;
...@@ -3790,7 +3789,7 @@ function backgroundTurnItemEventForRun(...@@ -3790,7 +3789,7 @@ function backgroundTurnItemEventForRun(
3790function subagentEvent(3789function subagentEvent(
3791 ids: BackgroundScenarioIds,3790 ids: BackgroundScenarioIds,
3792 status: "running" | "completed" | "idle",3791 status: "running" | "completed" | "idle",
3793): ProviderAdapterV2Event {3792): ProviderAdapter.ProviderAdapterV2Event {
3794 return {3793 return {
3795 type: "subagent.updated",3794 type: "subagent.updated",
3796 driver,3795 driver,
...@@ -3800,7 +3799,7 @@ function subagentEvent(...@@ -3800,7 +3799,7 @@ function subagentEvent(
3800 runId: ids.runId,3799 runId: ids.runId,
3801 status,3800 status,
3802 },3801 },
3803 } as ProviderAdapterV2Event;3802 } as ProviderAdapter.ProviderAdapterV2Event;
3804}3803}
38053804
3806function makeRunOwnedSubagentFixture(input: {3805function makeRunOwnedSubagentFixture(input: {
...@@ -3977,7 +3976,7 @@ function makeRunOwnedSubagentChildNodeFixture(input: {...@@ -3977,7 +3976,7 @@ function makeRunOwnedSubagentChildNodeFixture(input: {
3977function rootTerminalEvent(3976function rootTerminalEvent(
3978 ids: BackgroundScenarioIds,3977 ids: BackgroundScenarioIds,
3979 status: "completed" | "interrupted" | "cancelled" | "failed",3978 status: "completed" | "interrupted" | "cancelled" | "failed",
3980): ProviderAdapterV2Event {3979): ProviderAdapter.ProviderAdapterV2Event {
3981 const common = {3980 const common = {
3982 type: "turn.terminal" as const,3981 type: "turn.terminal" as const,
3983 driver,3982 driver,
...@@ -4003,7 +4002,7 @@ function rootTerminalEvent(...@@ -4003,7 +4002,7 @@ function rootTerminalEvent(
40034002
4004function runBackgroundItemScenario(4003function runBackgroundItemScenario(
4005 key: string,4004 key: string,
4006 makeEvents: (ids: BackgroundScenarioIds) => ReadonlyArray<ProviderAdapterV2Event>,4005 makeEvents: (ids: BackgroundScenarioIds) => ReadonlyArray<ProviderAdapter.ProviderAdapterV2Event>,
4007 options?: {4006 options?: {
4008 readonly keepEventStreamOpen?: boolean;4007 readonly keepEventStreamOpen?: boolean;
4009 readonly loadInheritedBackgroundTurnItems?: () => Effect.Effect<4008 readonly loadInheritedBackgroundTurnItems?: () => Effect.Effect<
...@@ -4082,7 +4081,7 @@ function runBackgroundItemScenario(...@@ -4082,7 +4081,7 @@ function runBackgroundItemScenario(
4082 };4081 };
4083 }),4082 }),
4084 startTurn: () => Effect.void,4083 startTurn: () => Effect.void,
4085 } as unknown as ProviderAdapterV2SessionRuntime,4084 } as unknown as ProviderAdapter.ProviderAdapterV2SessionRuntime,
4086 run: {4085 run: {
4087 id: ids.runId,4086 id: ids.runId,
4088 threadId: ids.threadId,4087 threadId: ids.threadId,
apps/server/src/orchestration-v2/RunExecutionService.ts+15-16
...@@ -41,13 +41,7 @@ import * as ServerSettings from "../serverSettings.ts";...@@ -41,13 +41,7 @@ import * as ServerSettings from "../serverSettings.ts";
41import * as CheckpointService from "./CheckpointService.ts";41import * as CheckpointService from "./CheckpointService.ts";
42import * as EventSink from "./EventSink.ts";42import * as EventSink from "./EventSink.ts";
43import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator";43import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator";
44import type {44import * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter";
45 ProviderAdapterV2Event,
46 ProviderAdapterV2RuntimePolicy,
47 ProviderAdapterV2SessionRuntime,
48 ProviderAdapterV2TurnMessage,
49} from "@t3tools/provider-core/server/ProviderAdapter";
50import { ProviderAdapterTurnStartError } from "@t3tools/provider-core/server/ProviderAdapter";
51import * as ProviderEventIngestor from "./ProviderEventIngestor.ts";45import * as ProviderEventIngestor from "./ProviderEventIngestor.ts";
52import type { ProjectionStoreV2Error } from "./ProjectionStore.ts";46import type { ProjectionStoreV2Error } from "./ProjectionStore.ts";
53import {47import {
...@@ -79,7 +73,10 @@ export interface InheritedBackgroundTurnItemRoute {...@@ -79,7 +73,10 @@ export interface InheritedBackgroundTurnItemRoute {
79 readonly runId: OrchestrationV2Run["id"];73 readonly runId: OrchestrationV2Run["id"];
80}74}
8175
82type ProviderTerminalEvent = Extract<ProviderAdapterV2Event, { readonly type: "turn.terminal" }>;76type ProviderTerminalEvent = Extract<
77 ProviderAdapter.ProviderAdapterV2Event,
78 { readonly type: "turn.terminal" }
79>;
8380
84function isTerminalProviderTurnStatus(status: OrchestrationV2ProviderTurn["status"]): boolean {81function isTerminalProviderTurnStatus(status: OrchestrationV2ProviderTurn["status"]): boolean {
85 return (82 return (
...@@ -351,7 +348,7 @@ export function makeProviderEventRoutingState(input: {...@@ -351,7 +348,7 @@ export function makeProviderEventRoutingState(input: {
351}348}
352349
353export function routeProviderEvent(350export function routeProviderEvent(
354 event: ProviderAdapterV2Event,351 event: ProviderAdapter.ProviderAdapterV2Event,
355 input: ProviderEventRouteIdentity,352 input: ProviderEventRouteIdentity,
356 state: ProviderEventRoutingState,353 state: ProviderEventRoutingState,
357): readonly [boolean, ProviderEventRoutingState] {354): readonly [boolean, ProviderEventRoutingState] {
...@@ -503,7 +500,7 @@ export interface RunExecutionServiceV2StartRootRunInput {...@@ -503,7 +500,7 @@ export interface RunExecutionServiceV2StartRootRunInput {
503 readonly commandId: CommandId;500 readonly commandId: CommandId;
504 readonly appThread: OrchestrationV2AppThread;501 readonly appThread: OrchestrationV2AppThread;
505 readonly providerSessionId: ProviderSessionId;502 readonly providerSessionId: ProviderSessionId;
506 readonly session: ProviderAdapterV2SessionRuntime;503 readonly session: ProviderAdapter.ProviderAdapterV2SessionRuntime;
507 readonly run: OrchestrationV2Run;504 readonly run: OrchestrationV2Run;
508 readonly rootNode: OrchestrationV2ExecutionNode;505 readonly rootNode: OrchestrationV2ExecutionNode;
509 readonly checkpointScope: OrchestrationV2CheckpointScope;506 readonly checkpointScope: OrchestrationV2CheckpointScope;
...@@ -521,9 +518,9 @@ export interface RunExecutionServiceV2StartRootRunInput {...@@ -521,9 +518,9 @@ export interface RunExecutionServiceV2StartRootRunInput {
521 readonly shouldStartProviderTurn?: () => Effect.Effect<boolean, ProjectionStoreV2Error>;518 readonly shouldStartProviderTurn?: () => Effect.Effect<boolean, ProjectionStoreV2Error>;
522 readonly shouldFinalizeRun?: () => Effect.Effect<boolean, ProjectionStoreV2Error>;519 readonly shouldFinalizeRun?: () => Effect.Effect<boolean, ProjectionStoreV2Error>;
523 readonly hasUnpairedRunInterruptRequest?: () => Effect.Effect<boolean, never>;520 readonly hasUnpairedRunInterruptRequest?: () => Effect.Effect<boolean, never>;
524 readonly message: ProviderAdapterV2TurnMessage;521 readonly message: ProviderAdapter.ProviderAdapterV2TurnMessage;
525 readonly modelSelection: ModelSelection;522 readonly modelSelection: ModelSelection;
526 readonly runtimePolicy: ProviderAdapterV2RuntimePolicy;523 readonly runtimePolicy: ProviderAdapter.ProviderAdapterV2RuntimePolicy;
527}524}
528525
529export interface RunExecutionServiceV2Shape {526export interface RunExecutionServiceV2Shape {
...@@ -738,7 +735,6 @@ export const layer: Layer.Layer<...@@ -738,7 +735,6 @@ export const layer: Layer.Layer<
738 providerInstanceId: input.run.providerInstanceId,735 providerInstanceId: input.run.providerInstanceId,
739 occurredAt: completedAt,736 occurredAt: completedAt,
740 payload: makeProviderFailureTurnItem({737 payload: makeProviderFailureTurnItem({
741 idAllocator,
742 driver: input.terminal.driver,738 driver: input.terminal.driver,
743 threadId: input.run.threadId,739 threadId: input.run.threadId,
744 runId: input.run.id,740 runId: input.run.id,
...@@ -1006,7 +1002,10 @@ export const layer: Layer.Layer<...@@ -1006,7 +1002,10 @@ export const layer: Layer.Layer<
1006 }1002 }
1007 yield* Ref.set(rootRunFinalized, true);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 Effect.gen(function* () {1009 Effect.gen(function* () {
1011 const routing = yield* Ref.get(eventRouting);1010 const routing = yield* Ref.get(eventRouting);
1012 if (event.type === "provider_turn.updated") {1011 if (event.type === "provider_turn.updated") {
...@@ -1403,7 +1402,7 @@ export const layer: Layer.Layer<...@@ -1403,7 +1402,7 @@ export const layer: Layer.Layer<
1403 const startTurn = compact1402 const startTurn = compact
1404 ? (input.session.compactThread?.(turnInput) ??1403 ? (input.session.compactThread?.(turnInput) ??
1405 Effect.fail(1404 Effect.fail(
1406 new ProviderAdapterTurnStartError({1405 new ProviderAdapter.ProviderAdapterTurnStartError({
1407 driver: input.session.driver,1406 driver: input.session.driver,
1408 threadId: input.run.threadId,1407 threadId: input.run.threadId,
1409 providerThreadId: input.providerThread.id,1408 providerThreadId: input.providerThread.id,
...@@ -1471,7 +1470,7 @@ export const layer: Layer.Layer<...@@ -1471,7 +1470,7 @@ export const layer: Layer.Layer<
1471);1470);
14721471
1473export function makeInterruptResultTurnItem(input: {1472export function makeInterruptResultTurnItem(input: {
1474 readonly idAllocator: IdAllocator.IdAllocatorV2Shape;1473 readonly idAllocator: IdAllocator.IdAllocatorV2["Service"];
1475 readonly run: OrchestrationV2Run;1474 readonly run: OrchestrationV2Run;
1476 readonly rootNode: OrchestrationV2ExecutionNode;1475 readonly rootNode: OrchestrationV2ExecutionNode;
1477 readonly providerThread: OrchestrationV2ProviderThread;1476 readonly providerThread: OrchestrationV2ProviderThread;
apps/server/src/orchestration-v2/RuntimePolicy.ts+4-7
...@@ -12,10 +12,7 @@ import * as Option from "effect/Option";...@@ -12,10 +12,7 @@ import * as Option from "effect/Option";
12import * as Schema from "effect/Schema";12import * as Schema from "effect/Schema";
1313
14import * as ProviderInstanceRegistry from "../provider/ProviderInstanceRegistry.ts";14import * as ProviderInstanceRegistry from "../provider/ProviderInstanceRegistry.ts";
15import {15import * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter";
16 ProviderAdapterV2RuntimePolicy,
17 type ProviderAdapterV2RuntimePolicy as ProviderAdapterV2RuntimePolicyType,
18} from "@t3tools/provider-core/server/ProviderAdapter";
19import * as ProjectStore from "./ProjectStore.ts";16import * as ProjectStore from "./ProjectStore.ts";
2017
21/**18/**
...@@ -52,7 +49,7 @@ export interface RuntimePolicyV2Shape {...@@ -52,7 +49,7 @@ export interface RuntimePolicyV2Shape {
52 readonly resolve: (input: {49 readonly resolve: (input: {
53 readonly thread: OrchestrationV2AppThread;50 readonly thread: OrchestrationV2AppThread;
54 readonly modelSelection: ModelSelection;51 readonly modelSelection: ModelSelection;
55 }) => Effect.Effect<ProviderAdapterV2RuntimePolicyType, RuntimePolicyV2Error>;52 }) => Effect.Effect<ProviderAdapter.ProviderAdapterV2RuntimePolicy, RuntimePolicyV2Error>;
56}53}
5754
58export class RuntimePolicyV2 extends Context.Service<RuntimePolicyV2, RuntimePolicyV2Shape>()(55export class RuntimePolicyV2 extends Context.Service<RuntimePolicyV2, RuntimePolicyV2Shape>()(
...@@ -128,7 +125,7 @@ export const layerFromProjectStore: Layer.Layer<...@@ -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 runtimeMode: providerRuntimeMode(input.thread.runtimeMode, supportedRuntimeModes),129 runtimeMode: providerRuntimeMode(input.thread.runtimeMode, supportedRuntimeModes),
133 interactionMode: input.thread.interactionMode,130 interactionMode: input.thread.interactionMode,
134 cwd,131 cwd,
...@@ -149,7 +146,7 @@ export function layerWithOverride(...@@ -149,7 +146,7 @@ export function layerWithOverride(
149 resolve: (input) =>146 resolve: (input) =>
150 base.resolve(input).pipe(147 base.resolve(input).pipe(
151 Effect.map((policy) =>148 Effect.map((policy) =>
152 ProviderAdapterV2RuntimePolicy.make({149 ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
153 ...policy,150 ...policy,
154 ...(override.cwd === undefined ? {} : { cwd: override.cwd }),151 ...(override.cwd === undefined ? {} : { cwd: override.cwd }),
155 ...(override.approvalPolicy === undefined152 ...(override.approvalPolicy === undefined
apps/server/src/orchestration-v2/RuntimeRequestService.test.ts+3-3
...@@ -12,7 +12,7 @@ import * as Effect from "effect/Effect";...@@ -12,7 +12,7 @@ import * as Effect from "effect/Effect";
12import * as Layer from "effect/Layer";12import * as Layer from "effect/Layer";
13import * as Option from "effect/Option";13import * as Option from "effect/Option";
1414
15import type { ProviderAdapterV2RuntimeRequestResponseInput } from "@t3tools/provider-core/server/ProviderAdapter";15import type * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter";
16import * as ProjectionStore from "./ProjectionStore.ts";16import * as ProjectionStore from "./ProjectionStore.ts";
17import * as ProviderSessionManager from "./ProviderSessionManager.ts";17import * as ProviderSessionManager from "./ProviderSessionManager.ts";
18import * as RuntimeRequestService from "./RuntimeRequestService.ts";18import * as RuntimeRequestService from "./RuntimeRequestService.ts";
...@@ -69,7 +69,7 @@ it.effect("forwards orchestrator-resolved runtime requests to the live adapter",...@@ -69,7 +69,7 @@ it.effect("forwards orchestrator-resolved runtime requests to the live adapter",
69 const providerSessionId = ProviderSessionId.make("provider-session-runtime-request-resolved");69 const providerSessionId = ProviderSessionId.make("provider-session-runtime-request-resolved");
70 const requestId = RuntimeRequestId.make("request-resolved");70 const requestId = RuntimeRequestId.make("request-resolved");
71 const respondToRuntimeRequest = vi.fn(71 const respondToRuntimeRequest = vi.fn(
72 (_input: ProviderAdapterV2RuntimeRequestResponseInput) => Effect.void,72 (_input: ProviderAdapter.ProviderAdapterV2RuntimeRequestResponseInput) => Effect.void,
73 );73 );
74 const getSession = vi.fn(() =>74 const getSession = vi.fn(() =>
75 Effect.succeed(75 Effect.succeed(
...@@ -133,7 +133,7 @@ it.effect("rejects expired runtime requests before invoking the live adapter", (...@@ -133,7 +133,7 @@ it.effect("rejects expired runtime requests before invoking the live adapter", (
133 const providerSessionId = ProviderSessionId.make("provider-session-runtime-request-expired");133 const providerSessionId = ProviderSessionId.make("provider-session-runtime-request-expired");
134 const requestId = RuntimeRequestId.make("request-expired");134 const requestId = RuntimeRequestId.make("request-expired");
135 const respondToRuntimeRequest = vi.fn(135 const respondToRuntimeRequest = vi.fn(
136 (_input: ProviderAdapterV2RuntimeRequestResponseInput) => Effect.void,136 (_input: ProviderAdapter.ProviderAdapterV2RuntimeRequestResponseInput) => Effect.void,
137 );137 );
138 const getSession = vi.fn(() =>138 const getSession = vi.fn(() =>
139 Effect.succeed(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,15 +28,10 @@ import { CodexProviderCapabilitiesV2 } from "./Adapters/CodexAdapterV2.ts";
28import * as EffectWorker from "./EffectWorker.ts";28import * as EffectWorker from "./EffectWorker.ts";
29import * as EventSink from "./EventSink.ts";29import * as EventSink from "./EventSink.ts";
30import * as Orchestrator from "./Orchestrator.ts";30import * as Orchestrator from "./Orchestrator.ts";
31import {
32 ProviderAdapterOpenSessionError,
33 type ProviderAdapterV2Event,
34 type ProviderAdapterV2Shape,
35 type ProviderAdapterV2TurnInput,
36} from "@t3tools/provider-core/server/ProviderAdapter";
37import * as ProviderAdapterRegistry from "./ProviderAdapterRegistry.ts";31import * as ProviderAdapterRegistry from "./ProviderAdapterRegistry.ts";
38import * as ProviderReplayHarness from "./testkit/ProviderReplayHarness.ts";32import * as ProviderReplayHarness from "./testkit/ProviderReplayHarness.ts";
39import { checkpointWorkspace } from "@t3tools/provider-testing/replayWorkspace";33import { checkpointWorkspace } from "@t3tools/provider-testing/replayWorkspace";
34import * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter";
4035
41const driver = ProviderDriverKind.make("codex");36const driver = ProviderDriverKind.make("codex");
42const providerInstanceId = ProviderInstanceId.make("codex-restart-test");37const providerInstanceId = ProviderInstanceId.make("codex-restart-test");
...@@ -69,7 +64,7 @@ const exclusiveCapabilities: OrchestrationV2ProviderCapabilities = {...@@ -69,7 +64,7 @@ const exclusiveCapabilities: OrchestrationV2ProviderCapabilities = {
69};64};
7065
71interface ActiveTurn {66interface ActiveTurn {
72 readonly input: ProviderAdapterV2TurnInput;67 readonly input: ProviderAdapter.ProviderAdapterV2TurnInput;
73 readonly providerTurnId: ProviderTurnId;68 readonly providerTurnId: ProviderTurnId;
74}69}
7570
...@@ -92,7 +87,7 @@ function makeRestartAdapter(...@@ -92,7 +87,7 @@ function makeRestartAdapter(
92 state: Ref.Ref<RestartAdapterState>,87 state: Ref.Ref<RestartAdapterState>,
93 sessionCapabilities: OrchestrationV2ProviderCapabilities = pooledCapabilities,88 sessionCapabilities: OrchestrationV2ProviderCapabilities = pooledCapabilities,
94 providerInstanceId = initialSelection.instanceId,89 providerInstanceId = initialSelection.instanceId,
95): ProviderAdapterV2Shape {90): ProviderAdapter.ProviderAdapterV2["Service"] {
96 return {91 return {
97 instanceId: providerInstanceId,92 instanceId: providerInstanceId,
98 driver,93 driver,
...@@ -125,14 +120,14 @@ function makeRestartAdapter(...@@ -125,14 +120,14 @@ function makeRestartAdapter(
125 ] as const;120 ] as const;
126 });121 });
127 if (failThisOpen) {122 if (failThisOpen) {
128 return yield* new ProviderAdapterOpenSessionError({123 return yield* new ProviderAdapter.ProviderAdapterOpenSessionError({
129 driver,124 driver,
130 providerSessionId: sessionInput.providerSessionId,125 providerSessionId: sessionInput.providerSessionId,
131 cause: "simulated replacement open failure",126 cause: "simulated replacement open failure",
132 });127 });
133 }128 }
134129
135 const events = yield* Queue.unbounded<ProviderAdapterV2Event>();130 const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>();
136 const now = yield* DateTime.now;131 const now = yield* DateTime.now;
137 const providerSession: OrchestrationV2ProviderSession = {132 const providerSession: OrchestrationV2ProviderSession = {
138 id: sessionInput.providerSessionId,133 id: sessionInput.providerSessionId,
...@@ -290,7 +285,9 @@ function makeRestartAdapter(...@@ -290,7 +285,9 @@ function makeRestartAdapter(
290 };285 };
291}286}
292287
293function makeCompletingHandoffAdapter(startCount: Ref.Ref<number>): ProviderAdapterV2Shape {288function makeCompletingHandoffAdapter(
289 startCount: Ref.Ref<number>,
290): ProviderAdapter.ProviderAdapterV2["Service"] {
294 return {291 return {
295 instanceId: handoffProviderInstanceId,292 instanceId: handoffProviderInstanceId,
296 driver: handoffDriver,293 driver: handoffDriver,
...@@ -298,7 +295,7 @@ function makeCompletingHandoffAdapter(startCount: Ref.Ref<number>): ProviderAdap...@@ -298,7 +295,7 @@ function makeCompletingHandoffAdapter(startCount: Ref.Ref<number>): ProviderAdap
298 planSelectionTransition: () => Effect.succeed({ type: "apply_on_next_turn" }),295 planSelectionTransition: () => Effect.succeed({ type: "apply_on_next_turn" }),
299 openSession: (sessionInput) =>296 openSession: (sessionInput) =>
300 Effect.gen(function* () {297 Effect.gen(function* () {
301 const events = yield* Queue.unbounded<ProviderAdapterV2Event>();298 const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>();
302 const now = yield* DateTime.now;299 const now = yield* DateTime.now;
303 return {300 return {
304 instanceId: handoffProviderInstanceId,301 instanceId: handoffProviderInstanceId,
...@@ -865,7 +862,7 @@ it.live.each(["active", "idle", "selection-command", "pooled", "separate-home"]...@@ -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 const layerRegistry = Layer.succeed(ProviderAdapterRegistry.ProviderAdapterRegistryV2, {867 const layerRegistry = Layer.succeed(ProviderAdapterRegistry.ProviderAdapterRegistryV2, {
871 get: (instanceId) =>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,15 +23,10 @@ import { CodexProviderCapabilitiesV2 } from "./Adapters/CodexAdapterV2.ts";
23import * as EffectWorker from "./EffectWorker.ts";23import * as EffectWorker from "./EffectWorker.ts";
24import * as EventSink from "./EventSink.ts";24import * as EventSink from "./EventSink.ts";
25import * as Orchestrator from "./Orchestrator.ts";25import * as Orchestrator from "./Orchestrator.ts";
26import {
27 ProviderAdapterSteerRunError,
28 type ProviderAdapterV2Event,
29 type ProviderAdapterV2Shape,
30 type ProviderAdapterV2TurnInput,
31} from "@t3tools/provider-core/server/ProviderAdapter";
32import * as ProviderAdapterRegistry from "./ProviderAdapterRegistry.ts";26import * as ProviderAdapterRegistry from "./ProviderAdapterRegistry.ts";
33import * as ProviderReplayHarness from "./testkit/ProviderReplayHarness.ts";27import * as ProviderReplayHarness from "./testkit/ProviderReplayHarness.ts";
34import { checkpointWorkspace } from "@t3tools/provider-testing/replayWorkspace";28import { checkpointWorkspace } from "@t3tools/provider-testing/replayWorkspace";
29import * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter";
3530
36const driver = ProviderDriverKind.make("codex");31const driver = ProviderDriverKind.make("codex");
37const instanceId = ProviderInstanceId.make("codex");32const instanceId = ProviderInstanceId.make("codex");
...@@ -67,8 +62,8 @@ it.effect.each(...@@ -67,8 +62,8 @@ it.effect.each(
67 Effect.scoped(62 Effect.scoped(
68 Effect.gen(function* () {63 Effect.gen(function* () {
69 const cwd = yield* checkpointWorkspace(`steering-completion-${timing.replaceAll(" ", "-")}`);64 const cwd = yield* checkpointWorkspace(`steering-completion-${timing.replaceAll(" ", "-")}`);
70 const events = yield* Queue.unbounded<ProviderAdapterV2Event>();65 const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>();
71 const started: ProviderAdapterV2TurnInput[] = [];66 const started: ProviderAdapter.ProviderAdapterV2TurnInput[] = [];
72 const steerEntered = yield* Deferred.make<void>();67 const steerEntered = yield* Deferred.make<void>();
73 const rejectSteer = yield* Deferred.make<void>();68 const rejectSteer = yield* Deferred.make<void>();
74 let steerCalls = 0;69 let steerCalls = 0;
...@@ -80,7 +75,7 @@ it.effect.each(...@@ -80,7 +75,7 @@ it.effect.each(
80 activeSteeringInterruptsTools: timing === "with interrupting native steering",75 activeSteeringInterruptsTools: timing === "with interrupting native steering",
81 },76 },
82 };77 };
83 const adapter: ProviderAdapterV2Shape = {78 const adapter: ProviderAdapter.ProviderAdapterV2["Service"] = {
84 instanceId,79 instanceId,
85 driver,80 driver,
86 getCapabilities: () => Effect.succeed(capabilities),81 getCapabilities: () => Effect.succeed(capabilities),
...@@ -153,7 +148,7 @@ it.effect.each(...@@ -153,7 +148,7 @@ it.effect.each(
153 if (timing === "after delivery") return;148 if (timing === "after delivery") return;
154 yield* Deferred.succeed(steerEntered, undefined);149 yield* Deferred.succeed(steerEntered, undefined);
155 yield* Deferred.await(rejectSteer);150 yield* Deferred.await(rejectSteer);
156 return yield* new ProviderAdapterSteerRunError({151 return yield* new ProviderAdapter.ProviderAdapterSteerRunError({
157 driver,152 driver,
158 providerThreadId: turn.providerThread.id,153 providerThreadId: turn.providerThread.id,
159 providerTurnId: turn.providerTurnId,154 providerTurnId: turn.providerTurnId,
...@@ -424,8 +419,8 @@ const composerSelection = {...@@ -424,8 +419,8 @@ const composerSelection = {
424419
425const nextTurnSelectionHarness = Effect.fn("nextTurnSelectionHarness")(function* (name: string) {420const nextTurnSelectionHarness = Effect.fn("nextTurnSelectionHarness")(function* (name: string) {
426 const cwd = yield* checkpointWorkspace(name);421 const cwd = yield* checkpointWorkspace(name);
427 const events = yield* Queue.unbounded<ProviderAdapterV2Event>();422 const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>();
428 const started: ProviderAdapterV2TurnInput[] = [];423 const started: ProviderAdapter.ProviderAdapterV2TurnInput[] = [];
429 const steered: string[] = [];424 const steered: string[] = [];
430 const capabilities = {425 const capabilities = {
431 ...CodexProviderCapabilitiesV2,426 ...CodexProviderCapabilitiesV2,
...@@ -435,7 +430,7 @@ const nextTurnSelectionHarness = Effect.fn("nextTurnSelectionHarness")(function*...@@ -435,7 +430,7 @@ const nextTurnSelectionHarness = Effect.fn("nextTurnSelectionHarness")(function*
435 supportsSteeringByInterruptRestart: false,430 supportsSteeringByInterruptRestart: false,
436 },431 },
437 };432 };
438 const adapter: ProviderAdapterV2Shape = {433 const adapter: ProviderAdapter.ProviderAdapterV2["Service"] = {
439 instanceId,434 instanceId,
440 driver,435 driver,
441 getCapabilities: () => Effect.succeed(capabilities),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 +7,7 @@ import type * as DateTime from "effect/DateTime";
7import * as Effect from "effect/Effect";7import * as Effect from "effect/Effect";
88
9import type { PendingOrchestrationEffectV2 } from "./EffectOutbox.ts";9import type { PendingOrchestrationEffectV2 } from "./EffectOutbox.ts";
10import type { IdAllocatorV2, IdAllocatorV2Error } from "@t3tools/provider-core/server/IdAllocator";10import type * as IdAllocator from "@t3tools/provider-core/server/IdAllocator";
1111
12export interface ThreadDeletionPlan {12export interface ThreadDeletionPlan {
13 readonly events: ReadonlyArray<OrchestrationV2DomainEvent>;13 readonly events: ReadonlyArray<OrchestrationV2DomainEvent>;
...@@ -23,8 +23,8 @@ export const planThreadDeletion = Effect.fn("ThreadDeletion.planThreadDeletion")...@@ -23,8 +23,8 @@ export const planThreadDeletion = Effect.fn("ThreadDeletion.planThreadDeletion")
23 >;23 >;
24 readonly attachmentIds: ReadonlyArray<string>;24 readonly attachmentIds: ReadonlyArray<string>;
25 readonly now: DateTime.Utc;25 readonly now: DateTime.Utc;
26 readonly idAllocator: IdAllocatorV2["Service"];26 readonly idAllocator: IdAllocator.IdAllocatorV2["Service"];
27}): Effect.fn.Return<ThreadDeletionPlan, IdAllocatorV2Error> {27}): Effect.fn.Return<ThreadDeletionPlan, IdAllocator.IdAllocatorV2Error> {
28 const { command, projection, now, idAllocator } = input;28 const { command, projection, now, idAllocator } = input;
29 const events: Array<OrchestrationV2DomainEvent> = [];29 const events: Array<OrchestrationV2DomainEvent> = [];
30 const effects: Array<PendingOrchestrationEffectV2> = [];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,15 +21,15 @@ import { CodexProviderCapabilitiesV2 } from "./Adapters/CodexAdapterV2.ts";
21import { ClaudeProviderCapabilitiesV2 } from "./Adapters/ClaudeAdapterV2.ts";21import { ClaudeProviderCapabilitiesV2 } from "./Adapters/ClaudeAdapterV2.ts";
22import * as EventSink from "./EventSink.ts";22import * as EventSink from "./EventSink.ts";
23import * as Orchestrator from "./Orchestrator.ts";23import * as Orchestrator from "./Orchestrator.ts";
24import type { ProviderAdapterV2Shape } from "@t3tools/provider-core/server/ProviderAdapter";
25import * as ProviderAdapterRegistry from "./ProviderAdapterRegistry.ts";24import * as ProviderAdapterRegistry from "./ProviderAdapterRegistry.ts";
26import * as ProviderReplayHarness from "./testkit/ProviderReplayHarness.ts";25import * as ProviderReplayHarness from "./testkit/ProviderReplayHarness.ts";
26import type * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter";
2727
28const forkCases = (["codex", "claudeAgent"] as const).flatMap((driverName) => {28const forkCases = (["codex", "claudeAgent"] as const).flatMap((driverName) => {
29 const driver = ProviderDriverKind.make(driverName);29 const driver = ProviderDriverKind.make(driverName);
30 const instanceId = ProviderInstanceId.make(driver);30 const instanceId = ProviderInstanceId.make(driver);
31 const modelSelection = { instanceId, model: "test-model" };31 const modelSelection = { instanceId, model: "test-model" };
32 const adapter: ProviderAdapterV2Shape = {32 const adapter: ProviderAdapter.ProviderAdapterV2["Service"] = {
33 instanceId,33 instanceId,
34 driver,34 driver,
35 getCapabilities: () =>35 getCapabilities: () =>
apps/server/src/orchestration-v2/ThreadLaunchService.test.ts+2-2
...@@ -54,12 +54,12 @@ import { CodexProviderCapabilitiesV2 } from "./Adapters/CodexAdapterV2.ts";...@@ -54,12 +54,12 @@ import { CodexProviderCapabilitiesV2 } from "./Adapters/CodexAdapterV2.ts";
54import * as CommandReceiptStore from "./CommandReceiptStore.ts";54import * as CommandReceiptStore from "./CommandReceiptStore.ts";
55import * as EffectOutbox from "./EffectOutbox.ts";55import * as EffectOutbox from "./EffectOutbox.ts";
56import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator";56import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator";
57import type { ProviderAdapterV2Shape } from "@t3tools/provider-core/server/ProviderAdapter";
58import * as ProviderAdapterRegistry from "./ProviderAdapterRegistry.ts";57import * as ProviderAdapterRegistry from "./ProviderAdapterRegistry.ts";
59import * as ThreadLaunch from "./ThreadLaunchService.ts";58import * as ThreadLaunch from "./ThreadLaunchService.ts";
60import * as ThreadManagement from "./ThreadManagementService.ts";59import * as ThreadManagement from "./ThreadManagementService.ts";
61import * as ThreadTitleRegeneration from "./ThreadTitleRegenerationService.ts";60import * as ThreadTitleRegeneration from "./ThreadTitleRegenerationService.ts";
62import * as ProviderReplayHarness from "./testkit/ProviderReplayHarness.ts";61import * as ProviderReplayHarness from "./testkit/ProviderReplayHarness.ts";
62import type * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter";
6363
64const projectId = ProjectId.make("project:launch-test");64const projectId = ProjectId.make("project:launch-test");
65const otherProjectId = ProjectId.make("project:launch-other");65const otherProjectId = ProjectId.make("project:launch-other");
...@@ -94,7 +94,7 @@ const adapter = {...@@ -94,7 +94,7 @@ const adapter = {
94 getCapabilities: () => Effect.succeed(CodexProviderCapabilitiesV2),94 getCapabilities: () => Effect.succeed(CodexProviderCapabilitiesV2),
95 planSelectionTransition: () => Effect.succeed({ type: "apply_on_next_turn" as const }),95 planSelectionTransition: () => Effect.succeed({ type: "apply_on_next_turn" as const }),
96 openSession: () => Effect.die("provider execution is disabled in launch tests"),96 openSession: () => Effect.die("provider execution is disabled in launch tests"),
97} as ProviderAdapterV2Shape;97} as ProviderAdapter.ProviderAdapterV2["Service"];
9898
99interface HarnessOptions {99interface HarnessOptions {
100 readonly managedFolders?: Layer.Layer<ManagedProjectFolders.ManagedProjectFolders>;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,10 +24,10 @@ import { CodexProviderCapabilitiesV2 } from "./Adapters/CodexAdapterV2.ts";
24import { OrchestrationEffectRequestV2 } from "./EffectOutbox.ts";24import { OrchestrationEffectRequestV2 } from "./EffectOutbox.ts";
25import * as Orchestrator from "./Orchestrator.ts";25import * as Orchestrator from "./Orchestrator.ts";
26import * as ProjectionStore from "./ProjectionStore.ts";26import * as ProjectionStore from "./ProjectionStore.ts";
27import type { ProviderAdapterV2Shape } from "@t3tools/provider-core/server/ProviderAdapter";
28import * as ProviderAdapterRegistry from "./ProviderAdapterRegistry.ts";27import * as ProviderAdapterRegistry from "./ProviderAdapterRegistry.ts";
29import * as ThreadManagementService from "./ThreadManagementService.ts";28import * as ThreadManagementService from "./ThreadManagementService.ts";
30import * as ProviderReplayHarness from "./testkit/ProviderReplayHarness.ts";29import * as ProviderReplayHarness from "./testkit/ProviderReplayHarness.ts";
30import type * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter";
3131
32const instanceId = ProviderInstanceId.make("codex");32const instanceId = ProviderInstanceId.make("codex");
33const modelSelection = { instanceId, model: "gpt-5.1-codex" };33const modelSelection = { instanceId, model: "gpt-5.1-codex" };
...@@ -37,7 +37,7 @@ const adapter = {...@@ -37,7 +37,7 @@ const adapter = {
37 getCapabilities: () => Effect.succeed(CodexProviderCapabilitiesV2),37 getCapabilities: () => Effect.succeed(CodexProviderCapabilitiesV2),
38 planSelectionTransition: () => Effect.succeed({ type: "apply_on_next_turn" as const }),38 planSelectionTransition: () => Effect.succeed({ type: "apply_on_next_turn" as const }),
39 openSession: () => Effect.die("Runs here never reach a provider"),39 openSession: () => Effect.die("Runs here never reach a provider"),
40} as ProviderAdapterV2Shape;40} as ProviderAdapter.ProviderAdapterV2["Service"];
41const layerDatabase = SqlitePersistence.layerMemory;41const layerDatabase = SqlitePersistence.layerMemory;
42// No effect worker: runs stay unstarted, so Stop ends them without a provider.42// No effect worker: runs stay unstarted, so Stop ends them without a provider.
43const layerTest = ThreadManagementService.layer.pipe(43const 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,11 +22,11 @@ import * as ServerSettings from "../serverSettings.ts";
22import * as TextGeneration from "../textGeneration/TextGeneration.ts";22import * as TextGeneration from "../textGeneration/TextGeneration.ts";
23import { CodexProviderCapabilitiesV2 } from "./Adapters/CodexAdapterV2.ts";23import { CodexProviderCapabilitiesV2 } from "./Adapters/CodexAdapterV2.ts";
24import * as EffectOutbox from "./EffectOutbox.ts";24import * as EffectOutbox from "./EffectOutbox.ts";
25import type { ProviderAdapterV2Shape } from "@t3tools/provider-core/server/ProviderAdapter";
26import * as ProviderAdapterRegistry from "./ProviderAdapterRegistry.ts";25import * as ProviderAdapterRegistry from "./ProviderAdapterRegistry.ts";
27import * as ThreadManagement from "./ThreadManagementService.ts";26import * as ThreadManagement from "./ThreadManagementService.ts";
28import * as ThreadTitleRegeneration from "./ThreadTitleRegenerationService.ts";27import * as ThreadTitleRegeneration from "./ThreadTitleRegenerationService.ts";
29import * as ProviderReplayHarness from "./testkit/ProviderReplayHarness.ts";28import * as ProviderReplayHarness from "./testkit/ProviderReplayHarness.ts";
29import type * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter";
3030
31const projectId = ProjectId.make("project:title-regeneration");31const projectId = ProjectId.make("project:title-regeneration");
32const modelSelection = {32const modelSelection = {
...@@ -40,7 +40,7 @@ const adapter = {...@@ -40,7 +40,7 @@ const adapter = {
40 getCapabilities: () => Effect.succeed(CodexProviderCapabilitiesV2),40 getCapabilities: () => Effect.succeed(CodexProviderCapabilitiesV2),
41 planSelectionTransition: () => Effect.succeed({ type: "apply_on_next_turn" as const }),41 planSelectionTransition: () => Effect.succeed({ type: "apply_on_next_turn" as const }),
42 openSession: () => Effect.die("provider execution is disabled in title regeneration tests"),42 openSession: () => Effect.die("provider execution is disabled in title regeneration tests"),
43} as ProviderAdapterV2Shape;43} as ProviderAdapter.ProviderAdapterV2["Service"];
4444
45function makeHarness(45function makeHarness(
46 options: {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,9 +2,9 @@ import { describe, expect, it } from "vite-plus/test";
2import { MessageId, NodeId, ProviderDriverKind, ThreadId, TurnItemId } from "@t3tools/contracts";2import { MessageId, NodeId, ProviderDriverKind, ThreadId, TurnItemId } from "@t3tools/contracts";
3import * as DateTime from "effect/DateTime";3import * as DateTime from "effect/DateTime";
4import { makeAssistantStreamingFilter, splitBufferedAssistantText } from "./assistantStreaming.ts";4import { makeAssistantStreamingFilter, splitBufferedAssistantText } from "./assistantStreaming.ts";
5import type { ProviderAdapterV2Event } from "@t3tools/provider-core/server/ProviderAdapter";5import type * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter";
66
7const message = (text: string, streaming = true): ProviderAdapterV2Event => ({7const message = (text: string, streaming = true): ProviderAdapter.ProviderAdapterV2Event => ({
8 type: "message.updated",8 type: "message.updated",
9 driver: ProviderDriverKind.make("codex"),9 driver: ProviderDriverKind.make("codex"),
10 message: {10 message: {
...@@ -27,7 +27,7 @@ const turnItem = (...@@ -27,7 +27,7 @@ const turnItem = (
27 text: string,27 text: string,
28 streaming = true,28 streaming = true,
29 type: "assistant_message" | "reasoning" = "assistant_message",29 type: "assistant_message" | "reasoning" = "assistant_message",
30): ProviderAdapterV2Event => ({30): ProviderAdapter.ProviderAdapterV2Event => ({
31 type: "turn_item.updated",31 type: "turn_item.updated",
32 driver: ProviderDriverKind.make("codex"),32 driver: ProviderDriverKind.make("codex"),
33 turnItem: {33 turnItem: {
...@@ -108,7 +108,7 @@ describe("V2 assistant streaming", () => {...@@ -108,7 +108,7 @@ describe("V2 assistant streaming", () => {
108 "suppresses running assistant nodes in %s mode while delivering tool and completed nodes",108 "suppresses running assistant nodes in %s mode while delivering tool and completed nodes",
109 (mode) => {109 (mode) => {
110 const filter = makeAssistantStreamingFilter(mode);110 const filter = makeAssistantStreamingFilter(mode);
111 const running: Extract<ProviderAdapterV2Event, { type: "node.updated" }> = {111 const running: Extract<ProviderAdapter.ProviderAdapterV2Event, { type: "node.updated" }> = {
112 type: "node.updated",112 type: "node.updated",
113 driver: ProviderDriverKind.make("codex"),113 driver: ProviderDriverKind.make("codex"),
114 node: {114 node: {
apps/server/src/orchestration-v2/assistantStreaming.ts+5-2
...@@ -1,5 +1,5 @@...@@ -1,5 +1,5 @@
1import type { ResponseStreamingMode } from "@t3tools/contracts";1import type { ResponseStreamingMode } from "@t3tools/contracts";
2import type { ProviderAdapterV2Event } from "@t3tools/provider-core/server/ProviderAdapter";2import type * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter";
33
4// An opening fence may sit at any indentation, since fences inside list4// An opening fence may sit at any indentation, since fences inside list
5// items are indented past the marker. A closing fence may be indented at most5// 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,7 +91,10 @@ export function splitBufferedAssistantText(text: string): { ready: string; rest:
91/** Buffer assistant messages and reasoning alike, keeping unfinished markdown out of the read model. */91/** Buffer assistant messages and reasoning alike, keeping unfinished markdown out of the read model. */
92export function makeAssistantStreamingFilter(mode: ResponseStreamingMode) {92export function makeAssistantStreamingFilter(mode: ResponseStreamingMode) {
93 const delivered = new Map<string, { text: string; at: number }>();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 if (98 if (
96 event.type === "node.updated" &&99 event.type === "node.updated" &&
97 event.node.kind === "assistant_message" &&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,14 +44,10 @@ import * as LegacyV1ThreadImporter from "./LegacyV1ThreadImporter.ts";
44import * as Orchestrator from "../Orchestrator.ts";44import * as Orchestrator from "../Orchestrator.ts";
45import * as ProjectionMaintenance from "../ProjectionMaintenance.ts";45import * as ProjectionMaintenance from "../ProjectionMaintenance.ts";
46import * as ProjectionStore from "../ProjectionStore.ts";46import * as ProjectionStore from "../ProjectionStore.ts";
47import {
48 ProviderAdapterProtocolError,
49 type ProviderAdapterV2Event,
50 type ProviderAdapterV2Shape,
51} from "@t3tools/provider-core/server/ProviderAdapter";
52import * as ProviderAdapterRegistry from "../ProviderAdapterRegistry.ts";47import * as ProviderAdapterRegistry from "../ProviderAdapterRegistry.ts";
53import * as ProviderReplayHarness from "../testkit/ProviderReplayHarness.ts";48import * as ProviderReplayHarness from "../testkit/ProviderReplayHarness.ts";
54import { checkpointWorkspace } from "@t3tools/provider-testing/replayWorkspace";49import { checkpointWorkspace } from "@t3tools/provider-testing/replayWorkspace";
50import * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter";
5551
56const PROJECT_ID = "project:cutover";52const PROJECT_ID = "project:cutover";
57const ACTIVE_THREAD = "thread:cutover:active";53const ACTIVE_THREAD = "thread:cutover:active";
...@@ -422,7 +418,7 @@ interface CapturedTurn {...@@ -422,7 +418,7 @@ interface CapturedTurn {
422}418}
423419
424const unimplemented = (detail: string) =>420const unimplemented = (detail: string) =>
425 Effect.fail(new ProviderAdapterProtocolError({ driver, detail }));421 Effect.fail(new ProviderAdapter.ProviderAdapterProtocolError({ driver, detail }));
426422
427const makeCodexAdapter = (capturedTurns: Ref.Ref<ReadonlyArray<CapturedTurn>>) =>423const makeCodexAdapter = (capturedTurns: Ref.Ref<ReadonlyArray<CapturedTurn>>) =>
428 ({424 ({
...@@ -432,7 +428,7 @@ const makeCodexAdapter = (capturedTurns: Ref.Ref<ReadonlyArray<CapturedTurn>>) =...@@ -432,7 +428,7 @@ const makeCodexAdapter = (capturedTurns: Ref.Ref<ReadonlyArray<CapturedTurn>>) =
432 planSelectionTransition: () => Effect.succeed({ type: "apply_on_next_turn" }),428 planSelectionTransition: () => Effect.succeed({ type: "apply_on_next_turn" }),
433 openSession: (sessionInput) =>429 openSession: (sessionInput) =>
434 Effect.gen(function* () {430 Effect.gen(function* () {
435 const events = yield* PubSub.unbounded<ProviderAdapterV2Event>();431 const events = yield* PubSub.unbounded<ProviderAdapter.ProviderAdapterV2Event>();
436 const now = yield* DateTime.now;432 const now = yield* DateTime.now;
437 const providerSession: OrchestrationV2ProviderSession = {433 const providerSession: OrchestrationV2ProviderSession = {
438 id: sessionInput.providerSessionId,434 id: sessionInput.providerSessionId,
...@@ -551,7 +547,7 @@ const makeCodexAdapter = (capturedTurns: Ref.Ref<ReadonlyArray<CapturedTurn>>) =...@@ -551,7 +547,7 @@ const makeCodexAdapter = (capturedTurns: Ref.Ref<ReadonlyArray<CapturedTurn>>) =
551 failure: null,547 failure: null,
552 threadDisposition: "reusable",548 threadDisposition: "reusable",
553 },549 },
554 ] satisfies ReadonlyArray<ProviderAdapterV2Event>);550 ] satisfies ReadonlyArray<ProviderAdapter.ProviderAdapterV2Event>);
555 }),551 }),
556 steerTurn: () => Effect.void,552 steerTurn: () => Effect.void,
557 interruptTurn: () => Effect.void,553 interruptTurn: () => Effect.void,
...@@ -561,7 +557,7 @@ const makeCodexAdapter = (capturedTurns: Ref.Ref<ReadonlyArray<CapturedTurn>>) =...@@ -561,7 +557,7 @@ const makeCodexAdapter = (capturedTurns: Ref.Ref<ReadonlyArray<CapturedTurn>>) =
561 forkThread: () => unimplemented("forkThread unused in cutover test"),557 forkThread: () => unimplemented("forkThread unused in cutover test"),
562 };558 };
563 }),559 }),
564 }) satisfies ProviderAdapterV2Shape;560 }) satisfies ProviderAdapter.ProviderAdapterV2["Service"];
565561
566const waitForIdle = Effect.fn("LegacyV1Cutover.waitForIdle")(function* (threadId: ThreadId) {562const waitForIdle = Effect.fn("LegacyV1Cutover.waitForIdle")(function* (threadId: ThreadId) {
567 const orchestrator = yield* Orchestrator.OrchestratorV2;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,16 +67,13 @@ import * as PullRequestWatchReactor from "./PullRequestWatchReactor.ts";
67import { PullRequestProviderError } from "../pullRequest/PullRequestProvider.ts";67import { PullRequestProviderError } from "../pullRequest/PullRequestProvider.ts";
68import * as PullRequestService from "../pullRequest/PullRequestService.ts";68import * as PullRequestService from "../pullRequest/PullRequestService.ts";
69import * as ProjectStore from "./ProjectStore.ts";69import * as ProjectStore from "./ProjectStore.ts";
70import type {
71 ProviderAdapterV2SessionRuntime,
72 ProviderAdapterV2Shape,
73} from "@t3tools/provider-core/server/ProviderAdapter";
74import * as ProviderSessionManager from "./ProviderSessionManager.ts";70import * as ProviderSessionManager from "./ProviderSessionManager.ts";
75import * as RuntimeLayer from "./runtimeLayer.ts";71import * as RuntimeLayer from "./runtimeLayer.ts";
76import { shellStreamItemFromThreadShell } from "./ShellStream.ts";72import { shellStreamItemFromThreadShell } from "./ShellStream.ts";
77import { CodexProviderCapabilitiesV2 } from "./Adapters/CodexAdapterV2.ts";73import { CodexProviderCapabilitiesV2 } from "./Adapters/CodexAdapterV2.ts";
78import * as ThreadManagementService from "./ThreadManagementService.ts";74import * as ThreadManagementService from "./ThreadManagementService.ts";
79import * as ThreadCommandExecutor from "./ThreadCommandExecutor.ts";75import * as ThreadCommandExecutor from "./ThreadCommandExecutor.ts";
76import type * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter";
8077
81const layerPlatformTest = Layer.merge(78const layerPlatformTest = Layer.merge(
82 NodeServices.layer,79 NodeServices.layer,
...@@ -119,7 +116,7 @@ const orchestrationAdapter = {...@@ -119,7 +116,7 @@ const orchestrationAdapter = {
119 getCapabilities: () => Effect.succeed(CodexProviderCapabilitiesV2),116 getCapabilities: () => Effect.succeed(CodexProviderCapabilitiesV2),
120 planSelectionTransition: () => Effect.succeed({ type: "apply_on_next_turn" }),117 planSelectionTransition: () => Effect.succeed({ type: "apply_on_next_turn" }),
121 openSession: () => Effect.die("sessions are not used by lifecycle tests"),118 openSession: () => Effect.die("sessions are not used by lifecycle tests"),
122} as ProviderAdapterV2Shape;119} as ProviderAdapter.ProviderAdapterV2["Service"];
123const providerInstance = {120const providerInstance = {
124 instanceId: modelSelection.instanceId,121 instanceId: modelSelection.instanceId,
125 driverKind: driver,122 driverKind: driver,
...@@ -928,7 +925,9 @@ it.layer(layerTest)("OrchestrationV2LayerLive", (it) => {...@@ -928,7 +925,9 @@ it.layer(layerTest)("OrchestrationV2LayerLive", (it) => {
928 const sessionSpy = vi925 const sessionSpy = vi
929 .spyOn(sessions, "get")926 .spyOn(sessions, "get")
930 .mockReturnValue(927 .mockReturnValue(
931 Effect.succeed(Option.some({ providerSession } as ProviderAdapterV2SessionRuntime)),928 Effect.succeed(
929 Option.some({ providerSession } as ProviderAdapter.ProviderAdapterV2SessionRuntime),
930 ),
932 );931 );
933 yield* Effect.addFinalizer(() => Effect.sync(() => sessionSpy.mockRestore()));932 yield* Effect.addFinalizer(() => Effect.sync(() => sessionSpy.mockRestore()));
934933
apps/server/src/orchestration-v2/runtimeLayer.ts+1-1
...@@ -30,7 +30,7 @@ import * as ProjectionStore from "./ProjectionStore.ts";...@@ -30,7 +30,7 @@ import * as ProjectionStore from "./ProjectionStore.ts";
30import * as ProjectionMaintenance from "./ProjectionMaintenance.ts";30import * as ProjectionMaintenance from "./ProjectionMaintenance.ts";
31import * as ProjectStore from "./ProjectStore.ts";31import * as ProjectStore from "./ProjectStore.ts";
32import * as ProviderAdapterRegistry from "./ProviderAdapterRegistry.ts";32import * as ProviderAdapterRegistry from "./ProviderAdapterRegistry.ts";
33import * as ProviderContinuationRequests from "@t3tools/provider-core/server/continuationRequests";33import * as ProviderContinuationRequests from "@t3tools/provider-core/server/ProviderContinuationRequests";
34import * as ProviderContinuationService from "./ProviderContinuationService.ts";34import * as ProviderContinuationService from "./ProviderContinuationService.ts";
35import * as ThreadTitleRegenerationService from "./ThreadTitleRegenerationService.ts";35import * as ThreadTitleRegenerationService from "./ThreadTitleRegenerationService.ts";
36import * as ProviderEventIngestor from "./ProviderEventIngestor.ts";36import * 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,7 +35,7 @@ import * as ProjectionStore from "../ProjectionStore.ts";
35import * as ProjectStore from "../ProjectStore.ts";35import * as ProjectStore from "../ProjectStore.ts";
36import * as ProviderAdapterRegistry from "../ProviderAdapterRegistry.ts";36import * as ProviderAdapterRegistry from "../ProviderAdapterRegistry.ts";
37import * as ProviderAuthService from "../../provider/ProviderAuthService.ts";37import * as ProviderAuthService from "../../provider/ProviderAuthService.ts";
38import * as ProviderContinuationRequests from "@t3tools/provider-core/server/continuationRequests";38import * as ProviderContinuationRequests from "@t3tools/provider-core/server/ProviderContinuationRequests";
39import * as ProviderContinuationService from "../ProviderContinuationService.ts";39import * as ProviderContinuationService from "../ProviderContinuationService.ts";
40import * as ProviderEventIngestor from "../ProviderEventIngestor.ts";40import * as ProviderEventIngestor from "../ProviderEventIngestor.ts";
41import * as ProviderRuntimeRecoveryService from "../ProviderRuntimeRecoveryService.ts";41import * 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,14 +52,6 @@ import * as EffectWorker from "../EffectWorker.ts";
52import * as EffectOutbox from "../EffectOutbox.ts";52import * as EffectOutbox from "../EffectOutbox.ts";
53import * as ProjectionMaintenance from "../ProjectionMaintenance.ts";53import * as ProjectionMaintenance from "../ProjectionMaintenance.ts";
54import * as ProjectionStore from "../ProjectionStore.ts";54import * as ProjectionStore from "../ProjectionStore.ts";
55import {
56 type ProviderAdapterV2Event,
57 type ProviderAdapterV2HistoricalContext,
58 ProviderAdapterProtocolError,
59 ProviderAdapterTurnStartError,
60 type ProviderAdapterV2Shape,
61 type ProviderAdapterV2SessionRuntime,
62} from "@t3tools/provider-core/server/ProviderAdapter";
63import * as ProviderAdapterRegistry from "../ProviderAdapterRegistry.ts";55import * as ProviderAdapterRegistry from "../ProviderAdapterRegistry.ts";
64import { makeProviderFailure } from "@t3tools/provider-core/server/failure";56import { makeProviderFailure } from "@t3tools/provider-core/server/failure";
65import {57import {
...@@ -70,6 +62,7 @@ import {...@@ -70,6 +62,7 @@ import {
70} from "./fixtures/shared.ts";62} from "./fixtures/shared.ts";
71import * as ProviderReplayHarness from "./ProviderReplayHarness.ts";63import * as ProviderReplayHarness from "./ProviderReplayHarness.ts";
72import { checkpointWorkspace } from "@t3tools/provider-testing/replayWorkspace";64import { checkpointWorkspace } from "@t3tools/provider-testing/replayWorkspace";
65import * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter";
7366
74const encodeJson = Schema.encodeEffect(Schema.fromJsonString(Schema.Unknown));67const encodeJson = Schema.encodeEffect(Schema.fromJsonString(Schema.Unknown));
7568
...@@ -95,7 +88,7 @@ interface CapturedTurn {...@@ -95,7 +88,7 @@ interface CapturedTurn {
95}88}
9689
97function unimplemented(driver: ProviderDriverKind, detail: string) {90function unimplemented(driver: ProviderDriverKind, detail: string) {
98 return Effect.fail(new ProviderAdapterProtocolError({ driver, detail }));91 return Effect.fail(new ProviderAdapter.ProviderAdapterProtocolError({ driver, detail }));
99}92}
10093
101function makeTestAdapter(input: {94function makeTestAdapter(input: {
...@@ -116,7 +109,7 @@ function makeTestAdapter(input: {...@@ -116,7 +109,7 @@ function makeTestAdapter(input: {
116 readonly failResumeOnce?: Ref.Ref<boolean>;109 readonly failResumeOnce?: Ref.Ref<boolean>;
117 readonly initialContextUsage?: OrchestrationV2ProviderThread["contextUsage"];110 readonly initialContextUsage?: OrchestrationV2ProviderThread["contextUsage"];
118 readonly getModelContextWindow?: (selection: ModelSelection) => number | undefined;111 readonly getModelContextWindow?: (selection: ModelSelection) => number | undefined;
119 readonly canReuseContextUsage?: ProviderAdapterV2SessionRuntime["canReuseContextUsage"];112 readonly canReuseContextUsage?: ProviderAdapter.ProviderAdapterV2SessionRuntime["canReuseContextUsage"];
120 readonly tokenUsageByRunOrdinal?: Readonly<113 readonly tokenUsageByRunOrdinal?: Readonly<
121 Record<number, Omit<OrchestrationV2ProviderTurnTokenUsage, "updatedAt">>114 Record<number, Omit<OrchestrationV2ProviderTurnTokenUsage, "updatedAt">>
122 >;115 >;
...@@ -125,7 +118,7 @@ function makeTestAdapter(input: {...@@ -125,7 +118,7 @@ function makeTestAdapter(input: {
125 readonly holdRunOrdinal?: number;118 readonly holdRunOrdinal?: number;
126 readonly holdFirstTurn?: Deferred.Deferred<void>;119 readonly holdFirstTurn?: Deferred.Deferred<void>;
127 readonly releaseFirstTurn?: Deferred.Deferred<void>;120 readonly releaseFirstTurn?: Deferred.Deferred<void>;
128}): ProviderAdapterV2Shape {121}): ProviderAdapter.ProviderAdapterV2["Service"] {
129 return {122 return {
130 instanceId: input.instanceId,123 instanceId: input.instanceId,
131 driver: input.driver,124 driver: input.driver,
...@@ -133,7 +126,7 @@ function makeTestAdapter(input: {...@@ -133,7 +126,7 @@ function makeTestAdapter(input: {
133 planSelectionTransition: () => Effect.succeed({ type: "apply_on_next_turn" }),126 planSelectionTransition: () => Effect.succeed({ type: "apply_on_next_turn" }),
134 openSession: (sessionInput) =>127 openSession: (sessionInput) =>
135 Effect.gen(function* () {128 Effect.gen(function* () {
136 const events = yield* PubSub.unbounded<ProviderAdapterV2Event>();129 const events = yield* PubSub.unbounded<ProviderAdapter.ProviderAdapterV2Event>();
137 const now = yield* DateTime.now;130 const now = yield* DateTime.now;
138 const providerSession: OrchestrationV2ProviderSession = {131 const providerSession: OrchestrationV2ProviderSession = {
139 id: sessionInput.providerSessionId,132 id: sessionInput.providerSessionId,
...@@ -148,7 +141,7 @@ function makeTestAdapter(input: {...@@ -148,7 +141,7 @@ function makeTestAdapter(input: {
148 lastError: null,141 lastError: null,
149 };142 };
150143
151 const runtime: ProviderAdapterV2SessionRuntime = {144 const runtime: ProviderAdapter.ProviderAdapterV2SessionRuntime = {
152 instanceId: input.instanceId,145 instanceId: input.instanceId,
153 driver: input.driver,146 driver: input.driver,
154 providerSessionId: sessionInput.providerSessionId,147 providerSessionId: sessionInput.providerSessionId,
...@@ -204,7 +197,7 @@ function makeTestAdapter(input: {...@@ -204,7 +197,7 @@ function makeTestAdapter(input: {
204 ...(input.injectedHistory === undefined197 ...(input.injectedHistory === undefined
205 ? {}198 ? {}
206 : {199 : {
207 injectHistory: (history: ProviderAdapterV2HistoricalContext) =>200 injectHistory: (history: ProviderAdapter.ProviderAdapterV2HistoricalContext) =>
208 Ref.update(input.injectedHistory!, (current) => [201 Ref.update(input.injectedHistory!, (current) => [
209 ...current,202 ...current,
210 ...historyResponseItems(history.messages, history.context),203 ...historyResponseItems(history.messages, history.context),
...@@ -236,7 +229,7 @@ function makeTestAdapter(input: {...@@ -236,7 +229,7 @@ function makeTestAdapter(input: {
236 input.refuseStarts !== undefined &&229 input.refuseStarts !== undefined &&
237 (yield* Ref.getAndUpdate(input.refuseStarts, (left) => Math.max(0, left - 1))) > 0230 (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 driver: input.driver,233 driver: input.driver,
241 threadId: turnInput.threadId,234 threadId: turnInput.threadId,
242 providerThreadId: turnInput.providerThread.id,235 providerThreadId: turnInput.providerThread.id,
...@@ -280,7 +273,7 @@ function makeTestAdapter(input: {...@@ -280,7 +273,7 @@ function makeTestAdapter(input: {
280 input.responseByThreadId?.[turnInput.threadId]?.[turnInput.runOrdinal] ??273 input.responseByThreadId?.[turnInput.threadId]?.[turnInput.runOrdinal] ??
281 input.responseByRunOrdinal[turnInput.runOrdinal] ??274 input.responseByRunOrdinal[turnInput.runOrdinal] ??
282 `${input.driver} response for run ${turnInput.runOrdinal}`;275 `${input.driver} response for run ${turnInput.runOrdinal}`;
283 const providerEvents: ReadonlyArray<ProviderAdapterV2Event> = [276 const providerEvents: ReadonlyArray<ProviderAdapter.ProviderAdapterV2Event> = [
284 {277 {
285 type: "provider_turn.updated",278 type: "provider_turn.updated",
286 driver: input.driver,279 driver: input.driver,
apps/server/src/provider/CodexChatGptAuth.ts+2-2
...@@ -19,8 +19,8 @@ import * as Effect from "effect/Effect";...@@ -19,8 +19,8 @@ import * as Effect from "effect/Effect";
19import * as Option from "effect/Option";19import * as Option from "effect/Option";
20import * as Schema from "effect/Schema";20import * as Schema from "effect/Schema";
21import * as Semaphore from "effect/Semaphore";21import * as Semaphore from "effect/Semaphore";
22import * as ProviderAuthFlow from "@t3tools/provider-core/server/ProviderAuthFlow";22import * as ProviderAuthFlow from "@t3tools/provider-core/server/providerAuthFlow";
23import type { ProviderAuthFlowContext } from "@t3tools/provider-core/server/ProviderAuthFlow";23import type { ProviderAuthFlowContext } from "@t3tools/provider-core/server/providerAuthFlow";
24import { HttpClient, HttpClientRequest } from "effect/http";24import { HttpClient, HttpClientRequest } from "effect/http";
25import * as ProviderCredentialStore from "./ProviderCredentialStore.ts";25import * as ProviderCredentialStore from "./ProviderCredentialStore.ts";
26import { withChatGptSessionLock } from "./CodexChatGptSessionLock.ts";26import { withChatGptSessionLock } from "./CodexChatGptSessionLock.ts";
apps/server/src/provider/Drivers/AntigravityDriver.test.ts+1-1
...@@ -29,7 +29,7 @@ import {...@@ -29,7 +29,7 @@ import {
29 ANTIGRAVITY_AUTH_STDOUT_PREFIX,29 ANTIGRAVITY_AUTH_STDOUT_PREFIX,
30 resolveAntigravityInstanceDirectories,30 resolveAntigravityInstanceDirectories,
31} from "../antigravityAuthSupport.ts";31} from "../antigravityAuthSupport.ts";
32import * as ProviderEventLoggers from "../ProviderEventLoggers.ts";32import * as ProviderEventLoggers from "@t3tools/provider-core/server/ProviderEventLoggers";
33import * as ModelManifest from "../ModelManifest.ts";33import * as ModelManifest from "../ModelManifest.ts";
34import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator";34import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator";
35import { AntigravityDriver } from "./AntigravityDriver.ts";35import { AntigravityDriver } from "./AntigravityDriver.ts";
apps/server/src/provider/Drivers/AntigravityDriver.ts+5-5
...@@ -18,7 +18,7 @@ import * as Stream from "effect/Stream";...@@ -18,7 +18,7 @@ import * as Stream from "effect/Stream";
18import * as ChildProcessSpawner from "effect/process/ChildProcessSpawner";18import * as ChildProcessSpawner from "effect/process/ChildProcessSpawner";
19import type { AcpError } from "effect-acp/errors";19import type { AcpError } from "effect-acp/errors";
2020
21import { ProviderHost } from "@t3tools/provider-core/server/ProviderHost";21import * as ProviderHost from "@t3tools/provider-core/server/ProviderHost";
22import {22import {
23 isAntigravityTextGenerationAvailable,23 isAntigravityTextGenerationAvailable,
24 makeAntigravityTextGeneration,24 makeAntigravityTextGeneration,
...@@ -46,12 +46,12 @@ import {...@@ -46,12 +46,12 @@ import {
46 removeAntigravitySessionFiles,46 removeAntigravitySessionFiles,
47} from "../acp/AntigravitySessionFiles.ts";47} from "../acp/AntigravitySessionFiles.ts";
48import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator";48import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator";
49import * as ProviderContinuationRequests from "@t3tools/provider-core/server/continuationRequests";49import * as ProviderContinuationRequests from "@t3tools/provider-core/server/ProviderContinuationRequests";
50import { makeAntigravityAdapterV2 } from "../../orchestration-v2/Adapters/AntigravityAdapterV2.ts";50import { makeAntigravityAdapterV2 } from "../../orchestration-v2/Adapters/AntigravityAdapterV2.ts";
51import { makeAcpNativeLoggerFactory } from "@t3tools/provider-acp/server/nativeLogging";51import { makeAcpNativeLoggerFactory } from "@t3tools/provider-acp/server/nativeLogging";
52import { ProviderDriverError } from "../Errors.ts";52import { ProviderDriverError } from "../Errors.ts";
53import { makeAntigravityProvider } from "../AntigravityProvider.ts";53import { makeAntigravityProvider } from "../AntigravityProvider.ts";
54import * as ProviderEventLoggers from "../ProviderEventLoggers.ts";54import * as ProviderEventLoggers from "@t3tools/provider-core/server/ProviderEventLoggers";
55import * as ModelCatalog from "@t3tools/provider-core/server/ModelCatalog";55import * as ModelCatalog from "@t3tools/provider-core/server/ModelCatalog";
56import {56import {
57 defaultProviderContinuationIdentity,57 defaultProviderContinuationIdentity,
...@@ -68,7 +68,7 @@ const isNodeRuntimeUnavailableError = Schema.is(NodeRuntimeUnavailableError);...@@ -68,7 +68,7 @@ const isNodeRuntimeUnavailableError = Schema.is(NodeRuntimeUnavailableError);
6868
69export type AntigravityDriverEnv =69export type AntigravityDriverEnv =
70 | AntigravityInstallation.AntigravityInstallation70 | AntigravityInstallation.AntigravityInstallation
71 | ProviderHost71 | ProviderHost.ProviderHost
72 | ChildProcessSpawner.ChildProcessSpawner72 | ChildProcessSpawner.ChildProcessSpawner
73 | Crypto.Crypto73 | Crypto.Crypto
74 | FileSystem.FileSystem74 | FileSystem.FileSystem
...@@ -89,7 +89,7 @@ export const AntigravityDriver: ProviderDriver<AntigravitySettings, AntigravityD...@@ -89,7 +89,7 @@ export const AntigravityDriver: ProviderDriver<AntigravitySettings, AntigravityD
89 const fileSystem = yield* FileSystem.FileSystem;89 const fileSystem = yield* FileSystem.FileSystem;
90 const path = yield* Path.Path;90 const path = yield* Path.Path;
91 const spawner = yield* ChildProcessSpawner.ChildProcessSpawner;91 const spawner = yield* ChildProcessSpawner.ChildProcessSpawner;
92 const host = yield* ProviderHost;92 const host = yield* ProviderHost.ProviderHost;
93 const selfInvocation = yield* resolveSelfInvocation();93 const selfInvocation = yield* resolveSelfInvocation();
94 const installation = yield* AntigravityInstallation.AntigravityInstallation;94 const installation = yield* AntigravityInstallation.AntigravityInstallation;
95 const loggers = yield* ProviderEventLoggers.ProviderEventLoggers;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,10 +24,10 @@ import { HttpClient } from "effect/http";
24import { ChildProcessSpawner } from "effect/process";24import { ChildProcessSpawner } from "effect/process";
2525
26import { makeClaudeTextGeneration } from "../../textGeneration/ClaudeTextGeneration.ts";26import { makeClaudeTextGeneration } from "../../textGeneration/ClaudeTextGeneration.ts";
27import { ProviderHost } from "@t3tools/provider-core/server/ProviderHost";27import * as ProviderHost from "@t3tools/provider-core/server/ProviderHost";
28import * as ServerConfig from "../../config.ts";28import * as ServerConfig from "../../config.ts";
29import { expandHomePath } from "@t3tools/provider-core/server/pathExpansion";29import { expandHomePath } from "@t3tools/provider-core/server/pathExpansion";
30import * as ProviderEventLoggers from "../ProviderEventLoggers.ts";30import * as ProviderEventLoggers from "@t3tools/provider-core/server/ProviderEventLoggers";
31import {31import {
32 createClaudeAdapterV2,32 createClaudeAdapterV2,
33 type ClaudeAdapterV2DriverEnv,33 type ClaudeAdapterV2DriverEnv,
...@@ -94,7 +94,7 @@ const UPDATE = makePackageManagedProviderMaintenanceResolver({...@@ -94,7 +94,7 @@ const UPDATE = makePackageManagedProviderMaintenanceResolver({
9494
95export type ClaudeDriverEnv =95export type ClaudeDriverEnv =
96 | ClaudeAdapterV2DriverEnv96 | ClaudeAdapterV2DriverEnv
97 | ProviderHost97 | ProviderHost.ProviderHost
98 | ChildProcessSpawner.ChildProcessSpawner98 | ChildProcessSpawner.ChildProcessSpawner
99 | ResetCreditCoordinator.ResetCreditCoordinator99 | ResetCreditCoordinator.ResetCreditCoordinator
100 | Crypto.Crypto100 | Crypto.Crypto
...@@ -121,7 +121,6 @@ export const ClaudeDriver: ProviderDriver<ClaudeSettings, ClaudeDriverEnv> = {...@@ -121,7 +121,6 @@ export const ClaudeDriver: ProviderDriver<ClaudeSettings, ClaudeDriverEnv> = {
121 const { cwd } = yield* ServerConfig.ServerConfig;121 const { cwd } = yield* ServerConfig.ServerConfig;
122 const httpClient = yield* HttpClient.HttpClient;122 const httpClient = yield* HttpClient.HttpClient;
123 const resetCreditCoordinator = yield* ResetCreditCoordinator.ResetCreditCoordinator;123 const resetCreditCoordinator = yield* ResetCreditCoordinator.ResetCreditCoordinator;
124 const host = yield* ProviderHost;
125 const catalogService = yield* ModelCatalog.ModelCatalog;124 const catalogService = yield* ModelCatalog.ModelCatalog;
126 const modelCatalog = catalogService125 const modelCatalog = catalogService
127 .current(DRIVER_KIND)126 .current(DRIVER_KIND)
...@@ -237,7 +236,7 @@ export const ClaudeDriver: ProviderDriver<ClaudeSettings, ClaudeDriverEnv> = {...@@ -237,7 +236,7 @@ export const ClaudeDriver: ProviderDriver<ClaudeSettings, ClaudeDriverEnv> = {
237 Effect.provideService(Path.Path, path),236 Effect.provideService(Path.Path, path),
238 );237 );
239238
240 const snapshotSettings = makeProviderSnapshotSettingsSource(effectiveConfig, host.settings);239 const snapshotSettings = yield* makeProviderSnapshotSettingsSource(effectiveConfig);
241 const snapshot = yield* makeManagedServerProvider<ProviderSnapshotSettings<ClaudeSettings>>({240 const snapshot = yield* makeManagedServerProvider<ProviderSnapshotSettings<ClaudeSettings>>({
242 resolveMaintenance,241 resolveMaintenance,
243 getSettings: snapshotSettings.getSettings,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,7 +26,7 @@ import * as BackgroundPolicy from "../../background/BackgroundPolicy.ts";
26import * as ServerConfig from "../../config.ts";26import * as ServerConfig from "../../config.ts";
27import * as ServerSettings from "../../serverSettings.ts";27import * as ServerSettings from "../../serverSettings.ts";
28import * as ResetCreditCoordinator from "../resetCreditCoordinator.ts";28import * as ResetCreditCoordinator from "../resetCreditCoordinator.ts";
29import * as ProviderEventLoggers from "../ProviderEventLoggers.ts";29import * as ProviderEventLoggers from "@t3tools/provider-core/server/ProviderEventLoggers";
30import * as ModelManifest from "../ModelManifest.ts";30import * as ModelManifest from "../ModelManifest.ts";
31import {31import {
32 createProviderVersionAdvisory,32 createProviderVersionAdvisory,
...@@ -36,7 +36,7 @@ import {...@@ -36,7 +36,7 @@ import {
36import { CodexDriver } from "./CodexDriver.ts";36import { CodexDriver } from "./CodexDriver.ts";
37import * as CodexAdapterV2 from "../../orchestration-v2/Adapters/CodexAdapterV2.ts";37import * as CodexAdapterV2 from "../../orchestration-v2/Adapters/CodexAdapterV2.ts";
38import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator";38import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator";
39import { ProviderAdapterV2RuntimePolicy } from "@t3tools/provider-core/server/ProviderAdapter";39import * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter";
40import * as ProviderCredentialStore from "../ProviderCredentialStore.ts";40import * as ProviderCredentialStore from "../ProviderCredentialStore.ts";
41import * as ProviderHostLive from "../ProviderHostLive.ts";41import * as ProviderHostLive from "../ProviderHostLive.ts";
4242
...@@ -214,7 +214,7 @@ it.layer(layerTest)("CodexDriver", (it) => {...@@ -214,7 +214,7 @@ it.layer(layerTest)("CodexDriver", (it) => {
214 threadId,214 threadId,
215 providerSessionId: ProviderSessionId.make("managed-account-session"),215 providerSessionId: ProviderSessionId.make("managed-account-session"),
216 modelSelection: { instanceId, model: "gpt-5.4" },216 modelSelection: { instanceId, model: "gpt-5.4" },
217 runtimePolicy: ProviderAdapterV2RuntimePolicy.make({217 runtimePolicy: ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
218 runtimeMode: "full-access",218 runtimeMode: "full-access",
219 interactionMode: "default",219 interactionMode: "default",
220 cwd: serverConfig.stateDir,220 cwd: serverConfig.stateDir,
apps/server/src/provider/Drivers/CodexDriver.ts+4-5
...@@ -31,10 +31,10 @@ import { HttpClient } from "effect/http";...@@ -31,10 +31,10 @@ import { HttpClient } from "effect/http";
31import { ChildProcessSpawner } from "effect/process";31import { ChildProcessSpawner } from "effect/process";
3232
33import { makeCodexTextGeneration } from "../../textGeneration/CodexTextGeneration.ts";33import { makeCodexTextGeneration } from "../../textGeneration/CodexTextGeneration.ts";
34import { ProviderHost } from "@t3tools/provider-core/server/ProviderHost";34import * as ProviderHost from "@t3tools/provider-core/server/ProviderHost";
35import * as ServerConfig from "../../config.ts";35import * as ServerConfig from "../../config.ts";
36import { expandHomePath } from "@t3tools/provider-core/server/pathExpansion";36import { expandHomePath } from "@t3tools/provider-core/server/pathExpansion";
37import * as ProviderEventLoggers from "../ProviderEventLoggers.ts";37import * as ProviderEventLoggers from "@t3tools/provider-core/server/ProviderEventLoggers";
38import {38import {
39 createCodexAdapterV2,39 createCodexAdapterV2,
40 type CodexAdapterV2DriverEnv,40 type CodexAdapterV2DriverEnv,
...@@ -109,7 +109,7 @@ function makeCodexMaintenanceResolver(sharedHomePath: string) {...@@ -109,7 +109,7 @@ function makeCodexMaintenanceResolver(sharedHomePath: string) {
109 */109 */
110export type CodexDriverEnv =110export type CodexDriverEnv =
111 | CodexAdapterV2DriverEnv111 | CodexAdapterV2DriverEnv
112 | ProviderHost112 | ProviderHost.ProviderHost
113 | ChildProcessSpawner.ChildProcessSpawner113 | ChildProcessSpawner.ChildProcessSpawner
114 | ResetCreditCoordinator.ResetCreditCoordinator114 | ResetCreditCoordinator.ResetCreditCoordinator
115 | Crypto.Crypto115 | Crypto.Crypto
...@@ -147,7 +147,6 @@ export const CodexDriver: ProviderDriver<CodexSettings, CodexDriverEnv> = {...@@ -147,7 +147,6 @@ export const CodexDriver: ProviderDriver<CodexSettings, CodexDriverEnv> = {
147 const fileSystem = yield* FileSystem.FileSystem;147 const fileSystem = yield* FileSystem.FileSystem;
148 const pathService = yield* Path.Path;148 const pathService = yield* Path.Path;
149 const httpClient = yield* HttpClient.HttpClient;149 const httpClient = yield* HttpClient.HttpClient;
150 const host = yield* ProviderHost;
151 const modelCatalog = yield* ModelCatalog.ModelCatalog;150 const modelCatalog = yield* ModelCatalog.ModelCatalog;
152 const currentCatalog = modelCatalog.current(DRIVER_KIND);151 const currentCatalog = modelCatalog.current(DRIVER_KIND);
153 const processEnv = mergeProviderInstanceEnvironment(environment);152 const processEnv = mergeProviderInstanceEnvironment(environment);
...@@ -231,7 +230,7 @@ export const CodexDriver: ProviderDriver<CodexSettings, CodexDriverEnv> = {...@@ -231,7 +230,7 @@ export const CodexDriver: ProviderDriver<CodexSettings, CodexDriverEnv> = {
231 ),230 ),
232 Effect.provideService(ChildProcessSpawner.ChildProcessSpawner, spawner),231 Effect.provideService(ChildProcessSpawner.ChildProcessSpawner, spawner),
233 );232 );
234 const snapshotSettings = makeProviderSnapshotSettingsSource(effectiveConfig, host.settings);233 const snapshotSettings = yield* makeProviderSnapshotSettingsSource(effectiveConfig);
235 const snapshot = yield* makeManagedServerProvider<ProviderSnapshotSettings<CodexSettings>>({234 const snapshot = yield* makeManagedServerProvider<ProviderSnapshotSettings<CodexSettings>>({
236 resolveMaintenance,235 resolveMaintenance,
237 getSettings: snapshotSettings.getSettings,236 getSettings: snapshotSettings.getSettings,
apps/server/src/provider/Drivers/CodexManagedProvider.ts+2-2
...@@ -4,7 +4,7 @@ import * as Stream from "effect/Stream";...@@ -4,7 +4,7 @@ import * as Stream from "effect/Stream";
4import * as Option from "effect/Option";4import * as Option from "effect/Option";
5import { ChildProcessSpawner } from "effect/process";5import { ChildProcessSpawner } from "effect/process";
6import { makeCodexTextGeneration } from "../../textGeneration/CodexTextGeneration.ts";6import { makeCodexTextGeneration } from "../../textGeneration/CodexTextGeneration.ts";
7import { ProviderHost } from "@t3tools/provider-core/server/ProviderHost";7import * as ProviderHost from "@t3tools/provider-core/server/ProviderHost";
8import { chatGptModels } from "../CodexChatGptModels.ts";8import { chatGptModels } from "../CodexChatGptModels.ts";
9import { makeCodexManagedRuntime } from "../CodexManagedRuntime.ts";9import { makeCodexManagedRuntime } from "../CodexManagedRuntime.ts";
10import { ProviderDriverError } from "../Errors.ts";10import { ProviderDriverError } from "../Errors.ts";
...@@ -31,7 +31,7 @@ export const makeManagedCodexProvider = Effect.fn("makeManagedCodexProvider")(fu...@@ -31,7 +31,7 @@ export const makeManagedCodexProvider = Effect.fn("makeManagedCodexProvider")(fu
31 const { instanceId, enabled, displayName, accentColor, config } = input;31 const { instanceId, enabled, displayName, accentColor, config } = input;
32 const http = yield* HttpClient.HttpClient;32 const http = yield* HttpClient.HttpClient;
33 const spawner = yield* ChildProcessSpawner.ChildProcessSpawner;33 const spawner = yield* ChildProcessSpawner.ChildProcessSpawner;
34 const host = yield* ProviderHost;34 const host = yield* ProviderHost.ProviderHost;
35 const runtime = yield* makeCodexManagedRuntime({35 const runtime = yield* makeCodexManagedRuntime({
36 instanceId,36 instanceId,
37 enabled,37 enabled,
apps/server/src/provider/EventNdjsonLogger.ts+13-11
...@@ -9,7 +9,7 @@ import * as NodeFS from "node:fs";...@@ -9,7 +9,7 @@ import * as NodeFS from "node:fs";
9import * as NodePath from "node:path";9import * as NodePath from "node:path";
1010
11import type { ThreadId } from "@t3tools/contracts";11import type { ThreadId } from "@t3tools/contracts";
12import type { EventNdjsonLogger } from "@t3tools/provider-core/server/ProviderEventLoggers";12import type * as ProviderEventLoggers from "@t3tools/provider-core/server/ProviderEventLoggers";
13import { RotatingFileSink } from "@t3tools/shared/logging";13import { RotatingFileSink } from "@t3tools/shared/logging";
14import { errorTag } from "@t3tools/shared/observability";14import { errorTag } from "@t3tools/shared/observability";
15import * as Clock from "effect/Clock";15import * as Clock from "effect/Clock";
...@@ -21,7 +21,7 @@ import * as Scope from "effect/Scope";...@@ -21,7 +21,7 @@ import * as Scope from "effect/Scope";
21import * as SynchronizedRef from "effect/SynchronizedRef";21import * as SynchronizedRef from "effect/SynchronizedRef";
2222
23import { toSafeThreadAttachmentSegment } from "../attachmentStore.ts";23import { toSafeThreadAttachmentSegment } from "../attachmentStore.ts";
24import type { ResourceAttribution } from "../resourceTelemetry/ResourceAttribution.ts";24import type * as ResourceAttribution from "../resourceTelemetry/ResourceAttribution.ts";
2525
26const MEBIBYTE = 1024 * 1024;26const MEBIBYTE = 1024 * 1024;
27const DAY_MS = 24 * 60 * 60 * 1_000;27const DAY_MS = 24 * 60 * 60 * 1_000;
...@@ -64,11 +64,9 @@ const transientAcpUpdates = new Set(["agent_message_chunk", "agent_thought_chunk...@@ -64,11 +64,9 @@ const transientAcpUpdates = new Set(["agent_message_chunk", "agent_thought_chunk
6464
65export type EventNdjsonStream = "native" | "canonical" | "orchestration";65export type EventNdjsonStream = "native" | "canonical" | "orchestration";
6666
67export type { EventNdjsonLogger };
68
69export interface EventNdjsonLogStore {67export interface EventNdjsonLogStore {
70 readonly filePath: string;68 readonly filePath: string;
71 readonly logger: (stream: EventNdjsonStream) => EventNdjsonLogger;69 readonly logger: (stream: EventNdjsonStream) => ProviderEventLoggers.EventNdjsonLogger;
72 readonly close: () => Effect.Effect<void>;70 readonly close: () => Effect.Effect<void>;
73}71}
7472
...@@ -81,7 +79,7 @@ export interface EventNdjsonLogStoreOptions {...@@ -81,7 +79,7 @@ export interface EventNdjsonLogStoreOptions {
81 readonly retentionCheckIntervalMs?: number;79 readonly retentionCheckIntervalMs?: number;
82 readonly maxBufferedBytes?: number;80 readonly maxBufferedBytes?: number;
83 readonly maxBufferedRecords?: number;81 readonly maxBufferedRecords?: number;
84 readonly attribution?: ResourceAttribution["Service"];82 readonly attribution?: ResourceAttribution.ResourceAttribution["Service"];
85}83}
8684
87export interface EventNdjsonLoggerOptions extends EventNdjsonLogStoreOptions {85export interface EventNdjsonLoggerOptions extends EventNdjsonLogStoreOptions {
...@@ -127,7 +125,7 @@ interface ResolvedOptions {...@@ -127,7 +125,7 @@ interface ResolvedOptions {
127 readonly retentionCheckIntervalMs: number;125 readonly retentionCheckIntervalMs: number;
128 readonly maxBufferedBytes: number;126 readonly maxBufferedBytes: number;
129 readonly maxBufferedRecords: number;127 readonly maxBufferedRecords: number;
130 readonly attribution: ResourceAttribution["Service"] | undefined;128 readonly attribution: ResourceAttribution.ResourceAttribution["Service"] | undefined;
131}129}
132130
133export interface PendingRecord {131export interface PendingRecord {
...@@ -731,8 +729,8 @@ export const makeEventNdjsonLogStore = Effect.fnUntraced(function* (...@@ -731,8 +729,8 @@ export const makeEventNdjsonLogStore = Effect.fnUntraced(function* (
731 yield* Scope.close(timerScope, Exit.void);729 yield* Scope.close(timerScope, Exit.void);
732 });730 });
733731
734 const loggerViews = new Map<EventNdjsonStream, EventNdjsonLogger>();732 const loggerViews = new Map<EventNdjsonStream, ProviderEventLoggers.EventNdjsonLogger>();
735 const logger = (stream: EventNdjsonStream): EventNdjsonLogger => {733 const logger = (stream: EventNdjsonStream): ProviderEventLoggers.EventNdjsonLogger => {
736 const existing = loggerViews.get(stream);734 const existing = loggerViews.get(stream);
737 if (existing) return existing;735 if (existing) return existing;
738736
...@@ -778,7 +776,11 @@ export const makeEventNdjsonLogStore = Effect.fnUntraced(function* (...@@ -778,7 +776,11 @@ export const makeEventNdjsonLogStore = Effect.fnUntraced(function* (
778 }776 }
779 });777 });
780778
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 loggerViews.set(stream, view);784 loggerViews.set(stream, view);
783 return view;785 return view;
784 };786 };
...@@ -789,7 +791,7 @@ export const makeEventNdjsonLogStore = Effect.fnUntraced(function* (...@@ -789,7 +791,7 @@ export const makeEventNdjsonLogStore = Effect.fnUntraced(function* (
789export const makeEventNdjsonLogger = Effect.fnUntraced(function* (791export const makeEventNdjsonLogger = Effect.fnUntraced(function* (
790 filePath: string,792 filePath: string,
791 options: EventNdjsonLoggerOptions,793 options: EventNdjsonLoggerOptions,
792): Effect.fn.Return<EventNdjsonLogger | undefined> {794): Effect.fn.Return<ProviderEventLoggers.EventNdjsonLogger | undefined> {
793 const store = yield* makeEventNdjsonLogStore(filePath, options).pipe(795 const store = yield* makeEventNdjsonLogStore(filePath, options).pipe(
794 Effect.catch((error) =>796 Effect.catch((error) =>
795 logWarning(error.message, { error }).pipe(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 */
27import * as Effect from "effect/Effect";
28import * as Layer from "effect/Layer";
29
30import * as ServerConfig from "../config.ts";
31import * as ResourceAttribution from "../resourceTelemetry/ResourceAttribution.ts";
32import {
33 NoOpProviderEventLoggers,
34 ProviderEventLoggers,
35} from "@t3tools/provider-core/server/ProviderEventLoggers";
36import * as EventNdjsonLogger from "./EventNdjsonLogger.ts";
37
38export { 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 */
46export 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
71export const layer = Layer.effect(ProviderEventLoggers, make);
apps/server/src/provider/ProviderHostLive.ts+4-4
...@@ -1,10 +1,10 @@...@@ -1,10 +1,10 @@
1/**1/**
2 * The server's implementation of `ProviderHost`, the only server surface2 * The server's implementation of `ProviderHost.ProviderHost`, the only server surface
3 * provider drivers and adapters may use.3 * provider drivers and adapters may use.
4 *4 *
5 * @module provider/ProviderHostLive5 * @module provider/ProviderHostLive
6 */6 */
7import { ProviderHost } from "@t3tools/provider-core/server/ProviderHost";7import * as ProviderHost from "@t3tools/provider-core/server/ProviderHost";
8import { ProviderCredentialError } from "@t3tools/provider-core/server/errors";8import { ProviderCredentialError } from "@t3tools/provider-core/server/errors";
9import * as Crypto from "effect/Crypto";9import * as Crypto from "effect/Crypto";
10import * as Effect from "effect/Effect";10import * as Effect from "effect/Effect";
...@@ -18,14 +18,14 @@ import * as ServerSettings from "../serverSettings.ts";...@@ -18,14 +18,14 @@ import * as ServerSettings from "../serverSettings.ts";
18import * as ProviderCredentialStore from "./ProviderCredentialStore.ts";18import * as ProviderCredentialStore from "./ProviderCredentialStore.ts";
1919
20export const layer = Layer.effect(20export const layer = Layer.effect(
21 ProviderHost,21 ProviderHost.ProviderHost,
22 Effect.gen(function* () {22 Effect.gen(function* () {
23 const config = yield* ServerConfig.ServerConfig;23 const config = yield* ServerConfig.ServerConfig;
24 const serverSettings = yield* ServerSettings.ServerSettingsService;24 const serverSettings = yield* ServerSettings.ServerSettingsService;
25 const backgroundPolicy = yield* BackgroundPolicy.BackgroundPolicy;25 const backgroundPolicy = yield* BackgroundPolicy.BackgroundPolicy;
26 const secrets = yield* ServerSecretStore.ServerSecretStore;26 const secrets = yield* ServerSecretStore.ServerSecretStore;
27 const crypto = yield* Crypto.Crypto;27 const crypto = yield* Crypto.Crypto;
28 return ProviderHost.of({28 return ProviderHost.ProviderHost.of({
29 paths: {29 paths: {
30 cwd: config.cwd,30 cwd: config.cwd,
31 baseDir: config.baseDir,31 baseDir: config.baseDir,
apps/server/src/provider/ProviderInstanceRegistry.test.ts+1-1
...@@ -61,7 +61,7 @@ import * as ModelManifest from "./ModelManifest.ts";...@@ -61,7 +61,7 @@ import * as ModelManifest from "./ModelManifest.ts";
61import * as OpenCodeRuntime from "@t3tools/provider-opencode/server/OpenCodeRuntime";61import * as OpenCodeRuntime from "@t3tools/provider-opencode/server/OpenCodeRuntime";
62import * as OpenCodeServerLedger from "@t3tools/provider-opencode/server/OpenCodeServerLedger";62import * as OpenCodeServerLedger from "@t3tools/provider-opencode/server/OpenCodeServerLedger";
63import * as ResetCreditCoordinator from "./resetCreditCoordinator.ts";63import * as ResetCreditCoordinator from "./resetCreditCoordinator.ts";
64import * as ProviderEventLoggers from "./ProviderEventLoggers.ts";64import * as ProviderEventLoggers from "@t3tools/provider-core/server/ProviderEventLoggers";
65import { makeProviderInstanceRegistry } from "./ProviderInstanceRegistry.ts";65import { makeProviderInstanceRegistry } from "./ProviderInstanceRegistry.ts";
66import * as ProviderOrchestrationAdapterInfrastructure from "./ProviderOrchestrationAdapterInfrastructure.ts";66import * as ProviderOrchestrationAdapterInfrastructure from "./ProviderOrchestrationAdapterInfrastructure.ts";
67import * as ProviderHostLive from "./ProviderHostLive.ts";67import * as ProviderHostLive from "./ProviderHostLive.ts";
apps/server/src/provider/ProviderInstanceRegistryHydration.ts+3-3
...@@ -40,7 +40,7 @@ import * as ProviderInstanceRegistryMutator from "./ProviderInstanceRegistryMuta...@@ -40,7 +40,7 @@ import * as ProviderInstanceRegistryMutator from "./ProviderInstanceRegistryMuta
40import * as ProviderOrchestrationAdapterInfrastructure from "./ProviderOrchestrationAdapterInfrastructure.ts";40import * as ProviderOrchestrationAdapterInfrastructure from "./ProviderOrchestrationAdapterInfrastructure.ts";
41import * as AcpRegistrySupport from "@t3tools/provider-acp-registry/server/AcpRegistrySupport";41import * as AcpRegistrySupport from "@t3tools/provider-acp-registry/server/AcpRegistrySupport";
42import * as ProviderHostLive from "./ProviderHostLive.ts";42import * as ProviderHostLive from "./ProviderHostLive.ts";
43import type { ProviderHost } from "@t3tools/provider-core/server/ProviderHost";43import type * as ProviderHost from "@t3tools/provider-core/server/ProviderHost";
44import type * as BackgroundPolicy from "../background/BackgroundPolicy.ts";44import type * as BackgroundPolicy from "../background/BackgroundPolicy.ts";
45import type * as ServerConfig from "../config.ts";45import type * as ServerConfig from "../config.ts";
4646
...@@ -49,10 +49,10 @@ type ProviderInstanceRegistryHydrationEnv =...@@ -49,10 +49,10 @@ type ProviderInstanceRegistryHydrationEnv =
49 BuiltInDriversEnv,49 BuiltInDriversEnv,
50 | ProviderOrchestrationAdapterInfrastructure.ProviderOrchestrationAdapterInfrastructure50 | ProviderOrchestrationAdapterInfrastructure.ProviderOrchestrationAdapterInfrastructure
51 | AcpRegistrySupport.AcpRegistryCatalog51 | AcpRegistrySupport.AcpRegistryCatalog
52 | ProviderHost52 | ProviderHost.ProviderHost
53 >53 >
54 | Settings.ServerSettingsService54 | Settings.ServerSettingsService
55 // Requirements of the `ProviderHost` the drivers receive.55 // Requirements of the `ProviderHost.ProviderHost` the drivers receive.
56 | BackgroundPolicy.BackgroundPolicy56 | BackgroundPolicy.BackgroundPolicy
57 | ServerConfig.ServerConfig;57 | ServerConfig.ServerConfig;
5858
apps/server/src/provider/ProviderOrchestrationAdapterInfrastructure.ts+1-1
...@@ -4,7 +4,7 @@ import * as ClaudeAdapterV2 from "../orchestration-v2/Adapters/ClaudeAdapterV2.t...@@ -4,7 +4,7 @@ import * as ClaudeAdapterV2 from "../orchestration-v2/Adapters/ClaudeAdapterV2.t
4import * as CodexAdapterV2 from "../orchestration-v2/Adapters/CodexAdapterV2.ts";4import * as CodexAdapterV2 from "../orchestration-v2/Adapters/CodexAdapterV2.ts";
5import * as CursorAgentSdk from "@t3tools/provider-cursor/server/CursorAgentSdk";5import * as CursorAgentSdk from "@t3tools/provider-cursor/server/CursorAgentSdk";
6import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator";6import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator";
7import * as ProviderContinuationRequests from "@t3tools/provider-core/server/continuationRequests";7import * as ProviderContinuationRequests from "@t3tools/provider-core/server/ProviderContinuationRequests";
88
9export type ProviderOrchestrationAdapterInfrastructure =9export type ProviderOrchestrationAdapterInfrastructure =
10 | ClaudeAdapterV2.ClaudeAgentSdkQueryRunner10 | ClaudeAdapterV2.ClaudeAgentSdkQueryRunner
apps/server/src/provider/ProviderRegistry.test.ts+1-1
...@@ -47,7 +47,7 @@ import { applyProviderCompatibility } from "./providerCompatibility.ts";...@@ -47,7 +47,7 @@ import { applyProviderCompatibility } from "./providerCompatibility.ts";
47import * as ResetCreditCoordinator from "./resetCreditCoordinator.ts";47import * as ResetCreditCoordinator from "./resetCreditCoordinator.ts";
48import * as OpenCodeRuntime from "@t3tools/provider-opencode/server/OpenCodeRuntime";48import * as OpenCodeRuntime from "@t3tools/provider-opencode/server/OpenCodeRuntime";
49import * as OpenCodeServerLedger from "@t3tools/provider-opencode/server/OpenCodeServerLedger";49import * as OpenCodeServerLedger from "@t3tools/provider-opencode/server/OpenCodeServerLedger";
50import * as ProviderEventLoggers from "./ProviderEventLoggers.ts";50import * as ProviderEventLoggers from "@t3tools/provider-core/server/ProviderEventLoggers";
51import * as ProviderInstanceRegistryHydration from "./ProviderInstanceRegistryHydration.ts";51import * as ProviderInstanceRegistryHydration from "./ProviderInstanceRegistryHydration.ts";
52import * as ServerConfig from "../config.ts";52import * as ServerConfig from "../config.ts";
53import * as ServerSettingsModule from "../serverSettings.ts";53import * as ServerSettingsModule from "../serverSettings.ts";
apps/server/src/server.ts+29-7
...@@ -1,3 +1,4 @@...@@ -1,3 +1,4 @@
1import * as ProviderEventLoggers from "@t3tools/provider-core/server/ProviderEventLoggers";
1import type { RelayManagedEndpointRuntimeConfig } from "@t3tools/contracts/relay";2import type { RelayManagedEndpointRuntimeConfig } from "@t3tools/contracts/relay";
2import * as Clock from "effect/Clock";3import * as Clock from "effect/Clock";
3import * as Random from "effect/Random";4import * as Random from "effect/Random";
...@@ -42,7 +43,7 @@ import * as AnalyticsService from "./telemetry/AnalyticsService.ts";...@@ -42,7 +43,7 @@ import * as AnalyticsService from "./telemetry/AnalyticsService.ts";
42import * as ProviderEventIngestor from "./orchestration-v2/ProviderEventIngestor.ts";43import * as ProviderEventIngestor from "./orchestration-v2/ProviderEventIngestor.ts";
43import * as ModelManifest from "./provider/ModelManifest.ts";44import * as ModelManifest from "./provider/ModelManifest.ts";
44import * as ResetCreditCoordinator from "./provider/resetCreditCoordinator.ts";45import * as ResetCreditCoordinator from "./provider/resetCreditCoordinator.ts";
45import * as ProviderEventLoggers from "./provider/ProviderEventLoggers.ts";46import * as EventNdjsonLogger from "./provider/EventNdjsonLogger.ts";
46import * as OpenCodeRuntime from "@t3tools/provider-opencode/server/OpenCodeRuntime";47import * as OpenCodeRuntime from "@t3tools/provider-opencode/server/OpenCodeRuntime";
47import * as OpenCodeServerLedger from "@t3tools/provider-opencode/server/OpenCodeServerLedger";48import * as OpenCodeServerLedger from "@t3tools/provider-opencode/server/OpenCodeServerLedger";
48import * as ProviderHostLive from "./provider/ProviderHostLive.ts";49import * as ProviderHostLive from "./provider/ProviderHostLive.ts";
...@@ -193,6 +194,30 @@ const layerApplicationObservability = EventLoopMonitor.layer.pipe(...@@ -193,6 +194,30 @@ const layerApplicationObservability = EventLoopMonitor.layer.pipe(
193 Layer.provideMerge(layerResourceAttribution),194 Layer.provideMerge(layerResourceAttribution),
194);195);
195196
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.
200const 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
196const layerPtyAdapter = NodePtyAdapter.layer;221const layerPtyAdapter = NodePtyAdapter.layer;
197222
198const layerServerSettings = ServerSettings.layer.pipe(223const layerServerSettings = ServerSettings.layer.pipe(
...@@ -599,17 +624,14 @@ const layerRuntimeCoreDependencies = layerRuntimeCoreDependenciesBase.pipe(...@@ -599,17 +624,14 @@ const layerRuntimeCoreDependencies = layerRuntimeCoreDependenciesBase.pipe(
599 // Search, prepare, status inspection, and turn launch share one registry624 // Search, prepare, status inspection, and turn launch share one registry
600 // cache so every client and provider instance sees the same prepared agents.625 // cache so every client and provider instance sees the same prepared agents.
601 Layer.provideMerge(AcpRegistrySupport.layerFromHost.pipe(Layer.provide(ProviderHostLive.layer))),626 Layer.provideMerge(AcpRegistrySupport.layerFromHost.pipe(Layer.provide(ProviderHostLive.layer))),
602 // Shared native/canonical NDJSON writers used by both the per-instance627 // Provider event loggers are shared by every driver instance and the
603 // V2 drivers and the orchestration runtime. Provide resource attribution so628 // orchestration runtime, so they are provided once here.
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.
607 // `ModelManifest.layer` is the model manifest, refreshed from the repo's629 // `ModelManifest.layer` is the model manifest, refreshed from the repo's
608 // `model-manifest.json` on `main`. Drivers read their entry through the630 // `model-manifest.json` on `main`. Drivers read their entry through the
609 // `ModelCatalog` port it provides.631 // `ModelCatalog` port it provides.
610 Layer.provideMerge(632 Layer.provideMerge(
611 Layer.mergeAll(633 Layer.mergeAll(
612 ProviderEventLoggers.layer,634 layerProviderEventLoggers,
613 ModelManifest.layerModelCatalog.pipe(Layer.provideMerge(ModelManifest.layer)),635 ModelManifest.layerModelCatalog.pipe(Layer.provideMerge(ModelManifest.layer)),
614 ResetCreditCoordinator.layer,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,7 +16,7 @@ import { ChildProcessSpawner } from "effect/process";
16import type * as AcpSchema from "effect-acp/compat";16import type * as AcpSchema from "effect-acp/compat";
1717
18import * as PtyAdapter from "@t3tools/shared/PtyAdapter";18import * as PtyAdapter from "@t3tools/shared/PtyAdapter";
19import * as ProviderAuthFlow from "@t3tools/provider-core/server/ProviderAuthFlow";19import * as ProviderAuthFlow from "@t3tools/provider-core/server/providerAuthFlow";
20import { normalizeAcpRegistryAuthMethods, normalizeAcpRegistryWebUrl } from "./probe.ts";20import { normalizeAcpRegistryAuthMethods, normalizeAcpRegistryWebUrl } from "./probe.ts";
21import * as AcpRegistrySupport from "./AcpRegistrySupport.ts";21import * as AcpRegistrySupport from "./AcpRegistrySupport.ts";
22import * as AcpRegistryRuntimeCoordinator from "./AcpRegistryRuntimeCoordinator.ts";22import * 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,7 +21,7 @@ import * as Schema from "effect/Schema";
21import * as Semaphore from "effect/Semaphore";21import * as Semaphore from "effect/Semaphore";
22import { ChildProcessSpawner } from "effect/process";22import { ChildProcessSpawner } from "effect/process";
2323
24import { ProviderHost } from "@t3tools/provider-core/server/ProviderHost";24import * as ProviderHost from "@t3tools/provider-core/server/ProviderHost";
25import { AcpRegistryAdapterV2Driver, type AcpRegistryAdapterV2DriverEnv } from "./adapter.ts";25import { AcpRegistryAdapterV2Driver, type AcpRegistryAdapterV2DriverEnv } from "./adapter.ts";
26import type { ProviderTextGeneration } from "@t3tools/provider-core/server/textGeneration";26import type { ProviderTextGeneration } from "@t3tools/provider-core/server/textGeneration";
27import { ProviderDriverError } from "@t3tools/provider-core/server/errors";27import { ProviderDriverError } from "@t3tools/provider-core/server/errors";
...@@ -455,7 +455,7 @@ export const checkAcpRegistryProviderReadiness = Effect.fn(...@@ -455,7 +455,7 @@ export const checkAcpRegistryProviderReadiness = Effect.fn(
455 : snapshot;455 : snapshot;
456});456});
457457
458export type AcpRegistryDriverEnv = AcpRegistryAdapterV2DriverEnv | ProviderHost;458export type AcpRegistryDriverEnv = AcpRegistryAdapterV2DriverEnv | ProviderHost.ProviderHost;
459459
460/** Canonical provider-instance wrapper for ACP Registry orchestration adapters. */460/** Canonical provider-instance wrapper for ACP Registry orchestration adapters. */
461export const AcpRegistryDriver: ProviderDriver<AcpRegistrySettings, AcpRegistryDriverEnv> = {461export const AcpRegistryDriver: ProviderDriver<AcpRegistrySettings, AcpRegistryDriverEnv> = {
...@@ -485,7 +485,7 @@ export const AcpRegistryDriver: ProviderDriver<AcpRegistrySettings, AcpRegistryD...@@ -485,7 +485,7 @@ export const AcpRegistryDriver: ProviderDriver<AcpRegistrySettings, AcpRegistryD
485 const crypto = yield* Crypto.Crypto;485 const crypto = yield* Crypto.Crypto;
486 const spawner = yield* ChildProcessSpawner.ChildProcessSpawner;486 const spawner = yield* ChildProcessSpawner.ChildProcessSpawner;
487 const hostEnvironment = yield* HostProcessEnvironment;487 const hostEnvironment = yield* HostProcessEnvironment;
488 const host = yield* ProviderHost;488 const host = yield* ProviderHost.ProviderHost;
489 const continuationIdentity = defaultProviderContinuationIdentity({489 const continuationIdentity = defaultProviderContinuationIdentity({
490 driverKind: DRIVER_KIND,490 driverKind: DRIVER_KIND,
491 instanceId,491 instanceId,
...@@ -619,7 +619,7 @@ export const AcpRegistryDriver: ProviderDriver<AcpRegistrySettings, AcpRegistryD...@@ -619,7 +619,7 @@ export const AcpRegistryDriver: ProviderDriver<AcpRegistrySettings, AcpRegistryD
619 }619 }
620 return { provider: enriched, generation: cacheState.generation };620 return { provider: enriched, generation: cacheState.generation };
621 });621 });
622 const snapshotSettings = makeProviderSnapshotSettingsSource(effectiveConfig, host.settings);622 const snapshotSettings = yield* makeProviderSnapshotSettingsSource(effectiveConfig);
623 const snapshot = yield* makeManagedServerProvider<623 const snapshot = yield* makeManagedServerProvider<
624 ProviderSnapshotSettings<AcpRegistrySettings>624 ProviderSnapshotSettings<AcpRegistrySettings>
625 >({625 >({
packages/provider-acp/src/server/adapter.ts+5-4
...@@ -95,7 +95,7 @@ import {...@@ -95,7 +95,7 @@ import {
95} from "@t3tools/provider-core/server/orchestrationInstructions";95} from "@t3tools/provider-core/server/orchestrationInstructions";
96import { buildRuntimeInstructions } from "@t3tools/provider-core/server/runtimeInstructions";96import { buildRuntimeInstructions } from "@t3tools/provider-core/server/runtimeInstructions";
97import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator";97import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator";
98import { type ProviderContinuationRequest } from "@t3tools/provider-core/server/continuationRequests";98import type * as ProviderContinuationRequests from "@t3tools/provider-core/server/ProviderContinuationRequests";
99import {99import {
100 type BackgroundWork,100 type BackgroundWork,
101 type BackgroundWorkReport,101 type BackgroundWorkReport,
...@@ -499,7 +499,9 @@ export interface AcpAdapterV2Options {...@@ -499,7 +499,9 @@ export interface AcpAdapterV2Options {
499 * / history-only behavior for null-activeTurn updates.499 * / history-only behavior for null-activeTurn updates.
500 */500 */
501 readonly continuationRequests?: {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 readonly testHooks?: {506 readonly testHooks?: {
505 /**507 /**
...@@ -743,7 +745,7 @@ function nativeThreadId(...@@ -743,7 +745,7 @@ function nativeThreadId(
743function makeProviderThread(input: {745function makeProviderThread(input: {
744 readonly driver: ProviderDriverKind;746 readonly driver: ProviderDriverKind;
745 readonly providerInstanceId: ProviderInstanceId;747 readonly providerInstanceId: ProviderInstanceId;
746 readonly idAllocator: IdAllocator.IdAllocatorV2Shape;748 readonly idAllocator: IdAllocator.IdAllocatorV2["Service"];
747 readonly appThreadId: OrchestrationV2ProviderThread["appThreadId"];749 readonly appThreadId: OrchestrationV2ProviderThread["appThreadId"];
748 readonly providerSessionId: OrchestrationV2ProviderThread["providerSessionId"];750 readonly providerSessionId: OrchestrationV2ProviderThread["providerSessionId"];
749 readonly nativeThreadId: string;751 readonly nativeThreadId: string;
...@@ -2226,7 +2228,6 @@ export const makeAcpAdapterV2 = Effect.fn("makeAcpAdapterV2")(function* (...@@ -2226,7 +2228,6 @@ export const makeAcpAdapterV2 = Effect.fn("makeAcpAdapterV2")(function* (
2226 type: "turn_item.updated",2228 type: "turn_item.updated",
2227 driver,2229 driver,
2228 turnItem: makeProviderRetryTurnItem({2230 turnItem: makeProviderRetryTurnItem({
2229 idAllocator,
2230 driver,2231 driver,
2231 threadId: context.input.threadId,2232 threadId: context.input.threadId,
2232 runId: context.input.runId,2233 runId: context.input.runId,
packages/provider-core/package.json+7-7
...@@ -23,10 +23,6 @@...@@ -23,10 +23,6 @@
23 "types": "./src/server/collectStreamText.ts",23 "types": "./src/server/collectStreamText.ts",
24 "import": "./src/server/collectStreamText.ts"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 "./server/driver": {26 "./server/driver": {
31 "types": "./src/server/driver.ts",27 "types": "./src/server/driver.ts",
32 "import": "./src/server/driver.ts"28 "import": "./src/server/driver.ts"
...@@ -103,9 +99,13 @@...@@ -103,9 +99,13 @@
103 "types": "./src/server/ProviderAdapter.ts",99 "types": "./src/server/ProviderAdapter.ts",
104 "import": "./src/server/ProviderAdapter.ts"100 "import": "./src/server/ProviderAdapter.ts"
105 },101 },
106 "./server/ProviderAuthFlow": {102 "./server/providerAuthFlow": {
107 "types": "./src/server/ProviderAuthFlow.ts",103 "types": "./src/server/providerAuthFlow.ts",
108 "import": "./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 "./server/ProviderEventLoggers": {110 "./server/ProviderEventLoggers": {
111 "types": "./src/server/ProviderEventLoggers.ts",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,7 +67,7 @@ export class IdAllocatorV2AllocationError extends Schema.TaggedError<IdAllocator
67export const IdAllocatorV2Error = Schema.Union([IdAllocatorV2AllocationError]);67export const IdAllocatorV2Error = Schema.Union([IdAllocatorV2AllocationError]);
68export type IdAllocatorV2Error = typeof IdAllocatorV2Error.Type;68export type IdAllocatorV2Error = typeof IdAllocatorV2Error.Type;
6969
70export interface IdAllocatorV2AllocateShape {70export interface IdAllocatorV2Allocate {
71 readonly command: (input: {71 readonly command: (input: {
72 readonly fixtureName: string;72 readonly fixtureName: string;
73 readonly commandName: string;73 readonly commandName: string;
...@@ -126,7 +126,7 @@ export interface IdAllocatorV2AllocateShape {...@@ -126,7 +126,7 @@ export interface IdAllocatorV2AllocateShape {
126 }) => Effect.Effect<PlanId, IdAllocatorV2Error>;126 }) => Effect.Effect<PlanId, IdAllocatorV2Error>;
127}127}
128128
129export interface IdAllocatorV2DeriveShape {129export interface IdAllocatorV2Derive {
130 readonly providerSession: (input: {130 readonly providerSession: (input: {
131 readonly providerInstanceId: ProviderInstanceId;131 readonly providerInstanceId: ProviderInstanceId;
132 }) => ProviderSessionId;132 }) => ProviderSessionId;
...@@ -180,14 +180,13 @@ export interface IdAllocatorV2DeriveShape {...@@ -180,14 +180,13 @@ export interface IdAllocatorV2DeriveShape {
180 readonly approvalTurnItem: (input: { readonly requestId: RuntimeRequestId }) => TurnItemId;180 readonly approvalTurnItem: (input: { readonly requestId: RuntimeRequestId }) => TurnItemId;
181}181}
182182
183export interface IdAllocatorV2Shape {183export class IdAllocatorV2 extends Context.Service<
184 readonly allocate: IdAllocatorV2AllocateShape;184 IdAllocatorV2,
185 readonly derive: IdAllocatorV2DeriveShape;185 {
186}186 readonly allocate: IdAllocatorV2Allocate;
187187 readonly derive: IdAllocatorV2Derive;
188export class IdAllocatorV2 extends Context.Service<IdAllocatorV2, IdAllocatorV2Shape>()(188 }
189 "@t3tools/provider-core/server/IdAllocator/IdAllocatorV2",189>()("@t3tools/provider-core/server/IdAllocator/IdAllocatorV2") {}
190) {}
191190
192const encodePart = (part: string | number): string => encodeURIComponent(String(part));191const encodePart = (part: string | number): string => encodeURIComponent(String(part));
193192
...@@ -252,6 +251,49 @@ const randomId =...@@ -252,6 +251,49 @@ const randomId =
252 ),251 ),
253 );252 );
254253
254/** Deterministic id derivations; pure, so callers that only derive need no allocator. */
255export 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
255export const layer: Layer.Layer<IdAllocatorV2> = Layer.succeed(297export const layer: Layer.Layer<IdAllocatorV2> = Layer.succeed(
256 IdAllocatorV2,298 IdAllocatorV2,
257 IdAllocatorV2.of({299 IdAllocatorV2.of({
...@@ -386,50 +428,6 @@ export const layer: Layer.Layer<IdAllocatorV2> = Layer.succeed(...@@ -386,50 +428,6 @@ export const layer: Layer.Layer<IdAllocatorV2> = Layer.succeed(
386 make: PlanId.make,428 make: PlanId.make,
387 })(input),429 })(input),
388 },430 },
389 derive: {431 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 },
434 }),432 }),
435);433);
packages/provider-core/src/server/ProviderAdapter.ts+17-18
...@@ -632,21 +632,20 @@ export interface ProviderAdapterV2SessionRuntime {...@@ -632,21 +632,20 @@ export interface ProviderAdapterV2SessionRuntime {
632 ) => Effect.Effect<OrchestrationV2ProviderThread, ProviderAdapterV2Error>;632 ) => Effect.Effect<OrchestrationV2ProviderThread, ProviderAdapterV2Error>;
633}633}
634634
635export interface ProviderAdapterV2Shape {635export class ProviderAdapterV2 extends Context.Service<
636 readonly instanceId: ProviderInstanceId;636 ProviderAdapterV2,
637 readonly driver: ProviderDriverKind;637 {
638 readonly getCapabilities: () => Effect.Effect<638 readonly instanceId: ProviderInstanceId;
639 OrchestrationV2ProviderCapabilities,639 readonly driver: ProviderDriverKind;
640 ProviderAdapterV2Error640 readonly getCapabilities: () => Effect.Effect<
641 >;641 OrchestrationV2ProviderCapabilities,
642 readonly planSelectionTransition: (642 ProviderAdapterV2Error
643 input: ProviderSelectionTransitionInput,643 >;
644 ) => Effect.Effect<ProviderSelectionTransitionPlan, ProviderAdapterV2Error>;644 readonly planSelectionTransition: (
645 readonly openSession: (645 input: ProviderSelectionTransitionInput,
646 input: ProviderAdapterV2OpenSessionInput,646 ) => Effect.Effect<ProviderSelectionTransitionPlan, ProviderAdapterV2Error>;
647 ) => Effect.Effect<ProviderAdapterV2SessionRuntime, ProviderAdapterV2Error, Scope.Scope>;647 readonly openSession: (
648}648 input: ProviderAdapterV2OpenSessionInput,
649649 ) => Effect.Effect<ProviderAdapterV2SessionRuntime, ProviderAdapterV2Error, Scope.Scope>;
650export class ProviderAdapterV2 extends Context.Service<ProviderAdapterV2, ProviderAdapterV2Shape>()(650 }
651 "@t3tools/provider-core/server/ProviderAdapter/ProviderAdapterV2",651>()("@t3tools/provider-core/server/ProviderAdapter/ProviderAdapterV2") {}
652) {}
packages/provider-core/src/server/ProviderAuthFlow.test.ts deleted-559
...@@ -1,559 +0,0 @@
1import * as NodeServices from "@effect/platform-node/NodeServices";
2import { assert, it } from "@effect/vitest";
3import {
4 ProviderInstanceId,
5 ProviderSetupError,
6 type ProviderAuthInteraction,
7 type ProviderAuthResponse,
8 type ProviderAuthState,
9} from "@t3tools/contracts";
10import * as Fiber from "effect/Fiber";
11import * as Exit from "effect/Exit";
12import * as Scope from "effect/Scope";
13import * as Deferred from "effect/Deferred";
14import * as Effect from "effect/Effect";
15import * as Option from "effect/Option";
16import * as Stream from "effect/Stream";
17import * as TestClock from "effect/testing/TestClock";
18
19import * as ProviderAuthFlow from "./ProviderAuthFlow.ts";
20
21const instanceId = ProviderInstanceId.make("auth-flow-test");
22const method = { id: "browser", name: "Browser", description: null, type: "agent" as const };
23
24it.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
48const 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
88it.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
119it.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
139it.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
157it.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
177it.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
194it.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
259it.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
293it.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
327const 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
391it.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
418it.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
440it.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
462it.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
478it.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
505it.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
522it.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 @@
1import {
2 ProviderSetupError,
3 type ProviderAuthInteraction,
4 type ProviderAuthMethod,
5 type ProviderAuthResponse,
6 type ProviderAuthState,
7 type ProviderInstanceId,
8} from "@t3tools/contracts";
9import * as Cause from "effect/Cause";
10import * as Option from "effect/Option";
11import * as Schema from "effect/Schema";
12import * as Clock from "effect/Clock";
13import * as Crypto from "effect/Crypto";
14import * as DateTime from "effect/DateTime";
15import * as Effect from "effect/Effect";
16import * as Deferred from "effect/Deferred";
17import * as Exit from "effect/Exit";
18import * as Fiber from "effect/Fiber";
19import * as Scope from "effect/Scope";
20import * as Semaphore from "effect/Semaphore";
21import * as Stream from "effect/Stream";
22import * as SubscriptionRef from "effect/SubscriptionRef";
23
24import type * as ProviderAuth from "./auth.ts";
25
26export 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
40const isSetupError = Schema.is(ProviderSetupError);
41
42const 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
49interface 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. */
62export 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 @@
1import {
2 type OrchestrationV2Notification,
3 MessageId,
4 ProviderDriverKind,
5 ProviderThreadId,
6 RunId,
7 ThreadId,
8} from "@t3tools/contracts";
9import * as Context from "effect/Context";
10import * as Effect from "effect/Effect";
11import * as Layer from "effect/Layer";
12import * as Option from "effect/Option";
13import * as Queue from "effect/Queue";
14
15export 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 */
59export 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
66export 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,33 +45,32 @@ export interface ProviderCredentials {
45 readonly remove: Effect.Effect<void, ProviderCredentialError>;45 readonly remove: Effect.Effect<void, ProviderCredentialError>;
46}46}
4747
48interface ProviderHostService {48export class ProviderHost extends Context.Service<
49 readonly paths: ProviderHostPaths;49 ProviderHost,
50 readonly settings: {50 {
51 readonly get: Effect.Effect<ServerSettings, ServerSettingsError>;51 readonly paths: ProviderHostPaths;
52 /** Runs `use` on a settings snapshot that no settings write can change until it finishes. */52 readonly settings: {
53 readonly withSnapshot: <A, E, R>(53 readonly get: Effect.Effect<ServerSettings, ServerSettingsError>;
54 use: (settings: ServerSettings) => Effect.Effect<A, E, R>,54 /** Runs `use` on a settings snapshot that no settings write can change until it finishes. */
55 ) => Effect.Effect<A, E | ServerSettingsError, R>;55 readonly withSnapshot: <A, E, R>(
56 /** Every settings change after subscription, starting with the next one. */56 use: (settings: ServerSettings) => Effect.Effect<A, E, R>,
57 readonly changes: Stream.Stream<ServerSettings>;57 ) => Effect.Effect<A, E | ServerSettingsError, R>;
58 /** Changes buffered from the moment the scoped subscription is acquired. */58 /** Every settings change after subscription, starting with the next one. */
59 readonly subscribe: Effect.Effect<Stream.Stream<ServerSettings>, never, Scope.Scope>;59 readonly changes: Stream.Stream<ServerSettings>;
60 };60 /** Changes buffered from the moment the scoped subscription is acquired. */
61 /** Whether background work for `scope` should run now (client demand and host power). */61 readonly subscribe: Effect.Effect<Stream.Stream<ServerSettings>, never, Scope.Scope>;
62 readonly shouldRunBackgroundWork: (scope: BackgroundScope) => Effect.Effect<boolean>;62 };
63 /**63 /** Whether background work for `scope` should run now (client demand and host power). */
64 * Absolute path of a stored chat attachment, or `null` when its id does not64 readonly shouldRunBackgroundWork: (scope: BackgroundScope) => Effect.Effect<boolean>;
65 * resolve inside the attachments directory.65 /**
66 */66 * Absolute path of a stored chat attachment, or `null` when its id does not
67 readonly resolveAttachmentPath: (attachment: ChatAttachment) => string | null;67 * resolve inside the attachments directory.
68 /** The credentials stored for `bindingId`, usually an instance id, under a provider-chosen namespace. */68 */
69 readonly credentials: (69 readonly resolveAttachmentPath: (attachment: ChatAttachment) => string | null;
70 namespace: string,70 /** The credentials stored for `bindingId`, usually an instance id, under a provider-chosen namespace. */
71 bindingId: string,71 readonly credentials: (
72 ) => Effect.Effect<ProviderCredentials>;72 namespace: string,
73}73 bindingId: string,
7474 ) => Effect.Effect<ProviderCredentials>;
75export class ProviderHost extends Context.Service<ProviderHost, ProviderHostService>()(75 }
76 "@t3tools/provider-core/server/ProviderHost",76>()("@t3tools/provider-core/server/ProviderHost") {}
77) {}
packages/provider-core/src/server/adapterDriver.ts+1-1
...@@ -39,7 +39,7 @@ export interface ProviderAdapterDriver<Config, R = never> {...@@ -39,7 +39,7 @@ export interface ProviderAdapterDriver<Config, R = never> {
39 readonly create: (39 readonly create: (
40 input: ProviderAdapterDriverCreateInput<Config>,40 input: ProviderAdapterDriverCreateInput<Config>,
41 ) => Effect.Effect<41 ) => Effect.Effect<
42 ProviderAdapter.ProviderAdapterV2Shape,42 ProviderAdapter.ProviderAdapterV2["Service"],
43 ProviderAdapterDriverCreateError,43 ProviderAdapterDriverCreateError,
44 R | Scope.Scope44 R | Scope.Scope
45 >;45 >;
packages/provider-core/src/server/continuationRequests.ts deleted-76
...@@ -1,76 +0,0 @@
1import {
2 type OrchestrationV2Notification,
3 MessageId,
4 ProviderDriverKind,
5 ProviderThreadId,
6 RunId,
7 ThreadId,
8} from "@t3tools/contracts";
9import * as Context from "effect/Context";
10import * as Effect from "effect/Effect";
11import * as Layer from "effect/Layer";
12import * as Option from "effect/Option";
13import * as Queue from "effect/Queue";
14
15export 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 */
59export 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
66export 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,7 +101,7 @@ export interface ProviderInstance {
101 ProviderConsumeResetCreditOutcome,101 ProviderConsumeResetCreditOutcome,
102 ProviderDriverError102 ProviderDriverError
103 >;103 >;
104 readonly orchestrationAdapter: ProviderAdapter.ProviderAdapterV2Shape;104 readonly orchestrationAdapter: ProviderAdapter.ProviderAdapterV2["Service"];
105 readonly textGeneration: ProviderTextGeneration;105 readonly textGeneration: ProviderTextGeneration;
106 readonly auth?: ProviderAuthController;106 readonly auth?: ProviderAuthController;
107 readonly acpSessionManagement?: {107 readonly acpSessionManagement?: {
packages/provider-core/src/server/failure.ts+3-5
...@@ -13,7 +13,7 @@ import type {...@@ -13,7 +13,7 @@ import type {
13import * as DateTime from "effect/DateTime";13import * as DateTime from "effect/DateTime";
14import * as Cause from "effect/Cause";14import * as Cause from "effect/Cause";
1515
16import type * as IdAllocator from "./IdAllocator.ts";16import * as IdAllocator from "./IdAllocator.ts";
17import * as Schema from "effect/Schema";17import * as Schema from "effect/Schema";
1818
19/** Raised when portable history does not fit the target provider's context window. */19/** Raised when portable history does not fit the target provider's context window. */
...@@ -172,7 +172,6 @@ export function makeProviderFailure(input: {...@@ -172,7 +172,6 @@ export function makeProviderFailure(input: {
172}172}
173173
174export function makeProviderFailureTurnItem(input: {174export function makeProviderFailureTurnItem(input: {
175 readonly idAllocator: IdAllocator.IdAllocatorV2Shape;
176 readonly driver: ProviderDriverKind;175 readonly driver: ProviderDriverKind;
177 readonly threadId: ThreadId;176 readonly threadId: ThreadId;
178 readonly runId: RunId | null;177 readonly runId: RunId | null;
...@@ -186,7 +185,7 @@ export function makeProviderFailureTurnItem(input: {...@@ -186,7 +185,7 @@ export function makeProviderFailureTurnItem(input: {
186 readonly occurredAt: DateTime.Utc;185 readonly occurredAt: DateTime.Utc;
187}): Extract<OrchestrationV2TurnItem, { readonly type: "error" }> {186}): Extract<OrchestrationV2TurnItem, { readonly type: "error" }> {
188 return {187 return {
189 id: input.idAllocator.derive.turnItemFromProviderItem({188 id: IdAllocator.derive.turnItemFromProviderItem({
190 driver: input.driver,189 driver: input.driver,
191 nativeItemId: `terminal-failure:${input.providerTurnId}`,190 nativeItemId: `terminal-failure:${input.providerTurnId}`,
192 }),191 }),
...@@ -210,7 +209,6 @@ export function makeProviderFailureTurnItem(input: {...@@ -210,7 +209,6 @@ export function makeProviderFailureTurnItem(input: {
210}209}
211210
212export function makeProviderRetryTurnItem(input: {211export function makeProviderRetryTurnItem(input: {
213 readonly idAllocator: IdAllocator.IdAllocatorV2Shape;
214 readonly driver: ProviderDriverKind;212 readonly driver: ProviderDriverKind;
215 readonly threadId: ThreadId;213 readonly threadId: ThreadId;
216 readonly runId: RunId | null;214 readonly runId: RunId | null;
...@@ -237,7 +235,7 @@ export function makeProviderRetryTurnItem(input: {...@@ -237,7 +235,7 @@ export function makeProviderRetryTurnItem(input: {
237 title = "Provider retry stopped";235 title = "Provider retry stopped";
238 }236 }
239 return {237 return {
240 id: input.idAllocator.derive.turnItemFromProviderItem({238 id: IdAllocator.derive.turnItemFromProviderItem({
241 driver: input.driver,239 driver: input.driver,
242 nativeItemId: `terminal-failure:${input.providerTurnId}`,240 nativeItemId: `terminal-failure:${input.providerTurnId}`,
243 }),241 }),
packages/provider-core/src/server/providerAuthFlow.test.ts created+559
...@@ -0,0 +1,559 @@
1import * as NodeServices from "@effect/platform-node/NodeServices";
2import { assert, it } from "@effect/vitest";
3import {
4 ProviderInstanceId,
5 ProviderSetupError,
6 type ProviderAuthInteraction,
7 type ProviderAuthResponse,
8 type ProviderAuthState,
9} from "@t3tools/contracts";
10import * as Fiber from "effect/Fiber";
11import * as Exit from "effect/Exit";
12import * as Scope from "effect/Scope";
13import * as Deferred from "effect/Deferred";
14import * as Effect from "effect/Effect";
15import * as Option from "effect/Option";
16import * as Stream from "effect/Stream";
17import * as TestClock from "effect/testing/TestClock";
18
19import * as ProviderAuthFlow from "./providerAuthFlow.ts";
20
21const instanceId = ProviderInstanceId.make("auth-flow-test");
22const method = { id: "browser", name: "Browser", description: null, type: "agent" as const };
23
24it.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
48const 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
88it.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
119it.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
139it.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
157it.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
177it.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
194it.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
259it.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
293it.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
327const 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
391it.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
418it.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
440it.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
462it.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
478it.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
505it.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
522it.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 @@
1import {
2 ProviderSetupError,
3 type ProviderAuthInteraction,
4 type ProviderAuthMethod,
5 type ProviderAuthResponse,
6 type ProviderAuthState,
7 type ProviderInstanceId,
8} from "@t3tools/contracts";
9import * as Cause from "effect/Cause";
10import * as Option from "effect/Option";
11import * as Schema from "effect/Schema";
12import * as Clock from "effect/Clock";
13import * as Crypto from "effect/Crypto";
14import * as DateTime from "effect/DateTime";
15import * as Effect from "effect/Effect";
16import * as Deferred from "effect/Deferred";
17import * as Exit from "effect/Exit";
18import * as Fiber from "effect/Fiber";
19import * as Scope from "effect/Scope";
20import * as Semaphore from "effect/Semaphore";
21import * as Stream from "effect/Stream";
22import * as SubscriptionRef from "effect/SubscriptionRef";
23
24import type * as ProviderAuth from "./auth.ts";
25
26export 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
40const isSetupError = Schema.is(ProviderSetupError);
41
42const 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
49interface 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. */
62export 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,7 +3,7 @@ import * as Effect from "effect/Effect";
3import * as Equal from "effect/Equal";3import * as Equal from "effect/Equal";
4import * as Stream from "effect/Stream";4import * as Stream from "effect/Stream";
55
6import type * as ProviderHost from "./ProviderHost.ts";6import * as ProviderHost from "./ProviderHost.ts";
77
8export interface ProviderSnapshotSettings<Settings> {8export interface ProviderSnapshotSettings<Settings> {
9 readonly provider: Settings;9 readonly provider: Settings;
...@@ -27,17 +27,23 @@ export function haveProviderSnapshotSettingsChanged<Settings>(...@@ -27,17 +27,23 @@ export function haveProviderSnapshotSettingsChanged<Settings>(
27 return !Equal.equals(previous, next);27 return !Equal.equals(previous, next);
28}28}
2929
30export function makeProviderSnapshotSettingsSource<Settings>(30/** The instance's settings paired with the host's server settings, as snapshots read them. */
31export const makeProviderSnapshotSettingsSource = <Settings>(
31 provider: Settings,32 provider: Settings,
32 settings: ProviderHost.ProviderHost["Service"]["settings"],33): Effect.Effect<
33): {34 {
34 readonly getSettings: Effect.Effect<ProviderSnapshotSettings<Settings>, ServerSettingsError>;35 readonly getSettings: Effect.Effect<ProviderSnapshotSettings<Settings>, ServerSettingsError>;
35 readonly streamSettings: Stream.Stream<ProviderSnapshotSettings<Settings>>;36 readonly streamSettings: Stream.Stream<ProviderSnapshotSettings<Settings>>;
36} {37 },
37 const mapSettings = (settings: ServerSettings) =>38 never,
38 makeProviderSnapshotSettings(provider, settings);39 ProviderHost.ProviderHost
39 return {40> =>
40 getSettings: settings.get.pipe(Effect.map(mapSettings)),41 Effect.gen(function* () {
41 streamSettings: settings.changes.pipe(Stream.map(mapSettings)),42 const { settings } = yield* ProviderHost.ProviderHost;
42 };43 const mapSettings = (current: ServerSettings) =>
43}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,7 +239,7 @@ function providerSession(input: {
239}239}
240240
241function makeProviderThread(input: {241function makeProviderThread(input: {
242 readonly idAllocator: IdAllocator.IdAllocatorV2Shape;242 readonly idAllocator: IdAllocator.IdAllocatorV2["Service"];
243 readonly providerInstanceId: ProviderInstanceId;243 readonly providerInstanceId: ProviderInstanceId;
244 readonly appThreadId: OrchestrationV2ProviderThread["appThreadId"];244 readonly appThreadId: OrchestrationV2ProviderThread["appThreadId"];
245 readonly providerSessionId: OrchestrationV2ProviderThread["providerSessionId"];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,7 +223,7 @@ export const CursorDriver: ProviderDriver<CursorSettings, CursorDriverEnv> = {
223 Effect.provide(CursorSdkCatalog.layer),223 Effect.provide(CursorSdkCatalog.layer),
224 );224 );
225225
226 const snapshotSettings = makeProviderSnapshotSettingsSource(effectiveConfig, host.settings);226 const snapshotSettings = yield* makeProviderSnapshotSettingsSource(effectiveConfig);
227 const snapshot = yield* makeManagedServerProvider<ProviderSnapshotSettings<CursorSettings>>({227 const snapshot = yield* makeManagedServerProvider<ProviderSnapshotSettings<CursorSettings>>({
228 resolveMaintenance: () => Effect.succeed(MAINTENANCE_CAPABILITIES),228 resolveMaintenance: () => Effect.succeed(MAINTENANCE_CAPABILITIES),
229 getSettings: snapshotSettings.getSettings,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,7 +58,7 @@ import { acpPermissionDisposition } from "@t3tools/provider-acp/server/clientPol
58import * as AcpSessionRuntime from "@t3tools/provider-acp/server/AcpSessionRuntime";58import * as AcpSessionRuntime from "@t3tools/provider-acp/server/AcpSessionRuntime";
59import * as ProviderEventLoggers from "@t3tools/provider-core/server/ProviderEventLoggers";59import * as ProviderEventLoggers from "@t3tools/provider-core/server/ProviderEventLoggers";
60import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator";60import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator";
61import * as ProviderContinuationRequests from "@t3tools/provider-core/server/continuationRequests";61import * as ProviderContinuationRequests from "@t3tools/provider-core/server/ProviderContinuationRequests";
62import * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter";62import * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter";
63import {63import {
64 ProviderAdapterDriverCreateError,64 ProviderAdapterDriverCreateError,
packages/provider-grok/src/server/driver.ts+1-1
...@@ -160,7 +160,7 @@ export const GrokDriver: ProviderDriver<GrokSettings, GrokDriverEnv> = {...@@ -160,7 +160,7 @@ export const GrokDriver: ProviderDriver<GrokSettings, GrokDriverEnv> = {
160 Effect.provideService(ChildProcessSpawner.ChildProcessSpawner, spawner),160 Effect.provideService(ChildProcessSpawner.ChildProcessSpawner, spawner),
161 );161 );
162162
163 const snapshotSettings = makeProviderSnapshotSettingsSource(effectiveConfig, host.settings);163 const snapshotSettings = yield* makeProviderSnapshotSettingsSource(effectiveConfig);
164 const snapshot = yield* makeManagedServerProvider<ProviderSnapshotSettings<GrokSettings>>({164 const snapshot = yield* makeManagedServerProvider<ProviderSnapshotSettings<GrokSettings>>({
165 resolveMaintenance,165 resolveMaintenance,
166 getSettings: snapshotSettings.getSettings,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,7 +36,7 @@ import type { MuseItem } from "./protocol.ts";
36import type { MuseSdkHost } from "./sdk.ts";36import type { MuseSdkHost } from "./sdk.ts";
37import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator";37import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator";
38import * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter";38import * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter";
39import type { ProviderContinuationRequest } from "@t3tools/provider-core/server/continuationRequests";39import type * as ProviderContinuationRequests from "@t3tools/provider-core/server/ProviderContinuationRequests";
40import { makeMuseAdapterV2, type MuseAdapterV2Options } from "./adapter.ts";40import { makeMuseAdapterV2, type MuseAdapterV2Options } from "./adapter.ts";
4141
42const testLayer = Layer.mergeAll(42const testLayer = Layer.mergeAll(
...@@ -766,7 +766,7 @@ describe("MuseAdapterV2", () => {...@@ -766,7 +766,7 @@ describe("MuseAdapterV2", () => {
766 it.effect("lets a user turn take a held Muse report turn, approvals included", () =>766 it.effect("lets a user turn take a held Muse report turn, approvals included", () =>
767 Effect.gen(function* () {767 Effect.gen(function* () {
768 const fake = yield* makeFakeMuse();768 const fake = yield* makeFakeMuse();
769 const offers: Array<ProviderContinuationRequest> = [];769 const offers: Array<ProviderContinuationRequests.ProviderContinuationRequest> = [];
770 const harness = yield* makeHarness(770 const harness = yield* makeHarness(
771 fake,771 fake,
772 INSTANCE_ID,772 INSTANCE_ID,
packages/provider-muse/src/server/adapter.ts+1-1
...@@ -74,7 +74,7 @@ import {...@@ -74,7 +74,7 @@ import {
74 backgroundWorkNotification,74 backgroundWorkNotification,
75 type BackgroundWorkReport,75 type BackgroundWorkReport,
76} from "@t3tools/provider-core/server/notification";76} from "@t3tools/provider-core/server/notification";
77import type * as ProviderContinuationRequests from "@t3tools/provider-core/server/continuationRequests";77import type * as ProviderContinuationRequests from "@t3tools/provider-core/server/ProviderContinuationRequests";
78import { makeProviderFailure } from "@t3tools/provider-core/server/failure";78import { makeProviderFailure } from "@t3tools/provider-core/server/failure";
79import { turnScopedSelectionTransition } from "@t3tools/provider-core/server/selectionTransition";79import { turnScopedSelectionTransition } from "@t3tools/provider-core/server/selectionTransition";
80import { museItemStatus, museToolPresentation } from "./itemPresentation.ts";80import { museItemStatus, museToolPresentation } from "./itemPresentation.ts";
packages/provider-muse/src/server/driver.ts+2-2
...@@ -14,7 +14,7 @@ import { makeMuseTextGeneration } from "./textGeneration.ts";...@@ -14,7 +14,7 @@ import { makeMuseTextGeneration } from "./textGeneration.ts";
14import { ProviderDriverError } from "@t3tools/provider-core/server/errors";14import { ProviderDriverError } from "@t3tools/provider-core/server/errors";
15import { makeMuseAdapterV2 } from "./adapter.ts";15import { makeMuseAdapterV2 } from "./adapter.ts";
16import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator";16import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator";
17import * as ProviderContinuationRequests from "@t3tools/provider-core/server/continuationRequests";17import * as ProviderContinuationRequests from "@t3tools/provider-core/server/ProviderContinuationRequests";
18import { checkMuseProviderStatus, makePendingMuseProvider } from "./status.ts";18import { checkMuseProviderStatus, makePendingMuseProvider } from "./status.ts";
19import * as ProviderEventLoggers from "@t3tools/provider-core/server/ProviderEventLoggers";19import * as ProviderEventLoggers from "@t3tools/provider-core/server/ProviderEventLoggers";
20import { makeManagedServerProvider } from "@t3tools/provider-core/server/managedProvider";20import { makeManagedServerProvider } from "@t3tools/provider-core/server/managedProvider";
...@@ -86,7 +86,7 @@ export const MuseDriver: ProviderDriver<MuseSettings, MuseDriverEnv> = {...@@ -86,7 +86,7 @@ export const MuseDriver: ProviderDriver<MuseSettings, MuseDriverEnv> = {
86 accentColor,86 accentColor,
87 continuationGroupKey: continuationIdentity.continuationKey,87 continuationGroupKey: continuationIdentity.continuationKey,
88 });88 });
89 const snapshotSettings = makeProviderSnapshotSettingsSource(effectiveConfig, host.settings);89 const snapshotSettings = yield* makeProviderSnapshotSettingsSource(effectiveConfig);
90 const resolveInstallation = yield* makeCachedProviderMaintenanceResolution(90 const resolveInstallation = yield* makeCachedProviderMaintenanceResolution(
91 resolveProviderMaintenanceCapabilitiesEffect(museMaintenance, {91 resolveProviderMaintenanceCapabilitiesEffect(museMaintenance, {
92 binaryPath: effectiveConfig.binaryPath,92 binaryPath: effectiveConfig.binaryPath,
packages/provider-opencode/src/server/adapter.ts+2-2
...@@ -459,7 +459,7 @@ function formatOpenCodeProtocolLogPayload(event: OpenCodeProtocolLogEvent) {...@@ -459,7 +459,7 @@ function formatOpenCodeProtocolLogPayload(event: OpenCodeProtocolLogEvent) {
459459
460export function makeOpenCodeProtocolLogger(input: {460export function makeOpenCodeProtocolLogger(input: {
461 readonly nativeEventLogger: ProviderEventLoggers.EventNdjsonLogger | undefined;461 readonly nativeEventLogger: ProviderEventLoggers.EventNdjsonLogger | undefined;
462 readonly idAllocator: IdAllocator.IdAllocatorV2Shape;462 readonly idAllocator: IdAllocator.IdAllocatorV2["Service"];
463 readonly providerInstanceId: ProviderInstanceId;463 readonly providerInstanceId: ProviderInstanceId;
464 readonly providerSessionId: ProviderSessionId;464 readonly providerSessionId: ProviderSessionId;
465 readonly threadId: ThreadId;465 readonly threadId: ThreadId;
...@@ -830,7 +830,7 @@ function taskSessionId(part: ToolPart): string | null {...@@ -830,7 +830,7 @@ function taskSessionId(part: ToolPart): string | null {
830}830}
831831
832function makeProviderThread(input: {832function makeProviderThread(input: {
833 readonly idAllocator: IdAllocator.IdAllocatorV2Shape;833 readonly idAllocator: IdAllocator.IdAllocatorV2["Service"];
834 readonly providerInstanceId: ProviderInstanceId;834 readonly providerInstanceId: ProviderInstanceId;
835 readonly providerSessionId: OrchestrationV2ProviderThread["providerSessionId"];835 readonly providerSessionId: OrchestrationV2ProviderThread["providerSessionId"];
836 readonly appThreadId: OrchestrationV2ProviderThread["appThreadId"];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,9 +123,9 @@ function byOpenCodeRuntime<A, E, R, PE>(
123 */123 */
124function selectOpenCodeRuntimeAdapter(input: {124function selectOpenCodeRuntimeAdapter(input: {
125 readonly probe: OpenCodeRuntimeProbe;125 readonly probe: OpenCodeRuntimeProbe;
126 readonly v1: ProviderAdapter.ProviderAdapterV2Shape;126 readonly v1: ProviderAdapter.ProviderAdapterV2["Service"];
127 readonly v2: ProviderAdapter.ProviderAdapterV2Shape;127 readonly v2: ProviderAdapter.ProviderAdapterV2["Service"];
128}): ProviderAdapter.ProviderAdapterV2Shape {128}): ProviderAdapter.ProviderAdapterV2["Service"] {
129 const pick = <PE>(probed: Effect.Effect<ProbedOpenCode | undefined, PE>) =>129 const pick = <PE>(probed: Effect.Effect<ProbedOpenCode | undefined, PE>) =>
130 byOpenCodeRuntime(probed, { v1: Effect.succeed(input.v1), v2: Effect.succeed(input.v2) });130 byOpenCodeRuntime(probed, { v1: Effect.succeed(input.v1), v2: Effect.succeed(input.v2) });
131 const hot = pick(Effect.map(input.probe.lastSuccess, Option.getOrUndefined));131 const hot = pick(Effect.map(input.probe.lastSuccess, Option.getOrUndefined));
...@@ -445,7 +445,7 @@ export const OpenCodeDriver: ProviderDriver<OpenCodeSettings, OpenCodeDriverEnv>...@@ -445,7 +445,7 @@ export const OpenCodeDriver: ProviderDriver<OpenCodeSettings, OpenCodeDriverEnv>
445 ),445 ),
446 );446 );
447447
448 const snapshotSettings = makeProviderSnapshotSettingsSource(effectiveConfig, host.settings);448 const snapshotSettings = yield* makeProviderSnapshotSettingsSource(effectiveConfig);
449 const snapshot = yield* makeManagedServerProvider<ProviderSnapshotSettings<OpenCodeSettings>>(449 const snapshot = yield* makeManagedServerProvider<ProviderSnapshotSettings<OpenCodeSettings>>(
450 {450 {
451 resolveMaintenance,451 resolveMaintenance,
packages/provider-opencode/src/server/v2/adapter.ts+2-2
...@@ -88,7 +88,7 @@ import {...@@ -88,7 +88,7 @@ import {
88 backgroundWorkNotification,88 backgroundWorkNotification,
89 type BackgroundWorkReport,89 type BackgroundWorkReport,
90} from "@t3tools/provider-core/server/notification";90} from "@t3tools/provider-core/server/notification";
91import * as ProviderContinuationRequests from "@t3tools/provider-core/server/continuationRequests";91import * as ProviderContinuationRequests from "@t3tools/provider-core/server/ProviderContinuationRequests";
92import { makeProviderFailure } from "@t3tools/provider-core/server/failure";92import { makeProviderFailure } from "@t3tools/provider-core/server/failure";
93import {93import {
94 makeSubagentChildThread,94 makeSubagentChildThread,
...@@ -858,7 +858,7 @@ export const make = Effect.fn("OpenCode2Adapter.make")(function* (instanceId: Pr...@@ -858,7 +858,7 @@ export const make = Effect.fn("OpenCode2Adapter.make")(function* (instanceId: Pr
858 });858 });
859859
860 const openSession = Effect.fn("OpenCode2Adapter.openSession")(function* (860 const openSession = Effect.fn("OpenCode2Adapter.openSession")(function* (
861 input: Parameters<ProviderAdapter.ProviderAdapterV2Shape["openSession"]>[0],861 input: Parameters<ProviderAdapter.ProviderAdapterV2["Service"]["openSession"]>[0],
862 initial: {862 initial: {
863 readonly connection: OpenCode2Server.OpenCode2Connection;863 readonly connection: OpenCode2Server.OpenCode2Connection;
864 readonly scope: Scope.Closeable;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,7 +32,7 @@ import * as TestClock from "effect/testing/TestClock";
32import { ChildProcess, ChildProcessSpawner } from "effect/process";32import { ChildProcess, ChildProcessSpawner } from "effect/process";
3333
34import { layerTestProviderHost } from "@t3tools/provider-testing/host";34import { layerTestProviderHost } from "@t3tools/provider-testing/host";
35import * as ProviderContinuationRequests from "@t3tools/provider-core/server/continuationRequests";35import * as ProviderContinuationRequests from "@t3tools/provider-core/server/ProviderContinuationRequests";
36import * as McpProviderSession from "@t3tools/provider-core/server/mcpSession";36import * as McpProviderSession from "@t3tools/provider-core/server/mcpSession";
37import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator";37import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator";
38import * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter";38import * 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,7 +67,7 @@ import { parsePiCompactCommand, type PiCompactCommand } from "./commands.ts";
67import { mergeProviderInstanceEnvironment } from "@t3tools/provider-core/server/instanceEnvironment";67import { mergeProviderInstanceEnvironment } from "@t3tools/provider-core/server/instanceEnvironment";
68import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator";68import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator";
69import * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter";69import * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter";
70import * as ProviderContinuationRequests from "@t3tools/provider-core/server/continuationRequests";70import * as ProviderContinuationRequests from "@t3tools/provider-core/server/ProviderContinuationRequests";
71import {71import {
72 ProviderAdapterDriverCreateError,72 ProviderAdapterDriverCreateError,
73 type ProviderAdapterDriver,73 type ProviderAdapterDriver,
...@@ -775,7 +775,6 @@ export const makePiAdapterV2 = Effect.fn("makePiAdapterV2")(function* (...@@ -775,7 +775,6 @@ export const makePiAdapterV2 = Effect.fn("makePiAdapterV2")(function* (
775 type: "turn_item.updated",775 type: "turn_item.updated",
776 driver: PI_PROVIDER,776 driver: PI_PROVIDER,
777 turnItem: makeProviderRetryTurnItem({777 turnItem: makeProviderRetryTurnItem({
778 idAllocator,
779 driver: PI_PROVIDER,778 driver: PI_PROVIDER,
780 threadId: turn.turnInput.threadId,779 threadId: turn.turnInput.threadId,
781 runId: turn.turnInput.runId,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,7 +142,7 @@ export const PiDriver: ProviderDriver<PiSettings, PiDriverEnv> = {
142 Effect.provideService(ChildProcessSpawner.ChildProcessSpawner, spawner),142 Effect.provideService(ChildProcessSpawner.ChildProcessSpawner, spawner),
143 );143 );
144144
145 const snapshotSettings = makeProviderSnapshotSettingsSource(effectiveConfig, host.settings);145 const snapshotSettings = yield* makeProviderSnapshotSettingsSource(effectiveConfig);
146 const snapshot = yield* makeManagedServerProvider<ProviderSnapshotSettings<PiSettings>>({146 const snapshot = yield* makeManagedServerProvider<ProviderSnapshotSettings<PiSettings>>({
147 resolveMaintenance,147 resolveMaintenance,
148 getSettings: snapshotSettings.getSettings,148 getSettings: snapshotSettings.getSettings,