1/**
2 * ProviderHost — what a provider package may ask of the server it runs in.
3 *
4 * Drivers and adapters run inside a T3 server but must not import it. The
5 * server provides this one service; everything a provider needs from its
6 * environment (directories, server settings, background demand, attachment
7 * files) goes through it, so a provider package depends only on
8 * `@t3tools/provider-core` and its own protocol code.
9 *
10 * @module provider-core/server/host
11 */
12import type {
13 BackgroundScope,
14 ChatAttachment,
15 ServerSettings,
16 ServerSettingsError,
17} from "@t3tools/contracts";
18import * as Context from "effect/Context";
19import type * as Effect from "effect/Effect";
20import type * as Option from "effect/Option";
21import type * as Scope from "effect/Scope";
22import type * as Stream from "effect/Stream";
23
24import type { ProviderCredentialError } from "./errors.ts";
25
26export interface ProviderHostPaths {
27 /** Directory the server was started in; the fallback cwd for sessions without a project. */
28 readonly cwd: string;
29 /** T3 home. */
30 readonly baseDir: string;
31 /** Server-owned state directory under the T3 home. */
32 readonly stateDir: string;
33 /** Scratch space for provider probes and generated helper files. */
34 readonly providerStatusCacheDir: string;
35 /** Where chat attachments are stored; agents that sandbox file reads must be granted it. */
36 readonly attachmentsDir: string;
37}
38
39/** One opaque credential blob in the server's secret store. Only the provider decodes it. */
40export interface ProviderCredentials {
41 /** Instances whose bindings are equal share these credentials. */
42 readonly binding: { readonly owner: "t3"; readonly key: string };
43 readonly get: Effect.Effect<Option.Option<Uint8Array>, ProviderCredentialError>;
44 readonly set: (credentials: Uint8Array) => Effect.Effect<void, ProviderCredentialError>;
45 readonly remove: Effect.Effect<void, ProviderCredentialError>;
46}
47
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") {}