1import type { OrchestrationV2HistoricalMessage } from "@t3tools/contracts";
2import {
3 ChatAttachment,
4 CheckpointId,
5 MessageId,
6 ModelSelection,
7 NodeId,
8 OrchestrationV2AppThread,
9 OrchestrationV2ConversationMessage,
10 OrchestrationV2ExecutionNode,
11 OrchestrationV2ProviderSession,
12 OrchestrationV2PlanArtifact,
13 OrchestrationV2ProviderCapabilities,
14 OrchestrationV2ProviderFailure,
15 OrchestrationV2ProviderRetry,
16 OrchestrationV2ProviderThread,
17 OrchestrationV2ProviderTurn,
18 OrchestrationV2RuntimeRequest,
19 OrchestrationV2Subagent,
20 OrchestrationV2TurnItem,
21 ProviderApprovalDecision,
22 ProviderInteractionMode,
23 ProviderDriverKind,
24 ProviderInstanceId,
25 PositiveInt,
26 ProviderUserInputAnswers,
27 ProviderSessionId,
28 ProviderThreadId,
29 ProviderTurnId,
30 RuntimeMode,
31 RuntimeRequestId,
32 RunAttemptId,
33 RunId,
34 ThreadId,
35 TurnItemId,
36} from "@t3tools/contracts";
37import * as Context from "effect/Context";
38import * as Schema from "effect/Schema";
39import type * as Effect from "effect/Effect";
40import type * as Scope from "effect/Scope";
41import type * as Stream from "effect/Stream";
42
43import type {
44 ProviderSelectionTransitionInput,
45 ProviderSelectionTransitionPlan,
46} from "./selectionTransition.ts";
47
48export const ProviderAdapterV2RuntimePolicy = Schema.Struct({
49 runtimeMode: RuntimeMode,
50 interactionMode: ProviderInteractionMode,
51 cwd: Schema.NullOr(Schema.String),
52 approvalPolicy: Schema.optional(Schema.Unknown),
53 sandboxPolicy: Schema.optional(Schema.Unknown),
54 /**
55 * The administrator's ceiling, when a managed policy sets one. `runtimeMode` is already under
56 * it; adapters use it to refuse native mode choices a user makes beside T3 Code's own.
57 */
58 maxRuntimeMode: Schema.optional(RuntimeMode),
59});
60export type ProviderAdapterV2RuntimePolicy = typeof ProviderAdapterV2RuntimePolicy.Type;
61
62export const ProviderAdapterV2TurnMessage = Schema.Struct({
63 messageId: MessageId,
64 text: Schema.String,
65 attachments: Schema.Array(ChatAttachment),
66 createdBy: OrchestrationV2ConversationMessage.fields.createdBy,
67 creationSource: OrchestrationV2ConversationMessage.fields.creationSource,
68 scheduledTaskId: OrchestrationV2ConversationMessage.fields.scheduledTaskId,
69 senderThreadId: OrchestrationV2ConversationMessage.fields.senderThreadId,
70});
71export type ProviderAdapterV2TurnMessage = typeof ProviderAdapterV2TurnMessage.Type;
72
73export const ProviderAdapterV2SessionStatus = Schema.Literals([
74 "starting",
75 "ready",
76 "running",
77 "waiting",
78 "stopped",
79 "error",
80]);
81export type ProviderAdapterV2SessionStatus = typeof ProviderAdapterV2SessionStatus.Type;
82
83export const ProviderAdapterV2Event = Schema.Union([
84 Schema.Struct({
85 type: Schema.Literal("app_thread.created"),
86 driver: ProviderDriverKind,
87 appThread: OrchestrationV2AppThread,
88 createOnly: Schema.optional(Schema.Boolean),
89 }),
90 Schema.Struct({
91 type: Schema.Literal("provider_session.updated"),
92 driver: ProviderDriverKind,
93 providerSession: OrchestrationV2ProviderSession,
94 }),
95 Schema.Struct({
96 type: Schema.Literal("provider_thread.updated"),
97 driver: ProviderDriverKind,
98 providerThread: OrchestrationV2ProviderThread,
99 /** Work remaining at this point in the event stream, including native continuations. */
100 pendingBackgroundWork: Schema.optional(Schema.Boolean),
101 /** Persist idle background snapshots before a run subscriber exists. */
102 sessionScoped: Schema.optional(Schema.Boolean),
103 retainedNativeTurnIds: Schema.optional(Schema.Array(Schema.String)),
104 }),
105 Schema.Struct({
106 type: Schema.Literal("provider_turn.updated"),
107 driver: ProviderDriverKind,
108 threadId: Schema.optional(ThreadId),
109 providerTurn: OrchestrationV2ProviderTurn,
110 }),
111 Schema.Struct({
112 type: Schema.Literal("node.updated"),
113 driver: ProviderDriverKind,
114 node: OrchestrationV2ExecutionNode,
115 }),
116 Schema.Struct({
117 type: Schema.Literal("subagent.updated"),
118 driver: ProviderDriverKind,
119 subagent: OrchestrationV2Subagent,
120 }),
121 Schema.Struct({
122 type: Schema.Literal("message.updated"),
123 driver: ProviderDriverKind,
124 message: OrchestrationV2ConversationMessage,
125 }),
126 Schema.Struct({
127 type: Schema.Literal("turn_item.updated"),
128 driver: ProviderDriverKind,
129 turnItem: OrchestrationV2TurnItem,
130 /** Persist recovered activity independently of a run subscription. */
131 sessionScoped: Schema.optional(Schema.Boolean),
132 }),
133 /** A steer the provider queued stays `pending` until the agent reads it; `cancelled` if dropped unread. */
134 Schema.Struct({
135 type: Schema.Literal("steer.updated"),
136 driver: ProviderDriverKind,
137 threadId: ThreadId,
138 runId: RunId,
139 messageId: MessageId,
140 status: Schema.Literals(["pending", "completed", "cancelled"]),
141 }),
142 // Start before the root terminal; clear only after the final covered projection.
143 // Consumer lifetime cannot depend on a producer probe that runs ahead of its queue.
144 Schema.Struct({
145 type: Schema.Literal("background_work.updated"),
146 driver: ProviderDriverKind,
147 providerTurnId: ProviderTurnId,
148 pending: Schema.Boolean,
149 }),
150 /** Unpersisted deltas go only to clients showing the row; final output arrives in turn_item.updated. */
151 Schema.Struct({
152 type: Schema.Literal("command_output.delta"),
153 driver: ProviderDriverKind,
154 threadId: ThreadId,
155 itemId: TurnItemId,
156 chunk: Schema.String,
157 }),
158 /** Unpersisted predicted next prompt; arrives after the run's root terminal. */
159 Schema.Struct({
160 type: Schema.Literal("prompt_suggestion.updated"),
161 driver: ProviderDriverKind,
162 threadId: ThreadId,
163 runId: RunId,
164 text: Schema.String,
165 }),
166 Schema.Struct({
167 type: Schema.Literal("runtime_request.updated"),
168 driver: ProviderDriverKind,
169 threadId: Schema.optional(ThreadId),
170 runtimeRequest: OrchestrationV2RuntimeRequest,
171 }),
172 Schema.Struct({
173 type: Schema.Literal("plan.updated"),
174 driver: ProviderDriverKind,
175 plan: OrchestrationV2PlanArtifact,
176 }),
177 Schema.Struct({
178 type: Schema.Literal("turn.terminal"),
179 driver: ProviderDriverKind,
180 providerThreadId: ProviderThreadId,
181 providerTurnId: ProviderTurnId,
182 runOrdinal: PositiveInt,
183 status: Schema.Literals(["completed", "interrupted", "cancelled"]),
184 failure: Schema.Null,
185 reason: Schema.optional(Schema.String),
186 threadDisposition: Schema.Literals(["reusable", "broken"]),
187 }),
188 Schema.Struct({
189 type: Schema.Literal("turn.terminal"),
190 driver: ProviderDriverKind,
191 providerThreadId: ProviderThreadId,
192 providerTurnId: ProviderTurnId,
193 runOrdinal: PositiveInt,
194 failureItemOrdinal: PositiveInt,
195 status: Schema.Literal("failed"),
196 failure: OrchestrationV2ProviderFailure,
197 retry: Schema.optional(OrchestrationV2ProviderRetry),
198 retryStartedAt: Schema.optional(Schema.DateTimeUtc),
199 threadDisposition: Schema.Literals(["reusable", "broken"]),
200 /** Child turns outlive root failure and report their own terminals; otherwise open children settle. */
201 childTurnsContinue: Schema.optional(Schema.Literal(true)),
202 }),
203]);
204export type ProviderAdapterV2Event = typeof ProviderAdapterV2Event.Type;
205
206export class ProviderAdapterCapabilitiesError extends Schema.TaggedError<ProviderAdapterCapabilitiesError>()(
207 "ProviderAdapterCapabilitiesError",
208 {
209 driver: ProviderDriverKind,
210 cause: Schema.optional(Schema.Defect()),
211 },
212) {
213 override get message(): string {
214 return `Failed to read ${this.driver} provider capabilities.`;
215 }
216}
217
218export class ProviderAdapterOpenSessionError extends Schema.TaggedError<ProviderAdapterOpenSessionError>()(
219 "ProviderAdapterOpenSessionError",
220 {
221 driver: ProviderDriverKind,
222 providerSessionId: ProviderSessionId,
223 requiresSignIn: Schema.optional(Schema.Literal(true)),
224 cause: Schema.optional(Schema.Defect()),
225 },
226) {
227 override get message(): string {
228 return `Failed to open ${this.driver} provider session ${this.providerSessionId}.`;
229 }
230}
231
232export class ProviderAdapterCloseSessionError extends Schema.TaggedError<ProviderAdapterCloseSessionError>()(
233 "ProviderAdapterCloseSessionError",
234 {
235 driver: ProviderDriverKind,
236 providerSessionId: ProviderSessionId,
237 cause: Schema.optional(Schema.Defect()),
238 },
239) {
240 override get message(): string {
241 return `Failed to close ${this.driver} provider session ${this.providerSessionId}.`;
242 }
243}
244
245export class ProviderAdapterResumeThreadError extends Schema.TaggedError<ProviderAdapterResumeThreadError>()(
246 "ProviderAdapterResumeThreadError",
247 {
248 driver: ProviderDriverKind,
249 providerSessionId: ProviderSessionId,
250 providerThreadId: ProviderThreadId,
251 cause: Schema.optional(Schema.Defect()),
252 },
253) {
254 override get message(): string {
255 return `Failed to resume ${this.driver} provider thread ${this.providerThreadId}.`;
256 }
257}
258
259/**
260 * The native thread is live in another process that owns its writes. Starting
261 * a replacement would split the conversation, so callers must not fall back.
262 */
263export class ProviderAdapterThreadInUseError extends Schema.TaggedError<ProviderAdapterThreadInUseError>()(
264 "ProviderAdapterThreadInUseError",
265 {
266 driver: ProviderDriverKind,
267 providerThreadId: ProviderThreadId,
268 detail: Schema.String,
269 cause: Schema.Defect(),
270 },
271) {
272 override get message(): string {
273 return this.detail;
274 }
275}
276
277/**
278 * The user sent `/compact` to a driver whose session has no native compaction.
279 */
280export class ProviderCompactionUnsupportedError extends Schema.TaggedError<ProviderCompactionUnsupportedError>()(
281 "ProviderCompactionUnsupportedError",
282 {
283 driver: ProviderDriverKind,
284 providerThreadId: ProviderThreadId,
285 },
286) {
287 override get message(): string {
288 return "This provider does not support /compact. Start a new thread to continue with a fresh context.";
289 }
290}
291
292export class ProviderAdapterEnsureThreadError extends Schema.TaggedError<ProviderAdapterEnsureThreadError>()(
293 "ProviderAdapterEnsureThreadError",
294 {
295 driver: ProviderDriverKind,
296 threadId: ThreadId,
297 cause: Schema.optional(Schema.Defect()),
298 },
299) {
300 override get message(): string {
301 return `Failed to ensure ${this.driver} provider thread for app thread ${this.threadId}.`;
302 }
303}
304
305export class ProviderAdapterReadThreadSnapshotError extends Schema.TaggedError<ProviderAdapterReadThreadSnapshotError>()(
306 "ProviderAdapterReadThreadSnapshotError",
307 {
308 driver: ProviderDriverKind,
309 providerThreadId: ProviderThreadId,
310 cause: Schema.optional(Schema.Defect()),
311 },
312) {
313 override get message(): string {
314 return `Failed to read ${this.driver} provider thread snapshot ${this.providerThreadId}.`;
315 }
316}
317
318export class ProviderAdapterRollbackThreadError extends Schema.TaggedError<ProviderAdapterRollbackThreadError>()(
319 "ProviderAdapterRollbackThreadError",
320 {
321 driver: ProviderDriverKind,
322 providerThreadId: ProviderThreadId,
323 checkpointId: Schema.optional(CheckpointId),
324 cause: Schema.optional(Schema.Defect()),
325 },
326) {
327 override get message(): string {
328 return `Failed to roll back ${this.driver} provider thread ${this.providerThreadId}.`;
329 }
330}
331
332export class ProviderAdapterForkThreadError extends Schema.TaggedError<ProviderAdapterForkThreadError>()(
333 "ProviderAdapterForkThreadError",
334 {
335 driver: ProviderDriverKind,
336 providerThreadId: ProviderThreadId,
337 cause: Schema.optional(Schema.Defect()),
338 },
339) {
340 override get message(): string {
341 return `Failed to fork ${this.driver} provider thread ${this.providerThreadId}.`;
342 }
343}
344
345export class ProviderAdapterTurnStartError extends Schema.TaggedError<ProviderAdapterTurnStartError>()(
346 "ProviderAdapterTurnStartError",
347 {
348 driver: ProviderDriverKind,
349 threadId: ThreadId,
350 providerThreadId: ProviderThreadId,
351 runId: RunId,
352 cause: Schema.optional(Schema.Defect()),
353 },
354) {
355 override get message(): string {
356 return `Failed to start run ${this.runId} on ${this.driver} provider thread ${this.providerThreadId}.`;
357 }
358}
359
360export class ProviderAdapterSteerRunUnsupportedError extends Schema.TaggedError<ProviderAdapterSteerRunUnsupportedError>()(
361 "ProviderAdapterSteerRunUnsupportedError",
362 {
363 driver: ProviderDriverKind,
364 providerThreadId: ProviderThreadId,
365 },
366) {
367 override get message(): string {
368 return `${this.driver} provider thread ${this.providerThreadId} does not support active-run steering.`;
369 }
370}
371
372export class ProviderAdapterSteerDeclinedError extends Schema.TaggedError<ProviderAdapterSteerDeclinedError>()(
373 "ProviderAdapterSteerDeclinedError",
374 {
375 driver: ProviderDriverKind,
376 providerThreadId: ProviderThreadId,
377 providerTurnId: ProviderTurnId,
378 },
379) {}
380
381export class ProviderAdapterSteerRunError extends Schema.TaggedError<ProviderAdapterSteerRunError>()(
382 "ProviderAdapterSteerRunError",
383 {
384 driver: ProviderDriverKind,
385 providerThreadId: ProviderThreadId,
386 providerTurnId: ProviderTurnId,
387 cause: Schema.optional(Schema.Defect()),
388 },
389) {
390 override get message(): string {
391 return `Failed to steer active run ${this.providerTurnId} on ${this.driver} provider thread ${this.providerThreadId}.`;
392 }
393}
394
395export class ProviderAdapterInterruptError extends Schema.TaggedError<ProviderAdapterInterruptError>()(
396 "ProviderAdapterInterruptError",
397 {
398 driver: ProviderDriverKind,
399 providerThreadId: ProviderThreadId,
400 providerTurnId: ProviderTurnId,
401 cause: Schema.optional(Schema.Defect()),
402 },
403) {
404 override get message(): string {
405 return `Failed to interrupt ${this.driver} provider turn ${this.providerTurnId}.`;
406 }
407}
408
409/** The provider did not confirm a stop in time; its processes may still be running. */
410export class ProviderAdapterInterruptTimeoutError extends Schema.TaggedError<ProviderAdapterInterruptTimeoutError>()(
411 "ProviderAdapterInterruptTimeoutError",
412 {
413 driver: ProviderDriverKind,
414 providerThreadId: ProviderThreadId,
415 providerTurnId: ProviderTurnId,
416 timeoutMs: Schema.Number,
417 },
418) {
419 override get message(): string {
420 return `The ${this.driver} provider did not confirm stopping turn ${this.providerTurnId} within ${this.timeoutMs} ms.`;
421 }
422}
423
424export class ProviderAdapterRuntimeRequestResponseError extends Schema.TaggedError<ProviderAdapterRuntimeRequestResponseError>()(
425 "ProviderAdapterRuntimeRequestResponseError",
426 {
427 driver: ProviderDriverKind,
428 requestId: RuntimeRequestId,
429 cause: Schema.optional(Schema.Defect()),
430 },
431) {
432 override get message(): string {
433 return `Failed to respond to ${this.driver} runtime request ${this.requestId}.`;
434 }
435}
436
437export class ProviderAdapterEventStreamError extends Schema.TaggedError<ProviderAdapterEventStreamError>()(
438 "ProviderAdapterEventStreamError",
439 {
440 driver: ProviderDriverKind,
441 providerSessionId: ProviderSessionId,
442 cause: Schema.optional(Schema.Defect()),
443 },
444) {
445 override get message(): string {
446 return `Failed while streaming ${this.driver} provider session ${this.providerSessionId} events.`;
447 }
448}
449
450// Only fixed reasons reach failure messages; detail can contain private configuration.
451const ProviderAdapterFailureReason = Schema.Literals([
452 "previous_turn_active",
453 "attachment_unreadable",
454]);
455export type ProviderAdapterFailureReason = typeof ProviderAdapterFailureReason.Type;
456
457export class ProviderAdapterProtocolError extends Schema.TaggedError<ProviderAdapterProtocolError>()(
458 "ProviderAdapterProtocolError",
459 {
460 driver: ProviderDriverKind,
461 detail: Schema.String,
462 reason: Schema.optional(ProviderAdapterFailureReason),
463 cause: Schema.optional(Schema.Defect()),
464 payload: Schema.optional(Schema.Unknown),
465 },
466) {
467 override get message(): string {
468 return `${this.driver} provider protocol error: ${this.detail}.`;
469 }
470}
471
472export class ProviderAdapterSessionStoppedError extends Schema.TaggedError<ProviderAdapterSessionStoppedError>()(
473 "ProviderAdapterSessionStoppedError",
474 { driver: ProviderDriverKind },
475) {
476 override get message(): string {
477 return `No running ${this.driver} session owns this thread.`;
478 }
479}
480
481export const ProviderAdapterV2Error = Schema.Union([
482 ProviderAdapterCapabilitiesError,
483 ProviderAdapterOpenSessionError,
484 ProviderAdapterCloseSessionError,
485 ProviderAdapterResumeThreadError,
486 ProviderAdapterThreadInUseError,
487 ProviderAdapterEnsureThreadError,
488 ProviderAdapterReadThreadSnapshotError,
489 ProviderAdapterRollbackThreadError,
490 ProviderAdapterForkThreadError,
491 ProviderAdapterTurnStartError,
492 ProviderAdapterSteerRunUnsupportedError,
493 ProviderAdapterSteerRunError,
494 ProviderAdapterSteerDeclinedError,
495 ProviderAdapterInterruptError,
496 ProviderAdapterInterruptTimeoutError,
497 ProviderAdapterRuntimeRequestResponseError,
498 ProviderAdapterEventStreamError,
499 ProviderAdapterProtocolError,
500 ProviderAdapterSessionStoppedError,
501]);
502export type ProviderAdapterV2Error = typeof ProviderAdapterV2Error.Type;
503
504export interface ProviderAdapterV2OpenSessionInput {
505 readonly threadId: ThreadId;
506 readonly providerSessionId: ProviderSessionId;
507 readonly modelSelection: ModelSelection;
508 readonly runtimePolicy: ProviderAdapterV2RuntimePolicy;
509 /** Labs flag, read live: emit `prompt_suggestion.updated` after completed turns, where the provider can. */
510 readonly promptSuggestions?: Effect.Effect<boolean>;
511 readonly resumeFromSession?: OrchestrationV2ProviderSession;
512 readonly initialNativeThreadId?: string;
513 readonly initialProviderItemIdentityVersion?: 2;
514}
515
516export interface ProviderAdapterV2EnsureThreadInput {
517 readonly threadId: ThreadId;
518 readonly title?: string;
519 readonly modelSelection: ModelSelection;
520 readonly runtimePolicy: ProviderAdapterV2RuntimePolicy;
521 readonly providerSessionId?: ProviderSessionId;
522 readonly existingProviderThread?: OrchestrationV2ProviderThread;
523 readonly forceNewNativeThread?: boolean;
524}
525
526export interface ProviderAdapterV2TurnInput {
527 readonly appThread: OrchestrationV2AppThread;
528 readonly threadId: ThreadId;
529 readonly runId: RunId;
530 readonly runOrdinal: number;
531 readonly nativeThreadHasTurns?: boolean;
532 readonly providerTurnOrdinal: number;
533 readonly restartContinuationOfRunId?: RunId;
534 readonly attemptId: RunAttemptId;
535 readonly rootNodeId: NodeId;
536 readonly providerThread: OrchestrationV2ProviderThread;
537 readonly message: ProviderAdapterV2TurnMessage;
538 readonly modelSelection: ModelSelection;
539 readonly runtimePolicy: ProviderAdapterV2RuntimePolicy;
540 readonly appContext?: ReadonlyArray<{ readonly key: string; readonly text: string }>;
541}
542
543export interface ProviderAdapterV2SteerInput {
544 readonly threadId: ThreadId;
545 readonly runId: RunId;
546 readonly providerThread: OrchestrationV2ProviderThread;
547 readonly providerTurnId: ProviderTurnId;
548 readonly message: ProviderAdapterV2TurnMessage;
549 /** The selection sent with the message when it differs from the running turn's. */
550 readonly modelSelection?: ModelSelection;
551}
552
553export interface ProviderAdapterV2InterruptInput {
554 readonly providerThread: OrchestrationV2ProviderThread;
555 readonly providerTurnId: ProviderTurnId;
556 readonly requestRuntimeRestart?: boolean;
557}
558
559export interface ProviderAdapterV2RuntimeRequestResponseInput {
560 readonly requestId: RuntimeRequestId;
561 readonly decision?: ProviderApprovalDecision;
562 readonly feedback?: string;
563 readonly answers?: ProviderUserInputAnswers;
564}
565
566export interface ProviderAdapterV2ThreadSnapshot {
567 readonly providerThread: OrchestrationV2ProviderThread;
568 readonly providerTurns: ReadonlyArray<OrchestrationV2ProviderTurn>;
569 readonly messages: ReadonlyArray<OrchestrationV2ConversationMessage>;
570 /** Ids in `messages` that hold the agent's reasoning, not its reply. */
571 readonly reasoningMessageIds?: ReadonlyArray<OrchestrationV2ConversationMessage["id"]>;
572 readonly runtimeRequests: ReadonlyArray<OrchestrationV2RuntimeRequest>;
573 readonly providerPayload?: unknown;
574}
575
576export interface ProviderAdapterV2ReadThreadSnapshotInput {
577 readonly providerThread: OrchestrationV2ProviderThread;
578}
579
580export type ProviderAdapterV2RollbackTarget =
581 | {
582 readonly type: "thread_start";
583 readonly checkpointId: CheckpointId;
584 readonly appRunOrdinal: 0;
585 }
586 | {
587 readonly type: "provider_turn";
588 readonly checkpointId: CheckpointId;
589 readonly appRunOrdinal: number;
590 readonly providerTurn: OrchestrationV2ProviderTurn;
591 readonly message?: Pick<OrchestrationV2ConversationMessage, "role" | "text" | "attachments">;
592 };
593
594export interface ProviderAdapterV2RollbackThreadInput {
595 readonly providerThread: OrchestrationV2ProviderThread;
596 readonly target: ProviderAdapterV2RollbackTarget;
597 readonly providerThreadTurns: ReadonlyArray<OrchestrationV2ProviderTurn>;
598}
599
600export interface ProviderAdapterV2ForkThreadInput {
601 readonly sourceProviderThread: OrchestrationV2ProviderThread;
602 readonly sourceProviderTurns?: ReadonlyArray<OrchestrationV2ProviderTurn>;
603 readonly providerTurnId?: ProviderTurnId;
604 readonly targetThreadId: ThreadId;
605 readonly ownerNodeId?: NodeId;
606 readonly modelSelection?: ModelSelection;
607 readonly runtimePolicy?: ProviderAdapterV2RuntimePolicy;
608}
609
610export interface ProviderAdapterV2EventSubscription {
611 readonly events: Stream.Stream<ProviderAdapterV2Event, ProviderAdapterV2Error>;
612 readonly close: Effect.Effect<void>;
613}
614
615export interface ProviderAdapterV2HistoricalContext {
616 readonly messages: ReadonlyArray<OrchestrationV2HistoricalMessage>;
617 readonly context: string;
618}
619
620export interface ProviderAdapterV2McpTool {
621 readonly name: string;
622 // Apps reject a `hostContext.toolInfo.tool` without one.
623 readonly inputSchema: unknown;
624 readonly _meta?: unknown;
625 readonly annotations?: unknown;
626}
627
628export interface ProviderAdapterV2McpCallToolResult {
629 readonly content: ReadonlyArray<unknown>;
630 readonly structuredContent?: unknown;
631 readonly isError?: boolean;
632 readonly _meta?: unknown;
633}
634
635export interface ProviderAdapterV2McpApps {
636 readonly listTools: (input: {
637 readonly providerThread: OrchestrationV2ProviderThread;
638 readonly server: string;
639 }) => Effect.Effect<ReadonlyArray<ProviderAdapterV2McpTool>, ProviderAdapterV2Error>;
640 readonly callTool: (input: {
641 readonly providerThread: OrchestrationV2ProviderThread;
642 readonly server: string;
643 readonly tool: string;
644 readonly arguments: Record<string, unknown>;
645 }) => Effect.Effect<ProviderAdapterV2McpCallToolResult, ProviderAdapterV2Error>;
646 readonly readResource: (input: {
647 readonly providerThread: OrchestrationV2ProviderThread;
648 readonly server: string;
649 readonly uri: string;
650 }) => Effect.Effect<{ readonly contents: ReadonlyArray<unknown> }, ProviderAdapterV2Error>;
651}
652
653export type ProviderAdapterV2SubagentOutcome = {
654 readonly status: "completed" | "failed" | "cancelled";
655 readonly completedAt: NonNullable<OrchestrationV2Subagent["completedAt"]>;
656 readonly result: OrchestrationV2Subagent["result"];
657};
658
659export interface ProviderAdapterV2SessionRuntime {
660 readonly instanceId: ProviderInstanceId;
661 readonly driver: ProviderDriverKind;
662 readonly providerSessionId: ProviderSessionId;
663 readonly providerSession: OrchestrationV2ProviderSession;
664 /** False keeps conversations alive when the provider cannot restore them after a restart. */
665 readonly canResumeAfterRestart?: boolean;
666 readonly events: Stream.Stream<ProviderAdapterV2Event, ProviderAdapterV2Error>;
667 /** Manager runtimes broadcast independent subscriptions; adapters may expose only a single-consumer stream. */
668 readonly subscribeEvents?: Effect.Effect<ProviderAdapterV2EventSubscription>;
669 readonly hasPendingBackgroundWork?: Effect.Effect<boolean>;
670 /** Root-subscription probe; replacement checks opt in to subagents owned by the live process. */
671 readonly hasPendingBackgroundWorkForThread?: (
672 providerThread: OrchestrationV2ProviderThread,
673 options?: { readonly includeSubagents?: boolean },
674 ) => Effect.Effect<boolean>;
675 /** Set only if all session background tasks end with its process; otherwise threads must keep Waiting. */
676 readonly backgroundTasksEndWithProcess?: true;
677 readonly getModelContextWindow?: (
678 modelSelection: ModelSelection,
679 cwd?: string | null,
680 ) => number | undefined;
681 readonly canReuseContextUsage?: (previous: ModelSelection, next: ModelSelection) => boolean;
682 /** Excludes turn-scoped options from native thread load matching. */
683 readonly normalizeThreadLoadModelSelection?: (selection: ModelSelection) => ModelSelection;
684 readonly ensureThread: (
685 input: ProviderAdapterV2EnsureThreadInput,
686 ) => Effect.Effect<OrchestrationV2ProviderThread, ProviderAdapterV2Error>;
687 readonly resumeThread: (input: {
688 readonly providerThread: OrchestrationV2ProviderThread;
689 readonly threadId?: ThreadId;
690 readonly modelSelection?: ModelSelection;
691 readonly runtimePolicy?: ProviderAdapterV2RuntimePolicy;
692 }) => Effect.Effect<OrchestrationV2ProviderThread, ProviderAdapterV2Error>;
693 /** False means the native protocol explicitly does not support history injection. */
694 readonly injectHistory?: (
695 input: ProviderAdapterV2HistoricalContext & {
696 readonly providerThread: OrchestrationV2ProviderThread;
697 },
698 ) => Effect.Effect<boolean, ProviderAdapterV2Error>;
699 readonly startTurn: (
700 input: ProviderAdapterV2TurnInput,
701 ) => Effect.Effect<void, ProviderAdapterV2Error>;
702 readonly compactThread?: (
703 input: ProviderAdapterV2TurnInput,
704 ) => Effect.Effect<void, ProviderAdapterV2Error>;
705 readonly steerTurn: (
706 input: ProviderAdapterV2SteerInput,
707 ) => Effect.Effect<void, ProviderAdapterV2Error>;
708 /** Applies a thread's new selection to its running process where the provider can; otherwise it waits for the next turn. */
709 readonly applyModelSelection?: (input: {
710 readonly providerThread: OrchestrationV2ProviderThread;
711 readonly modelSelection: ModelSelection;
712 }) => Effect.Effect<void, ProviderAdapterV2Error>;
713 readonly stopTask?: (input: {
714 readonly providerThread: OrchestrationV2ProviderThread;
715 readonly taskId: string;
716 }) => Effect.Effect<void, ProviderAdapterV2Error>;
717 /**
718 * Marks or unmarks background tasks the agent will resume from, using the ids the agent saw.
719 * Ids not running on this provider thread come back unmatched and change nothing.
720 */
721 readonly awaitBackgroundTasks?: (input: {
722 readonly providerThread: OrchestrationV2ProviderThread;
723 readonly taskIds: ReadonlyArray<string>;
724 readonly awaited: boolean;
725 }) => Effect.Effect<{ readonly matched: ReadonlyArray<string> }, ProviderAdapterV2Error>;
726 readonly interruptTurn: (
727 input: ProviderAdapterV2InterruptInput,
728 ) => Effect.Effect<void, ProviderAdapterV2Error>;
729 readonly stopSubagent?: (input: {
730 readonly providerThread: OrchestrationV2ProviderThread;
731 readonly nativeTaskId: string;
732 /** A delayed Stop must not interrupt a task resumed after this request. */
733 readonly requestedAt?: NonNullable<OrchestrationV2Subagent["startedAt"]>;
734 }) => Effect.Effect<void | ProviderAdapterV2SubagentOutcome, ProviderAdapterV2Error>;
735 readonly unloadThread?: (input: {
736 readonly providerThread: OrchestrationV2ProviderThread;
737 /** Also hide it from the provider's own clients where it can; resuming it must restore it. */
738 readonly archive?: boolean;
739 }) => Effect.Effect<void, ProviderAdapterV2Error>;
740 readonly respondToRuntimeRequest: (
741 input: ProviderAdapterV2RuntimeRequestResponseInput,
742 ) => Effect.Effect<void, ProviderAdapterV2Error>;
743 readonly readThreadSnapshot: (
744 input: ProviderAdapterV2ReadThreadSnapshotInput,
745 ) => Effect.Effect<ProviderAdapterV2ThreadSnapshot, ProviderAdapterV2Error>;
746 readonly uploadFeedback?: (input: {
747 readonly providerThread: OrchestrationV2ProviderThread;
748 readonly reason?: string;
749 }) => Effect.Effect<{ readonly feedbackId: string }, ProviderAdapterV2Error>;
750 /** Absent means non-interactive apps with plain tool rows. */
751 readonly mcpApps?: ProviderAdapterV2McpApps;
752 readonly rollbackThread: (
753 input: ProviderAdapterV2RollbackThreadInput,
754 ) => Effect.Effect<ProviderAdapterV2ThreadSnapshot, ProviderAdapterV2Error>;
755 readonly forkThread: (
756 input: ProviderAdapterV2ForkThreadInput,
757 ) => Effect.Effect<OrchestrationV2ProviderThread, ProviderAdapterV2Error>;
758}
759
760export class ProviderAdapterV2 extends Context.Service<
761 ProviderAdapterV2,
762 {
763 readonly instanceId: ProviderInstanceId;
764 readonly driver: ProviderDriverKind;
765 readonly getCapabilities: () => Effect.Effect<
766 OrchestrationV2ProviderCapabilities,
767 ProviderAdapterV2Error
768 >;
769 readonly planSelectionTransition: (
770 input: ProviderSelectionTransitionInput,
771 ) => Effect.Effect<ProviderSelectionTransitionPlan, ProviderAdapterV2Error>;
772 readonly openSession: (
773 input: ProviderAdapterV2OpenSessionInput,
774 ) => Effect.Effect<ProviderAdapterV2SessionRuntime, ProviderAdapterV2Error, Scope.Scope>;
775 }
776>()("@t3tools/provider-core/server/ProviderAdapter/ProviderAdapterV2") {}