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 {
1010 CLAUDE_AGENT_SDK_REPLAY_PROTOCOL,
1111} from "../src/orchestration-v2/Adapters/ClaudeAdapterV2.testkit.ts";
1212import { claudeRuntimeQueryPolicyForRuntimePolicy } from "../src/orchestration-v2/Adapters/ClaudeAdapterV2.ts";
13import {
14 ProviderAdapterV2RuntimePolicy,
15 type ProviderAdapterV2RuntimePolicy as ProviderAdapterV2RuntimePolicyType,
16} from "@t3tools/provider-core/server/ProviderAdapter";
13import * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter";
1714import type { RuntimePolicyV2Override } from "../src/orchestration-v2/RuntimePolicy.ts";
1815import { makeCheckpointWorkspace } from "@t3tools/provider-testing/replayWorkspace";
1916import { CLAUDE_MODEL_SELECTION } from "../src/orchestration-v2/testkit/fixtures/shared.ts";
......@@ -440,8 +437,8 @@ validateClaudeReplayRecordingSelection({
440437function runtimePolicyForRecording(input: {
441438 readonly cwd: string;
442439 readonly override?: RuntimePolicyV2Override;
443}): ProviderAdapterV2RuntimePolicyType {
444 return ProviderAdapterV2RuntimePolicy.make({
440}): ProviderAdapter.ProviderAdapterV2RuntimePolicy {
441 return ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
445442 runtimeMode: "full-access",
446443 interactionMode: "default",
447444 cwd: input.override?.cwd ?? input.cwd,
apps/server/scripts/record-grok-acp-replay-fixture.ts+3-3
......@@ -33,8 +33,8 @@ import {
3333} from "@t3tools/provider-grok/testing";
3434import { ACP_PROTOCOL } from "@t3tools/provider-acp/server/adapter";
3535import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator";
36import type { ProviderAdapterV2SessionRuntime } from "@t3tools/provider-core/server/ProviderAdapter";
37import * as ProviderContinuationRequests from "@t3tools/provider-core/server/continuationRequests";
36import type * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter";
37import * as ProviderContinuationRequests from "@t3tools/provider-core/server/ProviderContinuationRequests";
3838import * as ProviderAdapterRegistry from "../src/orchestration-v2/ProviderAdapterRegistry.ts";
3939import { provideDeterministicTestRuntime } from "../src/orchestration-v2/testkit/DeterministicRuntime.ts";
4040import { ORCHESTRATOR_REPLAY_FIXTURES } from "../src/orchestration-v2/testkit/fixtures/index.ts";
......@@ -474,7 +474,7 @@ const recordScenario = Effect.fn("recordGrokScenario")(function* (fixtureName: s
474474 ...adapter,
475475 openSession: (input) =>
476476 adapter.openSession(input).pipe(
477 Effect.map((session): ProviderAdapterV2SessionRuntime => ({
477 Effect.map((session): ProviderAdapter.ProviderAdapterV2SessionRuntime => ({
478478 ...session,
479479 startTurn: (turnInput) => onWallClock(session.startTurn(turnInput)),
480480 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";
1919import { CodexProviderCapabilitiesV2 } from "../orchestration-v2/Adapters/CodexAdapterV2.ts";
2020import * as Orchestrator from "../orchestration-v2/Orchestrator.ts";
2121import * as ProjectionStore from "../orchestration-v2/ProjectionStore.ts";
22import type { ProviderAdapterV2Shape } from "@t3tools/provider-core/server/ProviderAdapter";
2322import * as ProviderAdapterRegistry from "../orchestration-v2/ProviderAdapterRegistry.ts";
2423import * as ThreadManagement from "../orchestration-v2/ThreadManagementService.ts";
2524import * as ProviderReplayHarness from "../orchestration-v2/testkit/ProviderReplayHarness.ts";
2625import * as McpHttpServer from "./McpHttpServer.ts";
2726import * as McpInvocationContext from "./McpInvocationContext.ts";
2827import * as McpToolAccess from "./McpToolAccess.ts";
28import type * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter";
2929
3030// A Supervised outside agent renames a thread through a `writesThreads` tool.
3131// The thread's user raises it to full access after the tool's check but
......@@ -38,7 +38,7 @@ const adapter = {
3838 getCapabilities: () => Effect.succeed(CodexProviderCapabilitiesV2),
3939 planSelectionTransition: () => Effect.succeed({ type: "apply_on_next_turn" as const }),
4040 openSession: () => Effect.die("No provider process needed for metadata commands"),
41} as ProviderAdapterV2Shape;
41} as ProviderAdapter.ProviderAdapterV2["Service"];
4242const layerDatabase = SqlitePersistence.layerMemory;
4343const layerOrchestrator = Layer.mergeAll(
4444 layerDatabase,
apps/server/src/mcp/OrchestratorMcpService.test.ts+4-2
......@@ -28,7 +28,6 @@ import {
2828 OrchestratorProjectionError,
2929 OrchestratorThreadAboveModeLimitError,
3030} from "../orchestration-v2/Orchestrator.ts";
31import type { ProviderAdapterV2Shape } from "@t3tools/provider-core/server/ProviderAdapter";
3231import * as ProviderAdapterRegistry from "../orchestration-v2/ProviderAdapterRegistry.ts";
3332import * as ThreadManagementService from "../orchestration-v2/ThreadManagementService.ts";
3433import * as ProviderRegistry from "../provider/ProviderRegistry.ts";
......@@ -39,6 +38,7 @@ import * as SecretRequests from "../secrets/SecretRequests.ts";
3938import type { McpInvocationScope } from "./McpInvocationContext.ts";
4039import { idleThreadProjection, liveThreadShell } from "./McpToolAccess.testkit.ts";
4140import * as OrchestratorMcpService from "./OrchestratorMcpService.ts";
41import type * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter";
4242
4343describe("OrchestratorMcpService", () => {
4444 it.effect("retries terminal acknowledgement with a fresh command id", () =>
......@@ -810,7 +810,9 @@ describe("OrchestratorMcpService provider resolution", () => {
810810 list: () => Effect.succeed(instanceIds),
811811 get: (instanceId) =>
812812 instanceIds.includes(instanceId)
813 ? Effect.succeed({ instanceId } as unknown as ProviderAdapterV2Shape)
813 ? Effect.succeed({
814 instanceId,
815 } as unknown as ProviderAdapter.ProviderAdapterV2["Service"])
814816 : Effect.fail(
815817 new ProviderAdapterRegistry.ProviderAdapterRegistryLookupError({ instanceId }),
816818 ),
apps/server/src/mcp/OrchestratorMcpToolkit.integration.test.ts+12-15
......@@ -57,14 +57,8 @@ import { threadShellFromProjection } from "../orchestration-v2/ProjectionStore.t
5757import * as EventSink from "../orchestration-v2/EventSink.ts";
5858import * as Orchestrator from "../orchestration-v2/Orchestrator.ts";
5959import * 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";
6660import * 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";
6862import { checkpointWorkspace } from "@t3tools/provider-testing/replayWorkspace";
6963import * as ProviderReplayHarness from "../orchestration-v2/testkit/ProviderReplayHarness.ts";
7064import {
......@@ -79,6 +73,7 @@ import * as ServerSecretStore from "../auth/ServerSecretStore.ts";
7973import * as McpHttpServer from "./McpHttpServer.ts";
8074import * as McpInvocationContext from "./McpInvocationContext.ts";
8175import { delegatedTaskRun, hasPendingChildRuns } from "./OrchestratorMcpService.ts";
76import * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter";
8277
8378// Effect returns a declared tool failure as `isError` with its encoded payload
8479// as JSON text, never as `structuredContent`.
......@@ -146,7 +141,7 @@ interface CapturedTurn {
146141}
147142
148143function unsupported(driver: ProviderDriverKind, detail: string) {
149 return Effect.fail(new ProviderAdapterProtocolError({ driver, detail }));
144 return Effect.fail(new ProviderAdapter.ProviderAdapterProtocolError({ driver, detail }));
150145}
151146
152147function makeProviderSnapshot(input: {
......@@ -185,10 +180,12 @@ function makeDeterministicAdapter(input: {
185180 readonly driver: ProviderDriverKind;
186181 readonly capabilities: OrchestrationV2ProviderCapabilities;
187182 readonly capturedTurns: Ref.Ref<ReadonlyArray<CapturedTurn>>;
188 readonly shouldComplete: (turn: ProviderAdapterV2TurnInput) => boolean;
189 readonly terminalGate?: (turn: ProviderAdapterV2TurnInput) => Deferred.Deferred<void> | undefined;
190 readonly response: (turn: ProviderAdapterV2TurnInput) => string;
191}): ProviderAdapterV2Shape {
183 readonly shouldComplete: (turn: ProviderAdapter.ProviderAdapterV2TurnInput) => boolean;
184 readonly terminalGate?: (
185 turn: ProviderAdapter.ProviderAdapterV2TurnInput,
186 ) => Deferred.Deferred<void> | undefined;
187 readonly response: (turn: ProviderAdapter.ProviderAdapterV2TurnInput) => string;
188}): ProviderAdapter.ProviderAdapterV2["Service"] {
192189 return {
193190 instanceId: input.instanceId,
194191 driver: input.driver,
......@@ -196,7 +193,7 @@ function makeDeterministicAdapter(input: {
196193 planSelectionTransition: () => Effect.succeed({ type: "apply_on_next_turn" }),
197194 openSession: (sessionInput) =>
198195 Effect.gen(function* () {
199 const events = yield* PubSub.unbounded<ProviderAdapterV2Event>();
196 const events = yield* PubSub.unbounded<ProviderAdapter.ProviderAdapterV2Event>();
200197 const now = yield* DateTime.now;
201198 const providerSession: OrchestrationV2ProviderSession = {
202199 id: sessionInput.providerSessionId,
......@@ -211,12 +208,12 @@ function makeDeterministicAdapter(input: {
211208 lastError: null,
212209 };
213210
214 const publish = (providerEvents: ReadonlyArray<ProviderAdapterV2Event>) =>
211 const publish = (providerEvents: ReadonlyArray<ProviderAdapter.ProviderAdapterV2Event>) =>
215212 Effect.forEach(providerEvents, (event) => PubSub.publish(events, event), {
216213 discard: true,
217214 });
218215 const runOrdinals = new Map<ProviderTurnId, number>();
219 const turnInputs = new Map<ProviderTurnId, ProviderAdapterV2TurnInput>();
216 const turnInputs = new Map<ProviderTurnId, ProviderAdapter.ProviderAdapterV2TurnInput>();
220217
221218 return {
222219 instanceId: input.instanceId,
apps/server/src/mcpApps/McpAppRequests.test.ts+6-7
......@@ -14,10 +14,7 @@ import * as Layer from "effect/Layer";
1414import * as Option from "effect/Option";
1515
1616import * as Orchestrator from "../orchestration-v2/Orchestrator.ts";
17import type {
18 ProviderAdapterV2McpApps,
19 ProviderAdapterV2SessionRuntime,
20} from "@t3tools/provider-core/server/ProviderAdapter";
17import type * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter";
2118import * as ProviderSessionManager from "../orchestration-v2/ProviderSessionManager.ts";
2219import * as ThreadManagementService from "../orchestration-v2/ThreadManagementService.ts";
2320import * as McpAppModelContext from "./McpAppModelContext.ts";
......@@ -71,7 +68,7 @@ const storedContext = new Map<string, string>();
7168
7269function makeLayer(input: {
7370 readonly item: OrchestrationV2TurnItem | null;
74 readonly mcpApps?: ProviderAdapterV2McpApps;
71 readonly mcpApps?: ProviderAdapter.ProviderAdapterV2McpApps;
7572 readonly live?: boolean;
7673}) {
7774 return McpAppRequests.layer.pipe(
......@@ -121,7 +118,9 @@ function makeLayer(input: {
121118 : Option.some(
122119 (input.mcpApps === undefined
123120 ? {}
124 : { mcpApps: input.mcpApps }) as ProviderAdapterV2SessionRuntime,
121 : {
122 mcpApps: input.mcpApps,
123 }) as ProviderAdapter.ProviderAdapterV2SessionRuntime,
125124 ),
126125 ),
127126 }),
......@@ -138,7 +137,7 @@ const reason = <A, R>(effect: Effect.Effect<A, McpAppRequestError, R>) =>
138137
139138describe("McpAppRequests", () => {
140139 const calls: Array<{ server: string; tool: string }> = [];
141 const mcpApps: ProviderAdapterV2McpApps = {
140 const mcpApps: ProviderAdapter.ProviderAdapterV2McpApps = {
142141 listTools: () =>
143142 Effect.succeed([
144143 { name: "refresh", annotations: { readOnlyHint: true } },
apps/server/src/mcpApps/McpAppRequests.ts+4-7
......@@ -21,10 +21,7 @@ import * as Predicate from "effect/Predicate";
2121import * as Schema from "effect/Schema";
2222
2323import * as Orchestrator from "../orchestration-v2/Orchestrator.ts";
24import type {
25 ProviderAdapterV2McpApps,
26 ProviderAdapterV2McpTool,
27} from "@t3tools/provider-core/server/ProviderAdapter";
24import type * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter";
2825import * as ProviderSessionManager from "../orchestration-v2/ProviderSessionManager.ts";
2926import * as ThreadManagementService from "../orchestration-v2/ThreadManagementService.ts";
3027import * as McpAppModelContext from "./McpAppModelContext.ts";
......@@ -57,10 +54,10 @@ export class McpAppRequests extends Context.Service<
5754const encodeJson = Schema.encodeSync(Schema.fromJsonString(Schema.Unknown));
5855const utf8 = new TextEncoder();
5956
60const readOnlyHint = (tool: ProviderAdapterV2McpTool) =>
57const readOnlyHint = (tool: ProviderAdapter.ProviderAdapterV2McpTool) =>
6158 Predicate.isObject(tool.annotations) && tool.annotations.readOnlyHint === true;
6259
63const toolTitle = (tool: ProviderAdapterV2McpTool) =>
60const toolTitle = (tool: ProviderAdapter.ProviderAdapterV2McpTool) =>
6461 Predicate.isObject(tool.annotations) && typeof tool.annotations.title === "string"
6562 ? tool.annotations.title
6663 : undefined;
......@@ -112,7 +109,7 @@ const make = Effect.gen(function* () {
112109 .pipe(Effect.mapError((cause) => fail(input.threadId, "request-failed", cause))),
113110 );
114111 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;
116113 if (mcpApps === undefined) return yield* fail(input.threadId, "provider-unsupported");
117114 return { app, providerThread, mcpApps };
118115 });
apps/server/src/orchestration-v2/AcpRegistryOrchestratorV2.live.test.ts+1-1
......@@ -29,7 +29,7 @@ import * as CodexInstallation from "../provider/CodexInstallation.ts";
2929import * as ServerEnvironment from "../environment/ServerEnvironment.ts";
3030import * as ModelManifest from "../provider/ModelManifest.ts";
3131import * as ProviderInstanceRegistryHydration from "../provider/ProviderInstanceRegistryHydration.ts";
32import * as ProviderEventLoggers from "../provider/ProviderEventLoggers.ts";
32import * as ProviderEventLoggers from "@t3tools/provider-core/server/ProviderEventLoggers";
3333import * as OpenCodeRuntime from "@t3tools/provider-opencode/server/OpenCodeRuntime";
3434import * as OpenCodeServerLedger from "@t3tools/provider-opencode/server/OpenCodeServerLedger";
3535import * as ServerSettings from "../serverSettings.ts";
apps/server/src/orchestration-v2/Adapters/AcpAdapterV2.test.ts+189-171
......@@ -64,13 +64,8 @@ import {
6464 registerXAiBackgroundTaskTracking,
6565} from "@t3tools/provider-grok/testing";
6666import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator";
67import {
68 ProviderAdapterProtocolError,
69 ProviderAdapterV2RuntimePolicy,
70 type ProviderAdapterV2Event,
71 type ProviderAdapterV2TurnInput,
72} from "@t3tools/provider-core/server/ProviderAdapter";
73import type { ProviderContinuationRequest } from "@t3tools/provider-core/server/continuationRequests";
67import * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter";
68import type * as ProviderContinuationRequests from "@t3tools/provider-core/server/ProviderContinuationRequests";
7469import {
7570 AcpProviderCapabilitiesV2,
7671 acpProviderItemNativeId,
......@@ -540,7 +535,7 @@ function makeTurnInput(input: {
540535 readonly threadId: ThreadId;
541536 readonly providerThread: OrchestrationV2ProviderThread;
542537 readonly instanceId: ProviderInstanceId;
543 readonly runtimePolicy: ProviderAdapterV2RuntimePolicy;
538 readonly runtimePolicy: ProviderAdapter.ProviderAdapterV2RuntimePolicy;
544539 readonly now: DateTime.Utc;
545540 readonly ordinal?: number;
546541 readonly modelSelection?: ModelSelection;
......@@ -548,7 +543,7 @@ function makeTurnInput(input: {
548543 readonly messageCreatedBy?: "user" | "agent";
549544 readonly messageCreationSource?: "web" | "mobile" | "mcp" | "provider" | "server";
550545 readonly messageText?: string;
551}): ProviderAdapterV2TurnInput {
546}): ProviderAdapter.ProviderAdapterV2TurnInput {
552547 const ordinal = input.ordinal ?? 1;
553548 const suffix = `${input.threadId}:${ordinal}`;
554549 const modelSelection =
......@@ -625,7 +620,7 @@ describe("AcpAdapterV2", () => {
625620 }),
626621 },
627622 });
628 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({
623 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
629624 runtimeMode: "full-access",
630625 interactionMode: "default",
631626 cwd: process.cwd(),
......@@ -742,7 +737,7 @@ describe("AcpAdapterV2", () => {
742737 },
743738 selfInvocation,
744739 });
745 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({
740 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
746741 runtimeMode: "full-access",
747742 interactionMode: "default",
748743 cwd: process.cwd(),
......@@ -799,7 +794,7 @@ describe("AcpAdapterV2", () => {
799794 selfInvocation,
800795 });
801796 const policy = (interactionMode: "default" | "plan") =>
802 ProviderAdapterV2RuntimePolicy.make({
797 ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
803798 runtimeMode: "full-access",
804799 interactionMode,
805800 cwd: process.cwd(),
......@@ -820,7 +815,7 @@ describe("AcpAdapterV2", () => {
820815 const now = yield* DateTime.now;
821816 const runTurn = Effect.fnUntraced(function* (
822817 ordinal: number,
823 runtimePolicy: ProviderAdapterV2RuntimePolicy,
818 runtimePolicy: ProviderAdapter.ProviderAdapterV2RuntimePolicy,
824819 messageText: string,
825820 ) {
826821 yield* runtime.startTurn(
......@@ -917,7 +912,7 @@ describe("AcpAdapterV2", () => {
917912 selfInvocation,
918913 });
919914 const threadId = ThreadId.make("thread-acp-v2-plan-replay-boundary");
920 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({
915 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
921916 runtimeMode: "full-access",
922917 interactionMode: "default",
923918 cwd: process.cwd(),
......@@ -1091,7 +1086,7 @@ describe("AcpAdapterV2", () => {
10911086 });
10921087 const threadId = ThreadId.make("devin-streamed-write");
10931088 const modelSelection = { instanceId, model: "default" };
1094 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({
1089 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
10951090 runtimeMode: "full-access",
10961091 interactionMode: "default",
10971092 cwd: process.cwd(),
......@@ -1342,7 +1337,7 @@ describe("AcpAdapterV2", () => {
13421337 });
13431338 const threadId = ThreadId.make("devin-replay-parent");
13441339 const modelSelection = { instanceId, model: "default" };
1345 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({
1340 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
13461341 runtimeMode: "full-access",
13471342 interactionMode: "default",
13481343 cwd: process.cwd(),
......@@ -1504,7 +1499,7 @@ describe("AcpAdapterV2", () => {
15041499 selfInvocation,
15051500 });
15061501 const threadId = ThreadId.make("thread-acp-v2-fidelity");
1507 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({
1502 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
15081503 runtimeMode: "full-access",
15091504 interactionMode: "default",
15101505 cwd: process.cwd(),
......@@ -1684,7 +1679,7 @@ describe("AcpAdapterV2", () => {
16841679 selfInvocation,
16851680 });
16861681 const threadId = ThreadId.make("thread-acp-eager-resume");
1687 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({
1682 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
16881683 runtimeMode: "full-access",
16891684 interactionMode: "default",
16901685 cwd: process.cwd(),
......@@ -1765,7 +1760,7 @@ describe("AcpAdapterV2", () => {
17651760 selfInvocation,
17661761 });
17671762 const threadId = ThreadId.make("thread-acp-stale-eager-resume");
1768 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({
1763 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
17691764 runtimeMode: "full-access",
17701765 interactionMode: "default",
17711766 cwd: process.cwd(),
......@@ -1910,7 +1905,7 @@ describe("AcpAdapterV2", () => {
19101905 selfInvocation,
19111906 });
19121907 const threadId = ThreadId.make("thread-acp-unexpected-termination");
1913 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({
1908 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
19141909 runtimeMode: "full-access",
19151910 interactionMode: "default",
19161911 cwd: process.cwd(),
......@@ -1993,7 +1988,7 @@ describe("AcpAdapterV2", () => {
19931988 selfInvocation,
19941989 });
19951990 const threadId = ThreadId.make("thread-acp-provider-exit-running-command");
1996 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({
1991 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
19971992 runtimeMode: "full-access",
19981993 interactionMode: "default",
19991994 cwd: process.cwd(),
......@@ -2065,7 +2060,7 @@ describe("AcpAdapterV2", () => {
20652060 selfInvocation,
20662061 });
20672062 const threadId = ThreadId.make("thread-acp-cgroup-unavailable");
2068 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({
2063 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
20692064 runtimeMode: "full-access",
20702065 interactionMode: "default",
20712066 cwd: process.cwd(),
......@@ -2130,7 +2125,7 @@ describe("AcpAdapterV2", () => {
21302125 selfInvocation,
21312126 });
21322127 const threadId = ThreadId.make("thread-acp-cgroup-join-failure");
2133 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({
2128 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
21342129 runtimeMode: "full-access",
21352130 interactionMode: "default",
21362131 cwd: process.cwd(),
......@@ -2226,7 +2221,7 @@ describe("AcpAdapterV2", () => {
22262221 McpProviderSession.clearMcpProviderSession(targetThreadId);
22272222 }),
22282223 );
2229 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({
2224 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
22302225 runtimeMode: "full-access",
22312226 interactionMode: "default",
22322227 cwd: process.cwd(),
......@@ -2392,7 +2387,7 @@ describe("AcpAdapterV2", () => {
23922387 selfInvocation,
23932388 });
23942389 const threadId = ThreadId.make("thread-acp-no-client-fs");
2395 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({
2390 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
23962391 runtimeMode: "full-access",
23972392 interactionMode: "default",
23982393 cwd: workspace,
......@@ -2493,7 +2488,7 @@ describe("AcpAdapterV2", () => {
24932488 clientTerminals: { childProcessSpawner },
24942489 });
24952490 const threadId = ThreadId.make("thread-acp-unknown-permission-grant");
2496 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({
2491 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
24972492 runtimeMode: "approval-required",
24982493 interactionMode: "default",
24992494 cwd: process.cwd(),
......@@ -2598,7 +2593,7 @@ describe("AcpAdapterV2", () => {
25982593 selfInvocation,
25992594 });
26002595 const threadId = ThreadId.make("thread-acp-missing-native-thread");
2601 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({
2596 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
26022597 runtimeMode: "full-access",
26032598 interactionMode: "default",
26042599 cwd: process.cwd(),
......@@ -2629,7 +2624,7 @@ describe("AcpAdapterV2", () => {
26292624 .pipe(Effect.flip);
26302625
26312626 assert.equal(error._tag, "ProviderAdapterTurnStartError");
2632 assert.instanceOf(error.cause, ProviderAdapterProtocolError);
2627 assert.instanceOf(error.cause, ProviderAdapter.ProviderAdapterProtocolError);
26332628 assert.include(String(error.cause), "missing its ACP session id");
26342629 }).pipe(Effect.provide(layerTest), Effect.scoped),
26352630 );
......@@ -2676,7 +2671,7 @@ describe("AcpAdapterV2", () => {
26762671 selfInvocation,
26772672 });
26782673 const threadId = ThreadId.make("thread-acp-rollback-session");
2679 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({
2674 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
26802675 runtimeMode: "full-access",
26812676 interactionMode: "default",
26822677 cwd: process.cwd(),
......@@ -2689,7 +2684,7 @@ describe("AcpAdapterV2", () => {
26892684 runtimePolicy,
26902685 });
26912686 assert.isTrue(runtime.providerSession.capabilities.threads.canRollbackThread);
2692 const events = yield* Queue.unbounded<ProviderAdapterV2Event>();
2687 const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>();
26932688 yield* runtime.events.pipe(
26942689 Stream.runForEach((event) => Queue.offer(events, event)),
26952690 Effect.forkScoped,
......@@ -2883,7 +2878,7 @@ describe("AcpAdapterV2", () => {
28832878 continuationRequests: { offer: () => Effect.void },
28842879 });
28852880 const threadId = ThreadId.make("thread-acp-rollback-retry-generation");
2886 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({
2881 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
28872882 runtimeMode: "full-access",
28882883 interactionMode: "default",
28892884 cwd: process.cwd(),
......@@ -2899,7 +2894,7 @@ describe("AcpAdapterV2", () => {
28992894 return yield* Effect.die("ACP runtime must expose background work state");
29002895 }
29012896 const hasPendingBackgroundWork = runtime.hasPendingBackgroundWork;
2902 const events = yield* Queue.unbounded<ProviderAdapterV2Event>();
2897 const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>();
29032898 yield* runtime.events.pipe(
29042899 Stream.runForEach((event) => Queue.offer(events, event)),
29052900 Effect.forkScoped,
......@@ -2995,7 +2990,7 @@ describe("AcpAdapterV2", () => {
29952990 selfInvocation,
29962991 });
29972992 const threadId = ThreadId.make("thread-acp-rollback-failure-compensation");
2998 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({
2993 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
29992994 runtimeMode: "full-access",
30002995 interactionMode: "default",
30012996 cwd: process.cwd(),
......@@ -3009,7 +3004,7 @@ describe("AcpAdapterV2", () => {
30093004 modelSelection,
30103005 runtimePolicy,
30113006 });
3012 const events = yield* Queue.unbounded<ProviderAdapterV2Event>();
3007 const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>();
30133008 yield* runtime.events.pipe(
30143009 Stream.runForEach((event) => Queue.offer(events, event)),
30153010 Effect.forkScoped,
......@@ -3079,7 +3074,7 @@ describe("AcpAdapterV2", () => {
30793074 selfInvocation,
30803075 });
30813076 const threadId = ThreadId.make("thread-acp-idle-finalizer");
3082 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({
3077 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
30833078 runtimeMode: "full-access",
30843079 interactionMode: "default",
30853080 cwd: process.cwd(),
......@@ -3129,7 +3124,7 @@ describe("AcpAdapterV2", () => {
31293124 threadId: ThreadId.make(`grok-model-${model}`),
31303125 providerSessionId: ProviderSessionId.make(`grok-model-${model}`),
31313126 modelSelection: { instanceId, model },
3132 runtimePolicy: ProviderAdapterV2RuntimePolicy.make({
3127 runtimePolicy: ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
31333128 runtimeMode: "full-access",
31343129 interactionMode: "default",
31353130 cwd: process.cwd(),
......@@ -3173,7 +3168,7 @@ describe("AcpAdapterV2", () => {
31733168 ),
31743169 });
31753170 const threadId = ThreadId.make("grok-model-switch-back");
3176 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({
3171 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
31773172 runtimeMode: "full-access",
31783173 interactionMode: "default",
31793174 cwd: process.cwd(),
......@@ -3242,7 +3237,7 @@ describe("AcpAdapterV2", () => {
32423237 selfInvocation,
32433238 });
32443239 const threadId = ThreadId.make("thread-acp-unsupported-option");
3245 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({
3240 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
32463241 runtimeMode: "full-access",
32473242 interactionMode: "default",
32483243 cwd: process.cwd(),
......@@ -3286,7 +3281,7 @@ describe("AcpAdapterV2", () => {
32863281 });
32873282 const firstThreadId = ThreadId.make("thread-acp-active-setup:first");
32883283 const secondThreadId = ThreadId.make("thread-acp-active-setup:second");
3289 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({
3284 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
32903285 runtimeMode: "full-access",
32913286 interactionMode: "default",
32923287 cwd: process.cwd(),
......@@ -3400,7 +3395,7 @@ describe("AcpAdapterV2", () => {
34003395 selfInvocation,
34013396 });
34023397 const threadId = ThreadId.make("thread-acp-empty-successful-bash");
3403 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({
3398 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
34043399 runtimeMode: "full-access",
34053400 interactionMode: "default",
34063401 cwd: process.cwd(),
......@@ -3412,7 +3407,7 @@ describe("AcpAdapterV2", () => {
34123407 modelSelection,
34133408 runtimePolicy,
34143409 });
3415 const events = yield* Queue.unbounded<ProviderAdapterV2Event>();
3410 const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>();
34163411 yield* runtime.events.pipe(
34173412 Stream.runForEach((event) => Queue.offer(events, event)),
34183413 Effect.forkScoped,
......@@ -3540,7 +3535,7 @@ describe("AcpAdapterV2", () => {
35403535 selfInvocation,
35413536 });
35423537 const threadId = ThreadId.make("thread-acp-native-cancel");
3543 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({
3538 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
35443539 runtimeMode: "full-access",
35453540 interactionMode: "default",
35463541 cwd: process.cwd(),
......@@ -3642,7 +3637,7 @@ describe("AcpAdapterV2", () => {
36423637 selfInvocation,
36433638 });
36443639 const threadId = ThreadId.make("thread-acp-cancel-permission");
3645 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({
3640 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
36463641 runtimeMode: "approval-required",
36473642 interactionMode: "default",
36483643 cwd: process.cwd(),
......@@ -3741,7 +3736,7 @@ describe("AcpAdapterV2", () => {
37413736 selfInvocation,
37423737 });
37433738 const threadId = ThreadId.make("thread-acp-response-wins-permission");
3744 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({
3739 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
37453740 runtimeMode: "approval-required",
37463741 interactionMode: "default",
37473742 cwd: process.cwd(),
......@@ -3833,7 +3828,7 @@ describe("AcpAdapterV2", () => {
38333828 selfInvocation,
38343829 });
38353830 const threadId = ThreadId.make("thread-acp-reordered-elicitation");
3836 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({
3831 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
38373832 runtimeMode: "approval-required",
38383833 interactionMode: "default",
38393834 cwd: process.cwd(),
......@@ -3908,7 +3903,7 @@ describe("AcpAdapterV2", () => {
39083903 selfInvocation,
39093904 });
39103905 const threadId = ThreadId.make("thread-acp-mcp-approval-elicitation");
3911 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({
3906 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
39123907 runtimeMode: "full-access",
39133908 interactionMode: "default",
39143909 cwd: process.cwd(),
......@@ -3983,7 +3978,7 @@ describe("AcpAdapterV2", () => {
39833978 },
39843979 });
39853980 const threadId = ThreadId.make("thread-acp-normal-close-held-response");
3986 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({
3981 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
39873982 runtimeMode: "approval-required",
39883983 interactionMode: "default",
39893984 cwd: process.cwd(),
......@@ -4089,7 +4084,7 @@ describe("AcpAdapterV2", () => {
40894084 },
40904085 });
40914086 const threadId = ThreadId.make("thread-acp-close-wins-registration");
4092 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({
4087 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
40934088 runtimeMode: "approval-required",
40944089 interactionMode: "default",
40954090 cwd: process.cwd(),
......@@ -4208,7 +4203,7 @@ describe("AcpAdapterV2", () => {
42084203 },
42094204 });
42104205 const threadId = ThreadId.make("thread-acp-pending-response-timeout");
4211 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({
4206 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
42124207 runtimeMode: "approval-required",
42134208 interactionMode: "default",
42144209 cwd: process.cwd(),
......@@ -4333,7 +4328,7 @@ describe("AcpAdapterV2", () => {
43334328 selfInvocation,
43344329 });
43354330 const threadId = ThreadId.make("thread-acp-pending-response-cancel");
4336 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({
4331 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
43374332 runtimeMode: "approval-required",
43384333 interactionMode: "default",
43394334 cwd: process.cwd(),
......@@ -4442,7 +4437,7 @@ describe("AcpAdapterV2", () => {
44424437 selfInvocation,
44434438 });
44444439 const threadId = ThreadId.make(`thread-acp-immediate-permission-${name}`);
4445 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({
4440 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
44464441 runtimeMode: "full-access",
44474442 interactionMode: "default",
44484443 approvalPolicy: "never",
......@@ -4535,7 +4530,7 @@ describe("AcpAdapterV2", () => {
45354530 selfInvocation,
45364531 });
45374532 const threadId = ThreadId.make("thread-acp-immediate-url-elicitation");
4538 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({
4533 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
45394534 runtimeMode: "full-access",
45404535 interactionMode: "default",
45414536 cwd: process.cwd(),
......@@ -4626,7 +4621,7 @@ describe("AcpAdapterV2", () => {
46264621 selfInvocation,
46274622 });
46284623 const threadId = ThreadId.make("thread-acp-missing-response-ack");
4629 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({
4624 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
46304625 runtimeMode: "full-access",
46314626 interactionMode: "default",
46324627 approvalPolicy: "never",
......@@ -4708,7 +4703,7 @@ describe("AcpAdapterV2", () => {
47084703 selfInvocation,
47094704 });
47104705 const threadId = ThreadId.make("thread-acp-teardown-wins-elicitation");
4711 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({
4706 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
47124707 runtimeMode: "approval-required",
47134708 interactionMode: "default",
47144709 cwd: process.cwd(),
......@@ -4802,7 +4797,7 @@ describe("AcpAdapterV2", () => {
48024797 selfInvocation,
48034798 });
48044799 const threadId = ThreadId.make("thread-acp-cancel-timeout");
4805 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({
4800 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
48064801 runtimeMode: "full-access",
48074802 interactionMode: "default",
48084803 cwd: process.cwd(),
......@@ -4903,7 +4898,7 @@ describe("AcpAdapterV2", () => {
49034898 selfInvocation,
49044899 });
49054900 const threadId = ThreadId.make("thread-acp-double-stop");
4906 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({
4901 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
49074902 runtimeMode: "full-access",
49084903 interactionMode: "default",
49094904 cwd: process.cwd(),
......@@ -4915,7 +4910,7 @@ describe("AcpAdapterV2", () => {
49154910 modelSelection,
49164911 runtimePolicy,
49174912 });
4918 const events = yield* Queue.unbounded<ProviderAdapterV2Event>();
4913 const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>();
49194914 yield* runtime.events.pipe(
49204915 Stream.runForEach((event) => Queue.offer(events, event)),
49214916 Effect.forkScoped,
......@@ -5007,7 +5002,7 @@ describe("AcpAdapterV2", () => {
50075002 selfInvocation,
50085003 });
50095004 const threadId = ThreadId.make("thread-acp-interrupt-background-hold");
5010 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({
5005 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
50115006 runtimeMode: "full-access",
50125007 interactionMode: "default",
50135008 cwd: process.cwd(),
......@@ -5019,7 +5014,7 @@ describe("AcpAdapterV2", () => {
50195014 modelSelection,
50205015 runtimePolicy,
50215016 });
5022 const events = yield* Queue.unbounded<ProviderAdapterV2Event>();
5017 const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>();
50235018 yield* runtime.events.pipe(
50245019 Stream.runForEach((event) => Queue.offer(events, event)),
50255020 Effect.forkScoped,
......@@ -5111,7 +5106,7 @@ describe("AcpAdapterV2", () => {
51115106 selfInvocation,
51125107 });
51135108 const threadId = ThreadId.make("thread-acp-subagent-carryover");
5114 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({
5109 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
51155110 runtimeMode: "full-access",
51165111 interactionMode: "default",
51175112 cwd: process.cwd(),
......@@ -5123,7 +5118,7 @@ describe("AcpAdapterV2", () => {
51235118 modelSelection,
51245119 runtimePolicy,
51255120 });
5126 const events = yield* Queue.unbounded<ProviderAdapterV2Event>();
5121 const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>();
51275122 yield* runtime.events.pipe(
51285123 Stream.runForEach((event) => Queue.offer(events, event)),
51295124 Effect.forkScoped,
......@@ -5252,7 +5247,7 @@ describe("AcpAdapterV2", () => {
52525247 continuationRequests: { offer: () => Effect.void },
52535248 });
52545249 const threadId = ThreadId.make("thread-acp-carryover-pending-pin");
5255 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({
5250 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
52565251 runtimeMode: "full-access",
52575252 interactionMode: "default",
52585253 cwd: process.cwd(),
......@@ -5270,7 +5265,7 @@ describe("AcpAdapterV2", () => {
52705265 );
52715266 }
52725267 const hasPendingBackgroundWork = runtime.hasPendingBackgroundWork;
5273 const events = yield* Queue.unbounded<ProviderAdapterV2Event>();
5268 const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>();
52745269 yield* runtime.events.pipe(
52755270 Stream.runForEach((event) => Queue.offer(events, event)),
52765271 Effect.forkScoped,
......@@ -5416,7 +5411,7 @@ describe("AcpAdapterV2", () => {
54165411 continuationRequests: { offer: () => Effect.void },
54175412 });
54185413 const threadId = ThreadId.make("thread-acp-active-carryover-pending-pin");
5419 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({
5414 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
54205415 runtimeMode: "full-access",
54215416 interactionMode: "default",
54225417 cwd: process.cwd(),
......@@ -5436,7 +5431,7 @@ describe("AcpAdapterV2", () => {
54365431 );
54375432 }
54385433 const hasPendingBackgroundWork = runtime.hasPendingBackgroundWork;
5439 const events = yield* Queue.unbounded<ProviderAdapterV2Event>();
5434 const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>();
54405435 yield* runtime.events.pipe(
54415436 Stream.runForEach((event) => Queue.offer(events, event)),
54425437 Effect.forkScoped,
......@@ -5661,7 +5656,7 @@ describe("AcpAdapterV2", () => {
56615656 continuationRequests: { offer: () => Effect.void },
56625657 });
56635658 const threadId = ThreadId.make("thread-acp-subagent-finalize-window");
5664 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({
5659 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
56655660 runtimeMode: "full-access",
56665661 interactionMode: "default",
56675662 cwd: process.cwd(),
......@@ -5681,7 +5676,7 @@ describe("AcpAdapterV2", () => {
56815676 );
56825677 }
56835678 const hasPendingBackgroundWork = runtime.hasPendingBackgroundWork;
5684 const events = yield* Queue.unbounded<ProviderAdapterV2Event>();
5679 const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>();
56855680 yield* runtime.events.pipe(
56865681 Stream.runForEach((event) => Queue.offer(events, event)),
56875682 Effect.forkScoped,
......@@ -5796,7 +5791,8 @@ describe("AcpAdapterV2", () => {
57965791 new URL("../../../scripts/acp-mock-agent.ts", import.meta.url),
57975792 );
57985793 const protocolEvents = yield* Queue.bounded<EffectAcpProtocol.AcpProtocolLogEvent>(256);
5799 const continuationRequests: Array<ProviderContinuationRequest> = [];
5794 const continuationRequests: Array<ProviderContinuationRequests.ProviderContinuationRequest> =
5795 [];
58005796 const promptSettled = yield* Deferred.make<void>();
58015797 const instanceId = ProviderInstanceId.make("acp-test");
58025798 const childSessionId = "mock-child-session-post-settle";
......@@ -5859,7 +5855,7 @@ describe("AcpAdapterV2", () => {
58595855 },
58605856 });
58615857 const threadId = ThreadId.make("thread-acp-carryover-child-session-post-settle");
5862 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({
5858 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
58635859 runtimeMode: "full-access",
58645860 interactionMode: "default",
58655861 cwd: process.cwd(),
......@@ -5879,7 +5875,7 @@ describe("AcpAdapterV2", () => {
58795875 );
58805876 }
58815877 const hasPendingBackgroundWork = runtime.hasPendingBackgroundWork;
5882 const events = yield* Queue.unbounded<ProviderAdapterV2Event>();
5878 const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>();
58835879 yield* runtime.events.pipe(
58845880 Stream.runForEach((event) => Queue.offer(events, event)),
58855881 Effect.forkScoped,
......@@ -6046,7 +6042,7 @@ describe("AcpAdapterV2", () => {
60466042 continuationRequests: { offer: () => Effect.void },
60476043 });
60486044 const threadId = ThreadId.make("thread-acp-carryover-subagent-finished");
6049 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({
6045 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
60506046 runtimeMode: "full-access",
60516047 interactionMode: "default",
60526048 cwd: process.cwd(),
......@@ -6062,7 +6058,7 @@ describe("AcpAdapterV2", () => {
60626058 return yield* Effect.die("post-settle continuation must expose hasPendingBackgroundWork");
60636059 }
60646060 const hasPendingBackgroundWork = runtime.hasPendingBackgroundWork;
6065 const events = yield* Queue.unbounded<ProviderAdapterV2Event>();
6061 const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>();
60666062 yield* runtime.events.pipe(
60676063 Stream.runForEach((event) => Queue.offer(events, event)),
60686064 Effect.forkScoped,
......@@ -6151,7 +6147,8 @@ describe("AcpAdapterV2", () => {
61516147 new URL("../../../scripts/acp-mock-agent.ts", import.meta.url),
61526148 );
61536149 const protocolEvents = yield* Queue.bounded<EffectAcpProtocol.AcpProtocolLogEvent>(256);
6154 const continuationRequests: Array<ProviderContinuationRequest> = [];
6150 const continuationRequests: Array<ProviderContinuationRequests.ProviderContinuationRequest> =
6151 [];
61556152 const instanceId = ProviderInstanceId.make("acp-test");
61566153 const childSessionId = "019f44a6-4820-7402-925d-bc862ee711dd";
61576154 let subagentPhase: "spawn" | "complete" = "spawn";
......@@ -6210,7 +6207,7 @@ describe("AcpAdapterV2", () => {
62106207 },
62116208 });
62126209 const threadId = ThreadId.make("thread-acp-completed-root-eager-carryover");
6213 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({
6210 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
62146211 runtimeMode: "full-access",
62156212 interactionMode: "default",
62166213 cwd: process.cwd(),
......@@ -6230,7 +6227,7 @@ describe("AcpAdapterV2", () => {
62306227 );
62316228 }
62326229 const hasPendingBackgroundWork = runtime.hasPendingBackgroundWork;
6233 const events = yield* Queue.unbounded<ProviderAdapterV2Event>();
6230 const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>();
62346231 yield* runtime.events.pipe(
62356232 Stream.runForEach((event) => Queue.offer(events, event)),
62366233 Effect.forkScoped,
......@@ -6396,7 +6393,8 @@ describe("AcpAdapterV2", () => {
63966393 const mockAgentPath = yield* path.fromFileUrl(
63976394 new URL("../../../scripts/acp-mock-agent.ts", import.meta.url),
63986395 );
6399 const continuationRequests: Array<ProviderContinuationRequest> = [];
6396 const continuationRequests: Array<ProviderContinuationRequests.ProviderContinuationRequest> =
6397 [];
64006398 const bufferedAssistantText = "BUFFERED_WAKE_AFTER_USER_ATTACH";
64016399 const instanceId = ProviderInstanceId.make("acp-test");
64026400 let subagentPhase: "spawn" | "complete" = "spawn";
......@@ -6458,7 +6456,7 @@ describe("AcpAdapterV2", () => {
64586456 },
64596457 });
64606458 const threadId = ThreadId.make("thread-acp-carryover-root-session-continuation");
6461 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({
6459 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
64626460 runtimeMode: "full-access",
64636461 interactionMode: "default",
64646462 cwd: process.cwd(),
......@@ -6478,7 +6476,7 @@ describe("AcpAdapterV2", () => {
64786476 );
64796477 }
64806478 const hasPendingBackgroundWork = runtime.hasPendingBackgroundWork;
6481 const events = yield* Queue.unbounded<ProviderAdapterV2Event>();
6479 const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>();
64826480 yield* runtime.events.pipe(
64836481 Stream.runForEach((event) => Queue.offer(events, event)),
64846482 Effect.forkScoped,
......@@ -6678,7 +6676,8 @@ describe("AcpAdapterV2", () => {
66786676 const mockAgentPath = yield* path.fromFileUrl(
66796677 new URL("../../../scripts/acp-mock-agent.ts", import.meta.url),
66806678 );
6681 const continuationRequests: Array<ProviderContinuationRequest> = [];
6679 const continuationRequests: Array<ProviderContinuationRequests.ProviderContinuationRequest> =
6680 [];
66826681 const promptWireReturned = yield* Deferred.make<void>();
66836682 const releasePromptCompletion = yield* Deferred.make<void>();
66846683 const instanceId = ProviderInstanceId.make("acp-test");
......@@ -6750,7 +6749,7 @@ describe("AcpAdapterV2", () => {
67506749 },
67516750 });
67526751 const threadId = ThreadId.make("thread-acp-carryover-already-handled-re-report");
6753 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({
6752 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
67546753 runtimeMode: "full-access",
67556754 interactionMode: "default",
67566755 cwd: process.cwd(),
......@@ -6770,7 +6769,7 @@ describe("AcpAdapterV2", () => {
67706769 );
67716770 }
67726771 const hasPendingBackgroundWork = runtime.hasPendingBackgroundWork;
6773 const events = yield* Queue.unbounded<ProviderAdapterV2Event>();
6772 const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>();
67746773 yield* runtime.events.pipe(
67756774 Stream.runForEach((event) => Queue.offer(events, event)),
67766775 Effect.forkScoped,
......@@ -6914,7 +6913,8 @@ describe("AcpAdapterV2", () => {
69146913 new URL("../../../scripts/acp-mock-agent.ts", import.meta.url),
69156914 );
69166915 const protocolEvents = yield* Queue.bounded<EffectAcpProtocol.AcpProtocolLogEvent>(256);
6917 const continuationRequests: Array<ProviderContinuationRequest> = [];
6916 const continuationRequests: Array<ProviderContinuationRequests.ProviderContinuationRequest> =
6917 [];
69186918 const promptSettled = yield* Deferred.make<void>();
69196919 const instanceId = ProviderInstanceId.make("acp-test");
69206920 const childSessionId = "019f5470-bf92-7a90-afb3-5a6cea5b34a3";
......@@ -6988,7 +6988,7 @@ describe("AcpAdapterV2", () => {
69886988 },
69896989 });
69906990 const threadId = ThreadId.make("thread-acp-carryover-root-end-notice");
6991 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({
6991 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
69926992 runtimeMode: "full-access",
69936993 interactionMode: "default",
69946994 cwd: process.cwd(),
......@@ -7006,7 +7006,7 @@ describe("AcpAdapterV2", () => {
70067006 );
70077007 }
70087008 const hasPendingBackgroundWork = runtime.hasPendingBackgroundWork;
7009 const events = yield* Queue.unbounded<ProviderAdapterV2Event>();
7009 const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>();
70107010 yield* runtime.events.pipe(
70117011 Stream.runForEach((event) => Queue.offer(events, event)),
70127012 Effect.forkScoped,
......@@ -7136,7 +7136,8 @@ describe("AcpAdapterV2", () => {
71367136 const mockAgentPath = yield* path.fromFileUrl(
71377137 new URL("../../../scripts/acp-mock-agent.ts", import.meta.url),
71387138 );
7139 const continuationRequests: Array<ProviderContinuationRequest> = [];
7139 const continuationRequests: Array<ProviderContinuationRequests.ProviderContinuationRequest> =
7140 [];
71407141 const instanceId = ProviderInstanceId.make("acp-test");
71417142 const childSessionId = "mock-child-session-pending-continuation";
71427143 const bufferedAssistantText = "POST_SETTLE_BUFFERED_ASSISTANT_TEXT";
......@@ -7199,7 +7200,7 @@ describe("AcpAdapterV2", () => {
71997200 },
72007201 });
72017202 const threadId = ThreadId.make("thread-acp-carryover-child-pending-continuation");
7202 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({
7203 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
72037204 runtimeMode: "full-access",
72047205 interactionMode: "default",
72057206 cwd: process.cwd(),
......@@ -7219,7 +7220,7 @@ describe("AcpAdapterV2", () => {
72197220 );
72207221 }
72217222 const hasPendingBackgroundWork = runtime.hasPendingBackgroundWork;
7222 const events = yield* Queue.unbounded<ProviderAdapterV2Event>();
7223 const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>();
72237224 yield* runtime.events.pipe(
72247225 Stream.runForEach((event) => Queue.offer(events, event)),
72257226 Effect.forkScoped,
......@@ -7401,7 +7402,8 @@ describe("AcpAdapterV2", () => {
74017402 const mockAgentPath = yield* path.fromFileUrl(
74027403 new URL("../../../scripts/acp-mock-agent.ts", import.meta.url),
74037404 );
7404 const continuationRequests: Array<ProviderContinuationRequest> = [];
7405 const continuationRequests: Array<ProviderContinuationRequests.ProviderContinuationRequest> =
7406 [];
74057407 const instanceId = ProviderInstanceId.make("acp-test");
74067408 const childSessionId = "mock-child-session-root-then-child";
74077409 let subagentPhase: "spawn" | "complete" = "spawn";
......@@ -7463,7 +7465,7 @@ describe("AcpAdapterV2", () => {
74637465 },
74647466 });
74657467 const threadId = ThreadId.make("thread-acp-carryover-root-then-child");
7466 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({
7468 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
74677469 runtimeMode: "full-access",
74687470 interactionMode: "default",
74697471 cwd: process.cwd(),
......@@ -7483,7 +7485,7 @@ describe("AcpAdapterV2", () => {
74837485 );
74847486 }
74857487 const hasPendingBackgroundWork = runtime.hasPendingBackgroundWork;
7486 const events = yield* Queue.unbounded<ProviderAdapterV2Event>();
7488 const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>();
74877489 yield* runtime.events.pipe(
74887490 Stream.runForEach((event) => Queue.offer(events, event)),
74897491 Effect.forkScoped,
......@@ -7701,7 +7703,7 @@ describe("AcpAdapterV2", () => {
77017703 selfInvocation,
77027704 });
77037705 const threadId = ThreadId.make("thread-acp-settled-soft-steer");
7704 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({
7706 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
77057707 runtimeMode: "full-access",
77067708 interactionMode: "default",
77077709 cwd: process.cwd(),
......@@ -7713,7 +7715,7 @@ describe("AcpAdapterV2", () => {
77137715 modelSelection,
77147716 runtimePolicy,
77157717 });
7716 const events = yield* Queue.unbounded<ProviderAdapterV2Event>();
7718 const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>();
77177719 yield* runtime.events.pipe(
77187720 Stream.runForEach((event) => Queue.offer(events, event)),
77197721 Effect.forkScoped,
......@@ -7834,7 +7836,7 @@ describe("AcpAdapterV2", () => {
78347836 selfInvocation,
78357837 });
78367838 const threadId = ThreadId.make("thread-acp-unsettled-steer-stays-hard");
7837 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({
7839 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
78387840 runtimeMode: "full-access",
78397841 interactionMode: "default",
78407842 cwd: process.cwd(),
......@@ -7892,7 +7894,8 @@ describe("AcpAdapterV2", () => {
78927894 new URL("../../../scripts/acp-mock-agent.ts", import.meta.url),
78937895 );
78947896 const protocolEvents = yield* Queue.bounded<EffectAcpProtocol.AcpProtocolLogEvent>(256);
7895 const continuationRequests: Array<ProviderContinuationRequest> = [];
7897 const continuationRequests: Array<ProviderContinuationRequests.ProviderContinuationRequest> =
7898 [];
78967899 const instanceId = ProviderInstanceId.make("acp-test");
78977900 let cancelCalled = false;
78987901 let runtimeOrdinalSeen = 0;
......@@ -7940,7 +7943,7 @@ describe("AcpAdapterV2", () => {
79407943 },
79417944 });
79427945 const threadId = ThreadId.make("thread-acp-soft-mid-prompt-steer");
7943 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({
7946 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
79447947 runtimeMode: "full-access",
79457948 interactionMode: "default",
79467949 cwd: process.cwd(),
......@@ -7958,7 +7961,7 @@ describe("AcpAdapterV2", () => {
79587961 );
79597962 }
79607963 const hasPendingBackgroundWork = runtime.hasPendingBackgroundWork;
7961 const events = yield* Queue.unbounded<ProviderAdapterV2Event>();
7964 const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>();
79627965 yield* runtime.events.pipe(
79637966 Stream.runForEach((event) => Queue.offer(events, event)),
79647967 Effect.forkScoped,
......@@ -8115,7 +8118,7 @@ describe("AcpAdapterV2", () => {
81158118 continuationRequests: { offer: () => Effect.void },
81168119 });
81178120 const threadId = ThreadId.make("thread-acp-stop-quarantine-late-task");
8118 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({
8121 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
81198122 runtimeMode: "full-access",
81208123 interactionMode: "default",
81218124 cwd: process.cwd(),
......@@ -8133,7 +8136,7 @@ describe("AcpAdapterV2", () => {
81338136 );
81348137 }
81358138 const hasPendingBackgroundWork = runtime.hasPendingBackgroundWork;
8136 const events = yield* Queue.unbounded<ProviderAdapterV2Event>();
8139 const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>();
81378140 yield* runtime.events.pipe(
81388141 Stream.runForEach((event) => Queue.offer(events, event)),
81398142 Effect.forkScoped,
......@@ -8255,7 +8258,7 @@ describe("AcpAdapterV2", () => {
82558258 continuationRequests: { offer: () => Effect.void },
82568259 });
82578260 const threadId = ThreadId.make("thread-acp-production-stop-hard-kill");
8258 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({
8261 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
82598262 runtimeMode: "full-access",
82608263 interactionMode: "default",
82618264 cwd: process.cwd(),
......@@ -8267,7 +8270,7 @@ describe("AcpAdapterV2", () => {
82678270 modelSelection,
82688271 runtimePolicy,
82698272 });
8270 const events = yield* Queue.unbounded<ProviderAdapterV2Event>();
8273 const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>();
82718274 yield* runtime.events.pipe(
82728275 Stream.runForEach((event) => Queue.offer(events, event)),
82738276 Effect.forkScoped,
......@@ -8417,7 +8420,7 @@ describe("AcpAdapterV2", () => {
84178420 selfInvocation,
84188421 });
84198422 const threadId = ThreadId.make("thread-acp-stop-after-soft-steer-orphan");
8420 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({
8423 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
84218424 runtimeMode: "full-access",
84228425 interactionMode: "default",
84238426 cwd: process.cwd(),
......@@ -8431,7 +8434,7 @@ describe("AcpAdapterV2", () => {
84318434 modelSelection,
84328435 runtimePolicy,
84338436 });
8434 const events = yield* Queue.unbounded<ProviderAdapterV2Event>();
8437 const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>();
84358438 yield* runtime.events.pipe(
84368439 Stream.runForEach((event) => Queue.offer(events, event)),
84378440 Effect.forkScoped,
......@@ -8700,7 +8703,7 @@ describe("AcpAdapterV2", () => {
87008703 continuationRequests: { offer: () => Effect.void },
87018704 });
87028705 const threadId = ThreadId.make("thread-acp-direct-stop-deferred-terminal");
8703 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({
8706 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
87048707 runtimeMode: "full-access",
87058708 interactionMode: "default",
87068709 cwd: process.cwd(),
......@@ -8720,7 +8723,7 @@ describe("AcpAdapterV2", () => {
87208723 );
87218724 }
87228725 const hasPendingBackgroundWork = runtime.hasPendingBackgroundWork;
8723 const events = yield* Queue.unbounded<ProviderAdapterV2Event>();
8726 const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>();
87248727 yield* runtime.events.pipe(
87258728 Stream.runForEach((event) => Queue.offer(events, event)),
87268729 Effect.forkScoped,
......@@ -8904,7 +8907,7 @@ describe("AcpAdapterV2", () => {
89048907 selfInvocation,
89058908 });
89068909 const threadId = ThreadId.make("thread-acp-settled-soft-admission-race");
8907 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({
8910 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
89088911 runtimeMode: "full-access",
89098912 interactionMode: "default",
89108913 cwd: process.cwd(),
......@@ -8918,7 +8921,7 @@ describe("AcpAdapterV2", () => {
89188921 modelSelection,
89198922 runtimePolicy,
89208923 });
8921 const events = yield* Queue.unbounded<ProviderAdapterV2Event>();
8924 const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>();
89228925 yield* runtime.events.pipe(
89238926 Stream.runForEach((event) => Queue.offer(events, event)),
89248927 Effect.forkScoped,
......@@ -8999,7 +9002,8 @@ describe("AcpAdapterV2", () => {
89999002 new URL("../../../scripts/acp-mock-agent.ts", import.meta.url),
90009003 );
90019004 const protocolEvents = yield* Queue.bounded<EffectAcpProtocol.AcpProtocolLogEvent>(256);
9002 const continuationRequests: Array<ProviderContinuationRequest> = [];
9005 const continuationRequests: Array<ProviderContinuationRequests.ProviderContinuationRequest> =
9006 [];
90039007 const instanceId = ProviderInstanceId.make("acp-test");
90049008 const adapter = yield* makeAcpAdapterV2({
90059009 instanceId,
......@@ -9037,7 +9041,7 @@ describe("AcpAdapterV2", () => {
90379041 },
90389042 });
90399043 const threadId = ThreadId.make("thread-acp-already-handled-wake-pin");
9040 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({
9044 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
90419045 runtimeMode: "full-access",
90429046 interactionMode: "default",
90439047 cwd: process.cwd(),
......@@ -9057,7 +9061,7 @@ describe("AcpAdapterV2", () => {
90579061 );
90589062 }
90599063 const hasPendingBackgroundWork = runtime.hasPendingBackgroundWork;
9060 const events = yield* Queue.unbounded<ProviderAdapterV2Event>();
9064 const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>();
90619065 yield* runtime.events.pipe(
90629066 Stream.runForEach((event) => Queue.offer(events, event)),
90639067 Effect.forkScoped,
......@@ -9123,7 +9127,8 @@ describe("AcpAdapterV2", () => {
91239127 const mockAgentPath = yield* path.fromFileUrl(
91249128 new URL("../../../scripts/acp-mock-agent.ts", import.meta.url),
91259129 );
9126 const continuationRequests: Array<ProviderContinuationRequest> = [];
9130 const continuationRequests: Array<ProviderContinuationRequests.ProviderContinuationRequest> =
9131 [];
91279132 type RuntimeService = AcpSessionRuntime.AcpSessionRuntime["Service"];
91289133 let sessionUpdateHandler: Parameters<RuntimeService["handleSessionUpdate"]>[0] | undefined;
91299134 const instanceId = ProviderInstanceId.make("acp-test");
......@@ -9154,7 +9159,7 @@ describe("AcpAdapterV2", () => {
91549159 },
91559160 });
91569161 const threadId = ThreadId.make("thread-acp-sticky-continuation-dispatch");
9157 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({
9162 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
91589163 runtimeMode: "full-access",
91599164 interactionMode: "default",
91609165 cwd: process.cwd(),
......@@ -9168,7 +9173,7 @@ describe("AcpAdapterV2", () => {
91689173 modelSelection,
91699174 runtimePolicy,
91709175 });
9171 const events = yield* Queue.unbounded<ProviderAdapterV2Event>();
9176 const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>();
91729177 yield* runtime.events.pipe(
91739178 Stream.runForEach((event) => Queue.offer(events, event)),
91749179 Effect.forkScoped,
......@@ -9237,7 +9242,8 @@ describe("AcpAdapterV2", () => {
92379242 const mockAgentPath = yield* path.fromFileUrl(
92389243 new URL("../../../scripts/acp-mock-agent.ts", import.meta.url),
92399244 );
9240 const continuationRequests: Array<ProviderContinuationRequest> = [];
9245 const continuationRequests: Array<ProviderContinuationRequests.ProviderContinuationRequest> =
9246 [];
92419247 const userPromptStarted = yield* Deferred.make<void>();
92429248 const releaseUserPrompt = yield* Deferred.make<void>();
92439249 const bufferedAssistantText = "BUFFERED_WAKE_QUEUED_AFTER_USER";
......@@ -9284,7 +9290,7 @@ describe("AcpAdapterV2", () => {
92849290 },
92859291 });
92869292 const threadId = ThreadId.make("thread-acp-buffered-continuation-user-race");
9287 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({
9293 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
92889294 runtimeMode: "full-access",
92899295 interactionMode: "default",
92909296 cwd: process.cwd(),
......@@ -9304,7 +9310,7 @@ describe("AcpAdapterV2", () => {
93049310 );
93059311 }
93069312 const hasPendingBackgroundWork = runtime.hasPendingBackgroundWork;
9307 const events = yield* Queue.unbounded<ProviderAdapterV2Event>();
9313 const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>();
93089314 yield* runtime.events.pipe(
93099315 Stream.runForEach((event) => Queue.offer(events, event)),
93109316 Effect.forkScoped,
......@@ -9480,7 +9486,7 @@ describe("AcpAdapterV2", () => {
94809486 selfInvocation,
94819487 });
94829488 const threadId = ThreadId.make("thread-acp-injected-report-hold");
9483 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({
9489 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
94849490 runtimeMode: "full-access",
94859491 interactionMode: "default",
94869492 cwd: process.cwd(),
......@@ -9492,7 +9498,7 @@ describe("AcpAdapterV2", () => {
94929498 modelSelection,
94939499 runtimePolicy,
94949500 });
9495 const events = yield* Queue.unbounded<ProviderAdapterV2Event>();
9501 const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>();
94969502 yield* runtime.events.pipe(
94979503 Stream.runForEach((event) => Queue.offer(events, event)),
94989504 Effect.forkScoped,
......@@ -9515,7 +9521,7 @@ describe("AcpAdapterV2", () => {
95159521 // hydration are ingested: the hydrated monitor card carries the
95169522 // fetched listing.
95179523 let reportSeen = false;
9518 const trackReport = (event: ProviderAdapterV2Event): void => {
9524 const trackReport = (event: ProviderAdapter.ProviderAdapterV2Event): void => {
95199525 if (
95209526 event.type === "turn_item.updated" &&
95219527 event.turnItem.type === "assistant_message" &&
......@@ -9605,7 +9611,8 @@ describe("AcpAdapterV2", () => {
96059611 const triggerDir = yield* fileSystem.makeTempDirectoryScoped();
96069612 const triggerPath = path.join(triggerDir, "report-trigger");
96079613 const protocolEvents = yield* Queue.bounded<EffectAcpProtocol.AcpProtocolLogEvent>(256);
9608 const continuationRequests: Array<ProviderContinuationRequest> = [];
9614 const continuationRequests: Array<ProviderContinuationRequests.ProviderContinuationRequest> =
9615 [];
96099616 const capturedMutation: {
96109617 current:
96119618 | ((mutation: {
......@@ -9662,7 +9669,7 @@ describe("AcpAdapterV2", () => {
96629669 },
96639670 });
96649671 const threadId = ThreadId.make("thread-acp-settle-hold-ext-complete-no-wake");
9665 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({
9672 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
96669673 runtimeMode: "full-access",
96679674 interactionMode: "default",
96689675 cwd: process.cwd(),
......@@ -9676,7 +9683,7 @@ describe("AcpAdapterV2", () => {
96769683 modelSelection,
96779684 runtimePolicy,
96789685 });
9679 const events = yield* Queue.unbounded<ProviderAdapterV2Event>();
9686 const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>();
96809687 yield* runtime.events.pipe(
96819688 Stream.runForEach((event) => Queue.offer(events, event)),
96829689 Effect.forkScoped,
......@@ -9696,7 +9703,7 @@ describe("AcpAdapterV2", () => {
96969703 });
96979704
96989705 let reportSeen = false;
9699 const trackReport = (event: ProviderAdapterV2Event): void => {
9706 const trackReport = (event: ProviderAdapter.ProviderAdapterV2Event): void => {
97009707 if (
97019708 event.type === "turn_item.updated" &&
97029709 event.turnItem.type === "assistant_message" &&
......@@ -9799,7 +9806,8 @@ describe("AcpAdapterV2", () => {
97999806 new URL("../../../scripts/acp-mock-agent.ts", import.meta.url),
98009807 );
98019808 const protocolEvents = yield* Queue.bounded<EffectAcpProtocol.AcpProtocolLogEvent>(256);
9802 const continuationRequests: Array<ProviderContinuationRequest> = [];
9809 const continuationRequests: Array<ProviderContinuationRequests.ProviderContinuationRequest> =
9810 [];
98039811 type RuntimeService = AcpSessionRuntime.AcpSessionRuntime["Service"];
98049812 let sessionUpdateHandler: Parameters<RuntimeService["handleSessionUpdate"]>[0] | undefined;
98059813 const promptGate = yield* Deferred.make<EffectAcpSchema.PromptResponse>();
......@@ -9854,7 +9862,7 @@ describe("AcpAdapterV2", () => {
98549862 },
98559863 });
98569864 const threadId = ThreadId.make("thread-acp-pre-settle-arm-cleared-by-report");
9857 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({
9865 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
98589866 runtimeMode: "full-access",
98599867 interactionMode: "default",
98609868 cwd: process.cwd(),
......@@ -9868,7 +9876,7 @@ describe("AcpAdapterV2", () => {
98689876 modelSelection,
98699877 runtimePolicy,
98709878 });
9871 const events = yield* Queue.unbounded<ProviderAdapterV2Event>();
9879 const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>();
98729880 yield* runtime.events.pipe(
98739881 Stream.runForEach((event) => Queue.offer(events, event)),
98749882 Effect.forkScoped,
......@@ -9965,7 +9973,7 @@ describe("AcpAdapterV2", () => {
99659973 yield* Effect.yieldNow;
99669974
99679975 let reportSeen = false;
9968 const trackReport = (event: ProviderAdapterV2Event): void => {
9976 const trackReport = (event: ProviderAdapter.ProviderAdapterV2Event): void => {
99699977 if (
99709978 event.type === "turn_item.updated" &&
99719979 event.turnItem.type === "assistant_message" &&
......@@ -10033,7 +10041,8 @@ describe("AcpAdapterV2", () => {
1003310041 new URL("../../../scripts/acp-mock-agent.ts", import.meta.url),
1003410042 );
1003510043 const protocolEvents = yield* Queue.bounded<EffectAcpProtocol.AcpProtocolLogEvent>(256);
10036 const continuationRequests: Array<ProviderContinuationRequest> = [];
10044 const continuationRequests: Array<ProviderContinuationRequests.ProviderContinuationRequest> =
10045 [];
1003710046 type RuntimeService = AcpSessionRuntime.AcpSessionRuntime["Service"];
1003810047 let sessionUpdateHandler: Parameters<RuntimeService["handleSessionUpdate"]>[0] | undefined;
1003910048 const promptGate = yield* Deferred.make<EffectAcpSchema.PromptResponse>();
......@@ -10095,7 +10104,7 @@ describe("AcpAdapterV2", () => {
1009510104 },
1009610105 });
1009710106 const threadId = ThreadId.make("thread-acp-staggered-midturn-keep-until-last");
10098 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({
10107 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
1009910108 runtimeMode: "full-access",
1010010109 interactionMode: "default",
1010110110 cwd: process.cwd(),
......@@ -10109,7 +10118,7 @@ describe("AcpAdapterV2", () => {
1010910118 modelSelection,
1011010119 runtimePolicy,
1011110120 });
10112 const events = yield* Queue.unbounded<ProviderAdapterV2Event>();
10121 const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>();
1011310122 yield* runtime.events.pipe(
1011410123 Stream.runForEach((event) => Queue.offer(events, event)),
1011510124 Effect.forkScoped,
......@@ -10246,7 +10255,8 @@ describe("AcpAdapterV2", () => {
1024610255 new URL("../../../scripts/acp-mock-agent.ts", import.meta.url),
1024710256 );
1024810257 const protocolEvents = yield* Queue.bounded<EffectAcpProtocol.AcpProtocolLogEvent>(256);
10249 const continuationRequests: Array<ProviderContinuationRequest> = [];
10258 const continuationRequests: Array<ProviderContinuationRequests.ProviderContinuationRequest> =
10259 [];
1025010260 type RuntimeService = AcpSessionRuntime.AcpSessionRuntime["Service"];
1025110261 let sessionUpdateHandler: Parameters<RuntimeService["handleSessionUpdate"]>[0] | undefined;
1025210262 const promptGate = yield* Deferred.make<EffectAcpSchema.PromptResponse>();
......@@ -10312,7 +10322,7 @@ describe("AcpAdapterV2", () => {
1031210322 },
1031310323 });
1031410324 const threadId = ThreadId.make("thread-acp-staggered-midturn-clear-on-interrupt");
10315 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({
10325 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
1031610326 runtimeMode: "full-access",
1031710327 interactionMode: "default",
1031810328 cwd: process.cwd(),
......@@ -10326,7 +10336,7 @@ describe("AcpAdapterV2", () => {
1032610336 modelSelection,
1032710337 runtimePolicy,
1032810338 });
10329 const events = yield* Queue.unbounded<ProviderAdapterV2Event>();
10339 const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>();
1033010340 yield* runtime.events.pipe(
1033110341 Stream.runForEach((event) => Queue.offer(events, event)),
1033210342 Effect.forkScoped,
......@@ -10455,7 +10465,8 @@ describe("AcpAdapterV2", () => {
1045510465 new URL("../../../scripts/acp-mock-agent.ts", import.meta.url),
1045610466 );
1045710467 const protocolEvents = yield* Queue.bounded<EffectAcpProtocol.AcpProtocolLogEvent>(256);
10458 const continuationRequests: Array<ProviderContinuationRequest> = [];
10468 const continuationRequests: Array<ProviderContinuationRequests.ProviderContinuationRequest> =
10469 [];
1045910470 type RuntimeService = AcpSessionRuntime.AcpSessionRuntime["Service"];
1046010471 let sessionUpdateHandler: Parameters<RuntimeService["handleSessionUpdate"]>[0] | undefined;
1046110472 const promptGates: Array<Deferred.Deferred<EffectAcpSchema.PromptResponse, never>> = [];
......@@ -10543,7 +10554,7 @@ describe("AcpAdapterV2", () => {
1054310554 },
1054410555 });
1054510556 const threadId = ThreadId.make("thread-acp-multiturn-in-turn-monitor-no-wake");
10546 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({
10557 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
1054710558 runtimeMode: "full-access",
1054810559 interactionMode: "default",
1054910560 cwd: process.cwd(),
......@@ -10563,7 +10574,7 @@ describe("AcpAdapterV2", () => {
1056310574 );
1056410575 }
1056510576 const hasPendingBackgroundWork = runtime.hasPendingBackgroundWork;
10566 const events = yield* Queue.unbounded<ProviderAdapterV2Event>();
10577 const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>();
1056710578 yield* runtime.events.pipe(
1056810579 Stream.runForEach((event) => Queue.offer(events, event)),
1056910580 Effect.forkScoped,
......@@ -10820,7 +10831,8 @@ describe("AcpAdapterV2", () => {
1082010831 const mockAgentPath = yield* path.fromFileUrl(
1082110832 new URL("../../../scripts/acp-mock-agent.ts", import.meta.url),
1082210833 );
10823 const continuationRequests: Array<ProviderContinuationRequest> = [];
10834 const continuationRequests: Array<ProviderContinuationRequests.ProviderContinuationRequest> =
10835 [];
1082410836 type RuntimeService = AcpSessionRuntime.AcpSessionRuntime["Service"];
1082510837 let sessionUpdateHandler: Parameters<RuntimeService["handleSessionUpdate"]>[0] | undefined;
1082610838 let applyMutation: AcpAdapterV2ExtensionContext["applyBackgroundTaskMutation"] | undefined;
......@@ -10860,7 +10872,7 @@ describe("AcpAdapterV2", () => {
1086010872 },
1086110873 });
1086210874 const threadId = ThreadId.make("thread-acp-wake-report-while-queued");
10863 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({
10875 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
1086410876 runtimeMode: "full-access",
1086510877 interactionMode: "default",
1086610878 cwd: process.cwd(),
......@@ -10872,7 +10884,7 @@ describe("AcpAdapterV2", () => {
1087210884 modelSelection,
1087310885 runtimePolicy,
1087410886 });
10875 const events = yield* Queue.unbounded<ProviderAdapterV2Event>();
10887 const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>();
1087610888 yield* runtime.events.pipe(
1087710889 Stream.runForEach((event) => Queue.offer(events, event)),
1087810890 Effect.forkScoped,
......@@ -10969,7 +10981,8 @@ describe("AcpAdapterV2", () => {
1096910981 new URL("../../../scripts/acp-mock-agent.ts", import.meta.url),
1097010982 );
1097110983 const protocolEvents = yield* Queue.bounded<EffectAcpProtocol.AcpProtocolLogEvent>(256);
10972 const continuationRequests: Array<ProviderContinuationRequest> = [];
10984 const continuationRequests: Array<ProviderContinuationRequests.ProviderContinuationRequest> =
10985 [];
1097310986 type RuntimeService = AcpSessionRuntime.AcpSessionRuntime["Service"];
1097410987 let sessionUpdateHandler: Parameters<RuntimeService["handleSessionUpdate"]>[0] | undefined;
1097510988 const promptGates: Array<Deferred.Deferred<EffectAcpSchema.PromptResponse, never>> = [];
......@@ -11038,7 +11051,7 @@ describe("AcpAdapterV2", () => {
1103811051 },
1103911052 });
1104011053 const threadId = ThreadId.make("thread-acp-mid-turn-dirty-wake-buffer");
11041 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({
11054 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
1104211055 runtimeMode: "full-access",
1104311056 interactionMode: "default",
1104411057 cwd: process.cwd(),
......@@ -11058,7 +11071,7 @@ describe("AcpAdapterV2", () => {
1105811071 );
1105911072 }
1106011073 const hasPendingBackgroundWork = runtime.hasPendingBackgroundWork;
11061 const events = yield* Queue.unbounded<ProviderAdapterV2Event>();
11074 const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>();
1106211075 yield* runtime.events.pipe(
1106311076 Stream.runForEach((event) => Queue.offer(events, event)),
1106411077 Effect.forkScoped,
......@@ -11294,7 +11307,8 @@ describe("AcpAdapterV2", () => {
1129411307 new URL("../../../scripts/acp-mock-agent.ts", import.meta.url),
1129511308 );
1129611309 const protocolEvents = yield* Queue.bounded<EffectAcpProtocol.AcpProtocolLogEvent>(256);
11297 const continuationRequests: Array<ProviderContinuationRequest> = [];
11310 const continuationRequests: Array<ProviderContinuationRequests.ProviderContinuationRequest> =
11311 [];
1129811312 type RuntimeService = AcpSessionRuntime.AcpSessionRuntime["Service"];
1129911313 let sessionUpdateHandler: Parameters<RuntimeService["handleSessionUpdate"]>[0] | undefined;
1130011314 const promptGate = yield* Deferred.make<EffectAcpSchema.PromptResponse>();
......@@ -11344,7 +11358,7 @@ describe("AcpAdapterV2", () => {
1134411358 },
1134511359 });
1134611360 const threadId = ThreadId.make("thread-acp-mid-turn-unhandled-offers-after-finalize");
11347 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({
11361 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
1134811362 runtimeMode: "full-access",
1134911363 interactionMode: "default",
1135011364 cwd: process.cwd(),
......@@ -11358,7 +11372,7 @@ describe("AcpAdapterV2", () => {
1135811372 modelSelection,
1135911373 runtimePolicy,
1136011374 });
11361 const events = yield* Queue.unbounded<ProviderAdapterV2Event>();
11375 const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>();
1136211376 yield* runtime.events.pipe(
1136311377 Stream.runForEach((event) => Queue.offer(events, event)),
1136411378 Effect.forkScoped,
......@@ -11446,7 +11460,7 @@ describe("AcpAdapterV2", () => {
1144611460 content: { type: "text", text: " \n\t " },
1144711461 },
1144811462 });
11449 const lateEvents: Array<ProviderAdapterV2Event> = [];
11463 const lateEvents: Array<ProviderAdapter.ProviderAdapterV2Event> = [];
1145011464 let lateEvent = yield* Queue.poll(events);
1145111465 while (Option.isSome(lateEvent)) {
1145211466 lateEvents.push(lateEvent.value);
......@@ -11475,7 +11489,8 @@ describe("AcpAdapterV2", () => {
1147511489 new URL("../../../scripts/acp-mock-agent.ts", import.meta.url),
1147611490 );
1147711491 const protocolEvents = yield* Queue.bounded<EffectAcpProtocol.AcpProtocolLogEvent>(256);
11478 const continuationRequests: Array<ProviderContinuationRequest> = [];
11492 const continuationRequests: Array<ProviderContinuationRequests.ProviderContinuationRequest> =
11493 [];
1147911494 type RuntimeService = AcpSessionRuntime.AcpSessionRuntime["Service"];
1148011495 let sessionUpdateHandler: Parameters<RuntimeService["handleSessionUpdate"]>[0] | undefined;
1148111496 const promptGate = yield* Deferred.make<EffectAcpSchema.PromptResponse>();
......@@ -11529,7 +11544,7 @@ describe("AcpAdapterV2", () => {
1152911544 },
1153011545 });
1153111546 const threadId = ThreadId.make("thread-acp-empty-drain-continuation-quiet");
11532 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({
11547 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
1153311548 runtimeMode: "full-access",
1153411549 interactionMode: "default",
1153511550 cwd: process.cwd(),
......@@ -11543,7 +11558,7 @@ describe("AcpAdapterV2", () => {
1154311558 modelSelection,
1154411559 runtimePolicy,
1154511560 });
11546 const events = yield* Queue.unbounded<ProviderAdapterV2Event>();
11561 const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>();
1154711562 yield* runtime.events.pipe(
1154811563 Stream.runForEach((event) => Queue.offer(events, event)),
1154911564 Effect.forkScoped,
......@@ -11713,7 +11728,8 @@ describe("AcpAdapterV2", () => {
1171311728 new URL("../../../scripts/acp-mock-agent.ts", import.meta.url),
1171411729 );
1171511730 const protocolEvents = yield* Queue.bounded<EffectAcpProtocol.AcpProtocolLogEvent>(256);
11716 const continuationRequests: Array<ProviderContinuationRequest> = [];
11731 const continuationRequests: Array<ProviderContinuationRequests.ProviderContinuationRequest> =
11732 [];
1171711733 type RuntimeService = AcpSessionRuntime.AcpSessionRuntime["Service"];
1171811734 let sessionUpdateHandler: Parameters<RuntimeService["handleSessionUpdate"]>[0] | undefined;
1171911735 const promptGate = yield* Deferred.make<EffectAcpSchema.PromptResponse>();
......@@ -11765,7 +11781,7 @@ describe("AcpAdapterV2", () => {
1176511781 },
1176611782 });
1176711783 const threadId = ThreadId.make("thread-acp-empty-drain-continuation-no-wedge");
11768 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({
11784 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
1176911785 runtimeMode: "full-access",
1177011786 interactionMode: "default",
1177111787 cwd: process.cwd(),
......@@ -11779,7 +11795,7 @@ describe("AcpAdapterV2", () => {
1177911795 modelSelection,
1178011796 runtimePolicy,
1178111797 });
11782 const events = yield* Queue.unbounded<ProviderAdapterV2Event>();
11798 const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>();
1178311799 yield* runtime.events.pipe(
1178411800 Stream.runForEach((event) => Queue.offer(events, event)),
1178511801 Effect.forkScoped,
......@@ -11913,7 +11929,7 @@ describe("AcpAdapterV2", () => {
1191311929 selfInvocation,
1191411930 });
1191511931 const threadId = ThreadId.make("thread-acp-restart-after-interrupt");
11916 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({
11932 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
1191711933 runtimeMode: "full-access",
1191811934 interactionMode: "default",
1191911935 cwd: process.cwd(),
......@@ -12144,7 +12160,8 @@ describe("AcpAdapterV2", () => {
1214412160 const residualCallbackResponseLogPath = path.join(residualCallbackDir, "responses.log");
1214512161 const residualCallbackTriggerPath = path.join(residualCallbackDir, "trigger");
1214612162 const protocolEvents = yield* Queue.bounded<EffectAcpProtocol.AcpProtocolLogEvent>(256);
12147 const continuationRequests: Array<ProviderContinuationRequest> = [];
12163 const continuationRequests: Array<ProviderContinuationRequests.ProviderContinuationRequest> =
12164 [];
1214812165 const teardownStarted = yield* Deferred.make<void>();
1214912166 const releaseTeardown = yield* Deferred.make<void>();
1215012167 let terminatorCallCount = 0;
......@@ -12187,7 +12204,7 @@ describe("AcpAdapterV2", () => {
1218712204 },
1218812205 });
1218912206 const threadId = ThreadId.make("thread-acp-failed-hard-teardown");
12190 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({
12207 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
1219112208 runtimeMode: "full-access",
1219212209 interactionMode: "default",
1219312210 cwd: process.cwd(),
......@@ -12202,7 +12219,7 @@ describe("AcpAdapterV2", () => {
1220212219 runtimePolicy,
1220312220 })
1220412221 .pipe(Effect.provideService(Scope.Scope, sessionScope));
12205 const adapterEvents = yield* Queue.unbounded<ProviderAdapterV2Event>();
12222 const adapterEvents = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>();
1220612223 yield* runtime.events.pipe(
1220712224 Stream.runForEach((event) => Queue.offer(adapterEvents, event)),
1220812225 Effect.forkIn(sessionScope),
......@@ -12368,7 +12385,7 @@ describe("AcpAdapterV2", () => {
1236812385 selfInvocation,
1236912386 });
1237012387 const threadId = ThreadId.make("thread-acp-missing-hard-teardown");
12371 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({
12388 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
1237212389 runtimeMode: "full-access",
1237312390 interactionMode: "default",
1237412391 cwd: process.cwd(),
......@@ -12464,7 +12481,7 @@ describe("AcpAdapterV2", () => {
1246412481 selfInvocation,
1246512482 });
1246612483 const threadId = ThreadId.make("thread-acp-concurrent-hard-teardown");
12467 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({
12484 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
1246812485 runtimeMode: "full-access",
1246912486 interactionMode: "default",
1247012487 cwd: process.cwd(),
......@@ -12540,7 +12557,8 @@ describe("AcpAdapterV2", () => {
1254012557 new URL("../../../scripts/acp-mock-agent.ts", import.meta.url),
1254112558 );
1254212559 const protocolEvents = yield* Queue.bounded<EffectAcpProtocol.AcpProtocolLogEvent>(256);
12543 const continuationRequests: Array<ProviderContinuationRequest> = [];
12560 const continuationRequests: Array<ProviderContinuationRequests.ProviderContinuationRequest> =
12561 [];
1254412562 const responseLifecycle: Array<string> = [];
1254512563 type RuntimeService = AcpSessionRuntime.AcpSessionRuntime["Service"];
1254612564 type HandlerRecord = {
......@@ -12633,7 +12651,7 @@ describe("AcpAdapterV2", () => {
1263312651 },
1263412652 });
1263512653 const threadId = ThreadId.make("thread-acp-successful-teardown-callback-quarantine");
12636 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({
12654 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
1263712655 runtimeMode: "full-access",
1263812656 interactionMode: "default",
1263912657 cwd: process.cwd(),
......@@ -12647,7 +12665,7 @@ describe("AcpAdapterV2", () => {
1264712665 modelSelection,
1264812666 runtimePolicy,
1264912667 });
12650 const adapterEvents = yield* Queue.unbounded<ProviderAdapterV2Event>();
12668 const adapterEvents = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>();
1265112669 yield* runtime.events.pipe(
1265212670 Stream.runForEach((event) => Queue.offer(adapterEvents, event)),
1265312671 Effect.forkScoped,
......@@ -12849,7 +12867,7 @@ describe("AcpAdapterV2", () => {
1284912867 method: "_x.ai/ask_user_question",
1285012868 },
1285112869 ).pipe(Effect.forkScoped);
12852 let replacementRequest: ProviderAdapterV2Event | undefined;
12870 let replacementRequest: ProviderAdapter.ProviderAdapterV2Event | undefined;
1285312871 while (replacementRequest === undefined) {
1285412872 const event = yield* Queue.take(adapterEvents);
1285512873 if (event.type === "runtime_request.updated" && event.runtimeRequest.status === "pending") {
......@@ -12916,7 +12934,7 @@ describe("AcpAdapterV2", () => {
1291612934 selfInvocation,
1291712935 });
1291812936 const threadId = ThreadId.make("thread-acp-stale-deferred-cleanup");
12919 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({
12937 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
1292012938 runtimeMode: "approval-required",
1292112939 interactionMode: "default",
1292212940 cwd: process.cwd(),
......@@ -12928,7 +12946,7 @@ describe("AcpAdapterV2", () => {
1292812946 modelSelection,
1292912947 runtimePolicy,
1293012948 });
12931 const events = yield* Queue.unbounded<ProviderAdapterV2Event>();
12949 const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>();
1293212950 yield* runtime.events.pipe(
1293312951 Stream.runForEach((event) => Queue.offer(events, event)),
1293412952 Effect.forkScoped,
......@@ -12975,7 +12993,7 @@ describe("AcpAdapterV2", () => {
1297512993 yield* runtime.startTurn(
1297612994 makeTurnInput({ threadId, providerThread, instanceId, runtimePolicy, now, ordinal: 2 }),
1297712995 );
12978 let replacementPending: ProviderAdapterV2Event | undefined;
12996 let replacementPending: ProviderAdapter.ProviderAdapterV2Event | undefined;
1297912997 while (replacementPending === undefined) {
1298012998 const event = yield* Queue.take(events);
1298112999 if (event.type === "runtime_request.updated" && event.runtimeRequest.status === "pending") {
......@@ -13045,7 +13063,7 @@ describe("AcpAdapterV2", () => {
1304513063 selfInvocation,
1304613064 });
1304713065 const threadId = ThreadId.make("thread-acp-concurrent-resume-teardown");
13048 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({
13066 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
1304913067 runtimeMode: "full-access",
1305013068 interactionMode: "default",
1305113069 cwd: process.cwd(),
......@@ -13192,7 +13210,7 @@ describe("AcpAdapterV2", () => {
1319213210 continuationRequests: { offer: () => Effect.void },
1319313211 });
1319413212 const threadId = ThreadId.make("thread-acp-direct-stop-running-command");
13195 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({
13213 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
1319613214 runtimeMode: "full-access",
1319713215 interactionMode: "default",
1319813216 cwd: process.cwd(),
......@@ -13206,7 +13224,7 @@ describe("AcpAdapterV2", () => {
1320613224 modelSelection,
1320713225 runtimePolicy,
1320813226 });
13209 const events = yield* Queue.unbounded<ProviderAdapterV2Event>();
13227 const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>();
1321013228 yield* runtime.events.pipe(
1321113229 Stream.runForEach((event) => Queue.offer(events, event)),
1321213230 Effect.forkScoped,
......@@ -13458,7 +13476,7 @@ describe("AcpAdapterV2", () => {
1345813476 selfInvocation,
1345913477 });
1346013478 const threadId = ThreadId.make("thread-acp-direct-stop-subagent-hold");
13461 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({
13479 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
1346213480 runtimeMode: "full-access",
1346313481 interactionMode: "default",
1346413482 cwd: process.cwd(),
......@@ -13472,7 +13490,7 @@ describe("AcpAdapterV2", () => {
1347213490 modelSelection,
1347313491 runtimePolicy,
1347413492 });
13475 const events = yield* Queue.unbounded<ProviderAdapterV2Event>();
13493 const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>();
1347613494 yield* runtime.events.pipe(
1347713495 Stream.runForEach((event) => Queue.offer(events, event)),
1347813496 Effect.forkScoped,
......@@ -13602,7 +13620,7 @@ describe("AcpAdapterV2", () => {
1360213620 selfInvocation,
1360313621 });
1360413622 const threadId = ThreadId.make("thread-acp-restart-active-in-process");
13605 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({
13623 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
1360613624 runtimeMode: "full-access",
1360713625 interactionMode: "default",
1360813626 cwd: process.cwd(),
......@@ -13614,7 +13632,7 @@ describe("AcpAdapterV2", () => {
1361413632 modelSelection,
1361513633 runtimePolicy,
1361613634 });
13617 const events = yield* Queue.unbounded<ProviderAdapterV2Event>();
13635 const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>();
1361813636 yield* runtime.events.pipe(
1361913637 Stream.runForEach((event) => Queue.offer(events, event)),
1362013638 Effect.forkScoped,
apps/server/src/orchestration-v2/Adapters/AcpRegistryAdapterV2.test.ts+4-4
......@@ -29,7 +29,7 @@ import {
2929 makeAcpReplayCompletenessAssertion,
3030 makeAcpReplayRuntime,
3131} from "./AcpAdapterV2.testkit.ts";
32import { ProviderAdapterV2RuntimePolicy } from "@t3tools/provider-core/server/ProviderAdapter";
32import * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter";
3333import { BUILT_IN_PROVIDER_ADAPTER_DRIVER_KINDS_V2 } from "../builtInProviderAdapterDrivers.ts";
3434import { AcpRegistryAdapterV2Driver } from "@t3tools/provider-acp-registry/server";
3535import {
......@@ -216,7 +216,7 @@ describe("AcpRegistryAdapterV2", () => {
216216 model: "default",
217217 options: [{ id: ACP_SESSION_MODE_OPTION_ID, value: input.storedModePick }],
218218 },
219 runtimePolicy: ProviderAdapterV2RuntimePolicy.make({
219 runtimePolicy: ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
220220 runtimeMode: "approval-required",
221221 interactionMode: "default",
222222 cwd: replayDir,
......@@ -304,7 +304,7 @@ describe("AcpRegistryAdapterV2", () => {
304304 );
305305 }),
306306 });
307 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({
307 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
308308 runtimeMode: "full-access",
309309 interactionMode: "default",
310310 cwd: process.cwd(),
......@@ -413,7 +413,7 @@ describe("AcpRegistryAdapterV2", () => {
413413 },
414414 });
415415 const threadId = ThreadId.make("thread-acp-registry-fixture");
416 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({
416 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
417417 runtimeMode: "full-access",
418418 interactionMode: "default",
419419 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";
2727import type * as AcpSessionRuntime from "@t3tools/provider-acp/server/AcpSessionRuntime";
2828import { makeAntigravityAcpRuntime } from "../../provider/acp/AntigravityAcpSupport.ts";
2929import * 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";
3131import {
3232 makeAntigravityAcpAdapterFlavor,
3333 makeAntigravityAdapterV2,
......@@ -74,7 +74,7 @@ describe("AntigravityAdapterV2 flavor", () => {
7474 it("maps runtime modes to the agent's native permission modes", () => {
7575 const mode = (runtimeMode: "approval-required" | "auto-accept-edits" | "full-access") =>
7676 flavor.sessionModeForPolicy?.(
77 ProviderAdapterV2RuntimePolicy.make({
77 ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
7878 runtimeMode,
7979 interactionMode: "default",
8080 cwd: "/workspace",
......@@ -190,7 +190,7 @@ describe("AntigravityAdapterV2 client file system", () => {
190190 yield* fileSystem.writeFileString(attachment, "pasted");
191191
192192 const threadId = ThreadId.make("thread-antigravity-containment");
193 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({
193 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
194194 runtimeMode: "full-access",
195195 interactionMode: "default",
196196 cwd: workspace,
......@@ -325,7 +325,7 @@ describe("AntigravityAdapterV2 workspace changes", () => {
325325 yield* fileSystem.writeFileString(path.join(workspaceA, "a.txt"), "from a");
326326 yield* fileSystem.writeFileString(path.join(workspaceB, "b.txt"), "from b");
327327 const policyFor = (cwd: string) =>
328 ProviderAdapterV2RuntimePolicy.make({
328 ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
329329 runtimeMode: "full-access",
330330 interactionMode: "default",
331331 cwd,
......@@ -476,7 +476,7 @@ describe("AntigravityAdapterV2 client file system under restrictive policies", (
476476 });
477477 yield* fileSystem.writeFileString(path.join(workspace, "existing.ts"), "existing");
478478 const threadId = ThreadId.make(`thread-antigravity-restrictive-${policy.runtimeMode}`);
479 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({
479 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
480480 ...policy,
481481 interactionMode: "default",
482482 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";
6161import { ThreadToolkit } from "../../mcp/toolkits/thread/tools.ts";
6262import { OrchestratorToolkit } from "../../mcp/toolkits/orchestrator/tools.ts";
6363import { ClaudeExecutableFileCheck } from "../../provider/Drivers/ClaudeExecutable.ts";
64import type { EventNdjsonLogger } from "../../provider/EventNdjsonLogger.ts";
65import {
66 ProviderAdapterV2RuntimePolicy,
67 type ProviderAdapterV2Event,
68 type ProviderAdapterV2TurnInput,
69} from "@t3tools/provider-core/server/ProviderAdapter";
70import type { ProviderContinuationRequest } from "@t3tools/provider-core/server/continuationRequests";
64import type * as ProviderEventLoggers from "@t3tools/provider-core/server/ProviderEventLoggers";
65import * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter";
66import type * as ProviderContinuationRequests from "@t3tools/provider-core/server/ProviderContinuationRequests";
7167import { makeProviderFailure } from "@t3tools/provider-core/server/failure";
7268import * as ClaudeAdapterV2 from "./ClaudeAdapterV2.ts";
7369import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator";
......@@ -81,7 +77,7 @@ const CLAUDE_TEST_MODEL_SELECTION = {
8177 model: "claude-sonnet-4-6",
8278 options: [{ id: "effort", value: "ultrathink" }],
8379} satisfies ModelSelection;
84const CLAUDE_TEST_RUNTIME_POLICY = ProviderAdapterV2RuntimePolicy.make({
80const CLAUDE_TEST_RUNTIME_POLICY = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
8581 runtimeMode: "full-access",
8682 interactionMode: "default",
8783 cwd: "/workspace",
......@@ -127,14 +123,14 @@ function makeClaudeTestTurnInput(input: {
127123 readonly now: DateTime.Utc;
128124 readonly attemptId: RunAttemptId;
129125 readonly text: string;
130 readonly attachments: ProviderAdapterV2TurnInput["message"]["attachments"];
126 readonly attachments: ProviderAdapter.ProviderAdapterV2TurnInput["message"]["attachments"];
131127 readonly providerTurnOrdinal?: number;
132128 readonly nativeThreadHasTurns?: boolean;
133 readonly messageCreatedBy?: ProviderAdapterV2TurnInput["message"]["createdBy"];
134 readonly messageCreationSource?: ProviderAdapterV2TurnInput["message"]["creationSource"];
129 readonly messageCreatedBy?: ProviderAdapter.ProviderAdapterV2TurnInput["message"]["createdBy"];
130 readonly messageCreationSource?: ProviderAdapter.ProviderAdapterV2TurnInput["message"]["creationSource"];
135131 readonly modelSelection?: ModelSelection;
136 readonly runtimePolicy?: ProviderAdapterV2RuntimePolicy;
137}): ProviderAdapterV2TurnInput {
132 readonly runtimePolicy?: ProviderAdapter.ProviderAdapterV2RuntimePolicy;
133}): ProviderAdapter.ProviderAdapterV2TurnInput {
138134 return {
139135 appThread: makeClaudeTestAppThread(input),
140136 threadId: input.threadId,
......@@ -298,7 +294,7 @@ describe("ClaudeAdapterV2 runtime query policy", () => {
298294
299295 it("maps canonical read-only never policy to Claude dontAsk with read-only tools", () => {
300296 const queryPolicy = ClaudeAdapterV2.claudeRuntimeQueryPolicyForRuntimePolicy(
301 ProviderAdapterV2RuntimePolicy.make({
297 ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
302298 runtimeMode: "full-access",
303299 interactionMode: "default",
304300 cwd: "/workspace",
......@@ -321,7 +317,7 @@ describe("ClaudeAdapterV2 runtime query policy", () => {
321317
322318 it("maps canonical read-only on-request policy to Claude default with callbacks", () => {
323319 const queryPolicy = ClaudeAdapterV2.claudeRuntimeQueryPolicyForRuntimePolicy(
324 ProviderAdapterV2RuntimePolicy.make({
320 ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
325321 runtimeMode: "full-access",
326322 interactionMode: "default",
327323 cwd: "/workspace",
......@@ -344,7 +340,7 @@ describe("ClaudeAdapterV2 runtime query policy", () => {
344340
345341 it("does not auto-allow reads for canonical restricted read-only never policy", () => {
346342 const queryPolicy = ClaudeAdapterV2.claudeRuntimeQueryPolicyForRuntimePolicy(
347 ProviderAdapterV2RuntimePolicy.make({
343 ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
348344 runtimeMode: "full-access",
349345 interactionMode: "default",
350346 cwd: "/workspace",
......@@ -370,7 +366,7 @@ describe("ClaudeAdapterV2 runtime query policy", () => {
370366
371367 it("maps default full-access policy to Claude bypass permissions", () => {
372368 const queryPolicy = ClaudeAdapterV2.claudeRuntimeQueryPolicyForRuntimePolicy(
373 ProviderAdapterV2RuntimePolicy.make({
369 ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
374370 runtimeMode: "full-access",
375371 interactionMode: "default",
376372 cwd: "/workspace",
......@@ -386,7 +382,7 @@ describe("ClaudeAdapterV2 runtime query policy", () => {
386382
387383 it("maps Auto runtime mode to Claude's AI-reviewed permission mode", () => {
388384 const queryPolicy = ClaudeAdapterV2.claudeRuntimeQueryPolicyForRuntimePolicy(
389 ProviderAdapterV2RuntimePolicy.make({
385 ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
390386 runtimeMode: "auto",
391387 interactionMode: "default",
392388 cwd: "/workspace",
......@@ -401,7 +397,7 @@ describe("ClaudeAdapterV2 runtime query policy", () => {
401397
402398 it("keeps approval-required mode interactive with danger-full-access sandboxing", () => {
403399 const queryPolicy = ClaudeAdapterV2.claudeRuntimeQueryPolicyForRuntimePolicy(
404 ProviderAdapterV2RuntimePolicy.make({
400 ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
405401 runtimeMode: "approval-required",
406402 interactionMode: "default",
407403 cwd: "/workspace",
......@@ -419,7 +415,7 @@ describe("ClaudeAdapterV2 runtime query policy", () => {
419415
420416 it("installs the permission callback for approval-required plan mode", () => {
421417 const queryPolicy = ClaudeAdapterV2.claudeRuntimeQueryPolicyForRuntimePolicy(
422 ProviderAdapterV2RuntimePolicy.make({
418 ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
423419 runtimeMode: "approval-required",
424420 interactionMode: "plan",
425421 cwd: "/workspace",
......@@ -434,7 +430,7 @@ describe("ClaudeAdapterV2 runtime query policy", () => {
434430
435431 it("honors never approvals for approval-required workspace-write policy", () => {
436432 const queryPolicy = ClaudeAdapterV2.claudeRuntimeQueryPolicyForRuntimePolicy(
437 ProviderAdapterV2RuntimePolicy.make({
433 ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
438434 runtimeMode: "approval-required",
439435 interactionMode: "default",
440436 cwd: "/workspace",
......@@ -453,7 +449,7 @@ describe("ClaudeAdapterV2 runtime query policy", () => {
453449
454450 it("honors never approvals for externally sandboxed policy", () => {
455451 const queryPolicy = ClaudeAdapterV2.claudeRuntimeQueryPolicyForRuntimePolicy(
456 ProviderAdapterV2RuntimePolicy.make({
452 ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
457453 runtimeMode: "approval-required",
458454 interactionMode: "default",
459455 cwd: "/workspace",
......@@ -595,7 +591,7 @@ describe("ClaudeAdapterV2 MCP query overrides", () => {
595591 const threadId = ThreadId.make("thread-claude-mcp-query-key");
596592 withMcpSession(threadId, () => {
597593 const queryPolicy = ClaudeAdapterV2.claudeRuntimeQueryPolicyForRuntimePolicy(
598 ProviderAdapterV2RuntimePolicy.make({
594 ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
599595 runtimeMode: "full-access",
600596 interactionMode: "default",
601597 cwd: "/workspace",
......@@ -627,7 +623,7 @@ describe("ClaudeAdapterV2 MCP query overrides", () => {
627623 const threadId = ThreadId.make("thread-claude-mcp-credential-rotation");
628624 withMcpSession(threadId, () => {
629625 const queryPolicy = ClaudeAdapterV2.claudeRuntimeQueryPolicyForRuntimePolicy(
630 ProviderAdapterV2RuntimePolicy.make({
626 ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
631627 runtimeMode: "approval-required",
632628 interactionMode: "default",
633629 cwd: "/workspace",
......@@ -750,7 +746,7 @@ describe("ClaudeAdapterV2 native protocol logging", () => {
750746 readonly event: unknown;
751747 readonly threadId: ThreadId | null;
752748 }> = [];
753 const logger: EventNdjsonLogger = {
749 const logger: ProviderEventLoggers.EventNdjsonLogger = {
754750 filePath: "/tmp/events.log",
755751 write: (event, threadId) =>
756752 Effect.sync(() => {
......@@ -993,7 +989,7 @@ describe("ClaudeAdapterV2 Auto-accept edits", () => {
993989 assertComplete: Effect.void,
994990 },
995991 });
996 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({
992 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
997993 runtimeMode: "auto-accept-edits",
998994 interactionMode: "default",
999995 cwd: "/workspace",
......@@ -1701,7 +1697,7 @@ describe("ClaudeAdapterV2 native fork", () => {
17011697 instanceId: ProviderInstanceId.make(ClaudeAdapterV2.CLAUDE_PROVIDER),
17021698 model: "claude-sonnet-4-6",
17031699 },
1704 runtimePolicy: ProviderAdapterV2RuntimePolicy.make({
1700 runtimePolicy: ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
17051701 runtimeMode: "full-access",
17061702 interactionMode: "default",
17071703 cwd: "/workspace",
......@@ -1713,7 +1709,7 @@ describe("ClaudeAdapterV2 native fork", () => {
17131709 instanceId: ProviderInstanceId.make(ClaudeAdapterV2.CLAUDE_PROVIDER),
17141710 model: "claude-sonnet-4-6",
17151711 },
1716 runtimePolicy: ProviderAdapterV2RuntimePolicy.make({
1712 runtimePolicy: ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
17171713 runtimeMode: "full-access",
17181714 interactionMode: "default",
17191715 cwd: "/workspace",
......@@ -1808,7 +1804,7 @@ describe("ClaudeAdapterV2 native fork", () => {
18081804 instanceId: ProviderInstanceId.make(ClaudeAdapterV2.CLAUDE_PROVIDER),
18091805 model: "claude-sonnet-4-6",
18101806 },
1811 runtimePolicy: ProviderAdapterV2RuntimePolicy.make({
1807 runtimePolicy: ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
18121808 runtimeMode: "full-access",
18131809 interactionMode: "default",
18141810 cwd: "/workspace",
......@@ -2120,11 +2116,16 @@ describe("ClaudeAdapterV2 background wake turns", () => {
21202116 });
21212117 const offeredMessages: Array<SDKUserMessage> = [];
21222118 const permissionModeChanges: Array<string> = [];
2123 const continuationRequests: Array<ProviderContinuationRequest> = [];
2119 const continuationRequests: Array<ProviderContinuationRequests.ProviderContinuationRequest> =
2120 [];
21242121 const terminalReceipts =
2125 yield* Queue.unbounded<Extract<ProviderAdapterV2Event, { type: "turn.terminal" }>>();
2122 yield* Queue.unbounded<
2123 Extract<ProviderAdapter.ProviderAdapterV2Event, { type: "turn.terminal" }>
2124 >();
21262125 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 >();
21282129 let openedOptions: ClaudeAdapterV2.ClaudeAgentSdkQueryOptions | undefined;
21292130 const adapter = ClaudeAdapterV2.makeClaudeAdapterV2({
21302131 instanceId: ClaudeAdapterV2.CLAUDE_DEFAULT_INSTANCE_ID,
......@@ -2210,7 +2211,7 @@ describe("ClaudeAdapterV2 background wake turns", () => {
22102211 modelSelection: CLAUDE_TEST_MODEL_SELECTION,
22112212 runtimePolicy: CLAUDE_TEST_RUNTIME_POLICY,
22122213 });
2213 const events: Array<ProviderAdapterV2Event> = [];
2214 const events: Array<ProviderAdapter.ProviderAdapterV2Event> = [];
22142215 yield* runtime.events.pipe(
22152216 Stream.runForEach((event) =>
22162217 Effect.gen(function* () {
......@@ -2231,7 +2232,9 @@ describe("ClaudeAdapterV2 background wake turns", () => {
22312232 const hasPendingBackgroundWork = runtime.hasPendingBackgroundWork;
22322233 const terminalEvents = () =>
22332234 events.filter(
2234 (event): event is Extract<ProviderAdapterV2Event, { type: "turn.terminal" }> =>
2235 (
2236 event,
2237 ): event is Extract<ProviderAdapter.ProviderAdapterV2Event, { type: "turn.terminal" }> =>
22352238 event.type === "turn.terminal",
22362239 );
22372240 return {
......@@ -2817,7 +2820,7 @@ describe("ClaudeAdapterV2 background wake turns", () => {
28172820 attemptId: RunAttemptId.make("attempt-claude-auth-failure"),
28182821 text: "Continue.",
28192822 attachments: [],
2820 runtimePolicy: ProviderAdapterV2RuntimePolicy.make({
2823 runtimePolicy: ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
28212824 runtimeMode: "full-access",
28222825 interactionMode: "default",
28232826 cwd,
......@@ -3633,10 +3636,16 @@ describe("ClaudeAdapterV2 background wake turns", () => {
36333636 ),
36343637 );
36353638
3636 const providerThreadRosterEvents = (events: ReadonlyArray<ProviderAdapterV2Event>) =>
3639 const providerThreadRosterEvents = (
3640 events: ReadonlyArray<ProviderAdapter.ProviderAdapterV2Event>,
3641 ) =>
36373642 events.filter(
3638 (event): event is Extract<ProviderAdapterV2Event, { type: "provider_thread.updated" }> =>
3639 event.type === "provider_thread.updated",
3643 (
3644 event,
3645 ): event is Extract<
3646 ProviderAdapter.ProviderAdapterV2Event,
3647 { type: "provider_thread.updated" }
3648 > => event.type === "provider_thread.updated",
36403649 );
36413650
36423651 it.effect(
......@@ -3932,8 +3941,9 @@ describe("ClaudeAdapterV2 background wake turns", () => {
39323941 });
39333942 const processQueues: Array<Queue.Queue<SDKMessage>> = [];
39343943 const firstCloseRequested = yield* Deferred.make<void>();
3935 const events: Array<ProviderAdapterV2Event> = [];
3936 const continuationRequests: Array<ProviderContinuationRequest> = [];
3944 const events: Array<ProviderAdapter.ProviderAdapterV2Event> = [];
3945 const continuationRequests: Array<ProviderContinuationRequests.ProviderContinuationRequest> =
3946 [];
39373947 const adapter = ClaudeAdapterV2.makeClaudeAdapterV2({
39383948 instanceId: ClaudeAdapterV2.CLAUDE_DEFAULT_INSTANCE_ID,
39393949 settings: DEFAULT_CLAUDE_SETTINGS,
......@@ -4179,7 +4189,7 @@ describe("ClaudeAdapterV2 background wake turns", () => {
41794189 readonly nativeThreadId: string;
41804190 readonly queue: Queue.Queue<SDKMessage>;
41814191 }> = [];
4182 const events: Array<ProviderAdapterV2Event> = [];
4192 const events: Array<ProviderAdapter.ProviderAdapterV2Event> = [];
41834193 const adapter = ClaudeAdapterV2.makeClaudeAdapterV2({
41844194 instanceId: ClaudeAdapterV2.CLAUDE_DEFAULT_INSTANCE_ID,
41854195 settings: DEFAULT_CLAUDE_SETTINGS,
......@@ -4430,7 +4440,7 @@ describe("ClaudeAdapterV2 background wake turns", () => {
44304440 Effect.gen(function* () {
44314441 const harness = yield* makeWakeHarness;
44324442 const now = yield* DateTime.now;
4433 const runtimePolicy = ProviderAdapterV2RuntimePolicy.make({
4443 const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
44344444 runtimeMode,
44354445 interactionMode: "default",
44364446 cwd: "/workspace",
......@@ -4731,7 +4741,7 @@ describe("ClaudeAdapterV2 background wake turns", () => {
47314741 Effect.gen(function* () {
47324742 const harness = yield* makeWakeHarness;
47334743 const now = yield* DateTime.now;
4734 const approvalPolicy = ProviderAdapterV2RuntimePolicy.make({
4744 const approvalPolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
47354745 runtimeMode: "approval-required",
47364746 interactionMode: "default",
47374747 cwd: "/workspace",
......@@ -5972,7 +5982,7 @@ describe("ClaudeAdapterV2 background wake turns", () => {
59725982 session_id: WAKE_NATIVE_SESSION,
59735983 });
59745984 const subagentRouting = (
5975 events: ReadonlyArray<ProviderAdapterV2Event>,
5985 events: ReadonlyArray<ProviderAdapter.ProviderAdapterV2Event>,
59765986 nativeToolIds: ReadonlyArray<string>,
59775987 ) => {
59785988 const childThreadId =
......@@ -6090,7 +6100,7 @@ describe("ClaudeAdapterV2 background wake turns", () => {
60906100 const userTurn = (
60916101 attempt: string,
60926102 providerTurnOrdinal: number,
6093 attachments: ProviderAdapterV2TurnInput["message"]["attachments"] = [],
6103 attachments: ProviderAdapter.ProviderAdapterV2TurnInput["message"]["attachments"] = [],
60946104 ) =>
60956105 harness.runtime.startTurn(
60966106 makeClaudeTestTurnInput({
......@@ -7838,8 +7848,12 @@ describe("ClaudeAdapterV2 background wake turns", () => {
78387848 const now = yield* DateTime.now;
78397849 const subagentEvents = () =>
78407850 harness.events.filter(
7841 (event): event is Extract<ProviderAdapterV2Event, { type: "subagent.updated" }> =>
7842 event.type === "subagent.updated",
7851 (
7852 event,
7853 ): event is Extract<
7854 ProviderAdapter.ProviderAdapterV2Event,
7855 { type: "subagent.updated" }
7856 > => event.type === "subagent.updated",
78437857 );
78447858
78457859 yield* harness.runtime.startTurn(
......@@ -7942,8 +7956,12 @@ describe("ClaudeAdapterV2 background wake turns", () => {
79427956 const now = yield* DateTime.now;
79437957 const subagentEvents = () =>
79447958 harness.events.filter(
7945 (event): event is Extract<ProviderAdapterV2Event, { type: "subagent.updated" }> =>
7946 event.type === "subagent.updated",
7959 (
7960 event,
7961 ): event is Extract<
7962 ProviderAdapter.ProviderAdapterV2Event,
7963 { type: "subagent.updated" }
7964 > => event.type === "subagent.updated",
79477965 );
79487966
79497967 yield* harness.runtime.startTurn(
......@@ -8059,8 +8077,12 @@ describe("ClaudeAdapterV2 background wake turns", () => {
80598077 const now = yield* DateTime.now;
80608078 const subagentEvents = () =>
80618079 harness.events.filter(
8062 (event): event is Extract<ProviderAdapterV2Event, { type: "subagent.updated" }> =>
8063 event.type === "subagent.updated",
8080 (
8081 event,
8082 ): event is Extract<
8083 ProviderAdapter.ProviderAdapterV2Event,
8084 { type: "subagent.updated" }
8085 > => event.type === "subagent.updated",
80648086 );
80658087
80668088 yield* harness.runtime.startTurn(
......@@ -8249,7 +8271,9 @@ describe("ClaudeAdapterV2 background wake turns", () => {
82498271 // already pre-opened by the wake buffer before the drain replay.
82508272 const nodeStatuses = harness.events
82518273 .filter(
8252 (event): event is Extract<ProviderAdapterV2Event, { type: "node.updated" }> =>
8274 (
8275 event,
8276 ): event is Extract<ProviderAdapter.ProviderAdapterV2Event, { type: "node.updated" }> =>
82538277 event.type === "node.updated" &&
82548278 event.node.kind === "subagent" &&
82558279 event.node.nativeItemRef?.nativeId === SUBAGENT_TASK_ID,
......@@ -8288,8 +8312,9 @@ describe("ClaudeAdapterV2 background wake turns", () => {
82888312 prefix: "t3-claude-v2-process-reset-",
82898313 });
82908314 const processQueues: Array<Queue.Queue<SDKMessage>> = [];
8291 const events: Array<ProviderAdapterV2Event> = [];
8292 const continuationRequests: Array<ProviderContinuationRequest> = [];
8315 const events: Array<ProviderAdapter.ProviderAdapterV2Event> = [];
8316 const continuationRequests: Array<ProviderContinuationRequests.ProviderContinuationRequest> =
8317 [];
82938318 const adapter = ClaudeAdapterV2.makeClaudeAdapterV2({
82948319 instanceId: ClaudeAdapterV2.CLAUDE_DEFAULT_INSTANCE_ID,
82958320 settings: DEFAULT_CLAUDE_SETTINGS,
......@@ -8472,8 +8497,9 @@ describe("ClaudeAdapterV2 background wake turns", () => {
84728497 prefix: "t3-claude-v2-buffer-replace-",
84738498 });
84748499 const processQueues: Array<Queue.Queue<SDKMessage>> = [];
8475 const events: Array<ProviderAdapterV2Event> = [];
8476 const continuationRequests: Array<ProviderContinuationRequest> = [];
8500 const events: Array<ProviderAdapter.ProviderAdapterV2Event> = [];
8501 const continuationRequests: Array<ProviderContinuationRequests.ProviderContinuationRequest> =
8502 [];
84778503 const adapter = ClaudeAdapterV2.makeClaudeAdapterV2({
84788504 instanceId: ClaudeAdapterV2.CLAUDE_DEFAULT_INSTANCE_ID,
84798505 settings: DEFAULT_CLAUDE_SETTINGS,
......@@ -8708,8 +8734,9 @@ describe("ClaudeAdapterV2 background wake turns", () => {
87088734 prefix: "t3-claude-v2-subagent-buffer-replace-",
87098735 });
87108736 const processQueues: Array<Queue.Queue<SDKMessage>> = [];
8711 const events: Array<ProviderAdapterV2Event> = [];
8712 const continuationRequests: Array<ProviderContinuationRequest> = [];
8737 const events: Array<ProviderAdapter.ProviderAdapterV2Event> = [];
8738 const continuationRequests: Array<ProviderContinuationRequests.ProviderContinuationRequest> =
8739 [];
87138740 const adapter = ClaudeAdapterV2.makeClaudeAdapterV2({
87148741 instanceId: ClaudeAdapterV2.CLAUDE_DEFAULT_INSTANCE_ID,
87158742 settings: DEFAULT_CLAUDE_SETTINGS,
......@@ -8775,8 +8802,12 @@ describe("ClaudeAdapterV2 background wake turns", () => {
87758802 const hasPendingBackgroundWork = runtime.hasPendingBackgroundWork;
87768803 const subagentEvents = () =>
87778804 events.filter(
8778 (event): event is Extract<ProviderAdapterV2Event, { type: "subagent.updated" }> =>
8779 event.type === "subagent.updated",
8805 (
8806 event,
8807 ): event is Extract<
8808 ProviderAdapter.ProviderAdapterV2Event,
8809 { type: "subagent.updated" }
8810 > => event.type === "subagent.updated",
87808811 );
87818812 const now = yield* DateTime.now;
87828813
......@@ -8880,7 +8911,9 @@ describe("ClaudeAdapterV2 background wake turns", () => {
88808911 assert.equal(finalSubagent?.result, SUBAGENT_SUMMARY);
88818912 assert.equal(finalSubagent?.runId, subagentEvents()[0]?.subagent.runId);
88828913 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" }> =>
88848917 event.type === "node.updated" &&
88858918 event.node.kind === "subagent" &&
88868919 event.node.nativeItemRef?.nativeId === SUBAGENT_TASK_ID,
......@@ -8902,7 +8935,7 @@ describe("ClaudeAdapterV2 background wake turns", () => {
89028935 prefix: "t3-claude-v2-model-change-running-subagent-",
89038936 });
89048937 const processQueues: Array<Queue.Queue<SDKMessage>> = [];
8905 const events: Array<ProviderAdapterV2Event> = [];
8938 const events: Array<ProviderAdapter.ProviderAdapterV2Event> = [];
89068939 const adapter = ClaudeAdapterV2.makeClaudeAdapterV2({
89078940 instanceId: ClaudeAdapterV2.CLAUDE_DEFAULT_INSTANCE_ID,
89088941 settings: DEFAULT_CLAUDE_SETTINGS,
......@@ -9069,7 +9102,7 @@ describe("ClaudeAdapterV2 background wake turns", () => {
90699102 });
90709103 let openCount = 0;
90719104 const processQueues: Array<Queue.Queue<SDKMessage>> = [];
9072 const events: Array<ProviderAdapterV2Event> = [];
9105 const events: Array<ProviderAdapter.ProviderAdapterV2Event> = [];
90739106 const adapter = ClaudeAdapterV2.makeClaudeAdapterV2({
90749107 instanceId: ClaudeAdapterV2.CLAUDE_DEFAULT_INSTANCE_ID,
90759108 settings: DEFAULT_CLAUDE_SETTINGS,
......@@ -9198,8 +9231,9 @@ describe("ClaudeAdapterV2 background wake turns", () => {
91989231 });
91999232 let openCount = 0;
92009233 const processQueues: Array<Queue.Queue<SDKMessage>> = [];
9201 const events: Array<ProviderAdapterV2Event> = [];
9202 const continuationRequests: Array<ProviderContinuationRequest> = [];
9234 const events: Array<ProviderAdapter.ProviderAdapterV2Event> = [];
9235 const continuationRequests: Array<ProviderContinuationRequests.ProviderContinuationRequest> =
9236 [];
92039237 const adapter = ClaudeAdapterV2.makeClaudeAdapterV2({
92049238 instanceId: ClaudeAdapterV2.CLAUDE_DEFAULT_INSTANCE_ID,
92059239 settings: DEFAULT_CLAUDE_SETTINGS,
......@@ -9394,7 +9428,7 @@ describe("ClaudeAdapterV2 background wake turns", () => {
93949428 const attachmentsDir = yield* fileSystem.makeTempDirectoryScoped({
93959429 prefix: "t3-claude-v2-first-open-fail-",
93969430 });
9397 const events: Array<ProviderAdapterV2Event> = [];
9431 const events: Array<ProviderAdapter.ProviderAdapterV2Event> = [];
93989432 const adapter = ClaudeAdapterV2.makeClaudeAdapterV2({
93999433 instanceId: ClaudeAdapterV2.CLAUDE_DEFAULT_INSTANCE_ID,
94009434 settings: DEFAULT_CLAUDE_SETTINGS,
......@@ -9496,7 +9530,7 @@ describe("ClaudeAdapterV2 background wake turns", () => {
94969530 uuid,
94979531 session_id: WAKE_NATIVE_SESSION,
94989532 });
9499 const goalStatuses = (events: ReadonlyArray<ProviderAdapterV2Event>) =>
9533 const goalStatuses = (events: ReadonlyArray<ProviderAdapter.ProviderAdapterV2Event>) =>
95009534 events.flatMap((event) =>
95019535 event.type === "provider_thread.updated" && event.providerThread.goal != null
95029536 ? [event.providerThread.goal]
apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts+7-11
......@@ -101,10 +101,9 @@ import {
101101} from "../../provider/ClaudeModelCatalog.ts";
102102import {
103103 boundProviderEventForLogging,
104 type EventNdjsonLogger,
105104 shouldPersistProviderEvent,
106105} from "../../provider/EventNdjsonLogger.ts";
107import * as ProviderEventLoggers from "../../provider/ProviderEventLoggers.ts";
106import * as ProviderEventLoggers from "@t3tools/provider-core/server/ProviderEventLoggers";
108107import {
109108 claudeRateLimitEventToUpdate,
110109 type ClaudeScopedLimitNames,
......@@ -135,7 +134,7 @@ import {
135134 type BackgroundWorkReport,
136135 backgroundWorkNotification,
137136} from "@t3tools/provider-core/server/notification";
138import * as ProviderContinuationRequests from "@t3tools/provider-core/server/continuationRequests";
137import * as ProviderContinuationRequests from "@t3tools/provider-core/server/ProviderContinuationRequests";
139138import {
140139 makeSubagentChildThread,
141140 makeSubagentConversationArtifacts,
......@@ -570,7 +569,7 @@ export function loggedClaudeQueryOptions(
570569}
571570
572571export function makeClaudeAgentSdkProtocolLogger(input: {
573 readonly nativeEventLogger: EventNdjsonLogger | undefined;
572 readonly nativeEventLogger: ProviderEventLoggers.EventNdjsonLogger | undefined;
574573 readonly threadId: ThreadId;
575574 readonly providerSessionId: OrchestrationV2ProviderSession["id"];
576575}): ClaudeAgentSdkProtocolLogger | undefined {
......@@ -1118,7 +1117,7 @@ function resultTextFromSdkMessage(
11181117}
11191118
11201119function makeProviderThread(input: {
1121 readonly idAllocator: IdAllocator.IdAllocatorV2Shape;
1120 readonly idAllocator: IdAllocator.IdAllocatorV2["Service"];
11221121 readonly providerInstanceId: ProviderInstanceId;
11231122 readonly appThreadId: OrchestrationV2ProviderThread["appThreadId"];
11241123 readonly ownerNodeId?: OrchestrationV2ProviderThread["ownerNodeId"];
......@@ -2581,7 +2580,7 @@ function providerFailureFromApiRetry(message: SDKAPIRetryMessage): Orchestration
25812580}
25822581
25832582function buildAssistantArtifacts(input: {
2584 readonly idAllocator: IdAllocator.IdAllocatorV2Shape;
2583 readonly idAllocator: IdAllocator.IdAllocatorV2["Service"];
25852584 readonly turnInput: ProviderAdapter.ProviderAdapterV2TurnInput;
25862585 readonly providerTurnId: OrchestrationV2ProviderTurn["id"];
25872586 readonly nativeItemId: string;
......@@ -3015,7 +3014,7 @@ export interface ClaudeAdapterV2Options {
30153014 readonly fileSystem: FileSystem.FileSystem;
30163015 readonly path: Path.Path;
30173016 readonly crypto: Crypto.Crypto;
3018 readonly idAllocator: IdAllocator.IdAllocatorV2Shape;
3017 readonly idAllocator: IdAllocator.IdAllocatorV2["Service"];
30193018 readonly queryRunner: ClaudeAgentSdkQueryRunnerShape;
30203019 readonly scopedLimitNames?: Ref.Ref<ClaudeScopedLimitNames>;
30213020 readonly onUsageLimits?: ServerProviderShape["applyUsageLimits"];
......@@ -3029,7 +3028,7 @@ export interface ClaudeAdapterV2Options {
30293028
30303029export function makeClaudeAdapterV2(
30313030 adapterOptions: ClaudeAdapterV2Options,
3032): ProviderAdapter.ProviderAdapterV2Shape {
3031): ProviderAdapter.ProviderAdapterV2["Service"] {
30333032 const { attachmentsDir, fileSystem, path, crypto, idAllocator, queryRunner } = adapterOptions;
30343033 const continuationRequests = adapterOptions.continuationRequests ?? {
30353034 offer: () => Effect.void,
......@@ -3356,7 +3355,6 @@ export function makeClaudeAdapterV2(
33563355 type: "turn_item.updated",
33573356 driver: CLAUDE_PROVIDER,
33583357 turnItem: makeProviderRetryTurnItem({
3359 idAllocator,
33603358 driver: CLAUDE_PROVIDER,
33613359 threadId: context.input.threadId,
33623360 runId: context.input.runId,
......@@ -5083,7 +5081,6 @@ export function makeClaudeAdapterV2(
50835081 type: "turn_item.updated",
50845082 driver: CLAUDE_PROVIDER,
50855083 turnItem: makeProviderRetryTurnItem({
5086 idAllocator,
50875084 driver: CLAUDE_PROVIDER,
50885085 threadId: input.context.input.threadId,
50895086 runId: input.context.input.runId,
......@@ -6089,7 +6086,6 @@ export function makeClaudeAdapterV2(
60896086 type: "turn_item.updated",
60906087 driver: CLAUDE_PROVIDER,
60916088 turnItem: makeProviderRetryTurnItem({
6092 idAllocator,
60936089 driver: CLAUDE_PROVIDER,
60946090 threadId: context.input.threadId,
60956091 runId: context.input.runId,
apps/server/src/orchestration-v2/Adapters/CodexAdapterV2.test.ts+108-57
......@@ -1,7 +1,6 @@
11import * as NodeOS from "node:os";
22
33import { historyResponseItems } from "@t3tools/provider-core/server/handoffBudget";
4import type { ProviderAdapterV2HistoricalContext } from "@t3tools/provider-core/server/ProviderAdapter";
54import {
65 makeProviderTextDeltaCoalescer,
76 type ProviderTextDeltaUpdate,
......@@ -56,21 +55,13 @@ import { ChildProcess, ChildProcessSpawner } from "effect/process";
5655import packageJson from "../../../package.json" with { type: "json" };
5756import * as ServerConfig from "../../config.ts";
5857import * as McpProviderSession from "@t3tools/provider-core/server/mcpSession";
59import type { EventNdjsonLogger } from "../../provider/EventNdjsonLogger.ts";
60import * as ProviderEventLoggers from "../../provider/ProviderEventLoggers.ts";
58import * as ProviderEventLoggers from "@t3tools/provider-core/server/ProviderEventLoggers";
6159import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator";
6260import * as EffectWorker from "../EffectWorker.ts";
6361import * as Orchestrator from "../Orchestrator.ts";
6462import * as ProviderReplayHarness from "../testkit/ProviderReplayHarness.ts";
65import {
66 ProviderAdapterForkThreadError,
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";
63import * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter";
64import type * as ProviderContinuationRequests from "@t3tools/provider-core/server/ProviderContinuationRequests";
7465import * as CodexAdapterV2 from "./CodexAdapterV2.ts";
7566import { makeReplayServerConfig, withCodexReplayChildMetadata } from "./CodexAdapterV2.testkit.ts";
7667import * as CodexAdapterV2Testkit from "./CodexAdapterV2.testkit.ts";
......@@ -767,7 +758,7 @@ describe("CodexAdapterV2 process spawning", () => {
767758 instanceId: CodexAdapterV2.CODEX_DEFAULT_INSTANCE_ID,
768759 threadId: ThreadId.make("thread-launch-args"),
769760 providerSessionId: ProviderSessionId.make("provider-session-launch-args"),
770 runtimePolicy: ProviderAdapterV2RuntimePolicy.make({
761 runtimePolicy: ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
771762 runtimeMode: "full-access",
772763 interactionMode: "default",
773764 cwd: "/workspace",
......@@ -1036,7 +1027,7 @@ describe("CodexAdapterV2 native protocol logging", () => {
10361027 readonly event: unknown;
10371028 readonly threadId: ThreadId | null;
10381029 }> = [];
1039 const logger: EventNdjsonLogger = {
1030 const logger: ProviderEventLoggers.EventNdjsonLogger = {
10401031 filePath: "/tmp/events.log",
10411032 write: (event, threadId) =>
10421033 Effect.sync(() => {
......@@ -1410,7 +1401,7 @@ describe("CodexAdapterV2 fork boundary", () => {
14101401 }),
14111402 );
14121403
1413 assert.instanceOf(error, ProviderAdapterForkThreadError);
1404 assert.instanceOf(error, ProviderAdapter.ProviderAdapterForkThreadError);
14141405 assert.include(String(error.cause), "provider-turn-missing");
14151406 }),
14161407 );
......@@ -1478,7 +1469,7 @@ const CODEX_TEST_MODEL_SELECTION = {
14781469 instanceId: CodexAdapterV2.CODEX_DEFAULT_INSTANCE_ID,
14791470 model: "gpt-5.4",
14801471} satisfies ModelSelection;
1481const CODEX_TEST_RUNTIME_POLICY = ProviderAdapterV2RuntimePolicy.make({
1472const CODEX_TEST_RUNTIME_POLICY = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
14821473 runtimeMode: "full-access",
14831474 interactionMode: "default",
14841475 cwd: "/workspace",
......@@ -1524,7 +1515,7 @@ function makeCodexTestTurnInput(input: {
15241515 readonly now: DateTime.Utc;
15251516 readonly attemptId: RunAttemptId;
15261517 readonly text: string;
1527}): ProviderAdapterV2TurnInput {
1518}): ProviderAdapter.ProviderAdapterV2TurnInput {
15281519 return {
15291520 appThread: makeCodexTestAppThread(input),
15301521 threadId: input.threadId,
......@@ -1747,7 +1738,7 @@ describe("CodexAdapterV2 session initialize", () => {
17471738 ),
17481739 Effect.mapError(
17491740 (cause) =>
1750 new ProviderAdapterOpenSessionError({
1741 new ProviderAdapter.ProviderAdapterOpenSessionError({
17511742 driver: CodexAdapterV2.CODEX_DRIVER_KIND,
17521743 providerSessionId: openInput.providerSessionId,
17531744 cause,
......@@ -1907,7 +1898,8 @@ describe("CodexAdapterV2 post-settle continuation", () => {
19071898
19081899 const makeCodexReplayHarness = (
19091900 transcript: CodexReplay.CodexAppServerReplayTranscript,
1910 onEvent: (event: ProviderAdapterV2Event) => Effect.Effect<unknown> = () => Effect.void,
1901 onEvent: (event: ProviderAdapter.ProviderAdapterV2Event) => Effect.Effect<unknown> = () =>
1902 Effect.void,
19111903 onRequest: (method: string, params: unknown) => Effect.Effect<void> = () => Effect.void,
19121904 readChildMetadata?: Parameters<typeof withCodexReplayChildMetadata>[2],
19131905 ) =>
......@@ -1915,13 +1907,14 @@ describe("CodexAdapterV2 post-settle continuation", () => {
19151907 const fileSystem = yield* FileSystem.FileSystem;
19161908 const idAllocator = yield* IdAllocator.IdAllocatorV2;
19171909 const serverConfig = yield* makeReplayServerConfig(transcript.scenario).pipe(Effect.orDie);
1918 const continuationRequests: Array<ProviderContinuationRequest> = [];
1910 const continuationRequests: Array<ProviderContinuationRequests.ProviderContinuationRequest> =
1911 [];
19191912 const clientFactory: CodexAdapterV2.CodexAppServerClientFactoryShape = {
19201913 open: (openInput) =>
19211914 Layer.build(CodexReplay.layerReplay(transcript)).pipe(
19221915 Effect.mapError(
19231916 (cause) =>
1924 new ProviderAdapterOpenSessionError({
1917 new ProviderAdapter.ProviderAdapterOpenSessionError({
19251918 driver: CodexAdapterV2.CODEX_DRIVER_KIND,
19261919 providerSessionId: openInput.providerSessionId,
19271920 cause,
......@@ -1975,7 +1968,7 @@ describe("CodexAdapterV2 post-settle continuation", () => {
19751968 modelSelection: CODEX_TEST_MODEL_SELECTION,
19761969 runtimePolicy: CODEX_TEST_RUNTIME_POLICY,
19771970 });
1978 const events: Array<ProviderAdapterV2Event> = [];
1971 const events: Array<ProviderAdapter.ProviderAdapterV2Event> = [];
19791972 const firstTerminal = yield* Deferred.make<void>();
19801973 yield* runtime.events.pipe(
19811974 Stream.runForEach((event) =>
......@@ -1998,13 +1991,19 @@ describe("CodexAdapterV2 post-settle continuation", () => {
19981991 const hasPendingBackgroundWork = runtime.hasPendingBackgroundWork;
19991992 const terminalEvents = () =>
20001993 events.filter(
2001 (event): event is Extract<ProviderAdapterV2Event, { type: "turn.terminal" }> =>
1994 (
1995 event,
1996 ): event is Extract<ProviderAdapter.ProviderAdapterV2Event, { type: "turn.terminal" }> =>
20021997 event.type === "turn.terminal",
20031998 );
20041999 const subagentUpdates = () =>
20052000 events.filter(
2006 (event): event is Extract<ProviderAdapterV2Event, { type: "subagent.updated" }> =>
2007 event.type === "subagent.updated",
2001 (
2002 event,
2003 ): event is Extract<
2004 ProviderAdapter.ProviderAdapterV2Event,
2005 { type: "subagent.updated" }
2006 > => event.type === "subagent.updated",
20082007 );
20092008 return {
20102009 runtime,
......@@ -2026,7 +2025,7 @@ describe("CodexAdapterV2 post-settle continuation", () => {
20262025 Effect.gen(function* () {
20272026 const nativeThreadId = `inject-${response}`;
20282027 const prompt = "Only the current request";
2029 const history: ProviderAdapterV2HistoricalContext = {
2028 const history: ProviderAdapter.ProviderAdapterV2HistoricalContext = {
20302029 context: "Historical conversation",
20312030 messages: (["user", "assistant"] as const).map((role) => ({
20322031 role,
......@@ -2528,12 +2527,14 @@ describe("CodexAdapterV2 post-settle continuation", () => {
25282527 }).pipe(Effect.scoped, Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))),
25292528 );
25302529
2531 const assistantMessages = (events: ReadonlyArray<ProviderAdapterV2Event>) =>
2530 const assistantMessages = (events: ReadonlyArray<ProviderAdapter.ProviderAdapterV2Event>) =>
25322531 events.filter(
2533 (event): event is Extract<ProviderAdapterV2Event, { type: "message.updated" }> =>
2532 (
2533 event,
2534 ): event is Extract<ProviderAdapter.ProviderAdapterV2Event, { type: "message.updated" }> =>
25342535 event.type === "message.updated" && event.message.role === "assistant",
25352536 );
2536 const assistantTurnItems = (events: ReadonlyArray<ProviderAdapterV2Event>) =>
2537 const assistantTurnItems = (events: ReadonlyArray<ProviderAdapter.ProviderAdapterV2Event>) =>
25372538 events.flatMap((event) =>
25382539 event.type === "turn_item.updated" && event.turnItem.type === "assistant_message"
25392540 ? [event.turnItem]
......@@ -4914,8 +4915,12 @@ describe("CodexAdapterV2 post-settle continuation", () => {
49144915 );
49154916
49164917 const providerTurnId = harness.events.find(
4917 (event): event is Extract<ProviderAdapterV2Event, { type: "provider_turn.updated" }> =>
4918 event.type === "provider_turn.updated",
4918 (
4919 event,
4920 ): event is Extract<
4921 ProviderAdapter.ProviderAdapterV2Event,
4922 { type: "provider_turn.updated" }
4923 > => event.type === "provider_turn.updated",
49194924 )?.providerTurn.id;
49204925 assert.isDefined(providerTurnId);
49214926
......@@ -4931,7 +4936,7 @@ describe("CodexAdapterV2 post-settle continuation", () => {
49314936 assert.isAtLeast(terminalIndex, 0);
49324937
49334938 let lastCommandBeforeTerminal:
4934 | Extract<ProviderAdapterV2Event, { type: "turn_item.updated" }>
4939 | Extract<ProviderAdapter.ProviderAdapterV2Event, { type: "turn_item.updated" }>
49354940 | undefined;
49364941 for (let index = 0; index < terminalIndex; index++) {
49374942 const event = harness.events[index];
......@@ -4997,8 +5002,12 @@ describe("CodexAdapterV2 post-settle continuation", () => {
49975002 );
49985003
49995004 const commandUpdates = harness.events.filter(
5000 (event): event is Extract<ProviderAdapterV2Event, { type: "turn_item.updated" }> =>
5001 event.type === "turn_item.updated" && event.turnItem.type === "command_execution",
5005 (
5006 event,
5007 ): event is Extract<
5008 ProviderAdapter.ProviderAdapterV2Event,
5009 { type: "turn_item.updated" }
5010 > => event.type === "turn_item.updated" && event.turnItem.type === "command_execution",
50025011 );
50035012 assert.isAtLeast(commandUpdates.length, 2, "start + interrupt terminalization");
50045013 assert.equal(commandUpdates[commandUpdates.length - 1]?.turnItem.status, "interrupted");
......@@ -5142,7 +5151,7 @@ describe("CodexAdapterV2 post-settle continuation", () => {
51425151 });
51435152
51445153 const assertChildProviderTerminalBeforeRoot = (
5145 events: ReadonlyArray<ProviderAdapterV2Event>,
5154 events: ReadonlyArray<ProviderAdapter.ProviderAdapterV2Event>,
51465155 rootThreadId: ThreadId,
51475156 ) => {
51485157 const terminalIndex = events.findIndex((event) => event.type === "turn.terminal");
......@@ -5199,8 +5208,12 @@ describe("CodexAdapterV2 post-settle continuation", () => {
51995208 "running child command item",
52005209 );
52015210 const providerTurnId = harness.events.find(
5202 (event): event is Extract<ProviderAdapterV2Event, { type: "provider_turn.updated" }> =>
5203 event.type === "provider_turn.updated" && event.threadId === harness.threadId,
5211 (
5212 event,
5213 ): event is Extract<
5214 ProviderAdapter.ProviderAdapterV2Event,
5215 { type: "provider_turn.updated" }
5216 > => event.type === "provider_turn.updated" && event.threadId === harness.threadId,
52045217 )?.providerTurn.id;
52055218 assert.isDefined(providerTurnId);
52065219
......@@ -5212,7 +5225,12 @@ describe("CodexAdapterV2 post-settle continuation", () => {
52125225 yield* awaitUntil(() => harness.terminalEvents().length === 1, "interrupted root terminal");
52135226 assert.equal(harness.terminalEvents()[0]?.status, "interrupted");
52145227 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 > =>
52165234 event.type === "turn_item.updated" &&
52175235 event.turnItem.type === "command_execution" &&
52185236 event.turnItem.nativeItemRef?.nativeId === INTERRUPT_CHILD_COMMAND_ITEM,
......@@ -5289,8 +5307,12 @@ describe("CodexAdapterV2 post-settle continuation", () => {
52895307 "running child command item",
52905308 );
52915309 const providerTurnId = harness.events.find(
5292 (event): event is Extract<ProviderAdapterV2Event, { type: "provider_turn.updated" }> =>
5293 event.type === "provider_turn.updated" && event.threadId === harness.threadId,
5310 (
5311 event,
5312 ): event is Extract<
5313 ProviderAdapter.ProviderAdapterV2Event,
5314 { type: "provider_turn.updated" }
5315 > => event.type === "provider_turn.updated" && event.threadId === harness.threadId,
52945316 )?.providerTurn.id;
52955317 assert.isDefined(providerTurnId);
52965318
......@@ -5361,8 +5383,12 @@ describe("CodexAdapterV2 post-settle continuation", () => {
53615383 "running child command item",
53625384 );
53635385 const providerTurnId = harness.events.find(
5364 (event): event is Extract<ProviderAdapterV2Event, { type: "provider_turn.updated" }> =>
5365 event.type === "provider_turn.updated" && event.threadId === harness.threadId,
5386 (
5387 event,
5388 ): event is Extract<
5389 ProviderAdapter.ProviderAdapterV2Event,
5390 { type: "provider_turn.updated" }
5391 > => event.type === "provider_turn.updated" && event.threadId === harness.threadId,
53665392 )?.providerTurn.id;
53675393 assert.isDefined(providerTurnId);
53685394
......@@ -5460,8 +5486,12 @@ describe("CodexAdapterV2 post-settle continuation", () => {
54605486 "running child command item",
54615487 );
54625488 const providerTurnId = harness.events.find(
5463 (event): event is Extract<ProviderAdapterV2Event, { type: "provider_turn.updated" }> =>
5464 event.type === "provider_turn.updated" && event.threadId === harness.threadId,
5489 (
5490 event,
5491 ): event is Extract<
5492 ProviderAdapter.ProviderAdapterV2Event,
5493 { type: "provider_turn.updated" }
5494 > => event.type === "provider_turn.updated" && event.threadId === harness.threadId,
54655495 )?.providerTurn.id;
54665496 assert.isDefined(providerTurnId);
54675497
......@@ -5481,7 +5511,12 @@ describe("CodexAdapterV2 post-settle continuation", () => {
54815511
54825512 yield* awaitUntil(() => harness.terminalEvents().length === 1, "interrupted root terminal");
54835513 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 > =>
54855520 event.type === "provider_turn.updated" &&
54865521 event.providerTurn.nativeTurnRef?.nativeId === INTERRUPT_LATE_CHILD_NATIVE_TURN,
54875522 );
......@@ -5589,8 +5624,12 @@ describe("CodexAdapterV2 post-settle continuation", () => {
55895624 "running child command item",
55905625 );
55915626 const providerTurnId = harness.events.find(
5592 (event): event is Extract<ProviderAdapterV2Event, { type: "provider_turn.updated" }> =>
5593 event.type === "provider_turn.updated" && event.threadId === harness.threadId,
5627 (
5628 event,
5629 ): event is Extract<
5630 ProviderAdapter.ProviderAdapterV2Event,
5631 { type: "provider_turn.updated" }
5632 > => event.type === "provider_turn.updated" && event.threadId === harness.threadId,
55945633 )?.providerTurn.id;
55955634 assert.isDefined(providerTurnId);
55965635
......@@ -5835,8 +5874,12 @@ describe("CodexAdapterV2 post-settle continuation", () => {
58355874 "running command item",
58365875 );
58375876 const providerTurnId = harness.events.find(
5838 (event): event is Extract<ProviderAdapterV2Event, { type: "provider_turn.updated" }> =>
5839 event.type === "provider_turn.updated",
5877 (
5878 event,
5879 ): event is Extract<
5880 ProviderAdapter.ProviderAdapterV2Event,
5881 { type: "provider_turn.updated" }
5882 > => event.type === "provider_turn.updated",
58405883 )?.providerTurn.id;
58415884 assert.isDefined(providerTurnId);
58425885
......@@ -5868,8 +5911,12 @@ describe("CodexAdapterV2 post-settle continuation", () => {
58685911 assert.lengthOf(terminalProviderTurnsBeforeLateEvents, 1);
58695912
58705913 const commandUpdatesBeforeLateEvents = harness.events.filter(
5871 (event): event is Extract<ProviderAdapterV2Event, { type: "turn_item.updated" }> =>
5872 event.type === "turn_item.updated" && event.turnItem.type === "command_execution",
5914 (
5915 event,
5916 ): event is Extract<
5917 ProviderAdapter.ProviderAdapterV2Event,
5918 { type: "turn_item.updated" }
5919 > => event.type === "turn_item.updated" && event.turnItem.type === "command_execution",
58735920 );
58745921 const terminalCommands = commandUpdatesBeforeLateEvents.filter(
58755922 (event) =>
......@@ -5993,8 +6040,12 @@ describe("CodexAdapterV2 post-settle continuation", () => {
59936040 "running command item",
59946041 );
59956042 const providerTurnId = harness.events.find(
5996 (event): event is Extract<ProviderAdapterV2Event, { type: "provider_turn.updated" }> =>
5997 event.type === "provider_turn.updated",
6043 (
6044 event,
6045 ): event is Extract<
6046 ProviderAdapter.ProviderAdapterV2Event,
6047 { type: "provider_turn.updated" }
6048 > => event.type === "provider_turn.updated",
59986049 )?.providerTurn.id;
59996050 assert.isDefined(providerTurnId);
60006051
......@@ -7663,7 +7714,7 @@ describe("CodexAdapterV2 post-settle continuation", () => {
76637714 }),
76647715 );
76657716
7666 assert.instanceOf(error, ProviderAdapterRollbackThreadError);
7717 assert.instanceOf(error, ProviderAdapter.ProviderAdapterRollbackThreadError);
76677718 assert.include(
76687719 errorCauseChainText(error),
76697720 "legacy",
......@@ -7904,7 +7955,7 @@ describe("CodexAdapterV2 post-settle continuation", () => {
79047955 }),
79057956 );
79067957
7907 assert.instanceOf(error, ProviderAdapterForkThreadError);
7958 assert.instanceOf(error, ProviderAdapter.ProviderAdapterForkThreadError);
79087959 assert.include(
79097960 errorCauseChainText(error),
79107961 "legacy",
......@@ -7969,7 +8020,7 @@ describe("CodexAdapterV2 post-settle continuation", () => {
79698020 }),
79708021 );
79718022
7972 assert.instanceOf(error, ProviderAdapterForkThreadError);
8023 assert.instanceOf(error, ProviderAdapter.ProviderAdapterForkThreadError);
79738024 assert.include(errorCauseChainText(error), "fork exploded");
79748025 }).pipe(Effect.scoped, Effect.provide(Layer.merge(IdAllocator.layer, NodeServices.layer))),
79758026 );
......@@ -8033,7 +8084,7 @@ describe("CodexAdapterV2 post-settle continuation", () => {
80338084 }),
80348085 ),
80358086 );
8036 const providerGoals = (events: ReadonlyArray<ProviderAdapterV2Event>) =>
8087 const providerGoals = (events: ReadonlyArray<ProviderAdapter.ProviderAdapterV2Event>) =>
80378088 events.flatMap((event) =>
80388089 event.type === "provider_thread.updated" ? [event.providerThread.goal?.status ?? null] : [],
80398090 );
apps/server/src/orchestration-v2/Adapters/CodexAdapterV2.testkit.ts+2-2
......@@ -15,7 +15,7 @@ import * as Schema from "effect/Schema";
1515
1616import * as ServerConfig from "../../config.ts";
1717import * 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";
1919import { ProviderAdapterDriverCreateError } from "@t3tools/provider-core/server/adapterDriver";
2020import * as ProviderAdapterRegistry from "../ProviderAdapterRegistry.ts";
2121import type { OrchestratorV2ProviderReplayHarness } from "../testkit/ProviderReplayHarness.ts";
......@@ -195,7 +195,7 @@ export function layer(input: {
195195 const context = yield* Layer.build(layerReplay).pipe(
196196 Effect.mapError(
197197 (cause) =>
198 new ProviderAdapterOpenSessionError({
198 new ProviderAdapter.ProviderAdapterOpenSessionError({
199199 driver: CodexAdapterV2.CODEX_DRIVER_KIND,
200200 providerSessionId: openInput.providerSessionId,
201201 cause,
apps/server/src/orchestration-v2/Adapters/CodexAdapterV2.ts+102-106
......@@ -102,10 +102,9 @@ import {
102102} from "../../provider/Drivers/CodexHomeLayout.ts";
103103import {
104104 boundProviderEventForLogging,
105 type EventNdjsonLogger,
106105 shouldPersistProviderEvent,
107106} from "../../provider/EventNdjsonLogger.ts";
108import { ProviderEventLoggers } from "../../provider/ProviderEventLoggers.ts";
107import * as ProviderEventLoggers from "@t3tools/provider-core/server/ProviderEventLoggers";
109108import { codexAppServerArgs, resolveCodexLaunchArgs } from "../../provider/codexLaunchArgs.ts";
110109import { mergeProviderInstanceEnvironment } from "@t3tools/provider-core/server/instanceEnvironment";
111110import * as McpProviderSession from "@t3tools/provider-core/server/mcpSession";
......@@ -121,11 +120,7 @@ import {
121120 type ProviderAdapterDriver,
122121 type ProviderAdapterDriverCreateInput,
123122} from "@t3tools/provider-core/server/adapterDriver";
124import { IdAllocatorV2, type IdAllocatorV2Shape } from "@t3tools/provider-core/server/IdAllocator";
125import {
126 type ProviderContinuationRequest,
127 ProviderContinuationRequests,
128} from "@t3tools/provider-core/server/continuationRequests";
123import * as ProviderContinuationRequests from "@t3tools/provider-core/server/ProviderContinuationRequests";
129124import { backgroundWorkNotification } from "@t3tools/provider-core/server/notification";
130125import {
131126 makeProviderFailure,
......@@ -137,35 +132,13 @@ import {
137132 isProviderNativeImageAttachment,
138133 providerMessageTextWithAttachmentPaths,
139134} 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";
164135import {
165136 makeSubagentChildThread,
166137 makeSubagentConversationArtifacts,
167138 subagentThreadTitle,
168139} 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
170143const CODEX_PROVIDER = ProviderDriverKind.make("codex");
171144export const CODEX_DRIVER_KIND = CODEX_PROVIDER;
......@@ -341,8 +314,11 @@ export const CodexProviderCapabilitiesV2 = {
341314 },
342315} satisfies OrchestrationV2ProviderCapabilities;
343316
344function toProtocolError(detail: string, payload?: unknown): ProviderAdapterProtocolError {
345 return new ProviderAdapterProtocolError({
317function toProtocolError(
318 detail: string,
319 payload?: unknown,
320): ProviderAdapter.ProviderAdapterProtocolError {
321 return new ProviderAdapter.ProviderAdapterProtocolError({
346322 driver: CODEX_PROVIDER,
347323 detail,
348324 ...(payload === undefined ? {} : { payload }),
......@@ -385,7 +361,7 @@ function mapCodexTurnStatus(
385361
386362function providerTurnStatusToTerminal(
387363 status: OrchestrationV2ProviderTurn["status"],
388): Extract<ProviderAdapterV2Event, { type: "turn.terminal" }>["status"] {
364): Extract<ProviderAdapter.ProviderAdapterV2Event, { type: "turn.terminal" }>["status"] {
389365 switch (status) {
390366 case "completed":
391367 return "completed";
......@@ -677,7 +653,7 @@ const decodeCodexTurnStartParamsWithCollaborationMode = Schema.decodeUnknownEffe
677653 CodexTurnStartParamsWithCollaborationMode,
678654);
679655const isProviderAdapterRuntimeRequestResponseError = Schema.is(
680 ProviderAdapterRuntimeRequestResponseError,
656 ProviderAdapter.ProviderAdapterRuntimeRequestResponseError,
681657);
682658
683659function codexRuntimeModeTurnDefaults(runtimeMode: RuntimeMode): {
......@@ -724,7 +700,7 @@ function codexRuntimeModeTurnDefaults(runtimeMode: RuntimeMode): {
724700export function buildCodexTurnStartParams(input: {
725701 readonly nativeThreadId: string;
726702 readonly codexInput: ReadonlyArray<CodexSchema.V2TurnStartParams__UserInput>;
727 readonly runtimePolicy: ProviderAdapterV2RuntimePolicy;
703 readonly runtimePolicy: ProviderAdapter.ProviderAdapterV2RuntimePolicy;
728704 readonly modelSelection: ModelSelection;
729705 readonly hasT3Mcp?: boolean;
730706 readonly browserToolsAvailable?: boolean;
......@@ -732,7 +708,7 @@ export function buildCodexTurnStartParams(input: {
732708 /** ChatGPT token sharing does not accept service tiers. */
733709 readonly omitServiceTier?: boolean;
734710 /** 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"];
736712}) {
737713 return Effect.gen(function* () {
738714 const runtimeModeDefaults = codexRuntimeModeTurnDefaults(input.runtimePolicy.runtimeMode);
......@@ -851,7 +827,7 @@ function getNativeThreadId(providerThread: OrchestrationV2ProviderThread) {
851827
852828function providerThreadFromCodexThread(input: {
853829 readonly appThreadId: ThreadId | null;
854 readonly idAllocator: IdAllocatorV2Shape;
830 readonly idAllocator: IdAllocator.IdAllocatorV2["Service"];
855831 readonly ownerNodeId: OrchestrationV2ProviderThread["ownerNodeId"];
856832 readonly providerSessionId: OrchestrationV2ProviderThread["providerSessionId"];
857833 readonly providerInstanceId: ProviderInstanceId;
......@@ -983,7 +959,7 @@ const countTerminalTurnsAfterBoundary = (
983959};
984960
985961const resolveCodexForkRollbackTurnCount = Effect.fn("CodexAdapterV2.resolveForkRollbackTurnCount")(
986 function* (input: ProviderAdapterV2ForkThreadInput) {
962 function* (input: ProviderAdapter.ProviderAdapterV2ForkThreadInput) {
987963 if (input.providerTurnId === undefined || input.sourceProviderTurns === undefined) {
988964 return 0;
989965 }
......@@ -993,7 +969,7 @@ const resolveCodexForkRollbackTurnCount = Effect.fn("CodexAdapterV2.resolveForkR
993969 input.providerTurnId,
994970 );
995971 if (rollbackTurnCount === null) {
996 return yield* new ProviderAdapterForkThreadError({
972 return yield* new ProviderAdapter.ProviderAdapterForkThreadError({
997973 driver: CODEX_PROVIDER,
998974 providerThreadId: input.sourceProviderThread.id,
999975 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
1012988 * only for source turns that predate native turn references.
1013989 */
1014990export const resolveCodexForkBoundary = Effect.fn("CodexAdapterV2.resolveForkBoundary")(function* (
1015 input: ProviderAdapterV2ForkThreadInput,
991 input: ProviderAdapter.ProviderAdapterV2ForkThreadInput,
1016992) {
1017993 const rollbackTurnCount = yield* resolveCodexForkRollbackTurnCount(input);
1018994 if (input.providerTurnId === undefined || input.sourceProviderTurns === undefined) {
......@@ -1066,14 +1042,14 @@ const readCodexThreadHistoryMetadata = Effect.fn("CodexAdapterV2.readThreadHisto
10661042);
10671043
10681044export const resolveCodexRollbackTurnCount = Effect.fn("CodexAdapterV2.resolveRollbackTurnCount")(
1069 function* (input: ProviderAdapterV2RollbackThreadInput) {
1045 function* (input: ProviderAdapter.ProviderAdapterV2RollbackThreadInput) {
10701046 const providerTurns = input.providerThreadTurns;
10711047 switch (input.target.type) {
10721048 case "thread_start":
10731049 return providerTurns.filter(isTerminalProviderTurn).length;
10741050 case "provider_turn": {
10751051 if (input.target.providerTurn.providerThreadId !== input.providerThread.id) {
1076 return yield* new ProviderAdapterRollbackThreadError({
1052 return yield* new ProviderAdapter.ProviderAdapterRollbackThreadError({
10771053 driver: CODEX_PROVIDER,
10781054 providerThreadId: input.providerThread.id,
10791055 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
10851061 input.target.providerTurn.id,
10861062 );
10871063 if (rollbackTurnCount === null) {
1088 return yield* new ProviderAdapterRollbackThreadError({
1064 return yield* new ProviderAdapter.ProviderAdapterRollbackThreadError({
10891065 driver: CODEX_PROVIDER,
10901066 providerThreadId: input.providerThread.id,
10911067 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 {
11291105 readonly failure: OrchestrationV2ProviderFailure;
11301106 };
11311107 readonly nativeStartReady?: Deferred.Deferred<void>;
1132 readonly input: ProviderAdapterV2TurnInput;
1108 readonly input: ProviderAdapter.ProviderAdapterV2TurnInput;
11331109 readonly projectionAppThread: OrchestrationV2AppThread;
11341110 readonly projectionThreadId: ThreadId;
1135 readonly projectionRunId: ProviderAdapterV2TurnInput["runId"] | null;
1111 readonly projectionRunId: ProviderAdapter.ProviderAdapterV2TurnInput["runId"] | null;
11361112 readonly nativeTurnId: string;
11371113 readonly providerThread: OrchestrationV2ProviderThread;
11381114 readonly providerTurnId: ProviderTurnId;
......@@ -1177,7 +1153,10 @@ function isPersistentCodexDynamicTool(item: CodexDynamicToolItem): boolean {
11771153 return Reflect.get(input, "persistent") === true;
11781154}
11791155
1180type CodexRootTerminalEvent = Extract<ProviderAdapterV2Event, { readonly type: "turn.terminal" }>;
1156type CodexRootTerminalEvent = Extract<
1157 ProviderAdapter.ProviderAdapterV2Event,
1158 { readonly type: "turn.terminal" }
1159>;
11811160
11821161/** A completed root turn whose run stays open for the goal turn Codex starts next. */
11831162interface CodexGoalHold {
......@@ -1294,12 +1273,12 @@ export interface CodexAppServerClientFactoryShape {
12941273 readonly instanceId: ProviderInstanceId;
12951274 readonly threadId: ThreadId;
12961275 readonly providerSessionId: OrchestrationV2ProviderSession["id"];
1297 readonly runtimePolicy: ProviderAdapterV2RuntimePolicy;
1276 readonly runtimePolicy: ProviderAdapter.ProviderAdapterV2RuntimePolicy;
12981277 readonly settings: CodexSettings;
12991278 readonly environment: NodeJS.ProcessEnv;
13001279 }) => Effect.Effect<
13011280 CodexClient.CodexAppServerClient["Service"],
1302 ProviderAdapterOpenSessionError,
1281 ProviderAdapter.ProviderAdapterOpenSessionError,
13031282 Scope.Scope
13041283 >;
13051284}
......@@ -1319,7 +1298,7 @@ export const CODEX_THREAD_CONFIG = { "tools.update_plan.enabled": true } as cons
13191298export function codexThreadRuntimeParams(input: {
13201299 readonly threadId: ThreadId | null;
13211300 readonly modelSelection?: { readonly model: string };
1322 readonly runtimePolicy?: ProviderAdapterV2RuntimePolicy;
1301 readonly runtimePolicy?: ProviderAdapter.ProviderAdapterV2RuntimePolicy;
13231302}): {
13241303 readonly cwd?: string;
13251304 readonly model?: string;
......@@ -1417,7 +1396,7 @@ const layerCodexAppServerClientFactoryCommand = (
14171396 Effect.provideService(Scope.Scope, scope),
14181397 Effect.mapError(
14191398 (cause) =>
1420 new ProviderAdapterOpenSessionError({
1399 new ProviderAdapter.ProviderAdapterOpenSessionError({
14211400 driver: CODEX_PROVIDER,
14221401 providerSessionId: input.providerSessionId,
14231402 cause,
......@@ -1434,7 +1413,7 @@ const layerCodexAppServerClientFactoryCommand = (
14341413 );
14351414
14361415export function makeCodexAppServerProtocolLogger(input: {
1437 readonly nativeEventLogger: EventNdjsonLogger | undefined;
1416 readonly nativeEventLogger: ProviderEventLoggers.EventNdjsonLogger | undefined;
14381417 readonly threadId: ThreadId;
14391418 readonly providerSessionId: OrchestrationV2ProviderSession["id"];
14401419}): CodexClient.CodexAppServerClientOptions["logger"] | undefined {
......@@ -1510,12 +1489,12 @@ function isSensitiveCodexProtocolKey(key: string): boolean {
15101489export const layerAppServerClientFactory: Layer.Layer<
15111490 CodexAppServerClientFactory,
15121491 never,
1513 ChildProcessSpawner.ChildProcessSpawner | ProviderEventLoggers
1492 ChildProcessSpawner.ChildProcessSpawner | ProviderEventLoggers.ProviderEventLoggers
15141493> = Layer.effect(
15151494 CodexAppServerClientFactory,
15161495 Effect.gen(function* () {
15171496 const spawner = yield* ChildProcessSpawner.ChildProcessSpawner;
1518 const { native: nativeEventLogger } = yield* ProviderEventLoggers;
1497 const { native: nativeEventLogger } = yield* ProviderEventLoggers.ProviderEventLoggers;
15191498
15201499 return CodexAppServerClientFactory.of({
15211500 open: (input) =>
......@@ -1536,7 +1515,7 @@ export const layerAppServerClientFactory: Layer.Layer<
15361515 Effect.provideService(Scope.Scope, scope),
15371516 Effect.mapError(
15381517 (cause) =>
1539 new ProviderAdapterOpenSessionError({
1518 new ProviderAdapter.ProviderAdapterOpenSessionError({
15401519 driver: CODEX_PROVIDER,
15411520 providerSessionId: input.providerSessionId,
15421521 cause,
......@@ -1569,7 +1548,7 @@ export type CodexAdapterV2DriverEnv =
15691548 | CodexAppServerClientFactory
15701549 | Crypto.Crypto
15711550 | FileSystem.FileSystem
1572 | IdAllocatorV2
1551 | IdAllocator.IdAllocatorV2
15731552 | Path.Path
15741553 | ServerConfig;
15751554
......@@ -1579,11 +1558,11 @@ export const createCodexAdapterV2 = (
15791558) =>
15801559 Effect.gen(function* () {
15811560 const clientFactory = yield* CodexAppServerClientFactory;
1582 const continuationRequests = yield* ProviderContinuationRequests;
1561 const continuationRequests = yield* ProviderContinuationRequests.ProviderContinuationRequests;
15831562 const crypto = yield* Crypto.Crypto;
15841563 const fileSystem = yield* FileSystem.FileSystem;
15851564 const hostEnvironment = yield* HostProcessEnvironment;
1586 const idAllocator = yield* IdAllocatorV2;
1565 const idAllocator = yield* IdAllocator.IdAllocatorV2;
15871566 const serverConfig = yield* ServerConfig;
15881567 const homeLayout = yield* resolveCodexHomeLayout(config);
15891568
......@@ -1628,18 +1607,22 @@ export const CodexAdapterV2Driver: ProviderAdapterDriver<CodexSettings, CodexAda
16281607};
16291608
16301609const layer: Layer.Layer<
1631 ProviderAdapterV2,
1610 ProviderAdapter.ProviderAdapterV2,
16321611 never,
1633 CodexAppServerClientFactory | Crypto.Crypto | FileSystem.FileSystem | IdAllocatorV2 | ServerConfig
1612 | CodexAppServerClientFactory
1613 | Crypto.Crypto
1614 | FileSystem.FileSystem
1615 | IdAllocator.IdAllocatorV2
1616 | ServerConfig
16341617> = Layer.effect(
1635 ProviderAdapterV2,
1618 ProviderAdapter.ProviderAdapterV2,
16361619 Effect.gen(function* () {
16371620 const clientFactory = yield* CodexAppServerClientFactory;
1638 const continuationRequests = yield* ProviderContinuationRequests;
1621 const continuationRequests = yield* ProviderContinuationRequests.ProviderContinuationRequests;
16391622 const crypto = yield* Crypto.Crypto;
16401623 const fileSystem = yield* FileSystem.FileSystem;
16411624 const hostEnvironment = yield* HostProcessEnvironment;
1642 const idAllocator = yield* IdAllocatorV2;
1625 const idAllocator = yield* IdAllocator.IdAllocatorV2;
16431626 const serverConfig = yield* ServerConfig;
16441627
16451628 return makeCodexAdapterV2({
......@@ -1670,7 +1653,7 @@ export interface CodexAdapterV2Options {
16701653 readonly resolveRuntime?: Effect.Effect<CodexEffectiveRuntime, ProviderSetupError, Scope.Scope>;
16711654 readonly crypto: Crypto.Crypto;
16721655 readonly fileSystem: FileSystem.FileSystem;
1673 readonly idAllocator: IdAllocatorV2Shape;
1656 readonly idAllocator: IdAllocator.IdAllocatorV2["Service"];
16741657 readonly serverConfig: ServerConfig["Service"];
16751658 /**
16761659 * Sink for post-settle background command completions so the orchestrator
......@@ -1678,15 +1661,19 @@ export interface CodexAdapterV2Options {
16781661 * projection-only handling for late item completions.
16791662 */
16801663 readonly continuationRequests?: {
1681 readonly offer: (request: ProviderContinuationRequest) => Effect.Effect<void>;
1664 readonly offer: (
1665 request: ProviderContinuationRequests.ProviderContinuationRequest,
1666 ) => Effect.Effect<void>;
16821667 };
16831668}
16841669
1685export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): ProviderAdapterV2Shape {
1670export function makeCodexAdapterV2(
1671 adapterOptions: CodexAdapterV2Options,
1672): ProviderAdapter.ProviderAdapterV2["Service"] {
16861673 const { clientFactory, crypto, fileSystem, idAllocator, serverConfig } = adapterOptions;
16871674 const continuationRequests = adapterOptions.continuationRequests;
16881675
1689 return ProviderAdapterV2.of({
1676 return ProviderAdapter.ProviderAdapterV2.of({
16901677 instanceId: adapterOptions.instanceId,
16911678 driver: CODEX_PROVIDER,
16921679 getCapabilities: () => Effect.succeed(CodexProviderCapabilitiesV2),
......@@ -1700,7 +1687,7 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi
17001687 : yield* adapterOptions.resolveRuntime.pipe(
17011688 Effect.mapError(
17021689 (cause) =>
1703 new ProviderAdapterOpenSessionError({
1690 new ProviderAdapter.ProviderAdapterOpenSessionError({
17041691 driver: CODEX_PROVIDER,
17051692 providerSessionId: input.providerSessionId,
17061693 cause,
......@@ -1800,7 +1787,7 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi
18001787 model: input.modelSelection.model,
18011788 now,
18021789 });
1803 const events = yield* Queue.unbounded<ProviderAdapterV2Event>();
1790 const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>();
18041791 const rateLimitSnapshot = yield* Ref.make<CodexRateLimitSnapshot | undefined>(undefined);
18051792 const limitedTurnItems = yield* Ref.make(
18061793 new Map<ProviderThreadId, Extract<OrchestrationV2TurnItem, { type: "error" }>>(),
......@@ -1833,7 +1820,9 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi
18331820 context.nativeTurnId,
18341821 ).hasSubagents = true;
18351822 };
1836 const pendingRootTurns = yield* Ref.make(new Map<string, ProviderAdapterV2TurnInput>());
1823 const pendingRootTurns = yield* Ref.make(
1824 new Map<string, ProviderAdapter.ProviderAdapterV2TurnInput>(),
1825 );
18371826 const turnWaiters = yield* Ref.make(new Map<string, Deferred.Deferred<void, never>>());
18381827 const subagentThreads = yield* Ref.make(new Map<string, CodexSubagentThreadContext>());
18391828 const subagentSelections = new Map<string, Omit<ModelSelection, "instanceId">>();
......@@ -1893,7 +1882,7 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi
18931882 const latestGoalTurnId = (providerTurnId: ProviderTurnId) =>
18941883 goalRuns.get(providerTurnId)?.at(-1)?.providerTurnId ?? providerTurnId;
18951884
1896 const emitProviderEvent = (event: ProviderAdapterV2Event) =>
1885 const emitProviderEvent = (event: ProviderAdapter.ProviderAdapterV2Event) =>
18971886 Queue.offer(events, event).pipe(Effect.asVoid);
18981887
18991888 /** Writes the thread's current native goal onto its root provider thread. */
......@@ -1942,7 +1931,6 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi
19421931 type: "turn_item.updated",
19431932 driver: CODEX_PROVIDER,
19441933 turnItem: makeProviderRetryTurnItem({
1945 idAllocator,
19461934 driver: CODEX_PROVIDER,
19471935 threadId: context.projectionThreadId,
19481936 runId: context.projectionRunId,
......@@ -1960,7 +1948,7 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi
19601948 });
19611949
19621950 const registerRootTurn = (input: {
1963 readonly turnInput: ProviderAdapterV2TurnInput;
1951 readonly turnInput: ProviderAdapter.ProviderAdapterV2TurnInput;
19641952 readonly nativeTurnId: string;
19651953 readonly startedAt: DateTime.Utc;
19661954 readonly waitForNativeStart?: boolean;
......@@ -3143,7 +3131,11 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi
31433131 });
31443132
31453133 const toCodexInput = (
3146 turnInput: Pick<ProviderAdapterV2TurnInput | ProviderAdapterV2SteerInput, "message">,
3134 turnInput: Pick<
3135 | ProviderAdapter.ProviderAdapterV2TurnInput
3136 | ProviderAdapter.ProviderAdapterV2SteerInput,
3137 "message"
3138 >,
31473139 ) =>
31483140 Effect.gen(function* () {
31493141 const inputItems: Array<CodexSchema.V2TurnStartParams__UserInput> = [];
......@@ -4539,7 +4531,6 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi
45394531 type: "turn_item.updated",
45404532 driver: CODEX_PROVIDER,
45414533 turnItem: makeProviderRetryTurnItem({
4542 idAllocator,
45434534 driver: CODEX_PROVIDER,
45444535 threadId: context.projectionThreadId,
45454536 runId: context.projectionRunId,
......@@ -5584,7 +5575,6 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi
55845575 }
55855576 if (current.status === "failed" && current.failure.class === "usage_limit") {
55865577 const item = makeProviderFailureTurnItem({
5587 idAllocator,
55885578 driver: CODEX_PROVIDER,
55895579 threadId: context.input.threadId,
55905580 runId: context.input.runId,
......@@ -5735,7 +5725,6 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi
57355725 type: "turn_item.updated",
57365726 driver: CODEX_PROVIDER,
57375727 turnItem: makeProviderRetryTurnItem({
5738 idAllocator,
57395728 driver: CODEX_PROVIDER,
57405729 threadId: input.context.projectionThreadId,
57415730 runId: input.context.projectionRunId,
......@@ -6000,7 +5989,7 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi
60005989 * assistant text, as Claude's own `/goal` output is.
60015990 */
60025991 const completeGoalCommandTurn = Effect.fnUntraced(function* (
6003 turnInput: ProviderAdapterV2TurnInput,
5992 turnInput: ProviderAdapter.ProviderAdapterV2TurnInput,
60045993 reply: string,
60055994 ) {
60065995 const now = yield* DateTime.now;
......@@ -6104,7 +6093,7 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi
61046093 * Stop also reaches.
61056094 */
61066095 const resolveGoalStopTarget = Effect.fnUntraced(function* (
6107 turnInput: ProviderAdapterV2InterruptInput,
6096 turnInput: ProviderAdapter.ProviderAdapterV2InterruptInput,
61086097 ) {
61096098 const goalThreadId = turnInput.providerThread.nativeThreadRef?.nativeId;
61106099 if (goalThreadId == null) {
......@@ -6172,7 +6161,7 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi
61726161
61736162 /** Starts a native turn with this run's full turn configuration. */
61746163 const startNativeTurn = (
6175 turnInput: ProviderAdapterV2TurnInput,
6164 turnInput: ProviderAdapter.ProviderAdapterV2TurnInput,
61766165 codexInput: ReadonlyArray<CodexSchema.V2TurnStartParams__UserInput>,
61776166 ) =>
61786167 Effect.gen(function* () {
......@@ -6227,7 +6216,10 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi
62276216 * turn Codex starts on its own reuses the last turn's settings. Codex
62286217 * continues later goal turns itself. Other commands settle with a reply.
62296218 */
6230 const runGoalCommand = (turnInput: ProviderAdapterV2TurnInput, command: CodexGoalCommand) =>
6219 const runGoalCommand = (
6220 turnInput: ProviderAdapter.ProviderAdapterV2TurnInput,
6221 command: CodexGoalCommand,
6222 ) =>
62316223 Effect.gen(function* () {
62326224 const threadId = yield* getNativeThreadId(turnInput.providerThread);
62336225 // Goal notifications during the command belong to this run's snapshot.
......@@ -6326,7 +6318,7 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi
63266318 }).pipe(
63276319 Effect.mapError(
63286320 (cause) =>
6329 new ProviderAdapterTurnStartError({
6321 new ProviderAdapter.ProviderAdapterTurnStartError({
63306322 driver: CODEX_PROVIDER,
63316323 threadId: turnInput.threadId,
63326324 providerThreadId: turnInput.providerThread.id,
......@@ -6339,8 +6331,12 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi
63396331 // MCP Apps reach their server through Codex's own MCP client, so any
63406332 // server the user configured for Codex works without T3 connecting to it.
63416333 const mcpAppsError = (detail: string) => (cause: unknown) =>
6342 new ProviderAdapterProtocolError({ driver: CODEX_PROVIDER, detail, payload: cause });
6343 const mcpApps: ProviderAdapterV2McpApps = {
6334 new ProviderAdapter.ProviderAdapterProtocolError({
6335 driver: CODEX_PROVIDER,
6336 detail,
6337 payload: cause,
6338 });
6339 const mcpApps: ProviderAdapter.ProviderAdapterV2McpApps = {
63446340 listTools: (toolsInput) =>
63456341 Effect.gen(function* () {
63466342 const threadId = yield* getNativeThreadId(toolsInput.providerThread);
......@@ -6394,7 +6390,7 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi
63946390 }).pipe(Effect.mapError(mcpAppsError("Codex MCP resource read failed."))),
63956391 };
63966392
6397 const runtime: ProviderAdapterV2SessionRuntime = {
6393 const runtime: ProviderAdapter.ProviderAdapterV2SessionRuntime = {
63986394 instanceId: adapterOptions.instanceId,
63996395 driver: CODEX_PROVIDER,
64006396 providerSessionId: input.providerSessionId,
......@@ -6471,7 +6467,7 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi
64716467 ),
64726468 Effect.mapError(
64736469 (cause) =>
6474 new ProviderAdapterEnsureThreadError({
6470 new ProviderAdapter.ProviderAdapterEnsureThreadError({
64756471 driver: CODEX_PROVIDER,
64766472 threadId: threadInput.threadId,
64776473 cause: normalizeCodexCause(cause),
......@@ -6535,7 +6531,7 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi
65356531 }).pipe(
65366532 Effect.mapError(
65376533 (cause) =>
6538 new ProviderAdapterResumeThreadError({
6534 new ProviderAdapter.ProviderAdapterResumeThreadError({
65396535 driver: CODEX_PROVIDER,
65406536 providerSessionId: input.providerSessionId,
65416537 providerThreadId: threadInput.providerThread.id,
......@@ -6563,7 +6559,7 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi
65636559 }).pipe(
65646560 Effect.mapError(
65656561 (cause) =>
6566 new ProviderAdapterTurnStartError({
6562 new ProviderAdapter.ProviderAdapterTurnStartError({
65676563 driver: CODEX_PROVIDER,
65686564 threadId: turnInput.threadId,
65696565 providerThreadId: turnInput.providerThread.id,
......@@ -6593,7 +6589,7 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi
65936589 }).pipe(
65946590 Effect.mapError(
65956591 (cause) =>
6596 new ProviderAdapterProtocolError({
6592 new ProviderAdapter.ProviderAdapterProtocolError({
65976593 driver: CODEX_PROVIDER,
65986594 detail: "Failed to inject historical context",
65996595 cause,
......@@ -6617,7 +6613,7 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi
66176613 }).pipe(
66186614 Effect.mapError(
66196615 (cause) =>
6620 new ProviderAdapterTurnStartError({
6616 new ProviderAdapter.ProviderAdapterTurnStartError({
66216617 driver: CODEX_PROVIDER,
66226618 threadId: turnInput.threadId,
66236619 providerThreadId: turnInput.providerThread.id,
......@@ -6655,7 +6651,7 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi
66556651 }).pipe(
66566652 Effect.mapError(
66576653 (cause) =>
6658 new ProviderAdapterSteerRunError({
6654 new ProviderAdapter.ProviderAdapterSteerRunError({
66596655 driver: CODEX_PROVIDER,
66606656 providerThreadId: turnInput.providerThread.id,
66616657 providerTurnId: turnInput.providerTurnId,
......@@ -6674,7 +6670,7 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi
66746670 Effect.mapError((cause) =>
66756671 cause._tag === "ProviderAdapterProtocolError"
66766672 ? cause
6677 : new ProviderAdapterProtocolError({
6673 : new ProviderAdapter.ProviderAdapterProtocolError({
66786674 driver: CODEX_PROVIDER,
66796675 detail: `Failed to unload Codex thread for provider thread ${unloadInput.providerThread.id}`,
66806676 cause: normalizeCodexCause(cause),
......@@ -7061,7 +7057,7 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi
70617057 }).pipe(
70627058 Effect.mapError(
70637059 (cause) =>
7064 new ProviderAdapterInterruptError({
7060 new ProviderAdapter.ProviderAdapterInterruptError({
70657061 driver: CODEX_PROVIDER,
70667062 providerThreadId: requestedInput.providerThread.id,
70677063 providerTurnId: requestedInput.providerTurnId,
......@@ -7075,7 +7071,7 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi
70757071 String(requestInput.requestId),
70767072 );
70777073 if (pending === undefined) {
7078 return yield* new ProviderAdapterRuntimeRequestResponseError({
7074 return yield* new ProviderAdapter.ProviderAdapterRuntimeRequestResponseError({
70797075 driver: CODEX_PROVIDER,
70807076 requestId: requestInput.requestId,
70817077 cause: toProtocolError(
......@@ -7085,7 +7081,7 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi
70857081 }
70867082 if (pending.type === "user_input") {
70877083 if (requestInput.answers === undefined) {
7088 return yield* new ProviderAdapterRuntimeRequestResponseError({
7084 return yield* new ProviderAdapter.ProviderAdapterRuntimeRequestResponseError({
70897085 driver: CODEX_PROVIDER,
70907086 requestId: requestInput.requestId,
70917087 cause: toProtocolError(
......@@ -7097,7 +7093,7 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi
70977093 return;
70987094 }
70997095 if (requestInput.decision === undefined) {
7100 return yield* new ProviderAdapterRuntimeRequestResponseError({
7096 return yield* new ProviderAdapter.ProviderAdapterRuntimeRequestResponseError({
71017097 driver: CODEX_PROVIDER,
71027098 requestId: requestInput.requestId,
71037099 cause: toProtocolError(
......@@ -7110,7 +7106,7 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi
71107106 Effect.mapError((cause) =>
71117107 isProviderAdapterRuntimeRequestResponseError(cause)
71127108 ? cause
7113 : new ProviderAdapterRuntimeRequestResponseError({
7109 : new ProviderAdapter.ProviderAdapterRuntimeRequestResponseError({
71147110 driver: CODEX_PROVIDER,
71157111 requestId: requestInput.requestId,
71167112 cause,
......@@ -7134,7 +7130,7 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi
71347130 }).pipe(
71357131 Effect.mapError(
71367132 (cause) =>
7137 new ProviderAdapterProtocolError({
7133 new ProviderAdapter.ProviderAdapterProtocolError({
71387134 driver: CODEX_PROVIDER,
71397135 detail: "Failed to upload Codex thread feedback.",
71407136 payload: cause,
......@@ -7167,7 +7163,7 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi
71677163 }).pipe(
71687164 Effect.mapError(
71697165 (cause) =>
7170 new ProviderAdapterReadThreadSnapshotError({
7166 new ProviderAdapter.ProviderAdapterReadThreadSnapshotError({
71717167 driver: CODEX_PROVIDER,
71727168 providerThreadId: threadInput.providerThread.id,
71737169 cause,
......@@ -7200,7 +7196,7 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi
72007196 Effect.andThen(readCodexThreadHistoryMetadata(client.raw, threadId)),
72017197 );
72027198 if (historyMode !== "paginated") {
7203 return yield* new ProviderAdapterRollbackThreadError({
7199 return yield* new ProviderAdapter.ProviderAdapterRollbackThreadError({
72047200 driver: CODEX_PROVIDER,
72057201 providerThreadId: threadInput.providerThread.id,
72067202 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
72447240 }).pipe(
72457241 Effect.mapError(
72467242 (cause) =>
7247 new ProviderAdapterRollbackThreadError({
7243 new ProviderAdapter.ProviderAdapterRollbackThreadError({
72487244 driver: CODEX_PROVIDER,
72497245 providerThreadId: threadInput.providerThread.id,
72507246 cause: normalizeCodexCause(cause),
......@@ -7275,7 +7271,7 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi
72757271 ),
72767272 Effect.mapError(
72777273 (cause) =>
7278 new ProviderAdapterForkThreadError({
7274 new ProviderAdapter.ProviderAdapterForkThreadError({
72797275 driver: CODEX_PROVIDER,
72807276 providerThreadId: threadInput.sourceProviderThread.id,
72817277 cause: normalizeCodexCause(cause),
......@@ -7291,7 +7287,7 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi
72917287 Effect.andThen(readCodexThreadHistoryMetadata(client.raw, response.thread.id)),
72927288 );
72937289 if (historyMode !== "paginated") {
7294 return yield* new ProviderAdapterForkThreadError({
7290 return yield* new ProviderAdapter.ProviderAdapterForkThreadError({
72957291 driver: CODEX_PROVIDER,
72967292 providerThreadId: threadInput.sourceProviderThread.id,
72977293 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
73037299 ),
73047300 Effect.mapError(
73057301 (cause) =>
7306 new ProviderAdapterForkThreadError({
7302 new ProviderAdapter.ProviderAdapterForkThreadError({
73077303 driver: CODEX_PROVIDER,
73087304 providerThreadId: threadInput.sourceProviderThread.id,
73097305 cause: normalizeCodexCause(cause),
......@@ -7328,7 +7324,7 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi
73287324 }).pipe(
73297325 Effect.mapError(
73307326 (cause) =>
7331 new ProviderAdapterForkThreadError({
7327 new ProviderAdapter.ProviderAdapterForkThreadError({
73327328 driver: CODEX_PROVIDER,
73337329 providerThreadId: threadInput.sourceProviderThread.id,
73347330 cause: normalizeCodexCause(cause),
......@@ -7340,7 +7336,7 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi
73407336 }).pipe(
73417337 Effect.mapError(
73427338 (cause) =>
7343 new ProviderAdapterOpenSessionError({
7339 new ProviderAdapter.ProviderAdapterOpenSessionError({
73447340 driver: CODEX_PROVIDER,
73457341 providerSessionId: input.providerSessionId,
73467342 cause,
apps/server/src/orchestration-v2/Adapters/CursorAdapterV2.testkit.ts+2-2
......@@ -28,7 +28,7 @@ import {
2828 cursorSdkModelSelection,
2929 makeCursorAgentOptions,
3030} from "@t3tools/provider-cursor/testing";
31import type { ProviderAdapterV2RuntimePolicy } from "@t3tools/provider-core/server/ProviderAdapter";
31import type * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter";
3232import type { RuntimePolicyV2Override } from "../RuntimePolicy.ts";
3333
3434const CursorAgentSdkReplayTranscript = Schema.Struct({
......@@ -625,7 +625,7 @@ function recordingRuntimePolicy(input: {
625625 readonly cwd: string;
626626 readonly interactionMode: "default" | "plan";
627627 readonly override?: Pick<RuntimePolicyV2Override, "approvalPolicy" | "sandboxPolicy">;
628}): ProviderAdapterV2RuntimePolicy {
628}): ProviderAdapter.ProviderAdapterV2RuntimePolicy {
629629 return {
630630 runtimeMode: "full-access",
631631 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
2626import type { ProviderInstance } from "@t3tools/provider-core/server/driver";
2727import * as ProviderInstanceRegistry from "../../provider/ProviderInstanceRegistry.ts";
2828import * 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";
3030import * as RuntimePolicy from "../RuntimePolicy.ts";
3131import { acpPermissionDisposition } from "@t3tools/provider-acp/server/clientPolicy";
3232import {
......@@ -68,7 +68,7 @@ function runtimePolicy(input: {
6868 readonly approvalPolicy?: unknown;
6969 readonly sandboxPolicy?: unknown;
7070}) {
71 return ProviderAdapterV2RuntimePolicy.make({
71 return ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
7272 runtimeMode: input.runtimeMode,
7373 interactionMode: "default",
7474 cwd: "/workspace",
......@@ -314,7 +314,7 @@ describe("Grok launch permission mode", () => {
314314 // Opens a session through the adapter's own Grok runtime factory and returns
315315 // the argv it tried to launch. The spawn fails after recording, so no
316316 // process starts.
317 const launchArgs = (runtimePolicy: ProviderAdapterV2RuntimePolicy) =>
317 const launchArgs = (runtimePolicy: ProviderAdapter.ProviderAdapterV2RuntimePolicy) =>
318318 Effect.gen(function* () {
319319 const launches: Array<ReadonlyArray<string>> = [];
320320 const childProcessSpawner = ChildProcessSpawner.make((command) => {
......@@ -352,9 +352,9 @@ describe("Grok launch permission mode", () => {
352352
353353 const policy = (
354354 runtimeMode: RuntimeMode,
355 override: Partial<ProviderAdapterV2RuntimePolicy> = {},
355 override: Partial<ProviderAdapter.ProviderAdapterV2RuntimePolicy> = {},
356356 ) =>
357 ProviderAdapterV2RuntimePolicy.make({
357 ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
358358 runtimeMode,
359359 interactionMode: "default",
360360 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";
1313import { GROK_ACP_CANCEL_META, GROK_ACP_INITIALIZE_META } from "@t3tools/provider-grok/testing";
1414import { makeXAiPromptCompletionRuntime } from "@t3tools/provider-grok/testing";
1515import * 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";
1717import * as ProviderAdapterRegistry from "../ProviderAdapterRegistry.ts";
1818import type { ProviderReplayGate } from "@t3tools/provider-testing/replayGate";
1919import type { OrchestratorV2ProviderReplayHarness } from "../testkit/ProviderReplayHarness.ts";
apps/server/src/orchestration-v2/Adapters/MuseAdapterV2.testkit.ts+1-1
......@@ -36,7 +36,7 @@ import {
3636} from "@t3tools/provider-muse/testing";
3737import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator";
3838import * as ProviderAdapterRegistry from "../ProviderAdapterRegistry.ts";
39import * as ProviderContinuationRequests from "@t3tools/provider-core/server/continuationRequests";
39import * as ProviderContinuationRequests from "@t3tools/provider-core/server/ProviderContinuationRequests";
4040import type { OrchestratorV2ProviderReplayHarness } from "../testkit/ProviderReplayHarness.ts";
4141import { 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";
3838import { TestClock } from "effect/testing";
3939import { describe } from "vite-plus/test";
4040
41import type {
42 ProviderAdapterV2Event,
43 ProviderAdapterV2SessionRuntime,
44} from "@t3tools/provider-core/server/ProviderAdapter";
41import type * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter";
4542import * as McpProviderSession from "@t3tools/provider-core/server/mcpSession";
4643import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator";
47import type { ProviderContinuationRequest } from "@t3tools/provider-core/server/continuationRequests";
48import * as ProviderContinuationRequests from "@t3tools/provider-core/server/continuationRequests";
44import * as ProviderContinuationRequests from "@t3tools/provider-core/server/ProviderContinuationRequests";
4945import {
5046 OPENCODE_2_STILL_STOPPING,
5147 OPENCODE_PROVIDER,
......@@ -347,21 +343,27 @@ const resumed = (
347343 return { runtime, thread };
348344 });
349345
350const requestOf = (runtime: ProviderAdapterV2SessionRuntime) =>
346const requestOf = (runtime: ProviderAdapter.ProviderAdapterV2SessionRuntime) =>
351347 runtime.events.pipe(
352348 Stream.filter(
353 (event): event is Extract<ProviderAdapterV2Event, { type: "runtime_request.updated" }> =>
354 event.type === "runtime_request.updated",
349 (
350 event,
351 ): event is Extract<
352 ProviderAdapter.ProviderAdapterV2Event,
353 { type: "runtime_request.updated" }
354 > => event.type === "runtime_request.updated",
355355 ),
356356 Stream.map((event) => event.runtimeRequest),
357357 Stream.runHead,
358358 Effect.map(Option.getOrUndefined),
359359 );
360360
361const terminalOf = (runtime: ProviderAdapterV2SessionRuntime) =>
361const terminalOf = (runtime: ProviderAdapter.ProviderAdapterV2SessionRuntime) =>
362362 runtime.events.pipe(
363363 Stream.filter(
364 (event): event is Extract<ProviderAdapterV2Event, { type: "turn.terminal" }> =>
364 (
365 event,
366 ): event is Extract<ProviderAdapter.ProviderAdapterV2Event, { type: "turn.terminal" }> =>
365367 event.type === "turn.terminal",
366368 ),
367369 Stream.runHead,
......@@ -637,7 +639,7 @@ describe("OpenCode2 adapter", () => {
637639 * The single reader of the runtime's events: resolves `attached` once the
638640 * running background child has its thread, then returns the turn's terminal.
639641 */
640 const watchBackgroundTurn = (runtime: ProviderAdapterV2SessionRuntime) =>
642 const watchBackgroundTurn = (runtime: ProviderAdapter.ProviderAdapterV2SessionRuntime) =>
641643 Effect.gen(function* () {
642644 const attached = yield* Deferred.make<void>();
643645 const terminal = yield* runtime.events.pipe(
......@@ -647,7 +649,9 @@ describe("OpenCode2 adapter", () => {
647649 : Effect.void,
648650 ),
649651 Stream.filter(
650 (event): event is Extract<ProviderAdapterV2Event, { type: "turn.terminal" }> =>
652 (
653 event,
654 ): event is Extract<ProviderAdapter.ProviderAdapterV2Event, { type: "turn.terminal" }> =>
651655 event.type === "turn.terminal",
652656 ),
653657 Stream.runHead,
......@@ -767,7 +771,9 @@ describe("OpenCode2 adapter", () => {
767771 : Effect.void,
768772 ),
769773 Stream.filter(
770 (event): event is Extract<ProviderAdapterV2Event, { type: "turn.terminal" }> =>
774 (
775 event,
776 ): event is Extract<ProviderAdapter.ProviderAdapterV2Event, { type: "turn.terminal" }> =>
771777 event.type === "turn.terminal",
772778 ),
773779 Stream.runHead,
......@@ -809,7 +815,9 @@ describe("OpenCode2 adapter", () => {
809815 const firstEnded = yield* Deferred.make<void>();
810816 const ended = yield* runtime.events.pipe(
811817 Stream.filter(
812 (event): event is Extract<ProviderAdapterV2Event, { type: "turn.terminal" }> =>
818 (
819 event,
820 ): event is Extract<ProviderAdapter.ProviderAdapterV2Event, { type: "turn.terminal" }> =>
813821 event.type === "turn.terminal",
814822 ),
815823 Stream.tap(() => Deferred.succeed(firstEnded, undefined)),
......@@ -1110,7 +1118,7 @@ describe("OpenCode2 adapter", () => {
11101118 event("session.execution.succeeded", { sessionID: SESSION }),
11111119 ];
11121120 /** 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) =>
11141122 Effect.gen(function* () {
11151123 const deep: Array<string> = [];
11161124 const middleTurns = new Map<string, string>();
......@@ -1325,7 +1333,7 @@ describe("OpenCode2 adapter", () => {
13251333 attemptId: RunAttemptId.make("attempt:opencode2-adapter:2"),
13261334 });
13271335 const stopFirstTurn = (
1328 runtime: ProviderAdapterV2SessionRuntime,
1336 runtime: ProviderAdapter.ProviderAdapterV2SessionRuntime,
13291337 thread: OrchestrationV2ProviderThread,
13301338 ) =>
13311339 Effect.gen(function* () {
......@@ -1336,10 +1344,12 @@ describe("OpenCode2 adapter", () => {
13361344 yield* TestClock.adjust("11 seconds");
13371345 yield* Fiber.join(interrupt);
13381346 });
1339 const terminals = (runtime: ProviderAdapterV2SessionRuntime, count: number) =>
1347 const terminals = (runtime: ProviderAdapter.ProviderAdapterV2SessionRuntime, count: number) =>
13401348 runtime.events.pipe(
13411349 Stream.filter(
1342 (event): event is Extract<ProviderAdapterV2Event, { type: "turn.terminal" }> =>
1350 (
1351 event,
1352 ): event is Extract<ProviderAdapter.ProviderAdapterV2Event, { type: "turn.terminal" }> =>
13431353 event.type === "turn.terminal",
13441354 ),
13451355 Stream.take(count),
......@@ -2113,7 +2123,9 @@ describe("OpenCode2 adapter", () => {
21132123 }),
21142124 ),
21152125 Stream.filter(
2116 (event): event is Extract<ProviderAdapterV2Event, { type: "turn.terminal" }> =>
2126 (
2127 event,
2128 ): event is Extract<ProviderAdapter.ProviderAdapterV2Event, { type: "turn.terminal" }> =>
21172129 event.type === "turn.terminal",
21182130 ),
21192131 Stream.tap(() => Deferred.succeed(firstEnded, undefined)),
......@@ -2331,7 +2343,7 @@ describe("OpenCode2 adapter", () => {
23312343 replyData("session.form.list", forms),
23322344 ];
23332345
2334 const turnItems = (collected: ReadonlyArray<ProviderAdapterV2Event>) =>
2346 const turnItems = (collected: ReadonlyArray<ProviderAdapter.ProviderAdapterV2Event>) =>
23352347 collected.flatMap((event) =>
23362348 event.type === "turn_item.updated" ? [`${event.turnItem.type}:${event.turnItem.status}`] : [],
23372349 );
......@@ -3043,7 +3055,7 @@ describe("OpenCode2 adapter", () => {
30433055 Effect.gen(function* () {
30443056 const kept = "msg_t3_turn_run-attempt:kept:1";
30453057 const dropped = "msg_t3_turn_run-attempt:dropped:1";
3046 const offers: Array<ProviderContinuationRequest> = [];
3058 const offers: Array<ProviderContinuationRequests.ProviderContinuationRequest> = [];
30473059 const { runtime, thread } = yield* resumed([
30483060 out("message.list", "<any>"),
30493061 reply("message.list", {
......@@ -3727,7 +3739,7 @@ describe("OpenCode2 adapter", () => {
37273739 */
37283740 const continued = (after: ReadonlyArray<ProviderReplayEntry>) =>
37293741 Effect.gen(function* () {
3730 const offers: Array<ProviderContinuationRequest> = [];
3742 const offers: Array<ProviderContinuationRequests.ProviderContinuationRequest> = [];
37313743 const { runtime, thread } = yield* resumed([
37323744 ...backgroundLaunch(CHILD),
37333745 event("session.execution.succeeded", { sessionID: SESSION }),
......@@ -3867,7 +3879,7 @@ describe("OpenCode2 adapter", () => {
38673879 Effect.gen(function* () {
38683880 const reportText = `<subagent sessionID="${CHILD}" state="completed" description="Sleep">\nCHILD_OK\n</subagent>`;
38693881 const steerId = `msg_t3_steer_${SESSION}:message:opencode2-adapter:steer`;
3870 const offers: Array<ProviderContinuationRequest> = [];
3882 const offers: Array<ProviderContinuationRequests.ProviderContinuationRequest> = [];
38713883 const { runtime, thread } = yield* resumed([
38723884 ...backgroundLaunch(CHILD),
38733885 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";
99import { OpenCodeAdapterV2Driver } from "@t3tools/provider-opencode/server";
1010import * as OpenCodeRuntime from "@t3tools/provider-opencode/server/OpenCodeRuntime";
1111import { layerTestProviderHost } from "@t3tools/provider-testing/host";
12import * as ProviderEventLoggers from "../../provider/ProviderEventLoggers.ts";
12import * as ProviderEventLoggers from "@t3tools/provider-core/server/ProviderEventLoggers";
1313import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator";
1414import { ProviderAdapterDriverCreateError } from "@t3tools/provider-core/server/adapterDriver";
1515import * 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";
2424import * as EffectWorker from "./EffectWorker.ts";
2525import * as EventSink from "./EventSink.ts";
2626import * as Orchestrator from "./Orchestrator.ts";
27import type {
28 ProviderAdapterV2Event,
29 ProviderAdapterV2InterruptInput,
30 ProviderAdapterV2Shape,
31 ProviderAdapterV2TurnInput,
32} from "@t3tools/provider-core/server/ProviderAdapter";
3327import * as ProviderAdapterRegistry from "./ProviderAdapterRegistry.ts";
3428import * as ProviderSessionManager from "./ProviderSessionManager.ts";
3529import * as ProviderReplayHarness from "./testkit/ProviderReplayHarness.ts";
3630import { checkpointWorkspace } from "@t3tools/provider-testing/replayWorkspace";
31import type * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter";
3732
3833const driver = ProviderDriverKind.make("codex");
3934const instanceId = ProviderInstanceId.make("codex");
......@@ -61,10 +56,10 @@ const stopEarlierBackgroundWork = ({
6156 Effect.scoped(
6257 Effect.gen(function* () {
6358 const cwd = yield* checkpointWorkspace("background-work-stop");
64 const events = yield* Queue.unbounded<ProviderAdapterV2Event>();
65 const started: ProviderAdapterV2TurnInput[] = [];
66 const interrupts: ProviderAdapterV2InterruptInput[] = [];
67 const adapter: ProviderAdapterV2Shape = {
59 const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>();
60 const started: ProviderAdapter.ProviderAdapterV2TurnInput[] = [];
61 const interrupts: ProviderAdapter.ProviderAdapterV2InterruptInput[] = [];
62 const adapter: ProviderAdapter.ProviderAdapterV2["Service"] = {
6863 instanceId,
6964 driver,
7065 getCapabilities: () => Effect.succeed(CodexProviderCapabilitiesV2),
apps/server/src/orchestration-v2/CheckpointCaptureService.ts+1-1
......@@ -252,7 +252,7 @@ export const layer: Layer.Layer<
252252);
253253
254254function makeCheckpointTurnItem(input: {
255 readonly idAllocator: IdAllocator.IdAllocatorV2Shape;
255 readonly idAllocator: IdAllocator.IdAllocatorV2["Service"];
256256 readonly run: OrchestrationV2Run;
257257 readonly rootNode: OrchestrationV2ExecutionNode;
258258 readonly providerThread: OrchestrationV2ProviderThread;
apps/server/src/orchestration-v2/CheckpointRollbackService.test.ts+2-2
......@@ -26,7 +26,7 @@ import * as EventSink from "./EventSink.ts";
2626import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator";
2727import * as ProjectionStore from "./ProjectionStore.ts";
2828import * as ProjectStore from "./ProjectStore.ts";
29import type { ProviderAdapterV2RollbackThreadInput } from "@t3tools/provider-core/server/ProviderAdapter";
29import type * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter";
3030import * as ProviderSessionManager from "./ProviderSessionManager.ts";
3131import * as RuntimePolicy from "./RuntimePolicy.ts";
3232
......@@ -457,7 +457,7 @@ it.effect.each([
457457 Layer.mock(ProviderSessionManager.ProviderSessionManagerV2)({
458458 open: () =>
459459 Effect.succeed({
460 rollbackThread: (input: ProviderAdapterV2RollbackThreadInput) =>
460 rollbackThread: (input: ProviderAdapter.ProviderAdapterV2RollbackThreadInput) =>
461461 Effect.gen(function* () {
462462 const count = yield* resolveCodexRollbackTurnCount(input);
463463 assert.equal(count, 2 - targetOrdinal);
apps/server/src/orchestration-v2/CheckpointRollbackService.ts+5-5
......@@ -20,10 +20,10 @@ import {
2020} from "./CheckpointRestoreSafety.ts";
2121import { CheckpointServiceV2 } from "./CheckpointService.ts";
2222import { EventSinkV2 } from "./EventSink.ts";
23import { IdAllocatorV2 } from "@t3tools/provider-core/server/IdAllocator";
23import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator";
2424import { ProjectionStoreV2 } from "./ProjectionStore.ts";
2525import * as ProjectStore from "./ProjectStore.ts";
26import type { ProviderAdapterV2RollbackTarget } from "@t3tools/provider-core/server/ProviderAdapter";
26import type * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter";
2727import { ProviderSessionManagerV2 } from "./ProviderSessionManager.ts";
2828import { RuntimePolicyV2 } from "./RuntimePolicy.ts";
2929import * as ThreadCommandExecutor from "./ThreadCommandExecutor.ts";
......@@ -85,7 +85,7 @@ export const layer: Layer.Layer<
8585 never,
8686 | CheckpointServiceV2
8787 | EventSinkV2
88 | IdAllocatorV2
88 | IdAllocator.IdAllocatorV2
8989 | ProjectionStoreV2
9090 | ProviderSessionManagerV2
9191 | RuntimePolicyV2
......@@ -98,7 +98,7 @@ export const layer: Layer.Layer<
9898 Effect.gen(function* () {
9999 const checkpoints = yield* CheckpointServiceV2;
100100 const eventSink = yield* EventSinkV2;
101 const ids = yield* IdAllocatorV2;
101 const ids = yield* IdAllocator.IdAllocatorV2;
102102 const projections = yield* ProjectionStoreV2;
103103 const sessions = yield* ProviderSessionManagerV2;
104104 const runtimePolicy = yield* RuntimePolicyV2;
......@@ -225,7 +225,7 @@ export const layer: Layer.Layer<
225225 turn.providerThreadId === providerThread.id &&
226226 (turn.runAttemptId === null || !rolledBackAttemptIds.has(turn.runAttemptId)),
227227 );
228 const rollbackTarget: ProviderAdapterV2RollbackTarget =
228 const rollbackTarget: ProviderAdapter.ProviderAdapterV2RollbackTarget =
229229 targetOrdinal === 0
230230 ? {
231231 type: "thread_start",
apps/server/src/orchestration-v2/CheckpointService.ts+2-2
......@@ -172,7 +172,7 @@ export const checkpointRefForScopeOrdinal = Effect.fn("checkpointRefForScopeOrdi
172172);
173173
174174function checkpointIdForScopeOrdinal(
175 idAllocator: IdAllocator.IdAllocatorV2Shape,
175 idAllocator: IdAllocator.IdAllocatorV2["Service"],
176176 input: {
177177 readonly scopeId: CheckpointScopeId;
178178 readonly ordinalWithinScope: number;
......@@ -185,7 +185,7 @@ function checkpointIdForScopeOrdinal(
185185}
186186
187187function makeRootRunScope(input: {
188 readonly idAllocator: IdAllocator.IdAllocatorV2Shape;
188 readonly idAllocator: IdAllocator.IdAllocatorV2["Service"];
189189 readonly threadId: ThreadId;
190190 readonly runId: RunId;
191191 readonly rootNodeId: NodeId;
apps/server/src/orchestration-v2/ContextHandoffBudget.test.ts+3-3
......@@ -1,4 +1,4 @@
1import type { ProviderAdapterV2HistoricalContext } from "@t3tools/provider-core/server/ProviderAdapter";
1import type * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter";
22import { assert, describe, it } from "@effect/vitest";
33import {
44 ContextHandoffId,
......@@ -501,7 +501,7 @@ describe("handoff delivery", () => {
501501 providerThread,
502502 budget: 16_000,
503503 alreadyDeliveredItemIds: new Set<string>(),
504 inject: (value: ProviderAdapterV2HistoricalContext) =>
504 inject: (value: ProviderAdapter.ProviderAdapterV2HistoricalContext) =>
505505 Effect.sync(() => {
506506 history.push(...historyResponseItems(value.messages, value.context));
507507 return true;
......@@ -588,7 +588,7 @@ describe("handoff delivery", () => {
588588 ...handoff,
589589 id: ContextHandoffId.make(`handoff:retry:${index}`),
590590 }));
591 let captured: ProviderAdapterV2HistoricalContext | undefined;
591 let captured: ProviderAdapter.ProviderAdapterV2HistoricalContext | undefined;
592592 const result = yield* deliverContextHandoffs({
593593 handoffs: many,
594594 providerThread,
apps/server/src/orchestration-v2/ContextHandoffDelivery.ts+2-2
......@@ -2,7 +2,7 @@ import type {
22 OrchestrationV2ContextHandoff,
33 OrchestrationV2ProviderThread,
44} from "@t3tools/contracts";
5import type { ProviderAdapterV2HistoricalContext } from "@t3tools/provider-core/server/ProviderAdapter";
5import type * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter";
66import { ContextHandoffBudgetError } from "@t3tools/provider-core/server/failure";
77import * as Effect from "effect/Effect";
88import * as Schema from "effect/Schema";
......@@ -25,7 +25,7 @@ export const deliverContextHandoffs = Effect.fn("orchestrationV2.deliverContextH
2525 readonly deferInline?: boolean;
2626 readonly alreadyDeliveredItemIds: ReadonlySet<string>;
2727 readonly inject?: (
28 history: ProviderAdapterV2HistoricalContext,
28 history: ProviderAdapter.ProviderAdapterV2HistoricalContext,
2929 ) => Effect.Effect<boolean, InjectError>;
3030 readonly persist: (handoff: OrchestrationV2ContextHandoff) => Effect.Effect<void, PersistError>;
3131 }) {
apps/server/src/orchestration-v2/CursorOrchestratorV2.live.test.ts+1-1
......@@ -29,7 +29,7 @@ import * as CodexInstallation from "../provider/CodexInstallation.ts";
2929import * as ServerEnvironment from "../environment/ServerEnvironment.ts";
3030import * as ModelManifest from "../provider/ModelManifest.ts";
3131import * as ProviderInstanceRegistryHydration from "../provider/ProviderInstanceRegistryHydration.ts";
32import * as ProviderEventLoggers from "../provider/ProviderEventLoggers.ts";
32import * as ProviderEventLoggers from "@t3tools/provider-core/server/ProviderEventLoggers";
3333import * as OpenCodeRuntime from "@t3tools/provider-opencode/server/OpenCodeRuntime";
3434import * as OpenCodeServerLedger from "@t3tools/provider-opencode/server/OpenCodeServerLedger";
3535import * 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";
3636import { CodexProviderCapabilitiesV2 } from "./Adapters/CodexAdapterV2.ts";
3737import * as EventSink from "./EventSink.ts";
3838import * as Orchestrator from "./Orchestrator.ts";
39import type { ProviderAdapterV2Shape } from "@t3tools/provider-core/server/ProviderAdapter";
4039import { continueRestartedRun } from "./RestartContinuation.ts";
4140import * as RuntimeLayer from "./runtimeLayer.ts";
4241import * as ProviderTurnStartServiceTestkit from "./ProviderTurnStartService.testkit.ts";
42import type * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter";
4343
4444const layerPlatformTest = Layer.merge(
4545 NodeServices.layer,
......@@ -74,7 +74,7 @@ const orchestrationAdapter = {
7474 getCapabilities: () => Effect.succeed(CodexProviderCapabilitiesV2),
7575 planSelectionTransition: () => Effect.succeed({ type: "apply_on_next_turn" }),
7676 openSession: () => Effect.die("sessions are not used by delegated completion tests"),
77} as ProviderAdapterV2Shape;
77} as ProviderAdapter.ProviderAdapterV2["Service"];
7878const providerInstance = {
7979 instanceId: modelSelection.instanceId,
8080 driverKind: driver,
apps/server/src/orchestration-v2/DispatchModeLimit.test.ts+2-2
......@@ -13,9 +13,9 @@ import { CodexProviderCapabilitiesV2 } from "./Adapters/CodexAdapterV2.ts";
1313import { DispatchModeLimit } from "./DispatchModeLimit.ts";
1414import * as Orchestrator from "./Orchestrator.ts";
1515import * as ProjectionStore from "./ProjectionStore.ts";
16import type { ProviderAdapterV2Shape } from "@t3tools/provider-core/server/ProviderAdapter";
1716import * as ProviderAdapterRegistry from "./ProviderAdapterRegistry.ts";
1817import * as ProviderReplayHarness from "./testkit/ProviderReplayHarness.ts";
18import type * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter";
1919
2020const instanceId = ProviderInstanceId.make("codex");
2121const adapter = {
......@@ -24,7 +24,7 @@ const adapter = {
2424 getCapabilities: () => Effect.succeed(CodexProviderCapabilitiesV2),
2525 planSelectionTransition: () => Effect.succeed({ type: "apply_on_next_turn" as const }),
2626 openSession: () => Effect.die("No provider process needed for metadata commands"),
27} as ProviderAdapterV2Shape;
27} as ProviderAdapter.ProviderAdapterV2["Service"];
2828const layerDatabase = SqlitePersistence.layerMemory;
2929const layerTest = Layer.mergeAll(
3030 layerDatabase,
apps/server/src/orchestration-v2/GrokOrchestratorV2.live.test.ts+1-1
......@@ -29,7 +29,7 @@ import * as CodexInstallation from "../provider/CodexInstallation.ts";
2929import * as ServerEnvironment from "../environment/ServerEnvironment.ts";
3030import * as ModelManifest from "../provider/ModelManifest.ts";
3131import * as ProviderInstanceRegistryHydration from "../provider/ProviderInstanceRegistryHydration.ts";
32import * as ProviderEventLoggers from "../provider/ProviderEventLoggers.ts";
32import * as ProviderEventLoggers from "@t3tools/provider-core/server/ProviderEventLoggers";
3333import * as OpenCodeRuntime from "@t3tools/provider-opencode/server/OpenCodeRuntime";
3434import * as OpenCodeServerLedger from "@t3tools/provider-opencode/server/OpenCodeServerLedger";
3535import * 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";
5454import * as ServerEnvironment from "../environment/ServerEnvironment.ts";
5555import * as ModelManifest from "../provider/ModelManifest.ts";
5656import * as ProviderInstanceRegistryHydration from "../provider/ProviderInstanceRegistryHydration.ts";
57import * as ProviderEventLoggers from "../provider/ProviderEventLoggers.ts";
57import * as ProviderEventLoggers from "@t3tools/provider-core/server/ProviderEventLoggers";
5858import * as OpenCode2Client from "@t3tools/provider-opencode/server/v2/OpenCode2Client";
5959import * as OpenCodeRuntime from "@t3tools/provider-opencode/server/OpenCodeRuntime";
6060import * as OpenCodeServerLedger from "@t3tools/provider-opencode/server/OpenCodeServerLedger";
......@@ -67,7 +67,7 @@ import * as ProviderInstanceRegistry from "../provider/ProviderInstanceRegistry.
6767import * as ProviderTurnStartServiceTestkit from "./ProviderTurnStartService.testkit.ts";
6868import * as RuntimeLayer from "./runtimeLayer.ts";
6969import * 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";
7171import * as ProviderContinuationService from "./ProviderContinuationService.ts";
7272import * as ThreadManagementService from "./ThreadManagementService.ts";
7373import * 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";
2525import { CodexProviderCapabilitiesV2 } from "./Adapters/CodexAdapterV2.ts";
2626import * as Orchestrator from "./Orchestrator.ts";
2727import * as ProjectionStore from "./ProjectionStore.ts";
28import type { ProviderAdapterV2Shape } from "@t3tools/provider-core/server/ProviderAdapter";
2928import * as ProviderAdapterRegistry from "./ProviderAdapterRegistry.ts";
3029import * as ProviderReplayHarness from "./testkit/ProviderReplayHarness.ts";
30import type * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter";
3131
3232const instanceId = ProviderInstanceId.make("codex");
3333const modelSelection = { instanceId, model: "gpt-5.1-codex" };
......@@ -37,7 +37,7 @@ const adapter = {
3737 getCapabilities: () => Effect.succeed(CodexProviderCapabilitiesV2),
3838 planSelectionTransition: () => Effect.succeed({ type: "apply_on_next_turn" as const }),
3939 openSession: () => Effect.die("No provider process needed for metadata controls"),
40} as ProviderAdapterV2Shape;
40} as ProviderAdapter.ProviderAdapterV2["Service"];
4141const layerDatabase = SqlitePersistence.layerMemory;
4242const layerTest = Layer.mergeAll(
4343 layerDatabase,
apps/server/src/orchestration-v2/Orchestrator.ts+7-7
......@@ -91,7 +91,7 @@ import { isUndeliveredMailboxSteer } from "./NotificationMailbox.ts";
9191import { EventSinkV2 } from "./EventSink.ts";
9292import * as EffectOutbox from "./EffectOutbox.ts";
9393import type { OrchestrationEffectRequestV2, PendingOrchestrationEffectV2 } from "./EffectOutbox.ts";
94import { IdAllocatorV2 } from "@t3tools/provider-core/server/IdAllocator";
94import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator";
9595import * as ThreadCommandExecutor from "./ThreadCommandExecutor.ts";
9696import { DispatchModeLimit, exceededDispatchModeLimit } from "./DispatchModeLimit.ts";
9797import {
......@@ -107,9 +107,8 @@ import {
107107 type ProjectionCheckpointContext,
108108 type ShellSnapshotOptions,
109109} from "./ProjectionStore.ts";
110import type { ProviderAdapterV2Shape } from "@t3tools/provider-core/server/ProviderAdapter";
111110import { ProviderAdapterRegistryV2 } from "./ProviderAdapterRegistry.ts";
112import { ProviderContinuationRequests } from "@t3tools/provider-core/server/continuationRequests";
111import * as ProviderContinuationRequests from "@t3tools/provider-core/server/ProviderContinuationRequests";
113112import { makeProviderFailure } from "@t3tools/provider-core/server/failure";
114113import * as RunExecutionService from "./RunExecutionService.ts";
115114import { ProviderSessionManagerV2 } from "./ProviderSessionManager.ts";
......@@ -128,6 +127,7 @@ import {
128127 ThreadForkServiceV2,
129128} from "./ThreadForkService.ts";
130129import { planThreadDeletion } from "./ThreadDeletion.ts";
130import type * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter";
131131
132132export class OrchestratorDispatchError extends Schema.TaggedError<OrchestratorDispatchError>()(
133133 "OrchestratorDispatchError",
......@@ -800,7 +800,7 @@ const makeOrchestrator = Effect.fn("orchestrationV2.Orchestrator.layer")(functio
800800 const contextHandoffService = yield* ContextHandoffServiceV2;
801801 const eventSink = yield* EventSinkV2;
802802 const commandReceipts = yield* CommandReceiptStoreV2;
803 const idAllocator = yield* IdAllocatorV2;
803 const idAllocator = yield* IdAllocator.IdAllocatorV2;
804804 const projects = yield* ProjectStore.ProjectStoreV2;
805805 const projectionStore = yield* ProjectionStoreV2;
806806 const effectOutbox = yield* EffectOutbox.EffectOutboxV2;
......@@ -823,7 +823,7 @@ const makeOrchestrator = Effect.fn("orchestrationV2.Orchestrator.layer")(functio
823823 const fileSystem = yield* FileSystem.FileSystem;
824824 const path = yield* Path.Path;
825825 const providerAdapters = yield* ProviderAdapterRegistryV2;
826 const continuationRequests = yield* ProviderContinuationRequests;
826 const continuationRequests = yield* ProviderContinuationRequests.ProviderContinuationRequests;
827827 const providerSessions = yield* ProviderSessionManagerV2;
828828 const providerSwitchService = yield* ProviderSwitchServiceV2;
829829 const runtimePolicy = yield* RuntimePolicyV2;
......@@ -857,7 +857,7 @@ const makeOrchestrator = Effect.fn("orchestrationV2.Orchestrator.layer")(functio
857857 );
858858
859859 const providerSessionIdFor = (input: {
860 readonly adapter: ProviderAdapterV2Shape;
860 readonly adapter: ProviderAdapter.ProviderAdapterV2["Service"];
861861 readonly providerInstanceId: ProviderInstanceId;
862862 readonly threadId: ThreadId;
863863 }) =>
......@@ -10955,7 +10955,7 @@ export const layer: Layer.Layer<
1095510955 | ContextHandoffServiceV2
1095610956 | EffectOutbox.EffectOutboxV2
1095710957 | EventSinkV2
10958 | IdAllocatorV2
10958 | IdAllocator.IdAllocatorV2
1095910959 | ProjectStore.ProjectStoreV2
1096010960 | ProviderAdapterRegistryV2
1096110961 | ProviderSessionManagerV2
apps/server/src/orchestration-v2/ProviderAdapterRegistry.test.ts+14-11
......@@ -18,36 +18,35 @@ import * as Schema from "effect/Schema";
1818import type * as Scope from "effect/Scope";
1919import * as Stream from "effect/Stream";
2020
21import * as ProviderAuthFlow from "@t3tools/provider-core/server/ProviderAuthFlow";
21import * as ProviderAuthFlow from "@t3tools/provider-core/server/providerAuthFlow";
2222import type { ProviderAuthController } from "../provider/ProviderAuthService.ts";
2323import type { ProviderInstance } from "@t3tools/provider-core/server/driver";
2424import * as ProviderInstanceRegistry from "../provider/ProviderInstanceRegistry.ts";
25import {
26 ProviderAdapterOpenSessionError,
27 type ProviderAdapterV2Shape,
28} from "@t3tools/provider-core/server/ProviderAdapter";
2925import {
3026 ProviderAdapterDriverCreateError,
3127 type ProviderAdapterDriver,
3228} from "@t3tools/provider-core/server/adapterDriver";
3329import * as ProviderAdapterRegistry from "./ProviderAdapterRegistry.ts";
30import * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter";
3431
3532const driver = ProviderDriverKind.make("codex");
3633const personalId = ProviderInstanceId.make("codex_personal");
3734const workId = ProviderInstanceId.make("codex_work");
3835
39const makeAdapter = (instanceId: ProviderInstanceId): ProviderAdapterV2Shape =>
36const makeAdapter = (
37 instanceId: ProviderInstanceId,
38): ProviderAdapter.ProviderAdapterV2["Service"] =>
4039 ({
4140 instanceId,
4241 driver,
4342 getCapabilities: () => Effect.die("capabilities are not used by this registry test"),
4443 planSelectionTransition: () => Effect.succeed({ type: "apply_on_next_turn" }),
4544 openSession: () => Effect.die("sessions are not used by this registry test"),
46 }) as ProviderAdapterV2Shape;
45 }) as ProviderAdapter.ProviderAdapterV2["Service"];
4746
4847const makeInstance = (
4948 instanceId: ProviderInstanceId,
50 orchestrationAdapter: ProviderAdapterV2Shape,
49 orchestrationAdapter: ProviderAdapter.ProviderAdapterV2["Service"],
5150): ProviderInstance => ({
5251 instanceId,
5352 driverKind: driver,
......@@ -101,7 +100,11 @@ const lifecycleConfigMap: ProviderInstanceConfigMap = {
101100const lifecycleAdapter = makeAdapter(lifecycleInstanceId);
102101
103102const makeLifecycleDriver = (
104 create: Effect.Effect<ProviderAdapterV2Shape, ProviderAdapterDriverCreateError, Scope.Scope>,
103 create: Effect.Effect<
104 ProviderAdapter.ProviderAdapterV2["Service"],
105 ProviderAdapterDriverCreateError,
106 Scope.Scope
107 >,
105108): ProviderAdapterDriver<Record<string, never>> => ({
106109 driverKind: lifecycleDriver,
107110 configSchema: Schema.Struct({}),
......@@ -263,7 +266,7 @@ it.effect(
263266 },
264267 })
265268 .pipe(Effect.flip);
266 assert.instanceOf(error, ProviderAdapterOpenSessionError);
269 assert.instanceOf(error, ProviderAdapter.ProviderAdapterOpenSessionError);
267270 assert.instanceOf(error.cause, ProviderSetupError);
268271 }),
269272);
......@@ -287,7 +290,7 @@ it.effect("interrupts admitted session startup when a shared peer signs out", ()
287290 authenticate: () => Effect.void,
288291 logout: Effect.void,
289292 });
290 const adapter: ProviderAdapterV2Shape = {
293 const adapter: ProviderAdapter.ProviderAdapterV2["Service"] = {
291294 ...workAdapter,
292295 openSession: () =>
293296 Effect.gen(function* () {
apps/server/src/orchestration-v2/ProviderAdapterRegistry.ts+8-8
......@@ -48,11 +48,11 @@ export type ProviderAdapterRegistryV2Error = typeof ProviderAdapterRegistryV2Err
4848export interface ProviderAdapterRegistryV2Shape {
4949 readonly get: (
5050 instanceId: ProviderInstanceId,
51 ) => Effect.Effect<ProviderAdapter.ProviderAdapterV2Shape, ProviderAdapterRegistryV2Error>;
51 ) => Effect.Effect<ProviderAdapter.ProviderAdapterV2["Service"], ProviderAdapterRegistryV2Error>;
5252 readonly list: () => Effect.Effect<ReadonlyArray<ProviderInstanceId>>;
5353 readonly getMetadata?: (instanceId: ProviderInstanceId) => Effect.Effect<
5454 {
55 readonly driver: ProviderAdapter.ProviderAdapterV2Shape["driver"];
55 readonly driver: ProviderAdapter.ProviderAdapterV2["Service"]["driver"];
5656 readonly continuationKey: string;
5757 readonly enabled: boolean;
5858 readonly capabilities: OrchestrationV2ProviderCapabilities;
......@@ -134,7 +134,7 @@ export const layerFromProviderInstanceRegistry: Layer.Layer<
134134 ),
135135 );
136136 },
137 } satisfies ProviderAdapter.ProviderAdapterV2Shape);
137 } satisfies ProviderAdapter.ProviderAdapterV2["Service"]);
138138 }),
139139 ),
140140 list: () =>
......@@ -169,7 +169,7 @@ export const ProviderAdapterRegistryBuildError = Schema.Union([ProviderAdapterDr
169169export type ProviderAdapterRegistryBuildError = typeof ProviderAdapterRegistryBuildError.Type;
170170
171171function makeRegistry(
172 adapters: ReadonlyArray<ProviderAdapter.ProviderAdapterV2Shape>,
172 adapters: ReadonlyArray<ProviderAdapter.ProviderAdapterV2["Service"]>,
173173): ProviderAdapterRegistryV2Shape {
174174 return {
175175 get: (instanceId) =>
......@@ -185,7 +185,7 @@ function makeRegistry(
185185}
186186
187187export function layerFromAdapters(
188 adapters: ReadonlyArray<ProviderAdapter.ProviderAdapterV2Shape>,
188 adapters: ReadonlyArray<ProviderAdapter.ProviderAdapterV2["Service"]>,
189189): Layer.Layer<ProviderAdapterRegistryV2> {
190190 return Layer.succeed(
191191 ProviderAdapterRegistryV2,
......@@ -194,7 +194,7 @@ export function layerFromAdapters(
194194}
195195
196196export function layerFromAdaptersEffect<R, E>(
197 adapters: Effect.Effect<ReadonlyArray<ProviderAdapter.ProviderAdapterV2Shape>, E, R>,
197 adapters: Effect.Effect<ReadonlyArray<ProviderAdapter.ProviderAdapterV2["Service"]>, E, R>,
198198): Layer.Layer<ProviderAdapterRegistryV2, E, R> {
199199 return Layer.effect(
200200 ProviderAdapterRegistryV2,
......@@ -203,7 +203,7 @@ export function layerFromAdaptersEffect<R, E>(
203203}
204204
205205export function layerSingle(
206 adapter: ProviderAdapter.ProviderAdapterV2Shape,
206 adapter: ProviderAdapter.ProviderAdapterV2["Service"],
207207): Layer.Layer<ProviderAdapterRegistryV2> {
208208 return layerFromAdapters([adapter]);
209209}
......@@ -217,7 +217,7 @@ const decodedConfigEnabled = (config: unknown): boolean | undefined => {
217217};
218218
219219interface LiveAdapterEntry {
220 readonly adapter: ProviderAdapter.ProviderAdapterV2Shape;
220 readonly adapter: ProviderAdapter.ProviderAdapterV2["Service"];
221221 readonly scope: Scope.Closeable;
222222 readonly entry: ProviderInstanceConfig;
223223}
apps/server/src/orchestration-v2/ProviderContinuationService.test.ts+1-1
......@@ -19,7 +19,7 @@ import * as Semaphore from "effect/Semaphore";
1919import * as TestClock from "effect/testing/TestClock";
2020
2121import * 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";
2323import * as ProviderContinuationService from "./ProviderContinuationService.ts";
2424import * 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";
44import * as Ref from "effect/Ref";
55
66import * 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";
88import * as ThreadManagementService from "./ThreadManagementService.ts";
99import { 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";
3131import * as EventSink from "./EventSink.ts";
3232import * as ProjectionStore from "./ProjectionStore.ts";
3333import * 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";
3535import { makeProviderFailureTurnItem } from "@t3tools/provider-core/server/failure";
3636import * as ThreadCommandExecutor from "./ThreadCommandExecutor.ts";
3737import { stripUnservedToolOutputImageBytes } from "./toolOutputImageBytes.ts";
......@@ -41,7 +41,7 @@ export class ProviderEventNormalizeError extends Schema.TaggedError<ProviderEven
4141 {
4242 providerSessionId: ProviderSessionId,
4343 threadId: ThreadId,
44 providerEvent: ProviderAdapterV2Event,
44 providerEvent: ProviderAdapter.ProviderAdapterV2Event,
4545 cause: Schema.optional(Schema.Defect()),
4646 },
4747) {
......@@ -93,7 +93,7 @@ export const layerAnalytics = Layer.effect(
9393);
9494
9595function providerTurnAnalyticsProperties(input: {
96 readonly driver: ProviderAdapterV2Event["driver"];
96 readonly driver: ProviderAdapter.ProviderAdapterV2Event["driver"];
9797 readonly providerTurn: OrchestrationV2ProviderTurn;
9898 readonly context?: ProviderTurnAnalyticsContext;
9999}): Readonly<Record<string, unknown>> {
......@@ -257,7 +257,7 @@ export interface ProviderEventIngestInput {
257257 readonly runId?: RunId;
258258 readonly nodeId?: NodeId;
259259 readonly rawEventId?: RawEventId;
260 readonly event: ProviderAdapterV2Event;
260 readonly event: ProviderAdapter.ProviderAdapterV2Event;
261261 readonly analyticsContext?: ProviderTurnAnalyticsContext;
262262}
263263
......@@ -657,7 +657,6 @@ export const layer: Layer.Layer<
657657 yield* makeDomainEvent(input, {
658658 type: "turn-item.updated",
659659 payload: makeProviderFailureTurnItem({
660 idAllocator,
661660 driver: input.event.driver,
662661 threadId: input.threadId,
663662 runId: input.runId ?? null,
apps/server/src/orchestration-v2/ProviderSessionManager.test.ts+15-19
......@@ -44,18 +44,11 @@ import * as EventSink from "./EventSink.ts";
4444import * as EventStore from "./EventStore.ts";
4545import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator";
4646import * 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";
5547import * as ProviderAdapterRegistry from "./ProviderAdapterRegistry.ts";
5648import * as ProviderEventIngestor from "./ProviderEventIngestor.ts";
5749import * as ThreadCommandExecutor from "./ThreadCommandExecutor.ts";
5850import * as ProviderSessionManager from "./ProviderSessionManager.ts";
51import * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter";
5952
6053const layerTestDatabase = SqlitePersistence.layerMemory;
6154const layerTestStores = Layer.merge(EventStore.layer, ProjectionStore.layer).pipe(
......@@ -167,7 +160,10 @@ interface TestProviderRuntimeState {
167160 readonly interruptCount: number;
168161 readonly resumeCount: number;
169162 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 >;
171167}
172168
173169const emptyState: TestProviderRuntimeState = {
......@@ -189,7 +185,7 @@ const runtimePolicy = {
189185 runtimeMode: "full-access",
190186 interactionMode: "default",
191187 cwd: process.cwd(),
192} satisfies ProviderAdapterV2RuntimePolicy;
188} satisfies ProviderAdapter.ProviderAdapterV2RuntimePolicy;
193189
194190function makeProviderSession(input: {
195191 readonly providerSessionId: ProviderSessionId;
......@@ -211,7 +207,7 @@ function makeProviderSession(input: {
211207}
212208
213209function makeThreadCreatedEvent(input: {
214 readonly idAllocator: IdAllocator.IdAllocatorV2Shape;
210 readonly idAllocator: IdAllocator.IdAllocatorV2["Service"];
215211 readonly threadId: ThreadId;
216212 readonly now: DateTime.Utc;
217213 readonly projectId?: ProjectId;
......@@ -264,7 +260,7 @@ function makeThreadCreatedEvent(input: {
264260}
265261
266262function makeProviderThread(input: {
267 readonly idAllocator: IdAllocator.IdAllocatorV2Shape;
263 readonly idAllocator: IdAllocator.IdAllocatorV2["Service"];
268264 readonly threadId: ThreadId;
269265 readonly providerSessionId: ProviderSessionId;
270266 readonly now: DateTime.Utc;
......@@ -299,7 +295,7 @@ function makeProviderThread(input: {
299295
300296function unimplemented(detail: string) {
301297 return Effect.fail(
302 new ProviderAdapterProtocolError({
298 new ProviderAdapter.ProviderAdapterProtocolError({
303299 driver: CODEX_DRIVER,
304300 detail,
305301 }),
......@@ -328,7 +324,7 @@ function makeProviderAdapter(
328324 /** Completed when a hanging scope close reaches its wedged finalizer. */
329325 readonly scopeCloseReached?: Deferred.Deferred<void>;
330326 } = {},
331): ProviderAdapterV2Shape {
327): ProviderAdapter.ProviderAdapterV2["Service"] {
332328 const countClose = Effect.addFinalizer(() =>
333329 Ref.update(state, (current) => ({
334330 ...current,
......@@ -371,7 +367,7 @@ function makeProviderAdapter(
371367 ]);
372368 }
373369 const now = yield* DateTime.now;
374 const events = yield* Queue.unbounded<ProviderAdapterV2Event, Cause.Done>();
370 const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event, Cause.Done>();
375371 const session = makeProviderSession({
376372 providerSessionId: input.providerSessionId,
377373 now,
......@@ -398,7 +394,7 @@ function makeProviderAdapter(
398394 providerSession: session,
399395 events: options.failEventStream
400396 ? Stream.fail(
401 new ProviderAdapterEventStreamError({
397 new ProviderAdapter.ProviderAdapterEventStreamError({
402398 driver: CODEX_DRIVER,
403399 providerSessionId: input.providerSessionId,
404400 cause: "process exited",
......@@ -442,7 +438,7 @@ function makeProviderAdapter(
442438 readThreadSnapshot: () => unimplemented("readThreadSnapshot unused in test"),
443439 rollbackThread: () => unimplemented("rollbackThread unused in test"),
444440 forkThread: () => unimplemented("forkThread unused in test"),
445 } satisfies ProviderAdapterV2SessionRuntime;
441 } satisfies ProviderAdapter.ProviderAdapterV2SessionRuntime;
446442 }),
447443 };
448444}
......@@ -674,7 +670,7 @@ function runBrowserAccessScenario(input: {
674670}
675671
676672function makePendingRuntimeRequestEvents(input: {
677 readonly idAllocator: IdAllocator.IdAllocatorV2Shape;
673 readonly idAllocator: IdAllocator.IdAllocatorV2["Service"];
678674 readonly threadId: ThreadId;
679675 readonly providerSessionId: ProviderSessionId;
680676 readonly providerThread: OrchestrationV2ProviderThread;
......@@ -795,7 +791,7 @@ function makePendingRuntimeRequestEvents(input: {
795791 driver: CODEX_DRIVER,
796792 turnItem,
797793 },
798 ] satisfies ReadonlyArray<ProviderAdapterV2Event>;
794 ] satisfies ReadonlyArray<ProviderAdapter.ProviderAdapterV2Event>;
799795 return { events, providerEvents, requestId, nodeId };
800796 });
801797}
apps/server/src/orchestration-v2/ProviderSessionManager.ts+37-31
......@@ -46,14 +46,7 @@ import * as McpSessionRegistry from "../mcp/McpSessionRegistry.ts";
4646import * as EventSink from "./EventSink.ts";
4747import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator";
4848import * as ProviderEventIngestor from "./ProviderEventIngestor.ts";
49import {
50 ProviderAdapterEventStreamError,
51 ProviderAdapterV2RuntimePolicy,
52 type ProviderAdapterV2Error,
53 type ProviderAdapterV2Event,
54 type ProviderAdapterV2EventSubscription,
55 type ProviderAdapterV2SessionRuntime,
56} from "@t3tools/provider-core/server/ProviderAdapter";
49import * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter";
5750import * as ProviderAdapterRegistry from "./ProviderAdapterRegistry.ts";
5851import * as ProjectionStore from "./ProjectionStore.ts";
5952
......@@ -161,14 +154,20 @@ export interface ProviderSessionManagerV2Shape {
161154 readonly threadId: ThreadId;
162155 readonly providerSessionId: ProviderSessionId;
163156 readonly modelSelection: ModelSelection;
164 readonly runtimePolicy: ProviderAdapterV2RuntimePolicy;
157 readonly runtimePolicy: ProviderAdapter.ProviderAdapterV2RuntimePolicy;
165158 readonly resumeFromSession?: OrchestrationV2ProviderSession;
166159 readonly initialNativeThreadId?: string;
167160 readonly initialProviderItemIdentityVersion?: 2;
168 }) => Effect.Effect<ProviderAdapterV2SessionRuntime, ProviderSessionManagerV2Error>;
161 }) => Effect.Effect<
162 ProviderAdapter.ProviderAdapterV2SessionRuntime,
163 ProviderSessionManagerV2Error
164 >;
169165 readonly get: (
170166 providerSessionId: ProviderSessionId,
171 ) => Effect.Effect<Option.Option<ProviderAdapterV2SessionRuntime>, ProviderSessionManagerV2Error>;
167 ) => Effect.Effect<
168 Option.Option<ProviderAdapter.ProviderAdapterV2SessionRuntime>,
169 ProviderSessionManagerV2Error
170 >;
172171 readonly close: (
173172 providerSessionId: ProviderSessionId,
174173 ) => Effect.Effect<void, ProviderSessionManagerV2Error>;
......@@ -211,8 +210,8 @@ interface LiveSessionEntry {
211210 */
212211 readonly mcpCredentialIdByThread: ReadonlyMap<ThreadId, string>;
213212 readonly supportsMultipleProviderThreads: boolean;
214 readonly runtime: ProviderAdapterV2SessionRuntime;
215 readonly exposedRuntime: ProviderAdapterV2SessionRuntime;
213 readonly runtime: ProviderAdapter.ProviderAdapterV2SessionRuntime;
214 readonly exposedRuntime: ProviderAdapter.ProviderAdapterV2SessionRuntime;
216215 readonly eventSubscribers: Ref.Ref<
217216 ReadonlyMap<number, Queue.Queue<ProviderSessionEventSignal, Cause.Done>>
218217 >;
......@@ -244,10 +243,10 @@ interface IdleThreadUnload {
244243}
245244
246245type ProviderSessionEventSignal =
247 | { readonly type: "event"; readonly event: ProviderAdapterV2Event }
246 | { readonly type: "event"; readonly event: ProviderAdapter.ProviderAdapterV2Event }
248247 | {
249248 readonly type: "failure";
250 readonly cause: Cause.Cause<ProviderAdapterV2Error>;
249 readonly cause: Cause.Cause<ProviderAdapter.ProviderAdapterV2Error>;
251250 };
252251
253252export interface ProviderSessionManagerV2LayerOptions {
......@@ -279,7 +278,9 @@ function sessionKey(providerSessionId: ProviderSessionId): string {
279278 * Their node and transcript item are runless too, so they bypass the normal
280279 * per-run subscriber and are persisted by the session event pump.
281280 */
282function sessionScopedRuntimeRequestThreadId(event: ProviderAdapterV2Event): ThreadId | undefined {
281function sessionScopedRuntimeRequestThreadId(
282 event: ProviderAdapter.ProviderAdapterV2Event,
283): ThreadId | undefined {
283284 switch (event.type) {
284285 case "runtime_request.updated":
285286 return event.runtimeRequest.providerTurnId === null ? event.threadId : undefined;
......@@ -298,7 +299,9 @@ function sessionScopedRuntimeRequestThreadId(event: ProviderAdapterV2Event): Thr
298299}
299300
300301function providerThreadRuntimeKey(
301 providerThread: Parameters<ProviderAdapterV2SessionRuntime["resumeThread"]>[0]["providerThread"],
302 providerThread: Parameters<
303 ProviderAdapter.ProviderAdapterV2SessionRuntime["resumeThread"]
304 >[0]["providerThread"],
302305): string {
303306 const nativeThreadRef = providerThread.nativeThreadRef;
304307 return nativeThreadRef === null
......@@ -308,10 +311,10 @@ function providerThreadRuntimeKey(
308311
309312function providerThreadLoadKey(input: {
310313 readonly providerThread: Parameters<
311 ProviderAdapterV2SessionRuntime["resumeThread"]
314 ProviderAdapter.ProviderAdapterV2SessionRuntime["resumeThread"]
312315 >[0]["providerThread"];
313316 readonly modelSelection?: ModelSelection;
314 readonly runtimePolicy?: ProviderAdapterV2RuntimePolicy;
317 readonly runtimePolicy?: ProviderAdapter.ProviderAdapterV2RuntimePolicy;
315318}): string {
316319 return JSON.stringify({
317320 providerThread: providerThreadRuntimeKey(input.providerThread),
......@@ -575,7 +578,7 @@ export const layerWithOptions = (
575578
576579 const failSubscribers = (entry: LiveSessionEntry, detail: string) =>
577580 Effect.gen(function* () {
578 const error = new ProviderAdapterEventStreamError({
581 const error = new ProviderAdapter.ProviderAdapterEventStreamError({
579582 driver: entry.runtime.driver,
580583 providerSessionId: entry.runtime.providerSessionId,
581584 cause: detail,
......@@ -617,7 +620,7 @@ export const layerWithOptions = (
617620 fiber === null ? Effect.void : Fiber.interrupt(fiber).pipe(Effect.ignore);
618621
619622 const writeProviderSessionEvents = (input: {
620 readonly runtime: ProviderAdapterV2SessionRuntime;
623 readonly runtime: ProviderAdapter.ProviderAdapterV2SessionRuntime;
621624 readonly threadIds: Iterable<ThreadId>;
622625 readonly type: "provider-session.attached" | "provider-session.updated";
623626 readonly payload: OrchestrationV2ProviderSession;
......@@ -1226,7 +1229,7 @@ export const layerWithOptions = (
12261229 const removeThreadAttachment = (input: {
12271230 readonly providerSessionId: ProviderSessionId;
12281231 readonly threadId: ThreadId;
1229 readonly runtime: ProviderAdapterV2SessionRuntime;
1232 readonly runtime: ProviderAdapter.ProviderAdapterV2SessionRuntime;
12301233 }) =>
12311234 Ref.update(sessions, (current) => {
12321235 const key = sessionKey(input.providerSessionId);
......@@ -1289,7 +1292,7 @@ export const layerWithOptions = (
12891292 readonly providerInstanceId: ProviderInstanceId;
12901293 }) =>
12911294 Effect.suspend(() => {
1292 let attachedTo: ProviderAdapterV2SessionRuntime | undefined;
1295 let attachedTo: ProviderAdapter.ProviderAdapterV2SessionRuntime | undefined;
12931296 let preparedForCleanup: PreparedMcpCredential | undefined;
12941297 let reservationDropped = false;
12951298 const dropReservation = () => {
......@@ -1631,7 +1634,7 @@ export const layerWithOptions = (
16311634 subscribers: Ref.Ref<
16321635 ReadonlyMap<number, Queue.Queue<ProviderSessionEventSignal, Cause.Done>>
16331636 >,
1634 ): Effect.Effect<ProviderAdapterV2EventSubscription> =>
1637 ): Effect.Effect<ProviderAdapter.ProviderAdapterV2EventSubscription> =>
16351638 Effect.gen(function* () {
16361639 const queue = yield* Queue.unbounded<ProviderSessionEventSignal, Cause.Done>();
16371640 const subscriberId = yield* Ref.getAndUpdate(nextSubscriberId, (value) => value + 1);
......@@ -1662,15 +1665,15 @@ export const layerWithOptions = (
16621665 ),
16631666 Stream.ensuring(close),
16641667 );
1665 return { events, close } satisfies ProviderAdapterV2EventSubscription;
1668 return { events, close } satisfies ProviderAdapter.ProviderAdapterV2EventSubscription;
16661669 });
16671670
16681671 const decorateRuntime = (
1669 runtime: ProviderAdapterV2SessionRuntime,
1672 runtime: ProviderAdapter.ProviderAdapterV2SessionRuntime,
16701673 eventSubscribers: Ref.Ref<
16711674 ReadonlyMap<number, Queue.Queue<ProviderSessionEventSignal, Cause.Done>>
16721675 >,
1673 ): ProviderAdapterV2SessionRuntime => {
1676 ): ProviderAdapter.ProviderAdapterV2SessionRuntime => {
16741677 const providerSessionId = runtime.providerSessionId;
16751678 const subscribeEvents = makeEventSubscription(eventSubscribers);
16761679 // Every provider's turn operations pass through here, so this is where they are
......@@ -1852,7 +1855,10 @@ export const layerWithOptions = (
18521855
18531856 const persistProviderSessionUpdate = (
18541857 entry: LiveSessionEntry,
1855 event: Extract<ProviderAdapterV2Event, { readonly type: "provider_session.updated" }>,
1858 event: Extract<
1859 ProviderAdapter.ProviderAdapterV2Event,
1860 { readonly type: "provider_session.updated" }
1861 >,
18561862 ) =>
18571863 Effect.gen(function* () {
18581864 const current = (yield* Ref.get(sessions)).get(
......@@ -1925,7 +1931,7 @@ export const layerWithOptions = (
19251931 .pipe(
19261932 Effect.mapError(
19271933 (cause) =>
1928 new ProviderAdapterEventStreamError({
1934 new ProviderAdapter.ProviderAdapterEventStreamError({
19291935 driver: entry.runtime.driver,
19301936 providerSessionId: entry.runtime.providerSessionId,
19311937 cause,
......@@ -1962,7 +1968,7 @@ export const layerWithOptions = (
19621968 const cause = Exit.isFailure(exit)
19631969 ? exit.cause
19641970 : Cause.fail(
1965 new ProviderAdapterEventStreamError({
1971 new ProviderAdapter.ProviderAdapterEventStreamError({
19661972 driver: entry.runtime.driver,
19671973 providerSessionId: entry.runtime.providerSessionId,
19681974 cause: "Provider event stream ended unexpectedly.",
......@@ -2212,7 +2218,7 @@ export const layerWithOptions = (
22122218 Effect.gen(function* () {
22132219 const entry = (yield* Ref.get(sessions)).get(sessionKey(providerSessionId));
22142220 if (entry === undefined) {
2215 return Option.none<ProviderAdapterV2SessionRuntime>();
2221 return Option.none<ProviderAdapter.ProviderAdapterV2SessionRuntime>();
22162222 }
22172223 yield* touchActivity(providerSessionId);
22182224 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";
1212import * as Layer from "effect/Layer";
1313
1414import { CodexProviderCapabilitiesV2 } from "./Adapters/CodexAdapterV2.ts";
15import type { ProviderAdapterV2Shape } from "@t3tools/provider-core/server/ProviderAdapter";
1615import * as ProviderAdapterRegistry from "./ProviderAdapterRegistry.ts";
1716import { acpSelectionTransition } from "@t3tools/provider-core/server/selectionTransition";
1817import * as ProviderSwitch from "./ProviderSwitchService.ts";
18import type * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter";
1919
2020const driver = ProviderDriverKind.make("codex");
2121const currentInstanceId = ProviderInstanceId.make("codex_primary");
......@@ -67,10 +67,12 @@ function deadSessionRecord(
6767
6868function layerTest(
6969 metadata: Readonly<Record<string, { continuationKey: string }>>,
70 planSelectionTransition: ProviderAdapterV2Shape["planSelectionTransition"] = () =>
70 planSelectionTransition: ProviderAdapter.ProviderAdapterV2["Service"]["planSelectionTransition"] = () =>
7171 Effect.succeed({ type: "restart_session" }),
7272) {
73 const adapter = (instanceId: ProviderInstanceId): ProviderAdapterV2Shape => ({
73 const adapter = (
74 instanceId: ProviderInstanceId,
75 ): ProviderAdapter.ProviderAdapterV2["Service"] => ({
7476 instanceId,
7577 driver,
7678 getCapabilities: () => Effect.succeed(capabilitiesWithoutModelSwitch),
apps/server/src/orchestration-v2/ProviderTurnControlService.test.ts+2-2
......@@ -24,7 +24,7 @@ import * as Stream from "effect/Stream";
2424
2525import { CodexProviderCapabilitiesV2 } from "./Adapters/CodexAdapterV2.ts";
2626import * as ProjectionStore from "./ProjectionStore.ts";
27import type { ProviderAdapterV2SessionRuntime } from "@t3tools/provider-core/server/ProviderAdapter";
27import type * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter";
2828import * as ProviderSessionManager from "./ProviderSessionManager.ts";
2929import * as ProviderTurnControlService from "./ProviderTurnControlService.ts";
3030
......@@ -177,7 +177,7 @@ it.effect(
177177 updatedAt: now,
178178 lastError: null,
179179 };
180 const runtime: ProviderAdapterV2SessionRuntime = {
180 const runtime: ProviderAdapter.ProviderAdapterV2SessionRuntime = {
181181 instanceId: providerInstanceId,
182182 driver,
183183 providerSessionId: oldSessionId,
apps/server/src/orchestration-v2/ProviderTurnStartService.test.ts+3-3
......@@ -32,7 +32,7 @@ import * as EventSink from "./EventSink.ts";
3232import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator";
3333import { CodexProviderCapabilitiesV2 } from "./Adapters/CodexAdapterV2.ts";
3434import * as ProjectionStore from "./ProjectionStore.ts";
35import { ProviderAdapterEventStreamError } from "@t3tools/provider-core/server/ProviderAdapter";
35import * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter";
3636import * as ProviderSessionManager from "./ProviderSessionManager.ts";
3737import * as ProviderTurnStart from "./ProviderTurnStartService.ts";
3838import * as RunExecutionService from "./RunExecutionService.ts";
......@@ -372,7 +372,7 @@ function makeLocalCommandHarness(input: {
372372 }).pipe(
373373 Effect.andThen(
374374 Effect.fail(
375 new ProviderAdapterEventStreamError({
375 new ProviderAdapter.ProviderAdapterEventStreamError({
376376 driver: providerThread.driver,
377377 providerSessionId,
378378 cause: input.ensureThreadFailure,
......@@ -385,7 +385,7 @@ function makeLocalCommandHarness(input: {
385385 driver: providerThread.driver,
386386 resumeThread: () =>
387387 Effect.fail(
388 new ProviderAdapterEventStreamError({
388 new ProviderAdapter.ProviderAdapterEventStreamError({
389389 driver: providerThread.driver,
390390 providerSessionId,
391391 cause: "native thread is gone",
apps/server/src/orchestration-v2/ProviderTurnStartService.ts+9-14
......@@ -37,12 +37,7 @@ import {
3737 latestNativeContextUsage,
3838} from "@t3tools/provider-core/server/handoffBudget";
3939import { deliverContextHandoffs } from "./ContextHandoffDelivery.ts";
40import {
41 ProviderAdapterTurnStartError,
42 type ProviderAdapterV2Error,
43 type ProviderAdapterV2HistoricalContext,
44 type ProviderAdapterV2SessionRuntime,
45} from "@t3tools/provider-core/server/ProviderAdapter";
40import * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter";
4641import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator";
4742import * as ProjectionStore from "./ProjectionStore.ts";
4843import * as ProviderSessionManager from "./ProviderSessionManager.ts";
......@@ -184,15 +179,15 @@ export const layer: Layer.Layer<
184179 };
185180
186181 const makeDeliverySession = (
187 session: ProviderAdapterV2SessionRuntime,
182 session: ProviderAdapter.ProviderAdapterV2SessionRuntime,
188183 startWithHandoffs: (
189 input: Parameters<ProviderAdapterV2SessionRuntime["startTurn"]>[0],
184 input: Parameters<ProviderAdapter.ProviderAdapterV2SessionRuntime["startTurn"]>[0],
190185 compact?: boolean,
191 ) => ReturnType<ProviderAdapterV2SessionRuntime["startTurn"]>,
186 ) => ReturnType<ProviderAdapter.ProviderAdapterV2SessionRuntime["startTurn"]>,
192187 ) => {
193188 let deliver: typeof startWithHandoffs | undefined = startWithHandoffs;
194189 const start = (
195 input: Parameters<ProviderAdapterV2SessionRuntime["startTurn"]>[0],
190 input: Parameters<ProviderAdapter.ProviderAdapterV2SessionRuntime["startTurn"]>[0],
196191 compact = false,
197192 ) =>
198193 Effect.suspend(() => {
......@@ -595,7 +590,7 @@ export const layer: Layer.Layer<
595590 // Only the provider's own thread load fails the run on the last attempt;
596591 // store, id and handoff failures around it keep their typed errors.
597592 const loadFromProvider = (
598 load: Effect.Effect<OrchestrationV2ProviderThread, ProviderAdapterV2Error>,
593 load: Effect.Effect<OrchestrationV2ProviderThread, ProviderAdapter.ProviderAdapterV2Error>,
599594 ) =>
600595 Effect.gen(function* () {
601596 const loaded = yield* Effect.result(load);
......@@ -672,7 +667,7 @@ export const layer: Layer.Layer<
672667 const resumed = yield* Effect.result(
673668 uncertainDelivery
674669 ? Effect.fail(
675 new ProviderAdapterTurnStartError({
670 new ProviderAdapter.ProviderAdapterTurnStartError({
676671 driver: session.driver,
677672 threadId: projection.thread.id,
678673 providerThreadId: providerThread.id,
......@@ -1143,7 +1138,7 @@ export const layer: Layer.Layer<
11431138 ...(session.injectHistory === undefined
11441139 ? {}
11451140 : {
1146 inject: (history: ProviderAdapterV2HistoricalContext) =>
1141 inject: (history: ProviderAdapter.ProviderAdapterV2HistoricalContext) =>
11471142 session.injectHistory!({
11481143 providerThread: runningProviderThread,
11491144 ...history,
......@@ -1211,7 +1206,7 @@ export const layer: Layer.Layer<
12111206 Effect.mapError((cause) =>
12121207 cause._tag === "ProviderAdapterTurnStartError"
12131208 ? cause
1214 : new ProviderAdapterTurnStartError({
1209 : new ProviderAdapter.ProviderAdapterTurnStartError({
12151210 driver: session.driver,
12161211 threadId: projection.thread.id,
12171212 providerThreadId: providerThread.id,
apps/server/src/orchestration-v2/RunExecutionService.test.ts+71-72
......@@ -43,14 +43,7 @@ import * as CheckpointService from "./CheckpointService.ts";
4343import type { PendingOrchestrationEffectV2 } from "./EffectOutbox.ts";
4444import * as EventSink from "./EventSink.ts";
4545import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator";
46import {
47 ProviderAdapterEventStreamError,
48 ProviderAdapterTurnStartError,
49 type ProviderAdapterV2Error,
50 type ProviderAdapterV2Event,
51 type ProviderAdapterV2SessionRuntime,
52 type ProviderAdapterV2TurnInput,
53} from "@t3tools/provider-core/server/ProviderAdapter";
46import * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter";
5447import * as ProjectionStore from "./ProjectionStore.ts";
5548import * as ProviderEventIngestor from "./ProviderEventIngestor.ts";
5649import * as McpAppModelContext from "../mcpApps/McpAppModelContext.ts";
......@@ -95,7 +88,7 @@ it.effect("routes shared-runtime events only to their owning root run", () =>
9588 providerThreadId: ProviderThreadId.make("provider-thread:shared-runtime:second"),
9689 };
9790 const firstTurnId = ProviderTurnId.make("provider-turn:shared-runtime:first");
98 const turnEvent: ProviderAdapterV2Event = {
91 const turnEvent: ProviderAdapter.ProviderAdapterV2Event = {
9992 type: "provider_turn.updated",
10093 driver,
10194 threadId: first.threadId,
......@@ -111,7 +104,7 @@ it.effect("routes shared-runtime events only to their owning root run", () =>
111104 completedAt: null,
112105 },
113106 };
114 const messageEvent: ProviderAdapterV2Event = {
107 const messageEvent: ProviderAdapter.ProviderAdapterV2Event = {
115108 type: "message.updated",
116109 driver,
117110 message: {
......@@ -129,7 +122,7 @@ it.effect("routes shared-runtime events only to their owning root run", () =>
129122 updatedAt: now,
130123 },
131124 };
132 const terminalEvent: ProviderAdapterV2Event = {
125 const terminalEvent: ProviderAdapter.ProviderAdapterV2Event = {
133126 type: "turn.terminal",
134127 driver,
135128 providerThreadId: first.providerThreadId,
......@@ -181,7 +174,7 @@ it("leaves a child thread created after the root turn ended to the run that is l
181174 attemptId: RunAttemptId.make("attempt:late-child"),
182175 providerThreadId: ProviderThreadId.make("provider-thread:late-child"),
183176 };
184 const childCreated = (childThreadId: ThreadId): ProviderAdapterV2Event =>
177 const childCreated = (childThreadId: ThreadId): ProviderAdapter.ProviderAdapterV2Event =>
185178 ({
186179 type: "app_thread.created",
187180 driver,
......@@ -193,7 +186,7 @@ it("leaves a child thread created after the root turn ended to the run that is l
193186 rootThreadId: threadId,
194187 },
195188 },
196 }) as ProviderAdapterV2Event;
189 }) as ProviderAdapter.ProviderAdapterV2Event;
197190 const earlyChild = ThreadId.make("thread:late-child:early");
198191 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", () =>
246239 ...oldAttempt,
247240 attemptId: RunAttemptId.make("attempt:shared-runtime:restart:new"),
248241 };
249 const oldTurnEvent: ProviderAdapterV2Event = {
242 const oldTurnEvent: ProviderAdapter.ProviderAdapterV2Event = {
250243 type: "provider_turn.updated",
251244 driver,
252245 threadId,
......@@ -299,33 +292,33 @@ it("routes only exact same-thread background items inherited from settled runs",
299292 type: "subagent",
300293 status: "running",
301294 },
302 } as Extract<ProviderAdapterV2Event, { type: "turn_item.updated" }>;
295 } as Extract<ProviderAdapter.ProviderAdapterV2Event, { type: "turn_item.updated" }>;
303296 const unrelatedRunItem = {
304297 ...inheritedRunning,
305298 turnItem: {
306299 ...inheritedRunning.turnItem,
307300 runId: RunId.make("run:inherited-background-routing:unrelated"),
308301 },
309 } as ProviderAdapterV2Event;
302 } as ProviderAdapter.ProviderAdapterV2Event;
310303 const unlistedPriorItem = {
311304 ...inheritedRunning,
312305 turnItem: {
313306 ...inheritedRunning.turnItem,
314307 id: TurnItemId.make("turn-item:inherited-background-routing:unrelated"),
315308 },
316 } as ProviderAdapterV2Event;
309 } as ProviderAdapter.ProviderAdapterV2Event;
317310 const unrelatedThreadItem = {
318311 ...inheritedRunning,
319312 turnItem: { ...inheritedRunning.turnItem, threadId: otherThreadId },
320 } as ProviderAdapterV2Event;
313 } as ProviderAdapter.ProviderAdapterV2Event;
321314 const ordinaryItem = {
322315 ...inheritedRunning,
323316 turnItem: { ...inheritedRunning.turnItem, type: "reasoning" as const },
324 } as ProviderAdapterV2Event;
317 } as ProviderAdapter.ProviderAdapterV2Event;
325318 const inheritedTerminal = {
326319 ...inheritedRunning,
327320 turnItem: { ...inheritedRunning.turnItem, status: "completed" as const },
328 } as ProviderAdapterV2Event;
321 } as ProviderAdapter.ProviderAdapterV2Event;
329322
330323 const [runningAccepted, afterRunning] = RunExecutionService.routeProviderEvent(
331324 inheritedRunning,
......@@ -515,7 +508,7 @@ it("does not carry interrupted or still-running child ownership into later attem
515508 startedAt: null,
516509 completedAt: null,
517510 },
518 } satisfies ProviderAdapterV2Event;
511 } satisfies ProviderAdapter.ProviderAdapterV2Event;
519512
520513 assert.isFalse(RunExecutionService.routeProviderEvent(lateChildNode, identity, state)[0]);
521514});
......@@ -550,7 +543,7 @@ it.effect("rechecks run ownership immediately before calling the provider", () =
550543 const session = {
551544 events: Stream.never,
552545 startTurn: () => Ref.update(providerStarts, (count) => count + 1),
553 } as unknown as ProviderAdapterV2SessionRuntime;
546 } as unknown as ProviderAdapter.ProviderAdapterV2SessionRuntime;
554547
555548 yield* runExecution.startRootRun({
556549 commandId: CommandId.make("command:run-execution-start-guard"),
......@@ -597,14 +590,15 @@ it.effect("rechecks run ownership immediately before calling the provider", () =
597590it.effect("passes the thread's MCP app context to the provider under a safe key", () =>
598591 Effect.gen(function* () {
599592 const runExecution = yield* RunExecutionService.RunExecutionServiceV2;
600 const started = yield* Deferred.make<ProviderAdapterV2TurnInput>();
593 const started = yield* Deferred.make<ProviderAdapter.ProviderAdapterV2TurnInput>();
601594 const threadId = ThreadId.make("thread:run-execution-app-context");
602595 const providerInstanceId = ProviderInstanceId.make("codex");
603596 const attemptId = RunAttemptId.make("attempt:run-execution-app-context");
604597 const session = {
605598 events: Stream.never,
606 startTurn: (input: ProviderAdapterV2TurnInput) => Deferred.succeed(started, input),
607 } as unknown as ProviderAdapterV2SessionRuntime;
599 startTurn: (input: ProviderAdapter.ProviderAdapterV2TurnInput) =>
600 Deferred.succeed(started, input),
601 } as unknown as ProviderAdapter.ProviderAdapterV2SessionRuntime;
608602
609603 yield* runExecution.startRootRun({
610604 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
716710 session: {
717711 events: Stream.never,
718712 startTurn: () => Ref.update(providerStarts, (count) => count + 1),
719 } as unknown as ProviderAdapterV2SessionRuntime,
713 } as unknown as ProviderAdapter.ProviderAdapterV2SessionRuntime,
720714 run: { id: runId, threadId, ordinal: 1, providerInstanceId } as OrchestrationV2Run,
721715 rootNode: {
722716 id: NodeId.make("node:run-execution-start-guard-read"),
......@@ -821,7 +815,7 @@ it.effect(
821815 Effect.sync(() => {
822816 calls.push("compact");
823817 }),
824 } as unknown as ProviderAdapterV2SessionRuntime,
818 } as unknown as ProviderAdapter.ProviderAdapterV2SessionRuntime,
825819 run: {
826820 id: RunId.make(`run:compact-routing:${index}`),
827821 threadId,
......@@ -888,7 +882,7 @@ it.effect("refreshes MCP credential liveness before calling the provider", () =>
888882 session: {
889883 events: Stream.never,
890884 startTurn: () => Ref.update(order, (entries) => [...entries, "start-turn"]),
891 } as unknown as ProviderAdapterV2SessionRuntime,
885 } as unknown as ProviderAdapter.ProviderAdapterV2SessionRuntime,
892886 run: {
893887 id: RunId.make("run:run-execution-mcp-liveness"),
894888 threadId,
......@@ -1000,7 +994,7 @@ it.effect("starts the provider when checkpoint baseline capture fails", () =>
1000994 session: {
1001995 events: Stream.never,
1002996 startTurn: () => Ref.update(providerStarts, (count) => count + 1),
1003 } as unknown as ProviderAdapterV2SessionRuntime,
997 } as unknown as ProviderAdapter.ProviderAdapterV2SessionRuntime,
1004998 run: {
1005999 id: runId,
10061000 threadId,
......@@ -1137,7 +1131,7 @@ it.effect.each(["failure", "interruption", "stale-attempt", "start-guard"] as co
11371131 session: {
11381132 events: Stream.never,
11391133 startTurn: () => Ref.update(providerStarts, (count) => count + 1),
1140 } as unknown as ProviderAdapterV2SessionRuntime,
1134 } as unknown as ProviderAdapter.ProviderAdapterV2SessionRuntime,
11411135 run: {
11421136 id: runId,
11431137 threadId,
......@@ -1288,7 +1282,7 @@ it.effect("keeps ingesting owned child events after the root turn terminalizes",
12881282 ),
12891283 ),
12901284 );
1291 const events: ReadonlyArray<ProviderAdapterV2Event> = [
1285 const events: ReadonlyArray<ProviderAdapter.ProviderAdapterV2Event> = [
12921286 {
12931287 type: "app_thread.created",
12941288 driver,
......@@ -1300,7 +1294,7 @@ it.effect("keeps ingesting owned child events after the root turn terminalizes",
13001294 rootThreadId: threadId,
13011295 },
13021296 },
1303 } as ProviderAdapterV2Event,
1297 } as ProviderAdapter.ProviderAdapterV2Event,
13041298 {
13051299 type: "provider_thread.updated",
13061300 driver,
......@@ -1308,7 +1302,7 @@ it.effect("keeps ingesting owned child events after the root turn terminalizes",
13081302 id: childProviderThreadId,
13091303 appThreadId: childThreadId,
13101304 },
1311 } as ProviderAdapterV2Event,
1305 } as ProviderAdapter.ProviderAdapterV2Event,
13121306 {
13131307 type: "subagent.updated",
13141308 driver,
......@@ -1318,7 +1312,7 @@ it.effect("keeps ingesting owned child events after the root turn terminalizes",
13181312 runId,
13191313 status: "running",
13201314 },
1321 } as ProviderAdapterV2Event,
1315 } as ProviderAdapter.ProviderAdapterV2Event,
13221316 {
13231317 type: "provider_turn.updated",
13241318 driver,
......@@ -1330,7 +1324,7 @@ it.effect("keeps ingesting owned child events after the root turn terminalizes",
13301324 runAttemptId: null,
13311325 status: "running",
13321326 },
1333 } as ProviderAdapterV2Event,
1327 } as ProviderAdapter.ProviderAdapterV2Event,
13341328 {
13351329 type: "turn.terminal",
13361330 driver,
......@@ -1353,7 +1347,7 @@ it.effect("keeps ingesting owned child events after the root turn terminalizes",
13531347 text: "Hello.",
13541348 streaming: false,
13551349 },
1356 } as ProviderAdapterV2Event,
1350 } as ProviderAdapter.ProviderAdapterV2Event,
13571351 {
13581352 type: "provider_turn.updated",
13591353 driver,
......@@ -1365,7 +1359,7 @@ it.effect("keeps ingesting owned child events after the root turn terminalizes",
13651359 runAttemptId: null,
13661360 status: "completed",
13671361 },
1368 } as ProviderAdapterV2Event,
1362 } as ProviderAdapter.ProviderAdapterV2Event,
13691363 {
13701364 type: "subagent.updated",
13711365 driver,
......@@ -1375,7 +1369,7 @@ it.effect("keeps ingesting owned child events after the root turn terminalizes",
13751369 runId,
13761370 status: "completed",
13771371 },
1378 } as ProviderAdapterV2Event,
1372 } as ProviderAdapter.ProviderAdapterV2Event,
13791373 ];
13801374
13811375 yield* Effect.gen(function* () {
......@@ -1387,7 +1381,7 @@ it.effect("keeps ingesting owned child events after the root turn terminalizes",
13871381 session: {
13881382 events: Stream.fromIterable(events),
13891383 startTurn: () => Effect.void,
1390 } as unknown as ProviderAdapterV2SessionRuntime,
1384 } as unknown as ProviderAdapter.ProviderAdapterV2SessionRuntime,
13911385 run: {
13921386 id: runId,
13931387 threadId,
......@@ -1788,7 +1782,7 @@ it.effect(
17881782 ],
17891783 updatedAt: now,
17901784 },
1791 } as ProviderAdapterV2Event,
1785 } as ProviderAdapter.ProviderAdapterV2Event,
17921786 rootTerminalEvent(ids, "completed"),
17931787 {
17941788 type: "provider_thread.updated",
......@@ -1799,12 +1793,12 @@ it.effect(
17991793 pendingBackgroundTasks: [],
18001794 updatedAt: now,
18011795 },
1802 } as ProviderAdapterV2Event,
1796 } as ProviderAdapter.ProviderAdapterV2Event,
18031797 ]),
18041798 close: Deferred.succeed(ingestionDone, undefined),
18051799 }),
18061800 startTurn: () => Effect.void,
1807 } as unknown as ProviderAdapterV2SessionRuntime,
1801 } as unknown as ProviderAdapter.ProviderAdapterV2SessionRuntime,
18081802 run: {
18091803 id: ids.runId,
18101804 threadId: ids.threadId,
......@@ -2000,7 +1994,7 @@ it.effect("drops late root provider-thread writes from a superseded attempt", ()
20001994 ],
20011995 updatedAt: now,
20021996 },
2003 } as ProviderAdapterV2Event,
1997 } as ProviderAdapter.ProviderAdapterV2Event,
20041998 rootTerminalEvent(ids, "completed"),
20051999 // Late snapshot after a replacement attempt claimed the same
20062000 // 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", ()
20162010 ],
20172011 updatedAt: now,
20182012 },
2019 } as ProviderAdapterV2Event,
2013 } as ProviderAdapter.ProviderAdapterV2Event,
20202014 ]),
20212015 close: Deferred.succeed(ingestionDone, undefined),
20222016 }),
20232017 startTurn: () => Effect.void,
2024 } as unknown as ProviderAdapterV2SessionRuntime,
2018 } as unknown as ProviderAdapter.ProviderAdapterV2SessionRuntime,
20252019 run: {
20262020 id: ids.runId,
20272021 threadId: ids.threadId,
......@@ -2197,7 +2191,7 @@ it.effect(
21972191 ],
21982192 updatedAt: now,
21992193 },
2200 } as ProviderAdapterV2Event,
2194 } as ProviderAdapter.ProviderAdapterV2Event,
22012195 // Late completion still writable (turn_item writes are not
22022196 // ownership-gated); stream must stay open for it.
22032197 backgroundTurnItemEvent(ids, "command_execution", "completed", 2),
......@@ -2205,7 +2199,7 @@ it.effect(
22052199 close: Deferred.succeed(ingestionDone, undefined),
22062200 }),
22072201 startTurn: () => Effect.void,
2208 } as unknown as ProviderAdapterV2SessionRuntime,
2202 } as unknown as ProviderAdapter.ProviderAdapterV2SessionRuntime,
22092203 run: {
22102204 id: ids.runId,
22112205 threadId: ids.threadId,
......@@ -2371,12 +2365,12 @@ it.effect(
23712365 ],
23722366 updatedAt: now,
23732367 },
2374 } as ProviderAdapterV2Event,
2368 } as ProviderAdapter.ProviderAdapterV2Event,
23752369 ]),
23762370 close: Deferred.succeed(ingestionDone, undefined),
23772371 }),
23782372 startTurn: () => Effect.void,
2379 } as unknown as ProviderAdapterV2SessionRuntime,
2373 } as unknown as ProviderAdapter.ProviderAdapterV2SessionRuntime,
23802374 run: {
23812375 id: ids.runId,
23822376 threadId: ids.threadId,
......@@ -2439,7 +2433,7 @@ it.effect(
24392433 );
24402434 const providerInstanceId = ProviderInstanceId.make("codex");
24412435 const written = yield* Ref.make<ReadonlyArray<OrchestrationV2DomainEvent>>([]);
2442 const ingested = yield* Ref.make<ReadonlyArray<ProviderAdapterV2Event>>([]);
2436 const ingested = yield* Ref.make<ReadonlyArray<ProviderAdapter.ProviderAdapterV2Event>>([]);
24432437 const ingestionDone = yield* Deferred.make<void>();
24442438 const layerTest = RunExecutionService.layer.pipe(
24452439 Layer.provide(
......@@ -2609,11 +2603,11 @@ it.effect(
26092603 completedAt: runningChildNode.startedAt,
26102604 },
26112605 },
2612 ] satisfies ReadonlyArray<ProviderAdapterV2Event>),
2606 ] satisfies ReadonlyArray<ProviderAdapter.ProviderAdapterV2Event>),
26132607 close: Deferred.succeed(ingestionDone, undefined),
26142608 }),
26152609 startTurn: () => Effect.void,
2616 } as unknown as ProviderAdapterV2SessionRuntime,
2610 } as unknown as ProviderAdapter.ProviderAdapterV2SessionRuntime,
26172611 run: {
26182612 id: ids.runId,
26192613 threadId: ids.threadId,
......@@ -2940,11 +2934,11 @@ it.effect(
29402934 },
29412935 },
29422936 rootTerminalEvent(ids, "interrupted"),
2943 ] satisfies ReadonlyArray<ProviderAdapterV2Event>),
2937 ] satisfies ReadonlyArray<ProviderAdapter.ProviderAdapterV2Event>),
29442938 close: Deferred.succeed(ingestionDone, undefined),
29452939 }),
29462940 startTurn: () => Effect.void,
2947 } as unknown as ProviderAdapterV2SessionRuntime,
2941 } as unknown as ProviderAdapter.ProviderAdapterV2SessionRuntime,
29482942 run: {
29492943 id: ids.runId,
29502944 threadId: ids.threadId,
......@@ -3439,7 +3433,7 @@ it.effect("refreshes pull requests after a provider stream exits with an error",
34393433 shouldFinalizeRun: () => Effect.succeed(true),
34403434 events: () =>
34413435 Stream.fail(
3442 new ProviderAdapterEventStreamError({
3436 new ProviderAdapter.ProviderAdapterEventStreamError({
34433437 driver,
34443438 providerSessionId: ProviderSessionId.make("session:exited"),
34453439 cause: "provider process exited",
......@@ -3468,7 +3462,7 @@ it.effect("refreshes pull requests only once when startup failure closes its eve
34683462 Deferred.await(ingestionStarted).pipe(
34693463 Effect.andThen(
34703464 Effect.fail(
3471 new ProviderAdapterTurnStartError({
3465 new ProviderAdapter.ProviderAdapterTurnStartError({
34723466 driver,
34733467 threadId: input.threadId,
34743468 providerThreadId: input.providerThread.id,
......@@ -3506,8 +3500,11 @@ function captureRootRunTermination(input: {
35063500 readonly seedOpenSubagent?: boolean;
35073501 readonly events?: (
35083502 ids: BackgroundScenarioIds,
3509 ) => Stream.Stream<ProviderAdapterV2Event, ProviderAdapterV2Error>;
3510 readonly startTurn?: ProviderAdapterV2SessionRuntime["startTurn"];
3503 ) => Stream.Stream<
3504 ProviderAdapter.ProviderAdapterV2Event,
3505 ProviderAdapter.ProviderAdapterV2Error
3506 >;
3507 readonly startTurn?: ProviderAdapter.ProviderAdapterV2SessionRuntime["startTurn"];
35113508 readonly refreshAfterTurn?: Effect.Effect<void>;
35123509}) {
35133510 return Effect.gen(function* () {
......@@ -3622,11 +3619,11 @@ function captureRootRunTermination(input: {
36223619 ]
36233620 : []),
36243621 rootTerminalEvent(ids, "interrupted"),
3625 ] satisfies ReadonlyArray<ProviderAdapterV2Event>),
3622 ] satisfies ReadonlyArray<ProviderAdapter.ProviderAdapterV2Event>),
36263623 close: Deferred.succeed(ingestionDone, undefined),
36273624 }),
36283625 startTurn: input.startTurn ?? (() => Effect.void),
3629 } as unknown as ProviderAdapterV2SessionRuntime,
3626 } as unknown as ProviderAdapter.ProviderAdapterV2SessionRuntime,
36303627 run: {
36313628 id: ids.runId,
36323629 threadId: ids.threadId,
......@@ -3716,7 +3713,9 @@ function backgroundScenarioIds(key: string): BackgroundScenarioIds {
37163713 };
37173714}
37183715
3719function childThreadCreatedEvent(ids: BackgroundScenarioIds): ProviderAdapterV2Event {
3716function childThreadCreatedEvent(
3717 ids: BackgroundScenarioIds,
3718): ProviderAdapter.ProviderAdapterV2Event {
37203719 return {
37213720 type: "app_thread.created",
37223721 driver,
......@@ -3728,14 +3727,14 @@ function childThreadCreatedEvent(ids: BackgroundScenarioIds): ProviderAdapterV2E
37283727 rootThreadId: ids.threadId,
37293728 },
37303729 },
3731 } as ProviderAdapterV2Event;
3730 } as ProviderAdapter.ProviderAdapterV2Event;
37323731}
37333732
37343733function childBackgroundTurnItemEvent(
37353734 ids: BackgroundScenarioIds,
37363735 status: "running" | "completed" | "idle",
37373736 ordinal: number,
3738): ProviderAdapterV2Event {
3737): ProviderAdapter.ProviderAdapterV2Event {
37393738 return {
37403739 type: "turn_item.updated",
37413740 driver,
......@@ -3748,7 +3747,7 @@ function childBackgroundTurnItemEvent(
37483747 type: "command_execution",
37493748 status,
37503749 },
3751 } as ProviderAdapterV2Event;
3750 } as ProviderAdapter.ProviderAdapterV2Event;
37523751}
37533752
37543753function backgroundTurnItemEvent(
......@@ -3757,7 +3756,7 @@ function backgroundTurnItemEvent(
37573756 status: "running" | "completed" | "idle",
37583757 ordinal: number,
37593758 itemId?: TurnItemId,
3760): ProviderAdapterV2Event {
3759): ProviderAdapter.ProviderAdapterV2Event {
37613760 return {
37623761 type: "turn_item.updated",
37633762 driver,
......@@ -3770,7 +3769,7 @@ function backgroundTurnItemEvent(
37703769 type,
37713770 status,
37723771 },
3773 } as ProviderAdapterV2Event;
3772 } as ProviderAdapter.ProviderAdapterV2Event;
37743773}
37753774
37763775function backgroundTurnItemEventForRun(
......@@ -3779,7 +3778,7 @@ function backgroundTurnItemEventForRun(
37793778 type: "command_execution" | "dynamic_tool" | "subagent",
37803779 status: "running" | "completed" | "idle",
37813780 ordinal: number,
3782): ProviderAdapterV2Event {
3781): ProviderAdapter.ProviderAdapterV2Event {
37833782 const event = backgroundTurnItemEvent(ids, type, status, ordinal);
37843783 if (event.type !== "turn_item.updated") {
37853784 return event;
......@@ -3790,7 +3789,7 @@ function backgroundTurnItemEventForRun(
37903789function subagentEvent(
37913790 ids: BackgroundScenarioIds,
37923791 status: "running" | "completed" | "idle",
3793): ProviderAdapterV2Event {
3792): ProviderAdapter.ProviderAdapterV2Event {
37943793 return {
37953794 type: "subagent.updated",
37963795 driver,
......@@ -3800,7 +3799,7 @@ function subagentEvent(
38003799 runId: ids.runId,
38013800 status,
38023801 },
3803 } as ProviderAdapterV2Event;
3802 } as ProviderAdapter.ProviderAdapterV2Event;
38043803}
38053804
38063805function makeRunOwnedSubagentFixture(input: {
......@@ -3977,7 +3976,7 @@ function makeRunOwnedSubagentChildNodeFixture(input: {
39773976function rootTerminalEvent(
39783977 ids: BackgroundScenarioIds,
39793978 status: "completed" | "interrupted" | "cancelled" | "failed",
3980): ProviderAdapterV2Event {
3979): ProviderAdapter.ProviderAdapterV2Event {
39813980 const common = {
39823981 type: "turn.terminal" as const,
39833982 driver,
......@@ -4003,7 +4002,7 @@ function rootTerminalEvent(
40034002
40044003function runBackgroundItemScenario(
40054004 key: string,
4006 makeEvents: (ids: BackgroundScenarioIds) => ReadonlyArray<ProviderAdapterV2Event>,
4005 makeEvents: (ids: BackgroundScenarioIds) => ReadonlyArray<ProviderAdapter.ProviderAdapterV2Event>,
40074006 options?: {
40084007 readonly keepEventStreamOpen?: boolean;
40094008 readonly loadInheritedBackgroundTurnItems?: () => Effect.Effect<
......@@ -4082,7 +4081,7 @@ function runBackgroundItemScenario(
40824081 };
40834082 }),
40844083 startTurn: () => Effect.void,
4085 } as unknown as ProviderAdapterV2SessionRuntime,
4084 } as unknown as ProviderAdapter.ProviderAdapterV2SessionRuntime,
40864085 run: {
40874086 id: ids.runId,
40884087 threadId: ids.threadId,
apps/server/src/orchestration-v2/RunExecutionService.ts+15-16
......@@ -41,13 +41,7 @@ import * as ServerSettings from "../serverSettings.ts";
4141import * as CheckpointService from "./CheckpointService.ts";
4242import * as EventSink from "./EventSink.ts";
4343import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator";
44import type {
45 ProviderAdapterV2Event,
46 ProviderAdapterV2RuntimePolicy,
47 ProviderAdapterV2SessionRuntime,
48 ProviderAdapterV2TurnMessage,
49} from "@t3tools/provider-core/server/ProviderAdapter";
50import { ProviderAdapterTurnStartError } from "@t3tools/provider-core/server/ProviderAdapter";
44import * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter";
5145import * as ProviderEventIngestor from "./ProviderEventIngestor.ts";
5246import type { ProjectionStoreV2Error } from "./ProjectionStore.ts";
5347import {
......@@ -79,7 +73,10 @@ export interface InheritedBackgroundTurnItemRoute {
7973 readonly runId: OrchestrationV2Run["id"];
8074}
8175
82type ProviderTerminalEvent = Extract<ProviderAdapterV2Event, { readonly type: "turn.terminal" }>;
76type ProviderTerminalEvent = Extract<
77 ProviderAdapter.ProviderAdapterV2Event,
78 { readonly type: "turn.terminal" }
79>;
8380
8481function isTerminalProviderTurnStatus(status: OrchestrationV2ProviderTurn["status"]): boolean {
8582 return (
......@@ -351,7 +348,7 @@ export function makeProviderEventRoutingState(input: {
351348}
352349
353350export function routeProviderEvent(
354 event: ProviderAdapterV2Event,
351 event: ProviderAdapter.ProviderAdapterV2Event,
355352 input: ProviderEventRouteIdentity,
356353 state: ProviderEventRoutingState,
357354): readonly [boolean, ProviderEventRoutingState] {
......@@ -503,7 +500,7 @@ export interface RunExecutionServiceV2StartRootRunInput {
503500 readonly commandId: CommandId;
504501 readonly appThread: OrchestrationV2AppThread;
505502 readonly providerSessionId: ProviderSessionId;
506 readonly session: ProviderAdapterV2SessionRuntime;
503 readonly session: ProviderAdapter.ProviderAdapterV2SessionRuntime;
507504 readonly run: OrchestrationV2Run;
508505 readonly rootNode: OrchestrationV2ExecutionNode;
509506 readonly checkpointScope: OrchestrationV2CheckpointScope;
......@@ -521,9 +518,9 @@ export interface RunExecutionServiceV2StartRootRunInput {
521518 readonly shouldStartProviderTurn?: () => Effect.Effect<boolean, ProjectionStoreV2Error>;
522519 readonly shouldFinalizeRun?: () => Effect.Effect<boolean, ProjectionStoreV2Error>;
523520 readonly hasUnpairedRunInterruptRequest?: () => Effect.Effect<boolean, never>;
524 readonly message: ProviderAdapterV2TurnMessage;
521 readonly message: ProviderAdapter.ProviderAdapterV2TurnMessage;
525522 readonly modelSelection: ModelSelection;
526 readonly runtimePolicy: ProviderAdapterV2RuntimePolicy;
523 readonly runtimePolicy: ProviderAdapter.ProviderAdapterV2RuntimePolicy;
527524}
528525
529526export interface RunExecutionServiceV2Shape {
......@@ -738,7 +735,6 @@ export const layer: Layer.Layer<
738735 providerInstanceId: input.run.providerInstanceId,
739736 occurredAt: completedAt,
740737 payload: makeProviderFailureTurnItem({
741 idAllocator,
742738 driver: input.terminal.driver,
743739 threadId: input.run.threadId,
744740 runId: input.run.id,
......@@ -1006,7 +1002,10 @@ export const layer: Layer.Layer<
10061002 }
10071003 yield* Ref.set(rootRunFinalized, true);
10081004 });
1009 const trackChildLifecycle = (event: ProviderAdapterV2Event, deliverable: boolean) =>
1005 const trackChildLifecycle = (
1006 event: ProviderAdapter.ProviderAdapterV2Event,
1007 deliverable: boolean,
1008 ) =>
10101009 Effect.gen(function* () {
10111010 const routing = yield* Ref.get(eventRouting);
10121011 if (event.type === "provider_turn.updated") {
......@@ -1403,7 +1402,7 @@ export const layer: Layer.Layer<
14031402 const startTurn = compact
14041403 ? (input.session.compactThread?.(turnInput) ??
14051404 Effect.fail(
1406 new ProviderAdapterTurnStartError({
1405 new ProviderAdapter.ProviderAdapterTurnStartError({
14071406 driver: input.session.driver,
14081407 threadId: input.run.threadId,
14091408 providerThreadId: input.providerThread.id,
......@@ -1471,7 +1470,7 @@ export const layer: Layer.Layer<
14711470);
14721471
14731472export function makeInterruptResultTurnItem(input: {
1474 readonly idAllocator: IdAllocator.IdAllocatorV2Shape;
1473 readonly idAllocator: IdAllocator.IdAllocatorV2["Service"];
14751474 readonly run: OrchestrationV2Run;
14761475 readonly rootNode: OrchestrationV2ExecutionNode;
14771476 readonly providerThread: OrchestrationV2ProviderThread;
apps/server/src/orchestration-v2/RuntimePolicy.ts+4-7
......@@ -12,10 +12,7 @@ import * as Option from "effect/Option";
1212import * as Schema from "effect/Schema";
1313
1414import * as ProviderInstanceRegistry from "../provider/ProviderInstanceRegistry.ts";
15import {
16 ProviderAdapterV2RuntimePolicy,
17 type ProviderAdapterV2RuntimePolicy as ProviderAdapterV2RuntimePolicyType,
18} from "@t3tools/provider-core/server/ProviderAdapter";
15import * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter";
1916import * as ProjectStore from "./ProjectStore.ts";
2017
2118/**
......@@ -52,7 +49,7 @@ export interface RuntimePolicyV2Shape {
5249 readonly resolve: (input: {
5350 readonly thread: OrchestrationV2AppThread;
5451 readonly modelSelection: ModelSelection;
55 }) => Effect.Effect<ProviderAdapterV2RuntimePolicyType, RuntimePolicyV2Error>;
52 }) => Effect.Effect<ProviderAdapter.ProviderAdapterV2RuntimePolicy, RuntimePolicyV2Error>;
5653}
5754
5855export class RuntimePolicyV2 extends Context.Service<RuntimePolicyV2, RuntimePolicyV2Shape>()(
......@@ -128,7 +125,7 @@ export const layerFromProjectStore: Layer.Layer<
128125 }),
129126 ),
130127 ));
131 return ProviderAdapterV2RuntimePolicy.make({
128 return ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
132129 runtimeMode: providerRuntimeMode(input.thread.runtimeMode, supportedRuntimeModes),
133130 interactionMode: input.thread.interactionMode,
134131 cwd,
......@@ -149,7 +146,7 @@ export function layerWithOverride(
149146 resolve: (input) =>
150147 base.resolve(input).pipe(
151148 Effect.map((policy) =>
152 ProviderAdapterV2RuntimePolicy.make({
149 ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
153150 ...policy,
154151 ...(override.cwd === undefined ? {} : { cwd: override.cwd }),
155152 ...(override.approvalPolicy === undefined
apps/server/src/orchestration-v2/RuntimeRequestService.test.ts+3-3
......@@ -12,7 +12,7 @@ import * as Effect from "effect/Effect";
1212import * as Layer from "effect/Layer";
1313import * 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";
1616import * as ProjectionStore from "./ProjectionStore.ts";
1717import * as ProviderSessionManager from "./ProviderSessionManager.ts";
1818import * as RuntimeRequestService from "./RuntimeRequestService.ts";
......@@ -69,7 +69,7 @@ it.effect("forwards orchestrator-resolved runtime requests to the live adapter",
6969 const providerSessionId = ProviderSessionId.make("provider-session-runtime-request-resolved");
7070 const requestId = RuntimeRequestId.make("request-resolved");
7171 const respondToRuntimeRequest = vi.fn(
72 (_input: ProviderAdapterV2RuntimeRequestResponseInput) => Effect.void,
72 (_input: ProviderAdapter.ProviderAdapterV2RuntimeRequestResponseInput) => Effect.void,
7373 );
7474 const getSession = vi.fn(() =>
7575 Effect.succeed(
......@@ -133,7 +133,7 @@ it.effect("rejects expired runtime requests before invoking the live adapter", (
133133 const providerSessionId = ProviderSessionId.make("provider-session-runtime-request-expired");
134134 const requestId = RuntimeRequestId.make("request-expired");
135135 const respondToRuntimeRequest = vi.fn(
136 (_input: ProviderAdapterV2RuntimeRequestResponseInput) => Effect.void,
136 (_input: ProviderAdapter.ProviderAdapterV2RuntimeRequestResponseInput) => Effect.void,
137137 );
138138 const getSession = vi.fn(() =>
139139 Effect.succeed(
apps/server/src/orchestration-v2/SelectionRestart.integration.test.ts+10-13
......@@ -28,15 +28,10 @@ import { CodexProviderCapabilitiesV2 } from "./Adapters/CodexAdapterV2.ts";
2828import * as EffectWorker from "./EffectWorker.ts";
2929import * as EventSink from "./EventSink.ts";
3030import * as Orchestrator from "./Orchestrator.ts";
31import {
32 ProviderAdapterOpenSessionError,
33 type ProviderAdapterV2Event,
34 type ProviderAdapterV2Shape,
35 type ProviderAdapterV2TurnInput,
36} from "@t3tools/provider-core/server/ProviderAdapter";
3731import * as ProviderAdapterRegistry from "./ProviderAdapterRegistry.ts";
3832import * as ProviderReplayHarness from "./testkit/ProviderReplayHarness.ts";
3933import { checkpointWorkspace } from "@t3tools/provider-testing/replayWorkspace";
34import * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter";
4035
4136const driver = ProviderDriverKind.make("codex");
4237const providerInstanceId = ProviderInstanceId.make("codex-restart-test");
......@@ -69,7 +64,7 @@ const exclusiveCapabilities: OrchestrationV2ProviderCapabilities = {
6964};
7065
7166interface ActiveTurn {
72 readonly input: ProviderAdapterV2TurnInput;
67 readonly input: ProviderAdapter.ProviderAdapterV2TurnInput;
7368 readonly providerTurnId: ProviderTurnId;
7469}
7570
......@@ -92,7 +87,7 @@ function makeRestartAdapter(
9287 state: Ref.Ref<RestartAdapterState>,
9388 sessionCapabilities: OrchestrationV2ProviderCapabilities = pooledCapabilities,
9489 providerInstanceId = initialSelection.instanceId,
95): ProviderAdapterV2Shape {
90): ProviderAdapter.ProviderAdapterV2["Service"] {
9691 return {
9792 instanceId: providerInstanceId,
9893 driver,
......@@ -125,14 +120,14 @@ function makeRestartAdapter(
125120 ] as const;
126121 });
127122 if (failThisOpen) {
128 return yield* new ProviderAdapterOpenSessionError({
123 return yield* new ProviderAdapter.ProviderAdapterOpenSessionError({
129124 driver,
130125 providerSessionId: sessionInput.providerSessionId,
131126 cause: "simulated replacement open failure",
132127 });
133128 }
134129
135 const events = yield* Queue.unbounded<ProviderAdapterV2Event>();
130 const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>();
136131 const now = yield* DateTime.now;
137132 const providerSession: OrchestrationV2ProviderSession = {
138133 id: sessionInput.providerSessionId,
......@@ -290,7 +285,9 @@ function makeRestartAdapter(
290285 };
291286}
292287
293function makeCompletingHandoffAdapter(startCount: Ref.Ref<number>): ProviderAdapterV2Shape {
288function makeCompletingHandoffAdapter(
289 startCount: Ref.Ref<number>,
290): ProviderAdapter.ProviderAdapterV2["Service"] {
294291 return {
295292 instanceId: handoffProviderInstanceId,
296293 driver: handoffDriver,
......@@ -298,7 +295,7 @@ function makeCompletingHandoffAdapter(startCount: Ref.Ref<number>): ProviderAdap
298295 planSelectionTransition: () => Effect.succeed({ type: "apply_on_next_turn" }),
299296 openSession: (sessionInput) =>
300297 Effect.gen(function* () {
301 const events = yield* Queue.unbounded<ProviderAdapterV2Event>();
298 const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>();
302299 const now = yield* DateTime.now;
303300 return {
304301 instanceId: handoffProviderInstanceId,
......@@ -865,7 +862,7 @@ it.live.each(["active", "idle", "selection-command", "pooled", "separate-home"]
865862 }),
866863 })),
867864 ),
868 } satisfies ProviderAdapterV2Shape;
865 } satisfies ProviderAdapter.ProviderAdapterV2["Service"];
869866 });
870867 const layerRegistry = Layer.succeed(ProviderAdapterRegistry.ProviderAdapterRegistryV2, {
871868 get: (instanceId) =>
apps/server/src/orchestration-v2/SteeringCompletion.integration.test.ts+8-13
......@@ -23,15 +23,10 @@ import { CodexProviderCapabilitiesV2 } from "./Adapters/CodexAdapterV2.ts";
2323import * as EffectWorker from "./EffectWorker.ts";
2424import * as EventSink from "./EventSink.ts";
2525import * as Orchestrator from "./Orchestrator.ts";
26import {
27 ProviderAdapterSteerRunError,
28 type ProviderAdapterV2Event,
29 type ProviderAdapterV2Shape,
30 type ProviderAdapterV2TurnInput,
31} from "@t3tools/provider-core/server/ProviderAdapter";
3226import * as ProviderAdapterRegistry from "./ProviderAdapterRegistry.ts";
3327import * as ProviderReplayHarness from "./testkit/ProviderReplayHarness.ts";
3428import { checkpointWorkspace } from "@t3tools/provider-testing/replayWorkspace";
29import * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter";
3530
3631const driver = ProviderDriverKind.make("codex");
3732const instanceId = ProviderInstanceId.make("codex");
......@@ -67,8 +62,8 @@ it.effect.each(
6762 Effect.scoped(
6863 Effect.gen(function* () {
6964 const cwd = yield* checkpointWorkspace(`steering-completion-${timing.replaceAll(" ", "-")}`);
70 const events = yield* Queue.unbounded<ProviderAdapterV2Event>();
71 const started: ProviderAdapterV2TurnInput[] = [];
65 const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>();
66 const started: ProviderAdapter.ProviderAdapterV2TurnInput[] = [];
7267 const steerEntered = yield* Deferred.make<void>();
7368 const rejectSteer = yield* Deferred.make<void>();
7469 let steerCalls = 0;
......@@ -80,7 +75,7 @@ it.effect.each(
8075 activeSteeringInterruptsTools: timing === "with interrupting native steering",
8176 },
8277 };
83 const adapter: ProviderAdapterV2Shape = {
78 const adapter: ProviderAdapter.ProviderAdapterV2["Service"] = {
8479 instanceId,
8580 driver,
8681 getCapabilities: () => Effect.succeed(capabilities),
......@@ -153,7 +148,7 @@ it.effect.each(
153148 if (timing === "after delivery") return;
154149 yield* Deferred.succeed(steerEntered, undefined);
155150 yield* Deferred.await(rejectSteer);
156 return yield* new ProviderAdapterSteerRunError({
151 return yield* new ProviderAdapter.ProviderAdapterSteerRunError({
157152 driver,
158153 providerThreadId: turn.providerThread.id,
159154 providerTurnId: turn.providerTurnId,
......@@ -424,8 +419,8 @@ const composerSelection = {
424419
425420const nextTurnSelectionHarness = Effect.fn("nextTurnSelectionHarness")(function* (name: string) {
426421 const cwd = yield* checkpointWorkspace(name);
427 const events = yield* Queue.unbounded<ProviderAdapterV2Event>();
428 const started: ProviderAdapterV2TurnInput[] = [];
422 const events = yield* Queue.unbounded<ProviderAdapter.ProviderAdapterV2Event>();
423 const started: ProviderAdapter.ProviderAdapterV2TurnInput[] = [];
429424 const steered: string[] = [];
430425 const capabilities = {
431426 ...CodexProviderCapabilitiesV2,
......@@ -435,7 +430,7 @@ const nextTurnSelectionHarness = Effect.fn("nextTurnSelectionHarness")(function*
435430 supportsSteeringByInterruptRestart: false,
436431 },
437432 };
438 const adapter: ProviderAdapterV2Shape = {
433 const adapter: ProviderAdapter.ProviderAdapterV2["Service"] = {
439434 instanceId,
440435 driver,
441436 getCapabilities: () => Effect.succeed(capabilities),
apps/server/src/orchestration-v2/ThreadDeletion.ts+3-3
......@@ -7,7 +7,7 @@ import type * as DateTime from "effect/DateTime";
77import * as Effect from "effect/Effect";
88
99import 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
1212export interface ThreadDeletionPlan {
1313 readonly events: ReadonlyArray<OrchestrationV2DomainEvent>;
......@@ -23,8 +23,8 @@ export const planThreadDeletion = Effect.fn("ThreadDeletion.planThreadDeletion")
2323 >;
2424 readonly attachmentIds: ReadonlyArray<string>;
2525 readonly now: DateTime.Utc;
26 readonly idAllocator: IdAllocatorV2["Service"];
27}): Effect.fn.Return<ThreadDeletionPlan, IdAllocatorV2Error> {
26 readonly idAllocator: IdAllocator.IdAllocatorV2["Service"];
27}): Effect.fn.Return<ThreadDeletionPlan, IdAllocator.IdAllocatorV2Error> {
2828 const { command, projection, now, idAllocator } = input;
2929 const events: Array<OrchestrationV2DomainEvent> = [];
3030 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";
2121import { ClaudeProviderCapabilitiesV2 } from "./Adapters/ClaudeAdapterV2.ts";
2222import * as EventSink from "./EventSink.ts";
2323import * as Orchestrator from "./Orchestrator.ts";
24import type { ProviderAdapterV2Shape } from "@t3tools/provider-core/server/ProviderAdapter";
2524import * as ProviderAdapterRegistry from "./ProviderAdapterRegistry.ts";
2625import * as ProviderReplayHarness from "./testkit/ProviderReplayHarness.ts";
26import type * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter";
2727
2828const forkCases = (["codex", "claudeAgent"] as const).flatMap((driverName) => {
2929 const driver = ProviderDriverKind.make(driverName);
3030 const instanceId = ProviderInstanceId.make(driver);
3131 const modelSelection = { instanceId, model: "test-model" };
32 const adapter: ProviderAdapterV2Shape = {
32 const adapter: ProviderAdapter.ProviderAdapterV2["Service"] = {
3333 instanceId,
3434 driver,
3535 getCapabilities: () =>
apps/server/src/orchestration-v2/ThreadLaunchService.test.ts+2-2
......@@ -54,12 +54,12 @@ import { CodexProviderCapabilitiesV2 } from "./Adapters/CodexAdapterV2.ts";
5454import * as CommandReceiptStore from "./CommandReceiptStore.ts";
5555import * as EffectOutbox from "./EffectOutbox.ts";
5656import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator";
57import type { ProviderAdapterV2Shape } from "@t3tools/provider-core/server/ProviderAdapter";
5857import * as ProviderAdapterRegistry from "./ProviderAdapterRegistry.ts";
5958import * as ThreadLaunch from "./ThreadLaunchService.ts";
6059import * as ThreadManagement from "./ThreadManagementService.ts";
6160import * as ThreadTitleRegeneration from "./ThreadTitleRegenerationService.ts";
6261import * as ProviderReplayHarness from "./testkit/ProviderReplayHarness.ts";
62import type * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter";
6363
6464const projectId = ProjectId.make("project:launch-test");
6565const otherProjectId = ProjectId.make("project:launch-other");
......@@ -94,7 +94,7 @@ const adapter = {
9494 getCapabilities: () => Effect.succeed(CodexProviderCapabilitiesV2),
9595 planSelectionTransition: () => Effect.succeed({ type: "apply_on_next_turn" as const }),
9696 openSession: () => Effect.die("provider execution is disabled in launch tests"),
97} as ProviderAdapterV2Shape;
97} as ProviderAdapter.ProviderAdapterV2["Service"];
9898
9999interface HarnessOptions {
100100 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";
2424import { OrchestrationEffectRequestV2 } from "./EffectOutbox.ts";
2525import * as Orchestrator from "./Orchestrator.ts";
2626import * as ProjectionStore from "./ProjectionStore.ts";
27import type { ProviderAdapterV2Shape } from "@t3tools/provider-core/server/ProviderAdapter";
2827import * as ProviderAdapterRegistry from "./ProviderAdapterRegistry.ts";
2928import * as ThreadManagementService from "./ThreadManagementService.ts";
3029import * as ProviderReplayHarness from "./testkit/ProviderReplayHarness.ts";
30import type * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter";
3131
3232const instanceId = ProviderInstanceId.make("codex");
3333const modelSelection = { instanceId, model: "gpt-5.1-codex" };
......@@ -37,7 +37,7 @@ const adapter = {
3737 getCapabilities: () => Effect.succeed(CodexProviderCapabilitiesV2),
3838 planSelectionTransition: () => Effect.succeed({ type: "apply_on_next_turn" as const }),
3939 openSession: () => Effect.die("Runs here never reach a provider"),
40} as ProviderAdapterV2Shape;
40} as ProviderAdapter.ProviderAdapterV2["Service"];
4141const layerDatabase = SqlitePersistence.layerMemory;
4242// No effect worker: runs stay unstarted, so Stop ends them without a provider.
4343const layerTest = ThreadManagementService.layer.pipe(
apps/server/src/orchestration-v2/ThreadTitleRegenerationService.test.ts+2-2
......@@ -22,11 +22,11 @@ import * as ServerSettings from "../serverSettings.ts";
2222import * as TextGeneration from "../textGeneration/TextGeneration.ts";
2323import { CodexProviderCapabilitiesV2 } from "./Adapters/CodexAdapterV2.ts";
2424import * as EffectOutbox from "./EffectOutbox.ts";
25import type { ProviderAdapterV2Shape } from "@t3tools/provider-core/server/ProviderAdapter";
2625import * as ProviderAdapterRegistry from "./ProviderAdapterRegistry.ts";
2726import * as ThreadManagement from "./ThreadManagementService.ts";
2827import * as ThreadTitleRegeneration from "./ThreadTitleRegenerationService.ts";
2928import * as ProviderReplayHarness from "./testkit/ProviderReplayHarness.ts";
29import type * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter";
3030
3131const projectId = ProjectId.make("project:title-regeneration");
3232const modelSelection = {
......@@ -40,7 +40,7 @@ const adapter = {
4040 getCapabilities: () => Effect.succeed(CodexProviderCapabilitiesV2),
4141 planSelectionTransition: () => Effect.succeed({ type: "apply_on_next_turn" as const }),
4242 openSession: () => Effect.die("provider execution is disabled in title regeneration tests"),
43} as ProviderAdapterV2Shape;
43} as ProviderAdapter.ProviderAdapterV2["Service"];
4444
4545function makeHarness(
4646 options: {
apps/server/src/orchestration-v2/assistantStreaming.test.ts+4-4
......@@ -2,9 +2,9 @@ import { describe, expect, it } from "vite-plus/test";
22import { MessageId, NodeId, ProviderDriverKind, ThreadId, TurnItemId } from "@t3tools/contracts";
33import * as DateTime from "effect/DateTime";
44import { 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 => ({
88 type: "message.updated",
99 driver: ProviderDriverKind.make("codex"),
1010 message: {
......@@ -27,7 +27,7 @@ const turnItem = (
2727 text: string,
2828 streaming = true,
2929 type: "assistant_message" | "reasoning" = "assistant_message",
30): ProviderAdapterV2Event => ({
30): ProviderAdapter.ProviderAdapterV2Event => ({
3131 type: "turn_item.updated",
3232 driver: ProviderDriverKind.make("codex"),
3333 turnItem: {
......@@ -108,7 +108,7 @@ describe("V2 assistant streaming", () => {
108108 "suppresses running assistant nodes in %s mode while delivering tool and completed nodes",
109109 (mode) => {
110110 const filter = makeAssistantStreamingFilter(mode);
111 const running: Extract<ProviderAdapterV2Event, { type: "node.updated" }> = {
111 const running: Extract<ProviderAdapter.ProviderAdapterV2Event, { type: "node.updated" }> = {
112112 type: "node.updated",
113113 driver: ProviderDriverKind.make("codex"),
114114 node: {
apps/server/src/orchestration-v2/assistantStreaming.ts+5-2
......@@ -1,5 +1,5 @@
11import 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
44// An opening fence may sit at any indentation, since fences inside list
55// 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:
9191/** Buffer assistant messages and reasoning alike, keeping unfinished markdown out of the read model. */
9292export function makeAssistantStreamingFilter(mode: ResponseStreamingMode) {
9393 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 => {
9598 if (
9699 event.type === "node.updated" &&
97100 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";
4444import * as Orchestrator from "../Orchestrator.ts";
4545import * as ProjectionMaintenance from "../ProjectionMaintenance.ts";
4646import * as ProjectionStore from "../ProjectionStore.ts";
47import {
48 ProviderAdapterProtocolError,
49 type ProviderAdapterV2Event,
50 type ProviderAdapterV2Shape,
51} from "@t3tools/provider-core/server/ProviderAdapter";
5247import * as ProviderAdapterRegistry from "../ProviderAdapterRegistry.ts";
5348import * as ProviderReplayHarness from "../testkit/ProviderReplayHarness.ts";
5449import { checkpointWorkspace } from "@t3tools/provider-testing/replayWorkspace";
50import * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter";
5551
5652const PROJECT_ID = "project:cutover";
5753const ACTIVE_THREAD = "thread:cutover:active";
......@@ -422,7 +418,7 @@ interface CapturedTurn {
422418}
423419
424420const unimplemented = (detail: string) =>
425 Effect.fail(new ProviderAdapterProtocolError({ driver, detail }));
421 Effect.fail(new ProviderAdapter.ProviderAdapterProtocolError({ driver, detail }));
426422
427423const makeCodexAdapter = (capturedTurns: Ref.Ref<ReadonlyArray<CapturedTurn>>) =>
428424 ({
......@@ -432,7 +428,7 @@ const makeCodexAdapter = (capturedTurns: Ref.Ref<ReadonlyArray<CapturedTurn>>) =
432428 planSelectionTransition: () => Effect.succeed({ type: "apply_on_next_turn" }),
433429 openSession: (sessionInput) =>
434430 Effect.gen(function* () {
435 const events = yield* PubSub.unbounded<ProviderAdapterV2Event>();
431 const events = yield* PubSub.unbounded<ProviderAdapter.ProviderAdapterV2Event>();
436432 const now = yield* DateTime.now;
437433 const providerSession: OrchestrationV2ProviderSession = {
438434 id: sessionInput.providerSessionId,
......@@ -551,7 +547,7 @@ const makeCodexAdapter = (capturedTurns: Ref.Ref<ReadonlyArray<CapturedTurn>>) =
551547 failure: null,
552548 threadDisposition: "reusable",
553549 },
554 ] satisfies ReadonlyArray<ProviderAdapterV2Event>);
550 ] satisfies ReadonlyArray<ProviderAdapter.ProviderAdapterV2Event>);
555551 }),
556552 steerTurn: () => Effect.void,
557553 interruptTurn: () => Effect.void,
......@@ -561,7 +557,7 @@ const makeCodexAdapter = (capturedTurns: Ref.Ref<ReadonlyArray<CapturedTurn>>) =
561557 forkThread: () => unimplemented("forkThread unused in cutover test"),
562558 };
563559 }),
564 }) satisfies ProviderAdapterV2Shape;
560 }) satisfies ProviderAdapter.ProviderAdapterV2["Service"];
565561
566562const waitForIdle = Effect.fn("LegacyV1Cutover.waitForIdle")(function* (threadId: ThreadId) {
567563 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";
6767import { PullRequestProviderError } from "../pullRequest/PullRequestProvider.ts";
6868import * as PullRequestService from "../pullRequest/PullRequestService.ts";
6969import * as ProjectStore from "./ProjectStore.ts";
70import type {
71 ProviderAdapterV2SessionRuntime,
72 ProviderAdapterV2Shape,
73} from "@t3tools/provider-core/server/ProviderAdapter";
7470import * as ProviderSessionManager from "./ProviderSessionManager.ts";
7571import * as RuntimeLayer from "./runtimeLayer.ts";
7672import { shellStreamItemFromThreadShell } from "./ShellStream.ts";
7773import { CodexProviderCapabilitiesV2 } from "./Adapters/CodexAdapterV2.ts";
7874import * as ThreadManagementService from "./ThreadManagementService.ts";
7975import * as ThreadCommandExecutor from "./ThreadCommandExecutor.ts";
76import type * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter";
8077
8178const layerPlatformTest = Layer.merge(
8279 NodeServices.layer,
......@@ -119,7 +116,7 @@ const orchestrationAdapter = {
119116 getCapabilities: () => Effect.succeed(CodexProviderCapabilitiesV2),
120117 planSelectionTransition: () => Effect.succeed({ type: "apply_on_next_turn" }),
121118 openSession: () => Effect.die("sessions are not used by lifecycle tests"),
122} as ProviderAdapterV2Shape;
119} as ProviderAdapter.ProviderAdapterV2["Service"];
123120const providerInstance = {
124121 instanceId: modelSelection.instanceId,
125122 driverKind: driver,
......@@ -928,7 +925,9 @@ it.layer(layerTest)("OrchestrationV2LayerLive", (it) => {
928925 const sessionSpy = vi
929926 .spyOn(sessions, "get")
930927 .mockReturnValue(
931 Effect.succeed(Option.some({ providerSession } as ProviderAdapterV2SessionRuntime)),
928 Effect.succeed(
929 Option.some({ providerSession } as ProviderAdapter.ProviderAdapterV2SessionRuntime),
930 ),
932931 );
933932 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";
3030import * as ProjectionMaintenance from "./ProjectionMaintenance.ts";
3131import * as ProjectStore from "./ProjectStore.ts";
3232import * as ProviderAdapterRegistry from "./ProviderAdapterRegistry.ts";
33import * as ProviderContinuationRequests from "@t3tools/provider-core/server/continuationRequests";
33import * as ProviderContinuationRequests from "@t3tools/provider-core/server/ProviderContinuationRequests";
3434import * as ProviderContinuationService from "./ProviderContinuationService.ts";
3535import * as ThreadTitleRegenerationService from "./ThreadTitleRegenerationService.ts";
3636import * 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";
3535import * as ProjectStore from "../ProjectStore.ts";
3636import * as ProviderAdapterRegistry from "../ProviderAdapterRegistry.ts";
3737import * as ProviderAuthService from "../../provider/ProviderAuthService.ts";
38import * as ProviderContinuationRequests from "@t3tools/provider-core/server/continuationRequests";
38import * as ProviderContinuationRequests from "@t3tools/provider-core/server/ProviderContinuationRequests";
3939import * as ProviderContinuationService from "../ProviderContinuationService.ts";
4040import * as ProviderEventIngestor from "../ProviderEventIngestor.ts";
4141import * 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";
5252import * as EffectOutbox from "../EffectOutbox.ts";
5353import * as ProjectionMaintenance from "../ProjectionMaintenance.ts";
5454import * 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";
6355import * as ProviderAdapterRegistry from "../ProviderAdapterRegistry.ts";
6456import { makeProviderFailure } from "@t3tools/provider-core/server/failure";
6557import {
......@@ -70,6 +62,7 @@ import {
7062} from "./fixtures/shared.ts";
7163import * as ProviderReplayHarness from "./ProviderReplayHarness.ts";
7264import { checkpointWorkspace } from "@t3tools/provider-testing/replayWorkspace";
65import * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter";
7366
7467const encodeJson = Schema.encodeEffect(Schema.fromJsonString(Schema.Unknown));
7568
......@@ -95,7 +88,7 @@ interface CapturedTurn {
9588}
9689
9790function unimplemented(driver: ProviderDriverKind, detail: string) {
98 return Effect.fail(new ProviderAdapterProtocolError({ driver, detail }));
91 return Effect.fail(new ProviderAdapter.ProviderAdapterProtocolError({ driver, detail }));
9992}
10093
10194function makeTestAdapter(input: {
......@@ -116,7 +109,7 @@ function makeTestAdapter(input: {
116109 readonly failResumeOnce?: Ref.Ref<boolean>;
117110 readonly initialContextUsage?: OrchestrationV2ProviderThread["contextUsage"];
118111 readonly getModelContextWindow?: (selection: ModelSelection) => number | undefined;
119 readonly canReuseContextUsage?: ProviderAdapterV2SessionRuntime["canReuseContextUsage"];
112 readonly canReuseContextUsage?: ProviderAdapter.ProviderAdapterV2SessionRuntime["canReuseContextUsage"];
120113 readonly tokenUsageByRunOrdinal?: Readonly<
121114 Record<number, Omit<OrchestrationV2ProviderTurnTokenUsage, "updatedAt">>
122115 >;
......@@ -125,7 +118,7 @@ function makeTestAdapter(input: {
125118 readonly holdRunOrdinal?: number;
126119 readonly holdFirstTurn?: Deferred.Deferred<void>;
127120 readonly releaseFirstTurn?: Deferred.Deferred<void>;
128}): ProviderAdapterV2Shape {
121}): ProviderAdapter.ProviderAdapterV2["Service"] {
129122 return {
130123 instanceId: input.instanceId,
131124 driver: input.driver,
......@@ -133,7 +126,7 @@ function makeTestAdapter(input: {
133126 planSelectionTransition: () => Effect.succeed({ type: "apply_on_next_turn" }),
134127 openSession: (sessionInput) =>
135128 Effect.gen(function* () {
136 const events = yield* PubSub.unbounded<ProviderAdapterV2Event>();
129 const events = yield* PubSub.unbounded<ProviderAdapter.ProviderAdapterV2Event>();
137130 const now = yield* DateTime.now;
138131 const providerSession: OrchestrationV2ProviderSession = {
139132 id: sessionInput.providerSessionId,
......@@ -148,7 +141,7 @@ function makeTestAdapter(input: {
148141 lastError: null,
149142 };
150143
151 const runtime: ProviderAdapterV2SessionRuntime = {
144 const runtime: ProviderAdapter.ProviderAdapterV2SessionRuntime = {
152145 instanceId: input.instanceId,
153146 driver: input.driver,
154147 providerSessionId: sessionInput.providerSessionId,
......@@ -204,7 +197,7 @@ function makeTestAdapter(input: {
204197 ...(input.injectedHistory === undefined
205198 ? {}
206199 : {
207 injectHistory: (history: ProviderAdapterV2HistoricalContext) =>
200 injectHistory: (history: ProviderAdapter.ProviderAdapterV2HistoricalContext) =>
208201 Ref.update(input.injectedHistory!, (current) => [
209202 ...current,
210203 ...historyResponseItems(history.messages, history.context),
......@@ -236,7 +229,7 @@ function makeTestAdapter(input: {
236229 input.refuseStarts !== undefined &&
237230 (yield* Ref.getAndUpdate(input.refuseStarts, (left) => Math.max(0, left - 1))) > 0
238231 )
239 return yield* new ProviderAdapterTurnStartError({
232 return yield* new ProviderAdapter.ProviderAdapterTurnStartError({
240233 driver: input.driver,
241234 threadId: turnInput.threadId,
242235 providerThreadId: turnInput.providerThread.id,
......@@ -280,7 +273,7 @@ function makeTestAdapter(input: {
280273 input.responseByThreadId?.[turnInput.threadId]?.[turnInput.runOrdinal] ??
281274 input.responseByRunOrdinal[turnInput.runOrdinal] ??
282275 `${input.driver} response for run ${turnInput.runOrdinal}`;
283 const providerEvents: ReadonlyArray<ProviderAdapterV2Event> = [
276 const providerEvents: ReadonlyArray<ProviderAdapter.ProviderAdapterV2Event> = [
284277 {
285278 type: "provider_turn.updated",
286279 driver: input.driver,
apps/server/src/provider/CodexChatGptAuth.ts+2-2
......@@ -19,8 +19,8 @@ import * as Effect from "effect/Effect";
1919import * as Option from "effect/Option";
2020import * as Schema from "effect/Schema";
2121import * as Semaphore from "effect/Semaphore";
22import * as ProviderAuthFlow from "@t3tools/provider-core/server/ProviderAuthFlow";
23import type { ProviderAuthFlowContext } from "@t3tools/provider-core/server/ProviderAuthFlow";
22import * as ProviderAuthFlow from "@t3tools/provider-core/server/providerAuthFlow";
23import type { ProviderAuthFlowContext } from "@t3tools/provider-core/server/providerAuthFlow";
2424import { HttpClient, HttpClientRequest } from "effect/http";
2525import * as ProviderCredentialStore from "./ProviderCredentialStore.ts";
2626import { withChatGptSessionLock } from "./CodexChatGptSessionLock.ts";
apps/server/src/provider/Drivers/AntigravityDriver.test.ts+1-1
......@@ -29,7 +29,7 @@ import {
2929 ANTIGRAVITY_AUTH_STDOUT_PREFIX,
3030 resolveAntigravityInstanceDirectories,
3131} from "../antigravityAuthSupport.ts";
32import * as ProviderEventLoggers from "../ProviderEventLoggers.ts";
32import * as ProviderEventLoggers from "@t3tools/provider-core/server/ProviderEventLoggers";
3333import * as ModelManifest from "../ModelManifest.ts";
3434import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator";
3535import { AntigravityDriver } from "./AntigravityDriver.ts";
apps/server/src/provider/Drivers/AntigravityDriver.ts+5-5
......@@ -18,7 +18,7 @@ import * as Stream from "effect/Stream";
1818import * as ChildProcessSpawner from "effect/process/ChildProcessSpawner";
1919import type { AcpError } from "effect-acp/errors";
2020
21import { ProviderHost } from "@t3tools/provider-core/server/ProviderHost";
21import * as ProviderHost from "@t3tools/provider-core/server/ProviderHost";
2222import {
2323 isAntigravityTextGenerationAvailable,
2424 makeAntigravityTextGeneration,
......@@ -46,12 +46,12 @@ import {
4646 removeAntigravitySessionFiles,
4747} from "../acp/AntigravitySessionFiles.ts";
4848import * 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";
5050import { makeAntigravityAdapterV2 } from "../../orchestration-v2/Adapters/AntigravityAdapterV2.ts";
5151import { makeAcpNativeLoggerFactory } from "@t3tools/provider-acp/server/nativeLogging";
5252import { ProviderDriverError } from "../Errors.ts";
5353import { makeAntigravityProvider } from "../AntigravityProvider.ts";
54import * as ProviderEventLoggers from "../ProviderEventLoggers.ts";
54import * as ProviderEventLoggers from "@t3tools/provider-core/server/ProviderEventLoggers";
5555import * as ModelCatalog from "@t3tools/provider-core/server/ModelCatalog";
5656import {
5757 defaultProviderContinuationIdentity,
......@@ -68,7 +68,7 @@ const isNodeRuntimeUnavailableError = Schema.is(NodeRuntimeUnavailableError);
6868
6969export type AntigravityDriverEnv =
7070 | AntigravityInstallation.AntigravityInstallation
71 | ProviderHost
71 | ProviderHost.ProviderHost
7272 | ChildProcessSpawner.ChildProcessSpawner
7373 | Crypto.Crypto
7474 | FileSystem.FileSystem
......@@ -89,7 +89,7 @@ export const AntigravityDriver: ProviderDriver<AntigravitySettings, AntigravityD
8989 const fileSystem = yield* FileSystem.FileSystem;
9090 const path = yield* Path.Path;
9191 const spawner = yield* ChildProcessSpawner.ChildProcessSpawner;
92 const host = yield* ProviderHost;
92 const host = yield* ProviderHost.ProviderHost;
9393 const selfInvocation = yield* resolveSelfInvocation();
9494 const installation = yield* AntigravityInstallation.AntigravityInstallation;
9595 const loggers = yield* ProviderEventLoggers.ProviderEventLoggers;
apps/server/src/provider/Drivers/ClaudeDriver.ts+4-5
......@@ -24,10 +24,10 @@ import { HttpClient } from "effect/http";
2424import { ChildProcessSpawner } from "effect/process";
2525
2626import { makeClaudeTextGeneration } from "../../textGeneration/ClaudeTextGeneration.ts";
27import { ProviderHost } from "@t3tools/provider-core/server/ProviderHost";
27import * as ProviderHost from "@t3tools/provider-core/server/ProviderHost";
2828import * as ServerConfig from "../../config.ts";
2929import { expandHomePath } from "@t3tools/provider-core/server/pathExpansion";
30import * as ProviderEventLoggers from "../ProviderEventLoggers.ts";
30import * as ProviderEventLoggers from "@t3tools/provider-core/server/ProviderEventLoggers";
3131import {
3232 createClaudeAdapterV2,
3333 type ClaudeAdapterV2DriverEnv,
......@@ -94,7 +94,7 @@ const UPDATE = makePackageManagedProviderMaintenanceResolver({
9494
9595export type ClaudeDriverEnv =
9696 | ClaudeAdapterV2DriverEnv
97 | ProviderHost
97 | ProviderHost.ProviderHost
9898 | ChildProcessSpawner.ChildProcessSpawner
9999 | ResetCreditCoordinator.ResetCreditCoordinator
100100 | Crypto.Crypto
......@@ -121,7 +121,6 @@ export const ClaudeDriver: ProviderDriver<ClaudeSettings, ClaudeDriverEnv> = {
121121 const { cwd } = yield* ServerConfig.ServerConfig;
122122 const httpClient = yield* HttpClient.HttpClient;
123123 const resetCreditCoordinator = yield* ResetCreditCoordinator.ResetCreditCoordinator;
124 const host = yield* ProviderHost;
125124 const catalogService = yield* ModelCatalog.ModelCatalog;
126125 const modelCatalog = catalogService
127126 .current(DRIVER_KIND)
......@@ -237,7 +236,7 @@ export const ClaudeDriver: ProviderDriver<ClaudeSettings, ClaudeDriverEnv> = {
237236 Effect.provideService(Path.Path, path),
238237 );
239238
240 const snapshotSettings = makeProviderSnapshotSettingsSource(effectiveConfig, host.settings);
239 const snapshotSettings = yield* makeProviderSnapshotSettingsSource(effectiveConfig);
241240 const snapshot = yield* makeManagedServerProvider<ProviderSnapshotSettings<ClaudeSettings>>({
242241 resolveMaintenance,
243242 getSettings: snapshotSettings.getSettings,
apps/server/src/provider/Drivers/CodexDriver.test.ts+3-3
......@@ -26,7 +26,7 @@ import * as BackgroundPolicy from "../../background/BackgroundPolicy.ts";
2626import * as ServerConfig from "../../config.ts";
2727import * as ServerSettings from "../../serverSettings.ts";
2828import * as ResetCreditCoordinator from "../resetCreditCoordinator.ts";
29import * as ProviderEventLoggers from "../ProviderEventLoggers.ts";
29import * as ProviderEventLoggers from "@t3tools/provider-core/server/ProviderEventLoggers";
3030import * as ModelManifest from "../ModelManifest.ts";
3131import {
3232 createProviderVersionAdvisory,
......@@ -36,7 +36,7 @@ import {
3636import { CodexDriver } from "./CodexDriver.ts";
3737import * as CodexAdapterV2 from "../../orchestration-v2/Adapters/CodexAdapterV2.ts";
3838import * 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";
4040import * as ProviderCredentialStore from "../ProviderCredentialStore.ts";
4141import * as ProviderHostLive from "../ProviderHostLive.ts";
4242
......@@ -214,7 +214,7 @@ it.layer(layerTest)("CodexDriver", (it) => {
214214 threadId,
215215 providerSessionId: ProviderSessionId.make("managed-account-session"),
216216 modelSelection: { instanceId, model: "gpt-5.4" },
217 runtimePolicy: ProviderAdapterV2RuntimePolicy.make({
217 runtimePolicy: ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({
218218 runtimeMode: "full-access",
219219 interactionMode: "default",
220220 cwd: serverConfig.stateDir,
apps/server/src/provider/Drivers/CodexDriver.ts+4-5
......@@ -31,10 +31,10 @@ import { HttpClient } from "effect/http";
3131import { ChildProcessSpawner } from "effect/process";
3232
3333import { makeCodexTextGeneration } from "../../textGeneration/CodexTextGeneration.ts";
34import { ProviderHost } from "@t3tools/provider-core/server/ProviderHost";
34import * as ProviderHost from "@t3tools/provider-core/server/ProviderHost";
3535import * as ServerConfig from "../../config.ts";
3636import { expandHomePath } from "@t3tools/provider-core/server/pathExpansion";
37import * as ProviderEventLoggers from "../ProviderEventLoggers.ts";
37import * as ProviderEventLoggers from "@t3tools/provider-core/server/ProviderEventLoggers";
3838import {
3939 createCodexAdapterV2,
4040 type CodexAdapterV2DriverEnv,
......@@ -109,7 +109,7 @@ function makeCodexMaintenanceResolver(sharedHomePath: string) {
109109 */
110110export type CodexDriverEnv =
111111 | CodexAdapterV2DriverEnv
112 | ProviderHost
112 | ProviderHost.ProviderHost
113113 | ChildProcessSpawner.ChildProcessSpawner
114114 | ResetCreditCoordinator.ResetCreditCoordinator
115115 | Crypto.Crypto
......@@ -147,7 +147,6 @@ export const CodexDriver: ProviderDriver<CodexSettings, CodexDriverEnv> = {
147147 const fileSystem = yield* FileSystem.FileSystem;
148148 const pathService = yield* Path.Path;
149149 const httpClient = yield* HttpClient.HttpClient;
150 const host = yield* ProviderHost;
151150 const modelCatalog = yield* ModelCatalog.ModelCatalog;
152151 const currentCatalog = modelCatalog.current(DRIVER_KIND);
153152 const processEnv = mergeProviderInstanceEnvironment(environment);
......@@ -231,7 +230,7 @@ export const CodexDriver: ProviderDriver<CodexSettings, CodexDriverEnv> = {
231230 ),
232231 Effect.provideService(ChildProcessSpawner.ChildProcessSpawner, spawner),
233232 );
234 const snapshotSettings = makeProviderSnapshotSettingsSource(effectiveConfig, host.settings);
233 const snapshotSettings = yield* makeProviderSnapshotSettingsSource(effectiveConfig);
235234 const snapshot = yield* makeManagedServerProvider<ProviderSnapshotSettings<CodexSettings>>({
236235 resolveMaintenance,
237236 getSettings: snapshotSettings.getSettings,
apps/server/src/provider/Drivers/CodexManagedProvider.ts+2-2
......@@ -4,7 +4,7 @@ import * as Stream from "effect/Stream";
44import * as Option from "effect/Option";
55import { ChildProcessSpawner } from "effect/process";
66import { makeCodexTextGeneration } from "../../textGeneration/CodexTextGeneration.ts";
7import { ProviderHost } from "@t3tools/provider-core/server/ProviderHost";
7import * as ProviderHost from "@t3tools/provider-core/server/ProviderHost";
88import { chatGptModels } from "../CodexChatGptModels.ts";
99import { makeCodexManagedRuntime } from "../CodexManagedRuntime.ts";
1010import { ProviderDriverError } from "../Errors.ts";
......@@ -31,7 +31,7 @@ export const makeManagedCodexProvider = Effect.fn("makeManagedCodexProvider")(fu
3131 const { instanceId, enabled, displayName, accentColor, config } = input;
3232 const http = yield* HttpClient.HttpClient;
3333 const spawner = yield* ChildProcessSpawner.ChildProcessSpawner;
34 const host = yield* ProviderHost;
34 const host = yield* ProviderHost.ProviderHost;
3535 const runtime = yield* makeCodexManagedRuntime({
3636 instanceId,
3737 enabled,
apps/server/src/provider/EventNdjsonLogger.ts+13-11
......@@ -9,7 +9,7 @@ import * as NodeFS from "node:fs";
99import * as NodePath from "node:path";
1010
1111import type { ThreadId } from "@t3tools/contracts";
12import type { EventNdjsonLogger } from "@t3tools/provider-core/server/ProviderEventLoggers";
12import type * as ProviderEventLoggers from "@t3tools/provider-core/server/ProviderEventLoggers";
1313import { RotatingFileSink } from "@t3tools/shared/logging";
1414import { errorTag } from "@t3tools/shared/observability";
1515import * as Clock from "effect/Clock";
......@@ -21,7 +21,7 @@ import * as Scope from "effect/Scope";
2121import * as SynchronizedRef from "effect/SynchronizedRef";
2222
2323import { toSafeThreadAttachmentSegment } from "../attachmentStore.ts";
24import type { ResourceAttribution } from "../resourceTelemetry/ResourceAttribution.ts";
24import type * as ResourceAttribution from "../resourceTelemetry/ResourceAttribution.ts";
2525
2626const MEBIBYTE = 1024 * 1024;
2727const DAY_MS = 24 * 60 * 60 * 1_000;
......@@ -64,11 +64,9 @@ const transientAcpUpdates = new Set(["agent_message_chunk", "agent_thought_chunk
6464
6565export type EventNdjsonStream = "native" | "canonical" | "orchestration";
6666
67export type { EventNdjsonLogger };
68
6967export interface EventNdjsonLogStore {
7068 readonly filePath: string;
71 readonly logger: (stream: EventNdjsonStream) => EventNdjsonLogger;
69 readonly logger: (stream: EventNdjsonStream) => ProviderEventLoggers.EventNdjsonLogger;
7270 readonly close: () => Effect.Effect<void>;
7371}
7472
......@@ -81,7 +79,7 @@ export interface EventNdjsonLogStoreOptions {
8179 readonly retentionCheckIntervalMs?: number;
8280 readonly maxBufferedBytes?: number;
8381 readonly maxBufferedRecords?: number;
84 readonly attribution?: ResourceAttribution["Service"];
82 readonly attribution?: ResourceAttribution.ResourceAttribution["Service"];
8583}
8684
8785export interface EventNdjsonLoggerOptions extends EventNdjsonLogStoreOptions {
......@@ -127,7 +125,7 @@ interface ResolvedOptions {
127125 readonly retentionCheckIntervalMs: number;
128126 readonly maxBufferedBytes: number;
129127 readonly maxBufferedRecords: number;
130 readonly attribution: ResourceAttribution["Service"] | undefined;
128 readonly attribution: ResourceAttribution.ResourceAttribution["Service"] | undefined;
131129}
132130
133131export interface PendingRecord {
......@@ -731,8 +729,8 @@ export const makeEventNdjsonLogStore = Effect.fnUntraced(function* (
731729 yield* Scope.close(timerScope, Exit.void);
732730 });
733731
734 const loggerViews = new Map<EventNdjsonStream, EventNdjsonLogger>();
735 const logger = (stream: EventNdjsonStream): EventNdjsonLogger => {
732 const loggerViews = new Map<EventNdjsonStream, ProviderEventLoggers.EventNdjsonLogger>();
733 const logger = (stream: EventNdjsonStream): ProviderEventLoggers.EventNdjsonLogger => {
736734 const existing = loggerViews.get(stream);
737735 if (existing) return existing;
738736
......@@ -778,7 +776,11 @@ export const makeEventNdjsonLogStore = Effect.fnUntraced(function* (
778776 }
779777 });
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;
782784 loggerViews.set(stream, view);
783785 return view;
784786 };
......@@ -789,7 +791,7 @@ export const makeEventNdjsonLogStore = Effect.fnUntraced(function* (
789791export const makeEventNdjsonLogger = Effect.fnUntraced(function* (
790792 filePath: string,
791793 options: EventNdjsonLoggerOptions,
792): Effect.fn.Return<EventNdjsonLogger | undefined> {
794): Effect.fn.Return<ProviderEventLoggers.EventNdjsonLogger | undefined> {
793795 const store = yield* makeEventNdjsonLogStore(filePath, options).pipe(
794796 Effect.catch((error) =>
795797 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 @@
11/**
2 * The server's implementation of `ProviderHost`, the only server surface
2 * The server's implementation of `ProviderHost.ProviderHost`, the only server surface
33 * provider drivers and adapters may use.
44 *
55 * @module provider/ProviderHostLive
66 */
7import { ProviderHost } from "@t3tools/provider-core/server/ProviderHost";
7import * as ProviderHost from "@t3tools/provider-core/server/ProviderHost";
88import { ProviderCredentialError } from "@t3tools/provider-core/server/errors";
99import * as Crypto from "effect/Crypto";
1010import * as Effect from "effect/Effect";
......@@ -18,14 +18,14 @@ import * as ServerSettings from "../serverSettings.ts";
1818import * as ProviderCredentialStore from "./ProviderCredentialStore.ts";
1919
2020export const layer = Layer.effect(
21 ProviderHost,
21 ProviderHost.ProviderHost,
2222 Effect.gen(function* () {
2323 const config = yield* ServerConfig.ServerConfig;
2424 const serverSettings = yield* ServerSettings.ServerSettingsService;
2525 const backgroundPolicy = yield* BackgroundPolicy.BackgroundPolicy;
2626 const secrets = yield* ServerSecretStore.ServerSecretStore;
2727 const crypto = yield* Crypto.Crypto;
28 return ProviderHost.of({
28 return ProviderHost.ProviderHost.of({
2929 paths: {
3030 cwd: config.cwd,
3131 baseDir: config.baseDir,
apps/server/src/provider/ProviderInstanceRegistry.test.ts+1-1
......@@ -61,7 +61,7 @@ import * as ModelManifest from "./ModelManifest.ts";
6161import * as OpenCodeRuntime from "@t3tools/provider-opencode/server/OpenCodeRuntime";
6262import * as OpenCodeServerLedger from "@t3tools/provider-opencode/server/OpenCodeServerLedger";
6363import * as ResetCreditCoordinator from "./resetCreditCoordinator.ts";
64import * as ProviderEventLoggers from "./ProviderEventLoggers.ts";
64import * as ProviderEventLoggers from "@t3tools/provider-core/server/ProviderEventLoggers";
6565import { makeProviderInstanceRegistry } from "./ProviderInstanceRegistry.ts";
6666import * as ProviderOrchestrationAdapterInfrastructure from "./ProviderOrchestrationAdapterInfrastructure.ts";
6767import * as ProviderHostLive from "./ProviderHostLive.ts";
apps/server/src/provider/ProviderInstanceRegistryHydration.ts+3-3
......@@ -40,7 +40,7 @@ import * as ProviderInstanceRegistryMutator from "./ProviderInstanceRegistryMuta
4040import * as ProviderOrchestrationAdapterInfrastructure from "./ProviderOrchestrationAdapterInfrastructure.ts";
4141import * as AcpRegistrySupport from "@t3tools/provider-acp-registry/server/AcpRegistrySupport";
4242import * as ProviderHostLive from "./ProviderHostLive.ts";
43import type { ProviderHost } from "@t3tools/provider-core/server/ProviderHost";
43import type * as ProviderHost from "@t3tools/provider-core/server/ProviderHost";
4444import type * as BackgroundPolicy from "../background/BackgroundPolicy.ts";
4545import type * as ServerConfig from "../config.ts";
4646
......@@ -49,10 +49,10 @@ type ProviderInstanceRegistryHydrationEnv =
4949 BuiltInDriversEnv,
5050 | ProviderOrchestrationAdapterInfrastructure.ProviderOrchestrationAdapterInfrastructure
5151 | AcpRegistrySupport.AcpRegistryCatalog
52 | ProviderHost
52 | ProviderHost.ProviderHost
5353 >
5454 | Settings.ServerSettingsService
55 // Requirements of the `ProviderHost` the drivers receive.
55 // Requirements of the `ProviderHost.ProviderHost` the drivers receive.
5656 | BackgroundPolicy.BackgroundPolicy
5757 | ServerConfig.ServerConfig;
5858
apps/server/src/provider/ProviderOrchestrationAdapterInfrastructure.ts+1-1
......@@ -4,7 +4,7 @@ import * as ClaudeAdapterV2 from "../orchestration-v2/Adapters/ClaudeAdapterV2.t
44import * as CodexAdapterV2 from "../orchestration-v2/Adapters/CodexAdapterV2.ts";
55import * as CursorAgentSdk from "@t3tools/provider-cursor/server/CursorAgentSdk";
66import * 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
99export type ProviderOrchestrationAdapterInfrastructure =
1010 | ClaudeAdapterV2.ClaudeAgentSdkQueryRunner
apps/server/src/provider/ProviderRegistry.test.ts+1-1
......@@ -47,7 +47,7 @@ import { applyProviderCompatibility } from "./providerCompatibility.ts";
4747import * as ResetCreditCoordinator from "./resetCreditCoordinator.ts";
4848import * as OpenCodeRuntime from "@t3tools/provider-opencode/server/OpenCodeRuntime";
4949import * as OpenCodeServerLedger from "@t3tools/provider-opencode/server/OpenCodeServerLedger";
50import * as ProviderEventLoggers from "./ProviderEventLoggers.ts";
50import * as ProviderEventLoggers from "@t3tools/provider-core/server/ProviderEventLoggers";
5151import * as ProviderInstanceRegistryHydration from "./ProviderInstanceRegistryHydration.ts";
5252import * as ServerConfig from "../config.ts";
5353import * as ServerSettingsModule from "../serverSettings.ts";
apps/server/src/server.ts+29-7
......@@ -1,3 +1,4 @@
1import * as ProviderEventLoggers from "@t3tools/provider-core/server/ProviderEventLoggers";
12import type { RelayManagedEndpointRuntimeConfig } from "@t3tools/contracts/relay";
23import * as Clock from "effect/Clock";
34import * as Random from "effect/Random";
......@@ -42,7 +43,7 @@ import * as AnalyticsService from "./telemetry/AnalyticsService.ts";
4243import * as ProviderEventIngestor from "./orchestration-v2/ProviderEventIngestor.ts";
4344import * as ModelManifest from "./provider/ModelManifest.ts";
4445import * as ResetCreditCoordinator from "./provider/resetCreditCoordinator.ts";
45import * as ProviderEventLoggers from "./provider/ProviderEventLoggers.ts";
46import * as EventNdjsonLogger from "./provider/EventNdjsonLogger.ts";
4647import * as OpenCodeRuntime from "@t3tools/provider-opencode/server/OpenCodeRuntime";
4748import * as OpenCodeServerLedger from "@t3tools/provider-opencode/server/OpenCodeServerLedger";
4849import * as ProviderHostLive from "./provider/ProviderHostLive.ts";
......@@ -193,6 +194,30 @@ const layerApplicationObservability = EventLoopMonitor.layer.pipe(
193194 Layer.provideMerge(layerResourceAttribution),
194195);
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
196221const layerPtyAdapter = NodePtyAdapter.layer;
197222
198223const layerServerSettings = ServerSettings.layer.pipe(
......@@ -599,17 +624,14 @@ const layerRuntimeCoreDependencies = layerRuntimeCoreDependenciesBase.pipe(
599624 // Search, prepare, status inspection, and turn launch share one registry
600625 // cache so every client and provider instance sees the same prepared agents.
601626 Layer.provideMerge(AcpRegistrySupport.layerFromHost.pipe(Layer.provide(ProviderHostLive.layer))),
602 // Shared native/canonical NDJSON writers used by both the per-instance
603 // V2 drivers and the orchestration runtime. Provide resource attribution so
604 // the rewritten telemetry pipeline can account for logical NDJSON writes.
605 // Provided once at the runtime level so every consumer sees the same
606 // logger instances.
627 // Provider event loggers are shared by every driver instance and the
628 // orchestration runtime, so they are provided once here.
607629 // `ModelManifest.layer` is the model manifest, refreshed from the repo's
608630 // `model-manifest.json` on `main`. Drivers read their entry through the
609631 // `ModelCatalog` port it provides.
610632 Layer.provideMerge(
611633 Layer.mergeAll(
612 ProviderEventLoggers.layer,
634 layerProviderEventLoggers,
613635 ModelManifest.layerModelCatalog.pipe(Layer.provideMerge(ModelManifest.layer)),
614636 ResetCreditCoordinator.layer,
615637 ),
packages/provider-acp-registry/src/server/auth.ts+1-1
......@@ -16,7 +16,7 @@ import { ChildProcessSpawner } from "effect/process";
1616import type * as AcpSchema from "effect-acp/compat";
1717
1818import * as PtyAdapter from "@t3tools/shared/PtyAdapter";
19import * as ProviderAuthFlow from "@t3tools/provider-core/server/ProviderAuthFlow";
19import * as ProviderAuthFlow from "@t3tools/provider-core/server/providerAuthFlow";
2020import { normalizeAcpRegistryAuthMethods, normalizeAcpRegistryWebUrl } from "./probe.ts";
2121import * as AcpRegistrySupport from "./AcpRegistrySupport.ts";
2222import * 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";
2121import * as Semaphore from "effect/Semaphore";
2222import { ChildProcessSpawner } from "effect/process";
2323
24import { ProviderHost } from "@t3tools/provider-core/server/ProviderHost";
24import * as ProviderHost from "@t3tools/provider-core/server/ProviderHost";
2525import { AcpRegistryAdapterV2Driver, type AcpRegistryAdapterV2DriverEnv } from "./adapter.ts";
2626import type { ProviderTextGeneration } from "@t3tools/provider-core/server/textGeneration";
2727import { ProviderDriverError } from "@t3tools/provider-core/server/errors";
......@@ -455,7 +455,7 @@ export const checkAcpRegistryProviderReadiness = Effect.fn(
455455 : snapshot;
456456});
457457
458export type AcpRegistryDriverEnv = AcpRegistryAdapterV2DriverEnv | ProviderHost;
458export type AcpRegistryDriverEnv = AcpRegistryAdapterV2DriverEnv | ProviderHost.ProviderHost;
459459
460460/** Canonical provider-instance wrapper for ACP Registry orchestration adapters. */
461461export const AcpRegistryDriver: ProviderDriver<AcpRegistrySettings, AcpRegistryDriverEnv> = {
......@@ -485,7 +485,7 @@ export const AcpRegistryDriver: ProviderDriver<AcpRegistrySettings, AcpRegistryD
485485 const crypto = yield* Crypto.Crypto;
486486 const spawner = yield* ChildProcessSpawner.ChildProcessSpawner;
487487 const hostEnvironment = yield* HostProcessEnvironment;
488 const host = yield* ProviderHost;
488 const host = yield* ProviderHost.ProviderHost;
489489 const continuationIdentity = defaultProviderContinuationIdentity({
490490 driverKind: DRIVER_KIND,
491491 instanceId,
......@@ -619,7 +619,7 @@ export const AcpRegistryDriver: ProviderDriver<AcpRegistrySettings, AcpRegistryD
619619 }
620620 return { provider: enriched, generation: cacheState.generation };
621621 });
622 const snapshotSettings = makeProviderSnapshotSettingsSource(effectiveConfig, host.settings);
622 const snapshotSettings = yield* makeProviderSnapshotSettingsSource(effectiveConfig);
623623 const snapshot = yield* makeManagedServerProvider<
624624 ProviderSnapshotSettings<AcpRegistrySettings>
625625 >({
packages/provider-acp/src/server/adapter.ts+5-4
......@@ -95,7 +95,7 @@ import {
9595} from "@t3tools/provider-core/server/orchestrationInstructions";
9696import { buildRuntimeInstructions } from "@t3tools/provider-core/server/runtimeInstructions";
9797import * 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";
9999import {
100100 type BackgroundWork,
101101 type BackgroundWorkReport,
......@@ -499,7 +499,9 @@ export interface AcpAdapterV2Options {
499499 * / history-only behavior for null-activeTurn updates.
500500 */
501501 readonly continuationRequests?: {
502 readonly offer: (request: ProviderContinuationRequest) => Effect.Effect<void>;
502 readonly offer: (
503 request: ProviderContinuationRequests.ProviderContinuationRequest,
504 ) => Effect.Effect<void>;
503505 };
504506 readonly testHooks?: {
505507 /**
......@@ -743,7 +745,7 @@ function nativeThreadId(
743745function makeProviderThread(input: {
744746 readonly driver: ProviderDriverKind;
745747 readonly providerInstanceId: ProviderInstanceId;
746 readonly idAllocator: IdAllocator.IdAllocatorV2Shape;
748 readonly idAllocator: IdAllocator.IdAllocatorV2["Service"];
747749 readonly appThreadId: OrchestrationV2ProviderThread["appThreadId"];
748750 readonly providerSessionId: OrchestrationV2ProviderThread["providerSessionId"];
749751 readonly nativeThreadId: string;
......@@ -2226,7 +2228,6 @@ export const makeAcpAdapterV2 = Effect.fn("makeAcpAdapterV2")(function* (
22262228 type: "turn_item.updated",
22272229 driver,
22282230 turnItem: makeProviderRetryTurnItem({
2229 idAllocator,
22302231 driver,
22312232 threadId: context.input.threadId,
22322233 runId: context.input.runId,
packages/provider-core/package.json+7-7
......@@ -23,10 +23,6 @@
2323 "types": "./src/server/collectStreamText.ts",
2424 "import": "./src/server/collectStreamText.ts"
2525 },
26 "./server/continuationRequests": {
27 "types": "./src/server/continuationRequests.ts",
28 "import": "./src/server/continuationRequests.ts"
29 },
3026 "./server/driver": {
3127 "types": "./src/server/driver.ts",
3228 "import": "./src/server/driver.ts"
......@@ -103,9 +99,13 @@
10399 "types": "./src/server/ProviderAdapter.ts",
104100 "import": "./src/server/ProviderAdapter.ts"
105101 },
106 "./server/ProviderAuthFlow": {
107 "types": "./src/server/ProviderAuthFlow.ts",
108 "import": "./src/server/ProviderAuthFlow.ts"
102 "./server/providerAuthFlow": {
103 "types": "./src/server/providerAuthFlow.ts",
104 "import": "./src/server/providerAuthFlow.ts"
105 },
106 "./server/ProviderContinuationRequests": {
107 "types": "./src/server/ProviderContinuationRequests.ts",
108 "import": "./src/server/ProviderContinuationRequests.ts"
109109 },
110110 "./server/ProviderEventLoggers": {
111111 "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
6767export const IdAllocatorV2Error = Schema.Union([IdAllocatorV2AllocationError]);
6868export type IdAllocatorV2Error = typeof IdAllocatorV2Error.Type;
6969
70export interface IdAllocatorV2AllocateShape {
70export interface IdAllocatorV2Allocate {
7171 readonly command: (input: {
7272 readonly fixtureName: string;
7373 readonly commandName: string;
......@@ -126,7 +126,7 @@ export interface IdAllocatorV2AllocateShape {
126126 }) => Effect.Effect<PlanId, IdAllocatorV2Error>;
127127}
128128
129export interface IdAllocatorV2DeriveShape {
129export interface IdAllocatorV2Derive {
130130 readonly providerSession: (input: {
131131 readonly providerInstanceId: ProviderInstanceId;
132132 }) => ProviderSessionId;
......@@ -180,14 +180,13 @@ export interface IdAllocatorV2DeriveShape {
180180 readonly approvalTurnItem: (input: { readonly requestId: RuntimeRequestId }) => TurnItemId;
181181}
182182
183export interface IdAllocatorV2Shape {
184 readonly allocate: IdAllocatorV2AllocateShape;
185 readonly derive: IdAllocatorV2DeriveShape;
186}
187
188export class IdAllocatorV2 extends Context.Service<IdAllocatorV2, IdAllocatorV2Shape>()(
189 "@t3tools/provider-core/server/IdAllocator/IdAllocatorV2",
190) {}
183export class IdAllocatorV2 extends Context.Service<
184 IdAllocatorV2,
185 {
186 readonly allocate: IdAllocatorV2Allocate;
187 readonly derive: IdAllocatorV2Derive;
188 }
189>()("@t3tools/provider-core/server/IdAllocator/IdAllocatorV2") {}
191190
192191const encodePart = (part: string | number): string => encodeURIComponent(String(part));
193192
......@@ -252,6 +251,49 @@ const randomId =
252251 ),
253252 );
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
255297export const layer: Layer.Layer<IdAllocatorV2> = Layer.succeed(
256298 IdAllocatorV2,
257299 IdAllocatorV2.of({
......@@ -386,50 +428,6 @@ export const layer: Layer.Layer<IdAllocatorV2> = Layer.succeed(
386428 make: PlanId.make,
387429 })(input),
388430 },
389 derive: {
390 providerSession: (input) =>
391 ProviderSessionId.make(
392 joinId("provider-session", "provider-instance", input.providerInstanceId, "shared"),
393 ),
394 delegatedTaskNode: (input) => NodeId.make(joinId("node", "delegated-task", input.commandId)),
395 delegatedTaskThread: (input) =>
396 ThreadId.make(joinId("thread", "delegated-task", input.commandId)),
397 delegatedTaskMessage: (input) =>
398 MessageId.make(joinId("message", "delegated-task", input.commandId)),
399 delegatedTaskTurnItem: (input) =>
400 TurnItemId.make(joinId("turn-item", "delegated-task", input.commandId)),
401 createdThreadTurnItem: (input) =>
402 TurnItemId.make(joinId("turn-item", "created-thread", input.commandId)),
403 threadFromProviderThread: deriveThreadFromProviderThread,
404 run: (input) => RunId.make(joinId("run", "thread", input.threadId, "ordinal", input.ordinal)),
405 runAttempt: (input) =>
406 RunAttemptId.make(
407 joinId("run-attempt", "run", input.runId, "attempt", input.attemptOrdinal),
408 ),
409 rootNode: (input) => NodeId.make(joinId("node", "run", input.runId, "root")),
410 rootNodeAttempt: (input) =>
411 NodeId.make(joinId("node", "run", input.runId, "attempt", input.attemptOrdinal, "root")),
412 userTurnItem: (input) => TurnItemId.make(joinId("turn-item", "message", input.messageId)),
413 runSignalTurnItem: (input) =>
414 TurnItemId.make(joinId("turn-item", "run", input.runId, "signal", input.signal)),
415 providerThread: deriveProviderThread,
416 providerTurn: (input) =>
417 ProviderTurnId.make(
418 joinId("provider-turn", "provider", input.driver, "native-turn", input.nativeTurnId),
419 ),
420 nodeFromProviderItem: (input) =>
421 NodeId.make(joinId("node", "provider", input.driver, "native-item", input.nativeItemId)),
422 messageFromProviderItem: (input) =>
423 MessageId.make(
424 joinId("message", "provider", input.driver, "native-item", input.nativeItemId),
425 ),
426 turnItemFromProviderItem: (input) =>
427 TurnItemId.make(
428 joinId("turn-item", "provider", input.driver, "native-item", input.nativeItemId),
429 ),
430 approvalNode: (input) => NodeId.make(joinId("node", "runtime-request", input.requestId)),
431 approvalTurnItem: (input) =>
432 TurnItemId.make(joinId("turn-item", "runtime-request", input.requestId)),
433 },
431 derive,
434432 }),
435433);
packages/provider-core/src/server/ProviderAdapter.ts+17-18
......@@ -632,21 +632,20 @@ export interface ProviderAdapterV2SessionRuntime {
632632 ) => Effect.Effect<OrchestrationV2ProviderThread, ProviderAdapterV2Error>;
633633}
634634
635export interface ProviderAdapterV2Shape {
636 readonly instanceId: ProviderInstanceId;
637 readonly driver: ProviderDriverKind;
638 readonly getCapabilities: () => Effect.Effect<
639 OrchestrationV2ProviderCapabilities,
640 ProviderAdapterV2Error
641 >;
642 readonly planSelectionTransition: (
643 input: ProviderSelectionTransitionInput,
644 ) => Effect.Effect<ProviderSelectionTransitionPlan, ProviderAdapterV2Error>;
645 readonly openSession: (
646 input: ProviderAdapterV2OpenSessionInput,
647 ) => Effect.Effect<ProviderAdapterV2SessionRuntime, ProviderAdapterV2Error, Scope.Scope>;
648}
649
650export class ProviderAdapterV2 extends Context.Service<ProviderAdapterV2, ProviderAdapterV2Shape>()(
651 "@t3tools/provider-core/server/ProviderAdapter/ProviderAdapterV2",
652) {}
635export class ProviderAdapterV2 extends Context.Service<
636 ProviderAdapterV2,
637 {
638 readonly instanceId: ProviderInstanceId;
639 readonly driver: ProviderDriverKind;
640 readonly getCapabilities: () => Effect.Effect<
641 OrchestrationV2ProviderCapabilities,
642 ProviderAdapterV2Error
643 >;
644 readonly planSelectionTransition: (
645 input: ProviderSelectionTransitionInput,
646 ) => Effect.Effect<ProviderSelectionTransitionPlan, ProviderAdapterV2Error>;
647 readonly openSession: (
648 input: ProviderAdapterV2OpenSessionInput,
649 ) => Effect.Effect<ProviderAdapterV2SessionRuntime, ProviderAdapterV2Error, Scope.Scope>;
650 }
651>()("@t3tools/provider-core/server/ProviderAdapter/ProviderAdapterV2") {}
packages/provider-core/src/server/ProviderAuthFlow.test.ts deleted-559
......@@ -1,559 +0,0 @@
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 {
4545 readonly remove: Effect.Effect<void, ProviderCredentialError>;
4646}
4747
48interface ProviderHostService {
49 readonly paths: ProviderHostPaths;
50 readonly settings: {
51 readonly get: Effect.Effect<ServerSettings, ServerSettingsError>;
52 /** Runs `use` on a settings snapshot that no settings write can change until it finishes. */
53 readonly withSnapshot: <A, E, R>(
54 use: (settings: ServerSettings) => Effect.Effect<A, E, R>,
55 ) => Effect.Effect<A, E | ServerSettingsError, R>;
56 /** Every settings change after subscription, starting with the next one. */
57 readonly changes: Stream.Stream<ServerSettings>;
58 /** Changes buffered from the moment the scoped subscription is acquired. */
59 readonly subscribe: Effect.Effect<Stream.Stream<ServerSettings>, never, Scope.Scope>;
60 };
61 /** Whether background work for `scope` should run now (client demand and host power). */
62 readonly shouldRunBackgroundWork: (scope: BackgroundScope) => Effect.Effect<boolean>;
63 /**
64 * Absolute path of a stored chat attachment, or `null` when its id does not
65 * resolve inside the attachments directory.
66 */
67 readonly resolveAttachmentPath: (attachment: ChatAttachment) => string | null;
68 /** The credentials stored for `bindingId`, usually an instance id, under a provider-chosen namespace. */
69 readonly credentials: (
70 namespace: string,
71 bindingId: string,
72 ) => Effect.Effect<ProviderCredentials>;
73}
74
75export class ProviderHost extends Context.Service<ProviderHost, ProviderHostService>()(
76 "@t3tools/provider-core/server/ProviderHost",
77) {}
48export class ProviderHost extends Context.Service<
49 ProviderHost,
50 {
51 readonly paths: ProviderHostPaths;
52 readonly settings: {
53 readonly get: Effect.Effect<ServerSettings, ServerSettingsError>;
54 /** Runs `use` on a settings snapshot that no settings write can change until it finishes. */
55 readonly withSnapshot: <A, E, R>(
56 use: (settings: ServerSettings) => Effect.Effect<A, E, R>,
57 ) => Effect.Effect<A, E | ServerSettingsError, R>;
58 /** Every settings change after subscription, starting with the next one. */
59 readonly changes: Stream.Stream<ServerSettings>;
60 /** Changes buffered from the moment the scoped subscription is acquired. */
61 readonly subscribe: Effect.Effect<Stream.Stream<ServerSettings>, never, Scope.Scope>;
62 };
63 /** Whether background work for `scope` should run now (client demand and host power). */
64 readonly shouldRunBackgroundWork: (scope: BackgroundScope) => Effect.Effect<boolean>;
65 /**
66 * Absolute path of a stored chat attachment, or `null` when its id does not
67 * resolve inside the attachments directory.
68 */
69 readonly resolveAttachmentPath: (attachment: ChatAttachment) => string | null;
70 /** The credentials stored for `bindingId`, usually an instance id, under a provider-chosen namespace. */
71 readonly credentials: (
72 namespace: string,
73 bindingId: string,
74 ) => Effect.Effect<ProviderCredentials>;
75 }
76>()("@t3tools/provider-core/server/ProviderHost") {}
packages/provider-core/src/server/adapterDriver.ts+1-1
......@@ -39,7 +39,7 @@ export interface ProviderAdapterDriver<Config, R = never> {
3939 readonly create: (
4040 input: ProviderAdapterDriverCreateInput<Config>,
4141 ) => Effect.Effect<
42 ProviderAdapter.ProviderAdapterV2Shape,
42 ProviderAdapter.ProviderAdapterV2["Service"],
4343 ProviderAdapterDriverCreateError,
4444 R | Scope.Scope
4545 >;
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 {
101101 ProviderConsumeResetCreditOutcome,
102102 ProviderDriverError
103103 >;
104 readonly orchestrationAdapter: ProviderAdapter.ProviderAdapterV2Shape;
104 readonly orchestrationAdapter: ProviderAdapter.ProviderAdapterV2["Service"];
105105 readonly textGeneration: ProviderTextGeneration;
106106 readonly auth?: ProviderAuthController;
107107 readonly acpSessionManagement?: {
packages/provider-core/src/server/failure.ts+3-5
......@@ -13,7 +13,7 @@ import type {
1313import * as DateTime from "effect/DateTime";
1414import * as Cause from "effect/Cause";
1515
16import type * as IdAllocator from "./IdAllocator.ts";
16import * as IdAllocator from "./IdAllocator.ts";
1717import * as Schema from "effect/Schema";
1818
1919/** Raised when portable history does not fit the target provider's context window. */
......@@ -172,7 +172,6 @@ export function makeProviderFailure(input: {
172172}
173173
174174export function makeProviderFailureTurnItem(input: {
175 readonly idAllocator: IdAllocator.IdAllocatorV2Shape;
176175 readonly driver: ProviderDriverKind;
177176 readonly threadId: ThreadId;
178177 readonly runId: RunId | null;
......@@ -186,7 +185,7 @@ export function makeProviderFailureTurnItem(input: {
186185 readonly occurredAt: DateTime.Utc;
187186}): Extract<OrchestrationV2TurnItem, { readonly type: "error" }> {
188187 return {
189 id: input.idAllocator.derive.turnItemFromProviderItem({
188 id: IdAllocator.derive.turnItemFromProviderItem({
190189 driver: input.driver,
191190 nativeItemId: `terminal-failure:${input.providerTurnId}`,
192191 }),
......@@ -210,7 +209,6 @@ export function makeProviderFailureTurnItem(input: {
210209}
211210
212211export function makeProviderRetryTurnItem(input: {
213 readonly idAllocator: IdAllocator.IdAllocatorV2Shape;
214212 readonly driver: ProviderDriverKind;
215213 readonly threadId: ThreadId;
216214 readonly runId: RunId | null;
......@@ -237,7 +235,7 @@ export function makeProviderRetryTurnItem(input: {
237235 title = "Provider retry stopped";
238236 }
239237 return {
240 id: input.idAllocator.derive.turnItemFromProviderItem({
238 id: IdAllocator.derive.turnItemFromProviderItem({
241239 driver: input.driver,
242240 nativeItemId: `terminal-failure:${input.providerTurnId}`,
243241 }),
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";
33import * as Equal from "effect/Equal";
44import * as Stream from "effect/Stream";
55
6import type * as ProviderHost from "./ProviderHost.ts";
6import * as ProviderHost from "./ProviderHost.ts";
77
88export interface ProviderSnapshotSettings<Settings> {
99 readonly provider: Settings;
......@@ -27,17 +27,23 @@ export function haveProviderSnapshotSettingsChanged<Settings>(
2727 return !Equal.equals(previous, next);
2828}
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>(
3132 provider: Settings,
32 settings: ProviderHost.ProviderHost["Service"]["settings"],
33): {
34 readonly getSettings: Effect.Effect<ProviderSnapshotSettings<Settings>, ServerSettingsError>;
35 readonly streamSettings: Stream.Stream<ProviderSnapshotSettings<Settings>>;
36} {
37 const mapSettings = (settings: ServerSettings) =>
38 makeProviderSnapshotSettings(provider, settings);
39 return {
40 getSettings: settings.get.pipe(Effect.map(mapSettings)),
41 streamSettings: settings.changes.pipe(Stream.map(mapSettings)),
42 };
43}
33): Effect.Effect<
34 {
35 readonly getSettings: Effect.Effect<ProviderSnapshotSettings<Settings>, ServerSettingsError>;
36 readonly streamSettings: Stream.Stream<ProviderSnapshotSettings<Settings>>;
37 },
38 never,
39 ProviderHost.ProviderHost
40> =>
41 Effect.gen(function* () {
42 const { settings } = yield* ProviderHost.ProviderHost;
43 const mapSettings = (current: ServerSettings) =>
44 makeProviderSnapshotSettings(provider, current);
45 return {
46 getSettings: settings.get.pipe(Effect.map(mapSettings)),
47 streamSettings: settings.changes.pipe(Stream.map(mapSettings)),
48 };
49 });
packages/provider-cursor/src/server/adapter.ts+1-1
......@@ -239,7 +239,7 @@ function providerSession(input: {
239239}
240240
241241function makeProviderThread(input: {
242 readonly idAllocator: IdAllocator.IdAllocatorV2Shape;
242 readonly idAllocator: IdAllocator.IdAllocatorV2["Service"];
243243 readonly providerInstanceId: ProviderInstanceId;
244244 readonly appThreadId: OrchestrationV2ProviderThread["appThreadId"];
245245 readonly providerSessionId: OrchestrationV2ProviderThread["providerSessionId"];
packages/provider-cursor/src/server/driver.ts+1-1
......@@ -223,7 +223,7 @@ export const CursorDriver: ProviderDriver<CursorSettings, CursorDriverEnv> = {
223223 Effect.provide(CursorSdkCatalog.layer),
224224 );
225225
226 const snapshotSettings = makeProviderSnapshotSettingsSource(effectiveConfig, host.settings);
226 const snapshotSettings = yield* makeProviderSnapshotSettingsSource(effectiveConfig);
227227 const snapshot = yield* makeManagedServerProvider<ProviderSnapshotSettings<CursorSettings>>({
228228 resolveMaintenance: () => Effect.succeed(MAINTENANCE_CAPABILITIES),
229229 getSettings: snapshotSettings.getSettings,
packages/provider-grok/src/server/adapter.ts+1-1
......@@ -58,7 +58,7 @@ import { acpPermissionDisposition } from "@t3tools/provider-acp/server/clientPol
5858import * as AcpSessionRuntime from "@t3tools/provider-acp/server/AcpSessionRuntime";
5959import * as ProviderEventLoggers from "@t3tools/provider-core/server/ProviderEventLoggers";
6060import * 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";
6262import * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter";
6363import {
6464 ProviderAdapterDriverCreateError,
packages/provider-grok/src/server/driver.ts+1-1
......@@ -160,7 +160,7 @@ export const GrokDriver: ProviderDriver<GrokSettings, GrokDriverEnv> = {
160160 Effect.provideService(ChildProcessSpawner.ChildProcessSpawner, spawner),
161161 );
162162
163 const snapshotSettings = makeProviderSnapshotSettingsSource(effectiveConfig, host.settings);
163 const snapshotSettings = yield* makeProviderSnapshotSettingsSource(effectiveConfig);
164164 const snapshot = yield* makeManagedServerProvider<ProviderSnapshotSettings<GrokSettings>>({
165165 resolveMaintenance,
166166 getSettings: snapshotSettings.getSettings,
packages/provider-muse/src/server/adapter.test.ts+2-2
......@@ -36,7 +36,7 @@ import type { MuseItem } from "./protocol.ts";
3636import type { MuseSdkHost } from "./sdk.ts";
3737import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator";
3838import * 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";
4040import { makeMuseAdapterV2, type MuseAdapterV2Options } from "./adapter.ts";
4141
4242const testLayer = Layer.mergeAll(
......@@ -766,7 +766,7 @@ describe("MuseAdapterV2", () => {
766766 it.effect("lets a user turn take a held Muse report turn, approvals included", () =>
767767 Effect.gen(function* () {
768768 const fake = yield* makeFakeMuse();
769 const offers: Array<ProviderContinuationRequest> = [];
769 const offers: Array<ProviderContinuationRequests.ProviderContinuationRequest> = [];
770770 const harness = yield* makeHarness(
771771 fake,
772772 INSTANCE_ID,
packages/provider-muse/src/server/adapter.ts+1-1
......@@ -74,7 +74,7 @@ import {
7474 backgroundWorkNotification,
7575 type BackgroundWorkReport,
7676} 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";
7878import { makeProviderFailure } from "@t3tools/provider-core/server/failure";
7979import { turnScopedSelectionTransition } from "@t3tools/provider-core/server/selectionTransition";
8080import { museItemStatus, museToolPresentation } from "./itemPresentation.ts";
packages/provider-muse/src/server/driver.ts+2-2
......@@ -14,7 +14,7 @@ import { makeMuseTextGeneration } from "./textGeneration.ts";
1414import { ProviderDriverError } from "@t3tools/provider-core/server/errors";
1515import { makeMuseAdapterV2 } from "./adapter.ts";
1616import * 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";
1818import { checkMuseProviderStatus, makePendingMuseProvider } from "./status.ts";
1919import * as ProviderEventLoggers from "@t3tools/provider-core/server/ProviderEventLoggers";
2020import { makeManagedServerProvider } from "@t3tools/provider-core/server/managedProvider";
......@@ -86,7 +86,7 @@ export const MuseDriver: ProviderDriver<MuseSettings, MuseDriverEnv> = {
8686 accentColor,
8787 continuationGroupKey: continuationIdentity.continuationKey,
8888 });
89 const snapshotSettings = makeProviderSnapshotSettingsSource(effectiveConfig, host.settings);
89 const snapshotSettings = yield* makeProviderSnapshotSettingsSource(effectiveConfig);
9090 const resolveInstallation = yield* makeCachedProviderMaintenanceResolution(
9191 resolveProviderMaintenanceCapabilitiesEffect(museMaintenance, {
9292 binaryPath: effectiveConfig.binaryPath,
packages/provider-opencode/src/server/adapter.ts+2-2
......@@ -459,7 +459,7 @@ function formatOpenCodeProtocolLogPayload(event: OpenCodeProtocolLogEvent) {
459459
460460export function makeOpenCodeProtocolLogger(input: {
461461 readonly nativeEventLogger: ProviderEventLoggers.EventNdjsonLogger | undefined;
462 readonly idAllocator: IdAllocator.IdAllocatorV2Shape;
462 readonly idAllocator: IdAllocator.IdAllocatorV2["Service"];
463463 readonly providerInstanceId: ProviderInstanceId;
464464 readonly providerSessionId: ProviderSessionId;
465465 readonly threadId: ThreadId;
......@@ -830,7 +830,7 @@ function taskSessionId(part: ToolPart): string | null {
830830}
831831
832832function makeProviderThread(input: {
833 readonly idAllocator: IdAllocator.IdAllocatorV2Shape;
833 readonly idAllocator: IdAllocator.IdAllocatorV2["Service"];
834834 readonly providerInstanceId: ProviderInstanceId;
835835 readonly providerSessionId: OrchestrationV2ProviderThread["providerSessionId"];
836836 readonly appThreadId: OrchestrationV2ProviderThread["appThreadId"];
packages/provider-opencode/src/server/driver.ts+4-4
......@@ -123,9 +123,9 @@ function byOpenCodeRuntime<A, E, R, PE>(
123123 */
124124function selectOpenCodeRuntimeAdapter(input: {
125125 readonly probe: OpenCodeRuntimeProbe;
126 readonly v1: ProviderAdapter.ProviderAdapterV2Shape;
127 readonly v2: ProviderAdapter.ProviderAdapterV2Shape;
128}): ProviderAdapter.ProviderAdapterV2Shape {
126 readonly v1: ProviderAdapter.ProviderAdapterV2["Service"];
127 readonly v2: ProviderAdapter.ProviderAdapterV2["Service"];
128}): ProviderAdapter.ProviderAdapterV2["Service"] {
129129 const pick = <PE>(probed: Effect.Effect<ProbedOpenCode | undefined, PE>) =>
130130 byOpenCodeRuntime(probed, { v1: Effect.succeed(input.v1), v2: Effect.succeed(input.v2) });
131131 const hot = pick(Effect.map(input.probe.lastSuccess, Option.getOrUndefined));
......@@ -445,7 +445,7 @@ export const OpenCodeDriver: ProviderDriver<OpenCodeSettings, OpenCodeDriverEnv>
445445 ),
446446 );
447447
448 const snapshotSettings = makeProviderSnapshotSettingsSource(effectiveConfig, host.settings);
448 const snapshotSettings = yield* makeProviderSnapshotSettingsSource(effectiveConfig);
449449 const snapshot = yield* makeManagedServerProvider<ProviderSnapshotSettings<OpenCodeSettings>>(
450450 {
451451 resolveMaintenance,
packages/provider-opencode/src/server/v2/adapter.ts+2-2
......@@ -88,7 +88,7 @@ import {
8888 backgroundWorkNotification,
8989 type BackgroundWorkReport,
9090} from "@t3tools/provider-core/server/notification";
91import * as ProviderContinuationRequests from "@t3tools/provider-core/server/continuationRequests";
91import * as ProviderContinuationRequests from "@t3tools/provider-core/server/ProviderContinuationRequests";
9292import { makeProviderFailure } from "@t3tools/provider-core/server/failure";
9393import {
9494 makeSubagentChildThread,
......@@ -858,7 +858,7 @@ export const make = Effect.fn("OpenCode2Adapter.make")(function* (instanceId: Pr
858858 });
859859
860860 const openSession = Effect.fn("OpenCode2Adapter.openSession")(function* (
861 input: Parameters<ProviderAdapter.ProviderAdapterV2Shape["openSession"]>[0],
861 input: Parameters<ProviderAdapter.ProviderAdapterV2["Service"]["openSession"]>[0],
862862 initial: {
863863 readonly connection: OpenCode2Server.OpenCode2Connection;
864864 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";
3232import { ChildProcess, ChildProcessSpawner } from "effect/process";
3333
3434import { layerTestProviderHost } from "@t3tools/provider-testing/host";
35import * as ProviderContinuationRequests from "@t3tools/provider-core/server/continuationRequests";
35import * as ProviderContinuationRequests from "@t3tools/provider-core/server/ProviderContinuationRequests";
3636import * as McpProviderSession from "@t3tools/provider-core/server/mcpSession";
3737import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator";
3838import * 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";
6767import { mergeProviderInstanceEnvironment } from "@t3tools/provider-core/server/instanceEnvironment";
6868import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator";
6969import * 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";
7171import {
7272 ProviderAdapterDriverCreateError,
7373 type ProviderAdapterDriver,
......@@ -775,7 +775,6 @@ export const makePiAdapterV2 = Effect.fn("makePiAdapterV2")(function* (
775775 type: "turn_item.updated",
776776 driver: PI_PROVIDER,
777777 turnItem: makeProviderRetryTurnItem({
778 idAllocator,
779778 driver: PI_PROVIDER,
780779 threadId: turn.turnInput.threadId,
781780 runId: turn.turnInput.runId,
packages/provider-pi/src/server/driver.ts+1-1
......@@ -142,7 +142,7 @@ export const PiDriver: ProviderDriver<PiSettings, PiDriverEnv> = {
142142 Effect.provideService(ChildProcessSpawner.ChildProcessSpawner, spawner),
143143 );
144144
145 const snapshotSettings = makeProviderSnapshotSettingsSource(effectiveConfig, host.settings);
145 const snapshotSettings = yield* makeProviderSnapshotSettingsSource(effectiveConfig);
146146 const snapshot = yield* makeManagedServerProvider<ProviderSnapshotSettings<PiSettings>>({
147147 resolveMaintenance,
148148 getSettings: snapshotSettings.getSettings,