Open History to see the last commit
1// Context tags are runtime singletons; drivers need multiple instances.
2import type {
3 ProviderConsumeResetCreditOutcome,
4 AcpRegistryListSessionsResult,
5 AcpRegistryListProvidersResult,
6 AcpRegistryOperationError,
7 AcpRegistrySetProviderInput,
8 ProviderDriverKind,
9 ResolvedProviderInstanceEnvironment,
10 ProviderInstanceId,
11 ServerProvider,
12 ServerProviderWorkspaceSnapshot,
13} from "@t3tools/contracts";
14import type * as Effect from "effect/Effect";
15import type * as Schema from "effect/Schema";
16import type * as Scope from "effect/Scope";
17
18import type * as ProviderAdapter from "./ProviderAdapter.ts";
19import type { ProviderAuthController } from "./auth.ts";
20import type { ProviderDriverError } from "./errors.ts";
21import type { ManagedServerProvider } from "./snapshot.ts";
22import type { ProviderTextGeneration } from "./textGeneration.ts";
23import type { ProviderUsageReader } from "./usage.ts";
24
25export interface ProviderDriverMetadata {
26 readonly displayName: string;
27 /** Defaults to true; false rejects multiple instances of a global resource. */
28 readonly supportsMultipleInstances?: boolean;
29 readonly hasDefaultInstance?: boolean;
30}
31
32export type ProviderWorkspaceSnapshot = ServerProvider &
33 Pick<ServerProviderWorkspaceSnapshot, "slashCommandsPending" | "optionDescriptors"> & {
34 /** Set only when the directory's catalog differs from the machine snapshot's. */
35 readonly workspaceModels?: ServerProviderWorkspaceSnapshot["models"];
36 /** The directory's catalog could not be read, so keep the models found before. */
37 readonly workspaceModelsPending?: boolean;
38 };
39
40export interface ProviderInstance {
41 readonly instanceId: ProviderInstanceId;
42 readonly driverKind: ProviderDriverKind;
43 readonly continuationIdentity: ProviderContinuationIdentity;
44 readonly displayName: string | undefined;
45 readonly accentColor?: string | undefined;
46 readonly enabled: boolean;
47 readonly snapshot: ManagedServerProvider;
48 readonly snapshotForCwd?: (
49 cwd: string,
50 ) => Effect.Effect<ProviderWorkspaceSnapshot, ProviderDriverError>;
51 readonly refreshModels?: () => Effect.Effect<void, ProviderDriverError>;
52 readonly invalidateCaches?: Effect.Effect<void>;
53 /** Moves a long-lived service onto a newly updated binary before its version is read back. */
54 readonly afterUpdate?: Effect.Effect<void, ProviderDriverError>;
55 readonly consumeResetCredit?: (
56 creditId: string | undefined,
57 ) => Effect.Effect<ProviderConsumeResetCreditOutcome, ProviderDriverError>;
58 readonly orchestrationAdapter: ProviderAdapter.ProviderAdapterV2["Service"];
59 readonly textGeneration: ProviderTextGeneration;
60 readonly auth?: ProviderAuthController;
61 readonly acpSessionManagement?: {
62 readonly listSessions: (input: {
63 readonly cwd: string;
64 readonly cursor?: string;
65 }) => Effect.Effect<AcpRegistryListSessionsResult, AcpRegistryOperationError>;
66 readonly logout: (cwd: string) => Effect.Effect<void, AcpRegistryOperationError>;
67 readonly deleteSession: (input: {
68 readonly cwd: string;
69 readonly sessionId: string;
70 }) => Effect.Effect<void, AcpRegistryOperationError>;
71 readonly listProviders: (
72 cwd: string,
73 ) => Effect.Effect<AcpRegistryListProvidersResult, AcpRegistryOperationError>;
74 readonly setProvider: (
75 input: Omit<AcpRegistrySetProviderInput, "instanceId" | "projectId"> & {
76 readonly cwd: string;
77 },
78 ) => Effect.Effect<void, AcpRegistryOperationError>;
79 readonly disableProvider: (input: {
80 readonly cwd: string;
81 readonly providerId: string;
82 }) => Effect.Effect<void, AcpRegistryOperationError>;
83 };
84}
85
86export interface ProviderContinuationIdentity {
87 readonly driverKind: ProviderDriverKind;
88 readonly continuationKey: string;
89}
90
91export function defaultProviderContinuationIdentity(input: {
92 readonly driverKind: ProviderDriverKind;
93 readonly instanceId: ProviderInstanceId;
94}): ProviderContinuationIdentity {
95 return {
96 driverKind: input.driverKind,
97 continuationKey: `${input.driverKind}:instance:${input.instanceId}`,
98 };
99}
100
101export interface ProviderDriverCreateInput<Config> {
102 readonly instanceId: ProviderInstanceId;
103 readonly displayName: string | undefined;
104 readonly accentColor?: string | undefined;
105 readonly environment: ResolvedProviderInstanceEnvironment;
106 readonly enabled: boolean;
107 readonly config: Config;
108}
109
110/** create calls with different instanceId/config must share no mutable state. */
111export interface ProviderDriver<Config, R = never, UsageR = never> {
112 readonly driverKind: ProviderDriverKind;
113 readonly metadata: ProviderDriverMetadata;
114 // Codec keeps DecodingServices = never; Schema would widen it through AnyProviderDriver.
115 readonly configSchema: Schema.Codec<Config, unknown>;
116 readonly defaultConfig: () => Config;
117 /** Failures become unavailable snapshots; never throw defects. */
118 readonly create: (
119 input: ProviderDriverCreateInput<Config>,
120 ) => Effect.Effect<ProviderInstance, ProviderDriverError, R | Scope.Scope>;
121 readonly usage?: ProviderUsageReader<Config, UsageR>;
122}
123
124export type AnyProviderDriver<R = never, UsageR = unknown> = ProviderDriver<any, R, UsageR>;
125
126export type ProviderUsageReaderEnv<Driver> =
127 Driver extends ProviderDriver<any, any, infer UsageR> ? UsageR : never;