@opencode-ai/ai 0.0.0-next-16745 → 0.0.0-next-16772

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -4,7 +4,7 @@ import { Endpoint, type EndpointPatch } from "./endpoint";
4
4
  import { RequestExecutor } from "./executor";
5
5
  import { Framing } from "./framing";
6
6
  import { HttpTransport } from "./transport";
7
- import type { HttpRequestTransform, Transport, TransportRuntime } from "./transport";
7
+ import type { HttpMiddleware, Transport, TransportRuntime } from "./transport";
8
8
  import type { Protocol } from "./protocol";
9
9
  import type { ProtocolID, ProviderOptions } from "../schema";
10
10
  import { AIError, GenerationOptions, HttpOptions, LLMRequest, LLMResponse, LanguageModel, LanguageModelLimits, LLMEvent, ProviderID } from "../schema";
@@ -63,7 +63,7 @@ export interface Interface {
63
63
  readonly generate: GenerateMethod;
64
64
  }
65
65
  export interface StreamOptions {
66
- readonly transform?: HttpRequestTransform;
66
+ readonly http?: HttpMiddleware;
67
67
  }
68
68
  export interface StreamMethod {
69
69
  (request: LLMRequest, options?: StreamOptions): Stream.Stream<LLMEvent, AIError>;
@@ -112,7 +112,7 @@ function makeFromTransport(input) {
112
112
  auth: routeInput.auth ?? Auth.none,
113
113
  encodeBody,
114
114
  headers: routeInput.headers,
115
- transform: options?.transform,
115
+ middleware: options?.http,
116
116
  }),
