@opencode/ai 0.0.0-dev-19444 → 0.0.0-dev-19446
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.
- package/dist/protocols/open-responses-channel.d.ts +2 -0
- package/dist/protocols/open-responses-channel.js +2 -2
- package/dist/protocols/open-responses-continuation.d.ts +7 -3
- package/dist/protocols/open-responses-continuation.js +8 -6
- package/dist/protocols/open-responses.d.ts +72 -0
- package/dist/protocols/open-responses.js +9 -0
- package/dist/providers/xai.js +4 -0
- package/package.json +3 -3
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Headers } from "effect/unstable/http";
|
|
2
2
|
import { HttpTransport, type Transport, type WebSocketChannelDriver } from "../route/transport/index.js";
|
|
3
|
+
import { OpenResponsesContinuation } from "./open-responses-continuation.js";
|
|
3
4
|
export interface Options {
|
|
4
5
|
readonly id: string;
|
|
5
6
|
readonly name: string;
|
|
@@ -7,6 +8,7 @@ export interface Options {
|
|
|
7
8
|
readonly enabled?: (url: string) => boolean;
|
|
8
9
|
readonly url?: (url: string) => string;
|
|
9
10
|
readonly headers?: (headers: Headers.Headers) => Headers.Headers;
|
|
11
|
+
readonly continuation?: OpenResponsesContinuation.Shape;
|
|
10
12
|
}
|
|
11
13
|
export interface Prepared {
|
|
12
14
|
readonly http: HttpTransport.HttpPrepared<string>;
|
|
@@ -10,7 +10,6 @@ const WebSocketResponseCreate = Schema.StructWithRest(Schema.Struct({ type: Sche
|
|
|
10
10
|
]);
|
|
11
11
|
const decodeMessage = ProviderShared.validateWith(Schema.decodeUnknownEffect(WebSocketResponseCreate));
|
|
12
12
|
const encodeMessage = Schema.encodeSync(Schema.fromJsonString(WebSocketResponseCreate));
|
|
13
|
-
const decodeEvent = Schema.decodeUnknownEffect(OpenResponses.protocol.stream.event);
|
|
14
13
|
const message = (body) => Effect.gen(function* () {
|
|
15
14
|
if (!ProviderShared.isRecord(body))
|
|
16
15
|
return yield* ProviderShared.invalidRequest("Open Responses WebSocket body must be a JSON object");
|
|
@@ -28,7 +27,7 @@ const driver = (options, body) => {
|
|
|
28
27
|
return { message: body, mode: "full" };
|
|
29
28
|
}),
|
|
30
29
|
observe: (_create, frame) => Effect.gen(function* () {
|
|
31
|
-
const event = yield*
|
|
30
|
+
const event = yield* OpenResponses.decodeChannelEvent(frame).pipe(Effect.mapError((cause) => ProviderShared.eventError(options.id, `Invalid ${options.name} WebSocket event`, frame, cause)));
|
|
32
31
|
if (terminal)
|
|
33
32
|
return yield* ProviderShared.eventError(options.id, `${options.name} emitted ${event.type} after a terminal event`, frame);
|
|
34
33
|
if (event.type === "error") {
|
|
@@ -97,6 +96,7 @@ export const transport = (options) => {
|
|
|
97
96
|
request: create.request,
|
|
98
97
|
message: create.message,
|
|
99
98
|
base,
|
|
99
|
+
continuation: options.continuation,
|
|
100
100
|
}),
|
|
101
101
|
};
|
|
102
102
|
})
|
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
import type { WebSocketChannelDriver } from "../route/transport/index.js";
|
|
2
|
+
/**
|
|
3
|
+
* Fields to send next to `previous_response_id` on an incremental step, or undefined to send the step in full.
|
|
4
|
+
* Whether omitted fields carry over from the continued response is provider behavior the route must know.
|
|
5
|
+
*/
|
|
6
|
+
export type Shape = (request: Readonly<Record<string, unknown>>) => Readonly<Record<string, unknown>> | undefined;
|
|
2
7
|
export interface DriverInput {
|
|
3
8
|
readonly id: string;
|
|
4
9
|
readonly name: string;
|
|
5
10
|
readonly request: Readonly<Record<string, unknown>>;
|
|
6
11
|
readonly message: string;
|
|
7
12
|
readonly base: WebSocketChannelDriver;
|
|
13
|
+
readonly continuation?: Shape;
|
|
8
14
|
}
|
|
9
15
|
export declare const driver: (input: DriverInput) => WebSocketChannelDriver;
|
|
10
|
-
export
|
|
11
|
-
readonly driver: (input: DriverInput) => WebSocketChannelDriver;
|
|
12
|
-
};
|
|
16
|
+
export * as OpenResponsesContinuation from "./open-responses-continuation.js";
|
|
@@ -4,7 +4,6 @@ import * as ProviderShared from "./shared.js";
|
|
|
4
4
|
import { OpenResponses } from "./open-responses.js";
|
|
5
5
|
const PROTOCOL = "open-responses.websocket.v1";
|
|
6
6
|
const VERSION = 1;
|
|
7
|
-
const decodeEvent = Schema.decodeUnknownEffect(OpenResponses.protocol.stream.event);
|
|
8
7
|
const checkpointValue = (checkpoint) => {
|
|
9
8
|
if (checkpoint?.protocol !== PROTOCOL || !ProviderShared.isRecord(checkpoint.value))
|
|
10
9
|
return undefined;
|
|
@@ -104,21 +103,24 @@ const rejected = (observation, recovery) => ({
|
|
|
104
103
|
});
|
|
105
104
|
export const driver = (input) => {
|
|
106
105
|
const { previous_response_id: _previousResponseID, ...request } = input.request;
|
|
106
|
+
const shape = input.continuation ?? ((fields) => fields);
|
|
107
107
|
let output = [];
|
|
108
108
|
return {
|
|
109
109
|
create: (checkpoint) => Effect.sync(() => {
|
|
110
110
|
output = [];
|
|
111
111
|
const previous = checkpointValue(checkpoint);
|
|
112
|
-
|
|
113
|
-
|
|
112
|
+
// Ask the route first: diffing the whole history is wasted when it declines the continuation.
|
|
113
|
+
const fields = previous ? shape(request) : undefined;
|
|
114
|
+
const delta = previous && fields ? incremental(request, previous) : undefined;
|
|
115
|
+
if (!previous || !fields || !delta)
|
|
114
116
|
return { message: ProviderShared.encodeJson(request), mode: "full" };
|
|
115
117
|
return {
|
|
116
|
-
message: ProviderShared.encodeJson({ ...
|
|
118
|
+
message: ProviderShared.encodeJson({ ...fields, input: delta, previous_response_id: previous.responseID }),
|
|
117
119
|
mode: "incremental",
|
|
118
120
|
};
|
|
119
121
|
}),
|
|
120
122
|
observe: (create, frame) => Effect.gen(function* () {
|
|
121
|
-
const event = yield*
|
|
123
|
+
const event = yield* OpenResponses.decodeChannelEvent(frame).pipe(Effect.mapError((cause) => ProviderShared.eventError(input.id, `Invalid ${input.name} WebSocket event`, frame, cause)));
|
|
122
124
|
const observation = yield* input.base.observe(create, frame);
|
|
123
125
|
if (event.type === "response.output_item.done" && event.item)
|
|
124
126
|
output.push(event.item);
|
|
@@ -165,4 +167,4 @@ export const driver = (input) => {
|
|
|
165
167
|
}),
|
|
166
168
|
};
|
|
167
169
|
};
|
|
168
|
-
export
|
|
170
|
+
export * as OpenResponsesContinuation from "./open-responses-continuation.js";
|
|
@@ -653,6 +653,78 @@ export type Event = Schema.Schema.Type<typeof Event>;
|
|
|
653
653
|
export type NormalizedEvent = Event & {
|
|
654
654
|
readonly item?: OutputItem | null;
|
|
655
655
|
};
|
|
656
|
+
/**
|
|
657
|
+
* Decodes one WebSocket frame. xAI answers a rejected `response.create` with `{ "error": { "message", "type" } }` and no
|
|
658
|
+
* event type; that envelope reads as an error event so the failure classifies instead of failing decoding.
|
|
659
|
+
*/
|
|
660
|
+
export declare const decodeChannelEvent: (frame: string) => Effect.Effect<{
|
|
661
|
+
readonly [x: string]: unknown;
|
|
662
|
+
readonly type: string;
|
|
663
|
+
readonly status?: unknown;
|
|
664
|
+
readonly code?: string | null | undefined;
|
|
665
|
+
readonly message?: string | undefined;
|
|
666
|
+
readonly headers?: unknown;
|
|
667
|
+
readonly text?: string | undefined;
|
|
668
|
+
readonly error?: {
|
|
669
|
+
readonly type?: string | null | undefined;
|
|
670
|
+
readonly code?: string | null | undefined;
|
|
671
|
+
readonly message?: string | null | undefined;
|
|
672
|
+
readonly param?: string | null | undefined;
|
|
673
|
+
} | null | undefined;
|
|
674
|
+
readonly item?: {
|
|
675
|
+
readonly [x: string]: unknown;
|
|
676
|
+
readonly type: string;
|
|
677
|
+
readonly id?: string | undefined;
|
|
678
|
+
readonly name?: string | undefined;
|
|
679
|
+
readonly namespace?: string | undefined;
|
|
680
|
+
readonly arguments?: string | undefined;
|
|
681
|
+
readonly encrypted_content?: string | null | undefined;
|
|
682
|
+
readonly call_id?: string | undefined;
|
|
683
|
+
} | null | undefined;
|
|
684
|
+
readonly delta?: string | undefined;
|
|
685
|
+
readonly response?: {
|
|
686
|
+
readonly [x: string]: unknown;
|
|
687
|
+
readonly id?: string | undefined;
|
|
688
|
+
readonly output?: readonly {
|
|
689
|
+
readonly [x: string]: unknown;
|
|
690
|
+
readonly type: string;
|
|
691
|
+
readonly id?: string | undefined;
|
|
692
|
+
readonly name?: string | undefined;
|
|
693
|
+
readonly namespace?: string | undefined;
|
|
694
|
+
readonly arguments?: string | undefined;
|
|
695
|
+
readonly encrypted_content?: string | null | undefined;
|
|
696
|
+
readonly call_id?: string | undefined;
|
|
697
|
+
}[] | undefined;
|
|
698
|
+
readonly error?: {
|
|
699
|
+
readonly type?: string | null | undefined;
|
|
700
|
+
readonly code?: string | null | undefined;
|
|
701
|
+
readonly message?: string | null | undefined;
|
|
702
|
+
readonly param?: string | null | undefined;
|
|
703
|
+
} | null | undefined;
|
|
704
|
+
readonly usage?: {
|
|
705
|
+
readonly input_tokens?: number | undefined;
|
|
706
|
+
readonly output_tokens?: number | undefined;
|
|
707
|
+
readonly output_tokens_details?: {
|
|
708
|
+
readonly reasoning_tokens?: number | undefined;
|
|
709
|
+
} | null | undefined;
|
|
710
|
+
readonly total_tokens?: number | undefined;
|
|
711
|
+
readonly input_tokens_details?: {
|
|
712
|
+
readonly cached_tokens?: number | undefined;
|
|
713
|
+
readonly cache_write_tokens?: number | undefined;
|
|
714
|
+
} | null | undefined;
|
|
715
|
+
} | null | undefined;
|
|
716
|
+
readonly service_tier?: string | null | undefined;
|
|
717
|
+
readonly incomplete_details?: {
|
|
718
|
+
readonly reason?: string | undefined;
|
|
719
|
+
} | null | undefined;
|
|
720
|
+
} | undefined;
|
|
721
|
+
readonly arguments?: string | undefined;
|
|
722
|
+
readonly param?: string | null | undefined;
|
|
723
|
+
readonly status_code?: unknown;
|
|
724
|
+
readonly item_id?: string | undefined;
|
|
725
|
+
readonly output_index?: number | undefined;
|
|
726
|
+
readonly summary_index?: number | undefined;
|
|
727
|
+
}, Schema.SchemaError, never>;
|
|
656
728
|
export interface ProviderAdapter {
|
|
657
729
|
readonly id: string;
|
|
658
730
|
readonly name: string;
|
|
@@ -288,6 +288,15 @@ export const Event = Schema.StructWithRest(Schema.Struct({
|
|
|
288
288
|
status_code: Schema.optional(Schema.Unknown),
|
|
289
289
|
headers: Schema.optional(Schema.Unknown),
|
|
290
290
|
}), [Schema.Record(Schema.String, Schema.Unknown)]);
|
|
291
|
+
const decodeEventValue = Schema.decodeUnknownEffect(Event);
|
|
292
|
+
const decodeFrame = Schema.decodeUnknownEffect(ProviderShared.Json);
|
|
293
|
+
/**
|
|
294
|
+
* Decodes one WebSocket frame. xAI answers a rejected `response.create` with `{ "error": { "message", "type" } }` and no
|
|
295
|
+
* event type; that envelope reads as an error event so the failure classifies instead of failing decoding.
|
|
296
|
+
*/
|
|
297
|
+
export const decodeChannelEvent = (frame) => decodeFrame(frame).pipe(Effect.flatMap((value) => decodeEventValue(ProviderShared.isRecord(value) && value.type === undefined && ProviderShared.isRecord(value.error)
|
|
298
|
+
? { ...value, type: "error" }
|
|
299
|
+
: value)));
|
|
291
300
|
const BASE_ADAPTER = { id: ADAPTER, name: NAME };
|
|
292
301
|
// =============================================================================
|
|
293
302
|
// Request Lowering
|
package/dist/providers/xai.js
CHANGED
|
@@ -20,6 +20,10 @@ const responsesRoute = Route.make({
|
|
|
20
20
|
id: "openai-responses",
|
|
21
21
|
name: "xAI Responses",
|
|
22
22
|
rotateAfterMs: RESPONSES_WEBSOCKET_ROTATE_AFTER_MS,
|
|
23
|
+
// xAI continues a chain only from stored responses: with `store: false` (the route default) `previous_response_id`
|
|
24
|
+
// fails with "Response with id=… not found", so those steps are sent in full over the reused connection. It also
|
|
25
|
+
// rejects `instructions` next to `previous_response_id` and keeps the instructions of the response it continues.
|
|
26
|
+
continuation: ({ instructions: _instructions, ...request }) => (request.store === false ? undefined : request),
|
|
23
27
|
}),
|
|
24
28
|
defaults: { providerOptions: { store: false, include: ["reasoning.encrypted_content"] } },
|
|
25
29
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json.schemastore.org/package.json",
|
|
3
|
-
"version": "0.0.0-dev-
|
|
3
|
+
"version": "0.0.0-dev-19446",
|
|
4
4
|
"name": "@opencode/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-rc.112",
|
|
33
|
-
"@opencode/http-recorder": "0.0.0-dev-
|
|
33
|
+
"@opencode/http-recorder": "0.0.0-dev-19446",
|
|
34
34
|
"@tsconfig/bun": "1.0.9",
|
|
35
35
|
"@types/bun": "1.4.0",
|
|
36
36
|
"@typescript/native-preview": "7.0.0-dev.20251207.1",
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
"@aws-sdk/credential-providers": "3.1057.0",
|
|
41
41
|
"@smithy/eventstream-codec": "4.2.14",
|
|
42
42
|
"@smithy/util-utf8": "4.2.2",
|
|
43
|
-
"@opencode/schema": "0.0.0-dev-
|
|
43
|
+
"@opencode/schema": "0.0.0-dev-19446",
|
|
44
44
|
"aws4fetch": "1.0.20",
|
|
45
45
|
"effect": "4.0.0-rc.112",
|
|
46
46
|
"google-auth-library": "10.5.0"
|