117
117
  streamPrepared: (prepared, request, runtime) => {
118
118
  const route = `${request.model.provider}/${request.model.route.id}`;
@@ -2,8 +2,10 @@ import { Context, Effect, Layer } from "effect";
2
2
  import { HttpClient, HttpClientRequest, HttpClientResponse } from "effect/unstable/http";
3
3
  import { AIError } from "../schema";
4
4
  export interface Interface {
5
- readonly execute: (request: HttpClientRequest.HttpClientRequest) => Effect.Effect<HttpClientResponse.HttpClientResponse, AIError>;
5
+ readonly execute: (request: HttpClientRequest.HttpClientRequest, middleware?: HttpMiddleware) => Effect.Effect<HttpClientResponse.HttpClientResponse, AIError>;
6
6
  }
7
+ export type HttpHandler = (request: HttpClientRequest.HttpClientRequest) => Effect.Effect<HttpClientResponse.HttpClientResponse, Error>;
8
+ export type HttpMiddleware = (request: HttpClientRequest.HttpClientRequest, handler: HttpHandler) => Effect.Effect<HttpClientResponse.HttpClientResponse, Error>;
7
9
  declare const Service_base: Context.ServiceClass<Service, "@opencode/AI/RequestExecutor", Interface>;
8
10
  export declare class Service extends Service_base {
9
11
  }
@@ -197,7 +197,7 @@ const toHttpError = (redactedNames) => (error) => {
197
197
  return transportError({ message: error.message, kind: "Timeout" });
198
198
  }
199
199
  if (!HttpClientError.isHttpClientError(error)) {
200
- return transportError({ message: "HTTP transport failed" });
200
+ return transportError({ message: error instanceof Error ? error.message : "HTTP transport failed" });
201
201
  }
202
202
  const request = "request" in error ? error.request : undefined;
203
203
  if (error.reason._tag === "TransportError") {
@@ -215,11 +215,16 @@ const toHttpError = (redactedNames) => (error) => {
215
215
  };
216
216
  export const layer = Layer.effect(Service, Effect.gen(function* () {
217
217
  const http = yield* HttpClient.HttpClient;
218
- const executeOnce = (request) => Effect.gen(function* () {
218
+ const executeOnce = (request, middleware) => Effect.gen(function* () {
219
219
  const redactedNames = yield* Headers.CurrentRedactedNames;
220
- return yield* http
221
- .execute(request)
222
- .pipe(Effect.mapError(toHttpError(redactedNames)), Effect.flatMap(statusError(request, redactedNames)));
220
+ if (!middleware)
221
+ return yield* http
222
+ .execute(request)
223
+ .pipe(Effect.mapError(toHttpError(redactedNames)), Effect.flatMap(statusError(request, redactedNames)));
224
+ const response = yield* middleware(request, (input) => http
225
+ .execute(input)
226
+ .pipe(Effect.mapError((cause) => (cause instanceof Error ? cause : new Error(String(cause)))))).pipe(Effect.mapError(toHttpError(redactedNames)));
227
+ return yield* statusError(response.request, redactedNames)(response);
223
228
  });
224
229
  return Service.of({
225
230
  execute: executeOnce,
@@ -13,4 +13,4 @@ export type { ApiKeyMode, AuthOverride, ProviderAuthOption } from "./auth-option
13
13
  export type { Definition as EndpointFn, EndpointInput } from "./endpoint";
14
14
  export type { Definition as FramingDef } from "./framing";
15
15
  export type { Protocol as ProtocolDef } from "./protocol";
16
- export type { HttpRequest, HttpRequestTransform, Transport as TransportDef, TransportRuntime } from "./transport";
16
+ export type { HttpHandler, HttpMiddleware, Transport as TransportDef, TransportRuntime } from "./transport";
@@ -1,7 +1,7 @@
1
1
  import { Effect } from "effect";
2
2
  import { Headers, HttpClientRequest } from "effect/unstable/http";
3
3
  import { Framing } from "../framing";
4
- import type { Transport, TransportPrepareInput } from "./index";
4
+ import type { HttpMiddleware, Transport, TransportPrepareInput } from "./index";
5
5
  export type JsonRequestInput<Body> = TransportPrepareInput<Body>;
6
6
  export interface JsonRequestParts<Body = unknown> {
7
7
  readonly url: string;
@@ -12,6 +12,7 @@ export interface JsonRequestParts<Body = unknown> {
12
12
  export interface HttpPrepared<Frame> {
13
13
  readonly request: HttpClientRequest.HttpClientRequest;
14
14
  readonly framing: Framing.Definition<Frame>;
15
+ readonly middleware?: HttpMiddleware;
15
16
  }
16
17
  export declare const jsonRequestParts: <Body>(input: JsonRequestInput<Body>) => Effect.Effect<{
17
18
  url: string;
@@ -41,19 +41,19 @@ export const httpJson = (input) => ({
41
41
  with: (patch) => httpJson({ ...input, ...patch }),
42
42
  prepare: (prepareInput) => Effect.gen(function* () {
43
43
  const parts = yield* jsonRequestParts({ ...prepareInput });
44
- const request = { url: parts.url, method: "POST", headers: { ...parts.headers }, body: parts.bodyText };
45
- yield* (prepareInput.transform?.(request) ?? Effect.void);
44
+ const request = ProviderShared.jsonPost({
45
+ url: parts.url,
46
+ body: parts.bodyText,
47
+ headers: parts.headers,
48
+ });
46
49
  return {
47
- request: ProviderShared.jsonPost({
48
- url: request.url,
49
- body: request.body ?? "",
50
- headers: Headers.fromInput(request.headers),
51
- }),
50
+ request,
52
51
  framing: input.framing,
52
+ middleware: prepareInput.middleware,
53
53
  };
54
54
  }),
55
55
  frames: (prepared, request, runtime) => Stream.unwrap(runtime.http
56
- .execute(prepared.request)
56
+ .execute(prepared.request, prepared.middleware)
57
57
  .pipe(Effect.map((response) => prepared.framing.frame(response.stream.pipe(Stream.mapError((error) => ProviderShared.eventError(`${request.model.provider}/${request.model.route.id}`, `Failed to read ${request.model.provider}/${request.model.route.id} stream`, ProviderShared.errorText(error)))))))),
58
58
  });
59
59
  export const sseJson = {
@@ -1,20 +1,13 @@
1
1
  import type { Effect, Stream } from "effect";
2
2
  import { Endpoint } from "../endpoint";
3
3
  import { Auth } from "../auth";
4
- import type { Interface as RequestExecutorInterface } from "../executor";
4
+ import type { HttpMiddleware, Interface as RequestExecutorInterface } from "../executor";
5
5
  import type { Interface as WebSocketExecutorInterface } from "./websocket";
6
6
  import type { AIError, LLMRequest } from "../../schema";
7
7
  export interface TransportRuntime {
8
8
  readonly http: RequestExecutorInterface;
9
9
  readonly webSocket?: WebSocketExecutorInterface;
10
10
  }
11
- export interface HttpRequest {
12
- url: string;
13
- readonly method: string;
14
- headers: Record<string, string>;
15
- body: string | undefined;
16
- }
17
- export type HttpRequestTransform = (request: HttpRequest) => Effect.Effect<void>;
18
11
  export interface Transport<Body, Prepared, Frame> {
19
12
  readonly id: string;
20
13
  readonly prepare: (input: TransportPrepareInput<Body>) => Effect.Effect<Prepared, AIError>;
@@ -29,7 +22,8 @@ export interface TransportPrepareInput<Body> {
29
22
  readonly headers?: (input: {
30
23
  readonly request: LLMRequest;
31
24
  }) => Record<string, string>;
32
- readonly transform?: HttpRequestTransform;
25
+ readonly middleware?: HttpMiddleware;
33
26
  }
34
27
  export * as HttpTransport from "./http";
28
+ export type { HttpHandler, HttpMiddleware } from "../executor";
35
29
  export { WebSocketExecutor, WebSocketTransport } from "./websocket";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/package.json",
3
- "version": "0.0.0-next-16745",
3
+ "version": "0.0.0-next-16772",
4
4
  "name": "@opencode-ai/ai",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -30,7 +30,7 @@
30
30
  "devDependencies": {
31
31
  "@clack/prompts": "1.0.0-alpha.1",
32
32
  "@effect/platform-node": "4.0.0-beta.101",
33
- "@opencode-ai/http-recorder": "0.0.0-next-16745",
33
+ "@opencode-ai/http-recorder": "0.0.0-next-16772",
34
34
  "@tsconfig/bun": "1.0.9",
35
35
  "@types/bun": "1.3.13",
36
36
  "@typescript/native-preview": "7.0.0-dev.20251207.1",
@@ -39,7 +39,7 @@
39
39
  "dependencies": {
40
40
  "@smithy/eventstream-codec": "4.2.14",
41
41
  "@smithy/util-utf8": "4.2.2",
42
- "@opencode-ai/schema": "0.0.0-next-16745",
42
+ "@opencode-ai/schema": "0.0.0-next-16772",
43
43
  "aws4fetch": "1.0.20",
44
44
  "effect": "4.0.0-beta.101",
45
45
  "google-auth-library": "10.5.0"