@opencode/ai 0.0.0-reserved → 2.0.1
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/README.md +879 -2
- package/dist/cache-policy.d.ts +2 -0
- package/dist/cache-policy.js +129 -0
- package/dist/image-client.d.ts +19 -0
- package/dist/image-client.js +28 -0
- package/dist/image.d.ts +110 -0
- package/dist/image.js +103 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.js +12 -0
- package/dist/llm.d.ts +252 -0
- package/dist/llm.js +74 -0
- package/dist/protocols/alibaba-chat.d.ts +225 -0
- package/dist/protocols/alibaba-chat.js +75 -0
- package/dist/protocols/alibaba-messages.d.ts +350 -0
- package/dist/protocols/alibaba-messages.js +40 -0
- package/dist/protocols/alibaba-responses.d.ts +255 -0
- package/dist/protocols/alibaba-responses.js +80 -0
- package/dist/protocols/anthropic-messages.d.ts +980 -0
- package/dist/protocols/anthropic-messages.js +1460 -0
- package/dist/protocols/bedrock-converse.d.ts +499 -0
- package/dist/protocols/bedrock-converse.js +643 -0
- package/dist/protocols/bedrock-event-stream.d.ts +9 -0
- package/dist/protocols/bedrock-event-stream.js +97 -0
- package/dist/protocols/gemini.d.ts +285 -0
- package/dist/protocols/gemini.js +671 -0
- package/dist/protocols/google-images.d.ts +30 -0
- package/dist/protocols/google-images.js +181 -0
- package/dist/protocols/index.d.ts +12 -0
- package/dist/protocols/index.js +12 -0
- package/dist/protocols/meta-images.d.ts +26 -0
- package/dist/protocols/meta-images.js +89 -0
- package/dist/protocols/meta-messages.d.ts +356 -0
- package/dist/protocols/meta-messages.js +41 -0
- package/dist/protocols/meta-responses.d.ts +473 -0
- package/dist/protocols/meta-responses.js +173 -0
- package/dist/protocols/mistral-chat.d.ts +540 -0
- package/dist/protocols/mistral-chat.js +624 -0
- package/dist/protocols/open-responses-channel.d.ts +25 -0
- package/dist/protocols/open-responses-channel.js +141 -0
- package/dist/protocols/open-responses-continuation.d.ts +16 -0
- package/dist/protocols/open-responses-continuation.js +170 -0
- package/dist/protocols/open-responses.d.ts +1715 -0
- package/dist/protocols/open-responses.js +1228 -0
- package/dist/protocols/openai-chat.d.ts +860 -0
- package/dist/protocols/openai-chat.js +966 -0
- package/dist/protocols/openai-compatible-chat.d.ts +109 -0
- package/dist/protocols/openai-compatible-chat.js +19 -0
- package/dist/protocols/openai-compatible-responses.d.ts +172 -0
- package/dist/protocols/openai-compatible-responses.js +18 -0
- package/dist/protocols/openai-images.d.ts +32 -0
- package/dist/protocols/openai-images.js +183 -0
- package/dist/protocols/openai-responses.d.ts +1389 -0
- package/dist/protocols/openai-responses.js +260 -0
- package/dist/protocols/shared.d.ts +193 -0
- package/dist/protocols/shared.js +290 -0
- package/dist/protocols/utils/bedrock-auth.d.ts +60 -0
- package/dist/protocols/utils/bedrock-auth.js +85 -0
- package/dist/protocols/utils/bedrock-cache.d.ts +19 -0
- package/dist/protocols/utils/bedrock-cache.js +49 -0
- package/dist/protocols/utils/bedrock-media.d.ts +55 -0
- package/dist/protocols/utils/bedrock-media.js +73 -0
- package/dist/protocols/utils/cache.d.ts +6 -0
- package/dist/protocols/utils/cache.js +5 -0
- package/dist/protocols/utils/gemini-tool-schema.d.ts +2 -0
- package/dist/protocols/utils/gemini-tool-schema.js +103 -0
- package/dist/protocols/utils/image-input.d.ts +21 -0
- package/dist/protocols/utils/image-input.js +20 -0
- package/dist/protocols/utils/lifecycle.d.ts +24 -0
- package/dist/protocols/utils/lifecycle.js +72 -0
- package/dist/protocols/utils/meta-image.d.ts +2 -0
- package/dist/protocols/utils/meta-image.js +13 -0
- package/dist/protocols/utils/open-responses-options.d.ts +56 -0
- package/dist/protocols/utils/open-responses-options.js +61 -0
- package/dist/protocols/utils/openai-image.d.ts +5 -0
- package/dist/protocols/utils/openai-image.js +18 -0
- package/dist/protocols/utils/openai-options.d.ts +16 -0
- package/dist/protocols/utils/openai-options.js +14 -0
- package/dist/protocols/utils/partial-json-options.d.ts +62 -0
- package/dist/protocols/utils/partial-json-options.js +52 -0
- package/dist/protocols/utils/partial-json.d.ts +8 -0
- package/dist/protocols/utils/partial-json.js +279 -0
- package/dist/protocols/utils/responses-checkpoint.d.ts +4 -0
- package/dist/protocols/utils/responses-checkpoint.js +107 -0
- package/dist/protocols/utils/responses-compaction.d.ts +4 -0
- package/dist/protocols/utils/responses-compaction.js +129 -0
- package/dist/protocols/utils/responses-hosted-tools.d.ts +26 -0
- package/dist/protocols/utils/responses-hosted-tools.js +32 -0
- package/dist/protocols/utils/tool-schema.d.ts +8 -0
- package/dist/protocols/utils/tool-schema.js +54 -0
- package/dist/protocols/utils/tool-stream.d.ts +710 -0
- package/dist/protocols/utils/tool-stream.js +155 -0
- package/dist/protocols/xai-images.d.ts +26 -0
- package/dist/protocols/xai-images.js +106 -0
- package/dist/protocols/xai-responses.d.ts +250 -0
- package/dist/protocols/xai-responses.js +81 -0
- package/dist/protocols/zai-chat.d.ts +202 -0
- package/dist/protocols/zai-chat.js +49 -0
- package/dist/protocols/zai-images.d.ts +22 -0
- package/dist/protocols/zai-images.js +79 -0
- package/dist/protocols/zai-messages.d.ts +344 -0
- package/dist/protocols/zai-messages.js +27 -0
- package/dist/protocols.d.ts +1 -0
- package/dist/protocols.js +1 -0
- package/dist/provider-error.d.ts +15 -0
- package/dist/provider-error.js +125 -0
- package/dist/provider-package.d.ts +15 -0
- package/dist/provider-package.js +1 -0
- package/dist/provider.d.ts +23 -0
- package/dist/provider.js +2 -0
- package/dist/providers/alibaba/chat.d.ts +1 -0
- package/dist/providers/alibaba/chat.js +1 -0
- package/dist/providers/alibaba/messages.d.ts +3 -0
- package/dist/providers/alibaba/messages.js +1 -0
- package/dist/providers/alibaba/responses.d.ts +3 -0
- package/dist/providers/alibaba/responses.js +1 -0
- package/dist/providers/alibaba.d.ts +711 -0
- package/dist/providers/alibaba.js +94 -0
- package/dist/providers/amazon-bedrock/mantle/chat.d.ts +2 -0
- package/dist/providers/amazon-bedrock/mantle/chat.js +1 -0
- package/dist/providers/amazon-bedrock/mantle/responses.d.ts +2 -0
- package/dist/providers/amazon-bedrock/mantle/responses.js +1 -0
- package/dist/providers/amazon-bedrock/mantle.d.ts +2 -0
- package/dist/providers/amazon-bedrock/mantle.js +1 -0
- package/dist/providers/amazon-bedrock-mantle.d.ts +313 -0
- package/dist/providers/amazon-bedrock-mantle.js +79 -0
- package/dist/providers/amazon-bedrock.d.ts +173 -0
- package/dist/providers/amazon-bedrock.js +41 -0
- package/dist/providers/anthropic-compatible.d.ts +309 -0
- package/dist/providers/anthropic-compatible.js +50 -0
- package/dist/providers/anthropic.d.ts +307 -0
- package/dist/providers/anthropic.js +42 -0
- package/dist/providers/azure/chat.d.ts +2 -0
- package/dist/providers/azure/chat.js +1 -0
- package/dist/providers/azure/responses.d.ts +2 -0
- package/dist/providers/azure/responses.js +1 -0
- package/dist/providers/azure.d.ts +598 -0
- package/dist/providers/azure.js +115 -0
- package/dist/providers/baseten.d.ts +228 -0
- package/dist/providers/baseten.js +38 -0
- package/dist/providers/cerebras.d.ts +227 -0
- package/dist/providers/cerebras.js +40 -0
- package/dist/providers/cloudflare-ai-gateway.d.ts +237 -0
- package/dist/providers/cloudflare-ai-gateway.js +65 -0
- package/dist/providers/cloudflare-workers-ai.d.ts +231 -0
- package/dist/providers/cloudflare-workers-ai.js +51 -0
- package/dist/providers/deepinfra.d.ts +227 -0
- package/dist/providers/deepinfra.js +43 -0
- package/dist/providers/deepseek.d.ts +228 -0
- package/dist/providers/deepseek.js +41 -0
- package/dist/providers/fireworks.d.ts +228 -0
- package/dist/providers/fireworks.js +38 -0
- package/dist/providers/google-vertex/chat.d.ts +2 -0
- package/dist/providers/google-vertex/chat.js +1 -0
- package/dist/providers/google-vertex/gemini.d.ts +2 -0
- package/dist/providers/google-vertex/gemini.js +1 -0
- package/dist/providers/google-vertex/messages.d.ts +2 -0
- package/dist/providers/google-vertex/messages.js +1 -0
- package/dist/providers/google-vertex/responses.d.ts +2 -0
- package/dist/providers/google-vertex/responses.js +1 -0
- package/dist/providers/google-vertex-chat.d.ts +132 -0
- package/dist/providers/google-vertex-chat.js +55 -0
- package/dist/providers/google-vertex-messages.d.ts +306 -0
- package/dist/providers/google-vertex-messages.js +75 -0
- package/dist/providers/google-vertex-responses.d.ts +197 -0
- package/dist/providers/google-vertex-responses.js +56 -0
- package/dist/providers/google-vertex-shared.d.ts +22 -0
- package/dist/providers/google-vertex-shared.js +72 -0
- package/dist/providers/google-vertex.d.ts +119 -0
- package/dist/providers/google-vertex.js +98 -0
- package/dist/providers/google.d.ts +114 -0
- package/dist/providers/google.js +42 -0
- package/dist/providers/groq.d.ts +314 -0
- package/dist/providers/groq.js +81 -0
- package/dist/providers/index.d.ts +31 -0
- package/dist/providers/index.js +31 -0
- package/dist/providers/meta/chat.d.ts +2 -0
- package/dist/providers/meta/chat.js +1 -0
- package/dist/providers/meta/messages.d.ts +2 -0
- package/dist/providers/meta/messages.js +1 -0
- package/dist/providers/meta/responses.d.ts +2 -0
- package/dist/providers/meta/responses.js +1 -0
- package/dist/providers/meta.d.ts +636 -0
- package/dist/providers/meta.js +113 -0
- package/dist/providers/minimax/chat.d.ts +4 -0
- package/dist/providers/minimax/chat.js +8 -0
- package/dist/providers/minimax/messages.d.ts +1 -0
- package/dist/providers/minimax/messages.js +1 -0
- package/dist/providers/minimax/responses.d.ts +4 -0
- package/dist/providers/minimax/responses.js +8 -0
- package/dist/providers/minimax.d.ts +598 -0
- package/dist/providers/minimax.js +89 -0
- package/dist/providers/mistral.d.ts +207 -0
- package/dist/providers/mistral.js +28 -0
- package/dist/providers/moonshot/chat.d.ts +1 -0
- package/dist/providers/moonshot/chat.js +1 -0
- package/dist/providers/moonshot/messages.d.ts +4 -0
- package/dist/providers/moonshot/messages.js +8 -0
- package/dist/providers/moonshot/responses.d.ts +4 -0
- package/dist/providers/moonshot/responses.js +8 -0
- package/dist/providers/moonshot.d.ts +596 -0
- package/dist/providers/moonshot.js +90 -0
- package/dist/providers/open-responses-options.d.ts +6 -0
- package/dist/providers/open-responses-options.js +1 -0
- package/dist/providers/openai/chat.d.ts +2 -0
- package/dist/providers/openai/chat.js +1 -0
- package/dist/providers/openai/responses.d.ts +2 -0
- package/dist/providers/openai/responses.js +1 -0
- package/dist/providers/openai-compatible/responses.d.ts +1 -0
- package/dist/providers/openai-compatible/responses.js +1 -0
- package/dist/providers/openai-compatible-responses.d.ts +195 -0
- package/dist/providers/openai-compatible-responses.js +32 -0
- package/dist/providers/openai-compatible.d.ts +130 -0
- package/dist/providers/openai-compatible.js +33 -0
- package/dist/providers/openai-options.d.ts +26 -0
- package/dist/providers/openai-options.js +41 -0
- package/dist/providers/openai.d.ts +404 -0
- package/dist/providers/openai.js +82 -0
- package/dist/providers/openrouter.d.ts +563 -0
- package/dist/providers/openrouter.js +114 -0
- package/dist/providers/togetherai.d.ts +227 -0
- package/dist/providers/togetherai.js +40 -0
- package/dist/providers/xai.d.ts +163 -0
- package/dist/providers/xai.js +88 -0
- package/dist/providers/zai/chat.d.ts +1 -0
- package/dist/providers/zai/chat.js +1 -0
- package/dist/providers/zai-coding-plan/chat.d.ts +1 -0
- package/dist/providers/zai-coding-plan/chat.js +1 -0
- package/dist/providers/zai-coding-plan/messages.d.ts +4 -0
- package/dist/providers/zai-coding-plan/messages.js +8 -0
- package/dist/providers/zai-coding-plan/responses.d.ts +4 -0
- package/dist/providers/zai-coding-plan/responses.js +8 -0
- package/dist/providers/zai-coding-plan.d.ts +577 -0
- package/dist/providers/zai-coding-plan.js +63 -0
- package/dist/providers/zai.d.ts +149 -0
- package/dist/providers/zai.js +53 -0
- package/dist/providers.d.ts +1 -0
- package/dist/providers.js +1 -0
- package/dist/route/auth-options.d.ts +34 -0
- package/dist/route/auth-options.js +14 -0
- package/dist/route/auth.d.ts +51 -0
- package/dist/route/auth.js +87 -0
- package/dist/route/client.d.ts +430 -0
- package/dist/route/client.js +307 -0
- package/dist/route/endpoint.d.ts +28 -0
- package/dist/route/endpoint.js +21 -0
- package/dist/route/executor.d.ts +27 -0
- package/dist/route/executor.js +183 -0
- package/dist/route/framing.d.ts +29 -0
- package/dist/route/framing.js +14 -0
- package/dist/route/index.d.ts +16 -0
- package/dist/route/index.js +9 -0
- package/dist/route/protocol.d.ts +76 -0
- package/dist/route/protocol.js +16 -0
- package/dist/route/transport/http.d.ts +34 -0
- package/dist/route/transport/http.js +68 -0
- package/dist/route/transport/index.d.ts +41 -0
- package/dist/route/transport/index.js +4 -0
- package/dist/route/transport/websocket-channel.d.ts +57 -0
- package/dist/route/transport/websocket-channel.js +1 -0
- package/dist/route/transport/websocket.d.ts +56 -0
- package/dist/route/transport/websocket.js +370 -0
- package/dist/route.d.ts +1 -0
- package/dist/route.js +1 -0
- package/dist/schema/errors.d.ts +170 -0
- package/dist/schema/errors.js +131 -0
- package/dist/schema/events.d.ts +8123 -0
- package/dist/schema/events.js +550 -0
- package/dist/schema/ids.d.ts +17 -0
- package/dist/schema/ids.js +10 -0
- package/dist/schema/index.d.ts +5 -0
- package/dist/schema/index.js +5 -0
- package/dist/schema/messages.d.ts +646 -0
- package/dist/schema/messages.js +301 -0
- package/dist/schema/options.d.ts +159 -0
- package/dist/schema/options.js +189 -0
- package/dist/testing.d.ts +1693 -0
- package/dist/testing.js +129 -0
- package/dist/tool-history.d.ts +2 -0
- package/dist/tool-history.js +84 -0
- package/dist/tool-runtime.d.ts +15 -0
- package/dist/tool-runtime.js +62 -0
- package/dist/tool.d.ts +136 -0
- package/dist/tool.js +66 -0
- package/dist/utils/record.d.ts +2 -0
- package/dist/utils/record.js +2 -0
- package/dist/utils/sanitize.d.ts +1 -0
- package/dist/utils/sanitize.js +12 -0
- package/package.json +43 -10
package/dist/testing.js
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
export * as TestLLM from "./testing.js";
|
|
2
|
+
import { LLMClient, } from "./route/client.js";
|
|
3
|
+
import { LLMEvent, LLMResponse, CompactionResponse, CompactionCheckpointResponse, } from "./schema/index.js";
|
|
4
|
+
import { Context, Deferred, Effect, Latch, Layer, Queue, Scope, Stream } from "effect";
|
|
5
|
+
export class Test extends Context.Service()("@opencode/ai/TestLLM/Test") {
|
|
6
|
+
}
|
|
7
|
+
/** @deprecated Use Test and testLayer for normal client methods and test controls. */
|
|
8
|
+
export class Service extends Context.Service()("@opencode/ai/TestLLM") {
|
|
9
|
+
}
|
|
10
|
+
export const complete = (options, ...events) => [
|
|
11
|
+
LLMEvent.stepStart({ index: 0 }),
|
|
12
|
+
...events,
|
|
13
|
+
LLMEvent.stepFinish({
|
|
14
|
+
index: 0,
|
|
15
|
+
reason: options.reason,
|
|
16
|
+
usage: options.usage,
|
|
17
|
+
providerMetadata: options.providerMetadata,
|
|
18
|
+
}),
|
|
19
|
+
LLMEvent.finish({ reason: options.reason, providerMetadata: options.providerMetadata }),
|
|
20
|
+
];
|
|
21
|
+
export const stop = (...events) => complete({ reason: { normalized: "stop" } }, ...events);
|
|
22
|
+
export const toolCalls = (...events) => complete({ reason: { normalized: "tool-calls" } }, ...events);
|
|
23
|
+
const textEvents = (value, id) => [
|
|
24
|
+
LLMEvent.textStart({ id }),
|
|
25
|
+
LLMEvent.textDelta({ id, text: value }),
|
|
26
|
+
LLMEvent.textEnd({ id }),
|
|
27
|
+
];
|
|
28
|
+
export const text = (value, id) => stop(...textEvents(value, id));
|
|
29
|
+
export const textWithUsage = (value, id, inputTokens) => complete({ reason: { normalized: "stop" }, usage: { inputTokens, nonCachedInputTokens: inputTokens } }, ...textEvents(value, id));
|
|
30
|
+
export const tool = (id, name, input) => toolCalls(LLMEvent.toolCall({ id, name, input }));
|
|
31
|
+
export const failAfter = (error, ...events) => Stream.fromIterable(events).pipe(Stream.concat(Stream.fail(error)));
|
|
32
|
+
export const hangAfter = (...events) => Stream.concat(Stream.fromIterable(events), Stream.never);
|
|
33
|
+
const make = (options) => Effect.sync(() => {
|
|
34
|
+
const requests = [];
|
|
35
|
+
const responses = [];
|
|
36
|
+
let started = Deferred.makeUnsafe();
|
|
37
|
+
let fallback = options.fallback;
|
|
38
|
+
let activeGate;
|
|
39
|
+
const wait = (count) => Effect.suspend(() => requests.length >= count ? Effect.void : Deferred.await(started).pipe(Effect.andThen(wait(count))));
|
|
40
|
+
const take = (request) => Effect.suspend(() => {
|
|
41
|
+
const count = requests.push(options.transformRequest?.(request) ?? request);
|
|
42
|
+
const waiting = started;
|
|
43
|
+
started = Deferred.makeUnsafe();
|
|
44
|
+
const gate = activeGate;
|
|
45
|
+
try {
|
|
46
|
+
const response = responses.shift() ?? (typeof fallback === "function" ? fallback(request) : fallback);
|
|
47
|
+
if (!response)
|
|
48
|
+
return Effect.die(new Error(`TestLLM has no response for request ${count}`));
|
|
49
|
+
if (!gate)
|
|
50
|
+
return Effect.succeed(response);
|
|
51
|
+
return Queue.offer(gate.started, undefined).pipe(Effect.andThen(gate.release.await), Effect.as(response));
|
|
52
|
+
}
|
|
53
|
+
finally {
|
|
54
|
+
// Waiters can resume synchronously; assign the reply and gate before notifying them.
|
|
55
|
+
Deferred.doneUnsafe(waiting, Effect.void);
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
const stream = (request) => Stream.unwrap(take(request).pipe(Effect.map((response) => {
|
|
59
|
+
if (response instanceof CompactionResponse || response instanceof CompactionCheckpointResponse)
|
|
60
|
+
return Stream.die("TestLLM generation requires an event response");
|
|
61
|
+
return Stream.isStream(response) ? response : Stream.fromIterable(response);
|
|
62
|
+
})));
|
|
63
|
+
function compact(request, options) {
|
|
64
|
+
return take(request).pipe(Effect.flatMap((response) => {
|
|
65
|
+
if (options?.mechanism === "trigger")
|
|
66
|
+
return response instanceof CompactionCheckpointResponse
|
|
67
|
+
? Effect.succeed(response)
|
|
68
|
+
: Effect.die("TestLLM trigger compaction requires a CompactionCheckpointResponse");
|
|
69
|
+
return response instanceof CompactionResponse
|
|
70
|
+
? Effect.succeed(response)
|
|
71
|
+
: Effect.die("TestLLM compaction requires a CompactionResponse");
|
|
72
|
+
}));
|
|
73
|
+
}
|
|
74
|
+
const test = Test.of({
|
|
75
|
+
compact,
|
|
76
|
+
stream,
|
|
77
|
+
generate: (request) => stream(request).pipe(Stream.runFold(LLMResponse.empty, LLMResponse.reduce), Effect.flatMap((state) => {
|
|
78
|
+
const response = LLMResponse.complete(state);
|
|
79
|
+
if (response)
|
|
80
|
+
return Effect.succeed(response);
|
|
81
|
+
return Effect.die("TestLLM response ended without a terminal finish event");
|
|
82
|
+
})),
|
|
83
|
+
requests: () => Effect.sync(() => [...requests]),
|
|
84
|
+
push: (...input) => Effect.sync(() => {
|
|
85
|
+
responses.push(...input);
|
|
86
|
+
}),
|
|
87
|
+
always: (response) => Effect.sync(() => {
|
|
88
|
+
fallback = response;
|
|
89
|
+
}),
|
|
90
|
+
serve: (responder) => Effect.sync(() => {
|
|
91
|
+
fallback = responder;
|
|
92
|
+
}),
|
|
93
|
+
wait,
|
|
94
|
+
gate: () => Effect.gen(function* () {
|
|
95
|
+
const gate = {
|
|
96
|
+
started: yield* Effect.acquireRelease(Queue.unbounded(), Queue.shutdown),
|
|
97
|
+
release: yield* Latch.make(),
|
|
98
|
+
};
|
|
99
|
+
activeGate = gate;
|
|
100
|
+
const release = Effect.sync(() => {
|
|
101
|
+
if (activeGate === gate)
|
|
102
|
+
activeGate = undefined;
|
|
103
|
+
}).pipe(Effect.andThen(gate.release.open), Effect.asVoid);
|
|
104
|
+
yield* Effect.addFinalizer(() => release);
|
|
105
|
+
return {
|
|
106
|
+
started: Queue.take(gate.started),
|
|
107
|
+
release,
|
|
108
|
+
};
|
|
109
|
+
}),
|
|
110
|
+
});
|
|
111
|
+
return { test, requests };
|
|
112
|
+
});
|
|
113
|
+
/** Provides one shared implementation under the normal client and test-control tags. */
|
|
114
|
+
export const testLayer = (options = {}) => Layer.effectContext(Effect.map(make(options), (implementation) => Context.make(LLMClient.Service, implementation.test).pipe(Context.add(Test, implementation.test))));
|
|
115
|
+
/** @deprecated Use testLayer; retained for published callers of the legacy control interface. */
|
|
116
|
+
export const layer = (options = {}) => Layer.effect(Service, Effect.map(make(options), (implementation) => Service.of({
|
|
117
|
+
requests: implementation.requests,
|
|
118
|
+
push: implementation.test.push,
|
|
119
|
+
always: implementation.test.always,
|
|
120
|
+
wait: implementation.test.wait,
|
|
121
|
+
gate: implementation.test.gate(),
|
|
122
|
+
client: implementation.test,
|
|
123
|
+
})));
|
|
124
|
+
/** @deprecated testLayer provides LLMClient.Service directly. */
|
|
125
|
+
export const clientLayer = Layer.effect(LLMClient.Service, Effect.map(Service, (service) => service.client));
|
|
126
|
+
export const push = (...responses) => Service.use((service) => service.push(...responses));
|
|
127
|
+
export const always = (response) => Service.use((service) => service.always(response));
|
|
128
|
+
export const wait = (count) => Service.use((service) => service.wait(count));
|
|
129
|
+
export const gate = Service.use((service) => service.gate);
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { Message, ToolResultPart } from "./schema/messages.js";
|
|
2
|
+
const EMPTY_TOOL_OUTPUT = "(no tool output)";
|
|
3
|
+
const MISSING_TOOL_RESULT = "Tool result missing";
|
|
4
|
+
export function normalizeToolHistory(messages) {
|
|
5
|
+
const normalized = [];
|
|
6
|
+
const pending = new Map();
|
|
7
|
+
const appendMissingResults = () => {
|
|
8
|
+
if (pending.size === 0)
|
|
9
|
+
return;
|
|
10
|
+
normalized.push(missingToolResults(pending.values()));
|
|
11
|
+
pending.clear();
|
|
12
|
+
};
|
|
13
|
+
for (const message of messages) {
|
|
14
|
+
if (message.role === "user" || message.role === "assistant")
|
|
15
|
+
appendMissingResults();
|
|
16
|
+
if (message.role === "tool") {
|
|
17
|
+
const tool = normalizeToolMessage(message, pending);
|
|
18
|
+
if (tool)
|
|
19
|
+
normalized.push(tool);
|
|
20
|
+
continue;
|
|
21
|
+
}
|
|
22
|
+
normalized.push(message);
|
|
23
|
+
if (message.role !== "assistant")
|
|
24
|
+
continue;
|
|
25
|
+
for (const part of message.content) {
|
|
26
|
+
if (part.type === "tool-call" && part.providerExecuted !== true)
|
|
27
|
+
pending.set(part.id, part);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return normalized.length === messages.length && normalized.every((message, index) => message === messages[index])
|
|
31
|
+
? messages
|
|
32
|
+
: normalized;
|
|
33
|
+
}
|
|
34
|
+
function missingToolResults(calls) {
|
|
35
|
+
return new Message({
|
|
36
|
+
role: "tool",
|
|
37
|
+
content: [...calls].map((call) => ToolResultPart.make({
|
|
38
|
+
id: call.id,
|
|
39
|
+
name: call.name,
|
|
40
|
+
namespace: call.namespace,
|
|
41
|
+
result: MISSING_TOOL_RESULT,
|
|
42
|
+
resultType: "error",
|
|
43
|
+
})),
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
function normalizeToolMessage(message, pending) {
|
|
47
|
+
const content = message.content.map((part) => {
|
|
48
|
+
if (part.type !== "tool-result" || part.providerExecuted === true)
|
|
49
|
+
return part;
|
|
50
|
+
const call = pending.get(part.id);
|
|
51
|
+
if (call)
|
|
52
|
+
pending.delete(part.id);
|
|
53
|
+
return normalizeToolResult(part, call);
|
|
54
|
+
});
|
|
55
|
+
if (content.length === 0)
|
|
56
|
+
return undefined;
|
|
57
|
+
if (content.every((part, index) => part === message.content[index]))
|
|
58
|
+
return message;
|
|
59
|
+
return new Message({
|
|
60
|
+
id: message.id,
|
|
61
|
+
role: message.role,
|
|
62
|
+
content,
|
|
63
|
+
metadata: message.metadata,
|
|
64
|
+
providerMetadata: message.providerMetadata,
|
|
65
|
+
native: message.native,
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
function normalizeToolResult(part, call) {
|
|
69
|
+
const named = call === undefined || (part.name === call.name && part.namespace === call.namespace)
|
|
70
|
+
? part
|
|
71
|
+
: { ...part, name: call.name, namespace: call.namespace };
|
|
72
|
+
if (named.result.type === "text" && named.result.value === "")
|
|
73
|
+
return { ...named, result: { type: "text", value: EMPTY_TOOL_OUTPUT } };
|
|
74
|
+
if (named.result.type === "error" && named.result.value === "")
|
|
75
|
+
return { ...named, result: { type: "error", value: EMPTY_TOOL_OUTPUT } };
|
|
76
|
+
if (named.result.type !== "content")
|
|
77
|
+
return named;
|
|
78
|
+
const value = named.result.value.filter((item) => item.type !== "text" || item.text !== "");
|
|
79
|
+
if (value.length === 0)
|
|
80
|
+
return { ...named, result: { type: "text", value: EMPTY_TOOL_OUTPUT } };
|
|
81
|
+
if (value.length === named.result.value.length)
|
|
82
|
+
return named;
|
|
83
|
+
return { ...named, result: { type: "content", value } };
|
|
84
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Effect } from "effect";
|
|
2
|
+
import { LLMEvent, type ToolCallPart, type ToolOutput as ToolOutputType, type ToolResultValue as ToolResultValueType } from "./schema/index.js";
|
|
3
|
+
import { type Tools } from "./tool.js";
|
|
4
|
+
export interface ToolSettlement {
|
|
5
|
+
readonly result: ToolResultValueType;
|
|
6
|
+
readonly output?: ToolOutputType;
|
|
7
|
+
}
|
|
8
|
+
export interface DispatchResult extends ToolSettlement {
|
|
9
|
+
readonly events: ReadonlyArray<LLMEvent>;
|
|
10
|
+
}
|
|
11
|
+
/** Execute one canonical tool call without owning provider IO or continuation. */
|
|
12
|
+
export declare const dispatch: (tools: Tools, call: ToolCallPart) => Effect.Effect<DispatchResult>;
|
|
13
|
+
export declare const ToolRuntime: {
|
|
14
|
+
readonly dispatch: (tools: Tools, call: ToolCallPart) => Effect.Effect<DispatchResult>;
|
|
15
|
+
};
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { Effect } from "effect";
|
|
2
|
+
import { LLMEvent, ToolFailure, ToolOutput, ToolResultValue, } from "./schema/index.js";
|
|
3
|
+
import {} from "./tool.js";
|
|
4
|
+
/** Execute one canonical tool call without owning provider IO or continuation. */
|
|
5
|
+
export const dispatch = (tools, call) => {
|
|
6
|
+
const name = call.namespace === undefined ? call.name : `${call.namespace}.${call.name}`;
|
|
7
|
+
const tool = tools[name];
|
|
8
|
+
if (!tool)
|
|
9
|
+
return Effect.succeed(result(call, { type: "error", value: `Unknown tool: ${name}` }));
|
|
10
|
+
if (!tool.execute)
|
|
11
|
+
return Effect.succeed(result(call, { type: "error", value: `Tool has no execute handler: ${name}` }));
|
|
12
|
+
return decodeAndExecute(tool, call).pipe(Effect.map((value) => result(call, value)), Effect.catchTag("Tool.Error", (failure) => Effect.succeed(result(call, { type: "error", value: failure.message }, failure.error))));
|
|
13
|
+
};
|
|
14
|
+
const decodeAndExecute = (tool, call) => tool._decode(call.input).pipe(Effect.mapError((error) => new ToolFailure({ message: `Invalid tool input: ${error.message}` })), Effect.flatMap((decoded) => tool.execute(decoded, {
|
|
15
|
+
id: call.id,
|
|
16
|
+
name: call.name,
|
|
17
|
+
namespace: call.namespace,
|
|
18
|
+
}).pipe(Effect.flatMap((value) => tool._encode(value).pipe(Effect.mapError((error) => new ToolFailure({
|
|
19
|
+
message: `Tool returned an invalid value for its success schema: ${error.message}`,
|
|
20
|
+
})))), Effect.map((encoded) => {
|
|
21
|
+
if (tool._legacyResult && ToolResultValue.is(encoded))
|
|
22
|
+
return { result: encoded, output: ToolOutput.fromResultValue(encoded) };
|
|
23
|
+
const output = tool._project(decoded, call.id, encoded);
|
|
24
|
+
const result = ToolOutput.toResultValue(output);
|
|
25
|
+
return result.type === "error" ? { result } : { result, output };
|
|
26
|
+
}))));
|
|
27
|
+
const result = (call, value, error) => {
|
|
28
|
+
const settlement = ToolResultValue.is(value) ? { result: value } : value;
|
|
29
|
+
return {
|
|
30
|
+
result: settlement.result,
|
|
31
|
+
output: settlement.output,
|
|
32
|
+
events: settlement.result.type === "error"
|
|
33
|
+
? [
|
|
34
|
+
LLMEvent.toolError({
|
|
35
|
+
id: call.id,
|
|
36
|
+
name: call.name,
|
|
37
|
+
namespace: call.namespace,
|
|
38
|
+
message: String(settlement.result.value),
|
|
39
|
+
error,
|
|
40
|
+
providerMetadata: call.providerMetadata,
|
|
41
|
+
}),
|
|
42
|
+
LLMEvent.toolResult({
|
|
43
|
+
id: call.id,
|
|
44
|
+
name: call.name,
|
|
45
|
+
namespace: call.namespace,
|
|
46
|
+
result: settlement.result,
|
|
47
|
+
providerMetadata: call.providerMetadata,
|
|
48
|
+
}),
|
|
49
|
+
]
|
|
50
|
+
: [
|
|
51
|
+
LLMEvent.toolResult({
|
|
52
|
+
id: call.id,
|
|
53
|
+
name: call.name,
|
|
54
|
+
namespace: call.namespace,
|
|
55
|
+
result: settlement.result,
|
|
56
|
+
output: settlement.output,
|
|
57
|
+
providerMetadata: call.providerMetadata,
|
|
58
|
+
}),
|
|
59
|
+
],
|
|
60
|
+
};
|
|
61
|
+
};
|
|
62
|
+
export const ToolRuntime = { dispatch };
|
package/dist/tool.d.ts
ADDED
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import { Effect, JsonSchema, Schema } from "effect";
|
|
2
|
+
import { Tool } from "@opencode/schema/tool";
|
|
3
|
+
import type { ToolCallPart, ToolDefinition as ToolDefinitionClass, ToolOutput as ToolOutputType } from "./schema/index.js";
|
|
4
|
+
import { ToolFailure } from "./schema/index.js";
|
|
5
|
+
/**
|
|
6
|
+
* Schema constraint for tool parameters / success values: no decoding or
|
|
7
|
+
* encoding services are allowed. Tools should be self-contained — anything
|
|
8
|
+
* beyond pure data conversion belongs in the handler closure.
|
|
9
|
+
*/
|
|
10
|
+
export type ToolSchema<T> = Schema.Codec<T, any, never, never>;
|
|
11
|
+
export interface ToolExecuteContext {
|
|
12
|
+
readonly id: ToolCallPart["id"];
|
|
13
|
+
readonly name: ToolCallPart["name"];
|
|
14
|
+
readonly namespace?: ToolCallPart["namespace"];
|
|
15
|
+
}
|
|
16
|
+
export type ToolExecute<Parameters extends ToolSchema<any>, Success extends ToolSchema<any>> = (params: Schema.Schema.Type<Parameters>, context?: ToolExecuteContext) => Effect.Effect<Schema.Schema.Type<Success>, ToolFailure>;
|
|
17
|
+
export interface ToolModelOutputInput<Parameters, Output> {
|
|
18
|
+
readonly id: ToolCallPart["id"];
|
|
19
|
+
readonly parameters: Parameters;
|
|
20
|
+
readonly output: Output;
|
|
21
|
+
}
|
|
22
|
+
export type ToolToModelOutput<Parameters extends ToolSchema<any>, Success extends ToolSchema<any>> = (input: ToolModelOutputInput<Schema.Schema.Type<Parameters>, Success["Encoded"]>) => ReadonlyArray<Tool.Content>;
|
|
23
|
+
/**
|
|
24
|
+
* A type-safe LLM tool. Each tool bundles its own description, parameter
|
|
25
|
+
* Schema and success Schema. The execute handler is optional: omit it when you
|
|
26
|
+
* only want to expose a tool schema to the model and handle tool calls outside
|
|
27
|
+
* this package.
|
|
28
|
+
*
|
|
29
|
+
* Errors must be expressed as `ToolFailure`. Unmapped errors and defects fail
|
|
30
|
+
* the stream.
|
|
31
|
+
*
|
|
32
|
+
* Internally each tool also carries memoized codecs and a precomputed
|
|
33
|
+
* `ToolDefinition` so callers do not rebuild them per invocation.
|
|
34
|
+
*/
|
|
35
|
+
export interface Definition<Parameters extends ToolSchema<any>, Success extends ToolSchema<any>> {
|
|
36
|
+
readonly description: string;
|
|
37
|
+
readonly parameters: Parameters;
|
|
38
|
+
readonly success: Success;
|
|
39
|
+
readonly execute?: ToolExecute<Parameters, Success>;
|
|
40
|
+
readonly toModelOutput?: ToolToModelOutput<Parameters, Success>;
|
|
41
|
+
readonly toStructuredOutput?: (output: Success["Encoded"]) => unknown;
|
|
42
|
+
/** @internal */
|
|
43
|
+
readonly _decode: (input: unknown) => Effect.Effect<Schema.Schema.Type<Parameters>, Schema.SchemaError>;
|
|
44
|
+
/** @internal */
|
|
45
|
+
readonly _encode: (value: Schema.Schema.Type<Success>) => Effect.Effect<unknown, Schema.SchemaError>;
|
|
46
|
+
/** @internal */
|
|
47
|
+
readonly _project: (parameters: Schema.Schema.Type<Parameters>, id: ToolCallPart["id"], output: unknown) => ToolOutputType;
|
|
48
|
+
/** @internal */
|
|
49
|
+
readonly _legacyResult: boolean;
|
|
50
|
+
/** @internal */
|
|
51
|
+
readonly _definition: ToolDefinitionClass;
|
|
52
|
+
}
|
|
53
|
+
export type AnyTool = Definition<any, any>;
|
|
54
|
+
export type ExecutableTool<Parameters extends ToolSchema<any>, Success extends ToolSchema<any>> = Definition<Parameters, Success> & {
|
|
55
|
+
readonly execute: ToolExecute<Parameters, Success>;
|
|
56
|
+
};
|
|
57
|
+
export type AnyExecutableTool = ExecutableTool<any, any>;
|
|
58
|
+
export type ExecutableTools = Record<string, AnyExecutableTool>;
|
|
59
|
+
/**
|
|
60
|
+
* Constructs a tool. Two input modes:
|
|
61
|
+
*
|
|
62
|
+
* 1. **Typed** — pass Effect `parameters` and `success` Schemas; inputs and
|
|
63
|
+
* outputs are statically typed and decoded/encoded automatically.
|
|
64
|
+
*
|
|
65
|
+
* ```ts
|
|
66
|
+
* Tool.make({
|
|
67
|
+
* description: "Get current weather",
|
|
68
|
+
* parameters: Schema.Struct({ city: Schema.String }),
|
|
69
|
+
* success: Schema.Struct({ temperature: Schema.Number }),
|
|
70
|
+
* execute: ({ city }) => Effect.succeed({ temperature: 22 }),
|
|
71
|
+
* })
|
|
72
|
+
* ```
|
|
73
|
+
*
|
|
74
|
+
* 2. **Dynamic** — pass raw JSON Schema as `jsonSchema`. Use this when the
|
|
75
|
+
* schema comes from an external source (MCP server, plugin manifest,
|
|
76
|
+
* dynamic config) and is not known at compile time. Inputs are typed as
|
|
77
|
+
* `unknown`; the handler is responsible for any validation it needs.
|
|
78
|
+
*
|
|
79
|
+
* ```ts
|
|
80
|
+
* Tool.make({
|
|
81
|
+
* description: "Look something up",
|
|
82
|
+
* jsonSchema: { type: "object", properties: { ... } },
|
|
83
|
+
* execute: (params) => Effect.succeed(...),
|
|
84
|
+
* })
|
|
85
|
+
* ```
|
|
86
|
+
*
|
|
87
|
+
* In both modes the produced tool flows through `toDefinitions(...)`
|
|
88
|
+
* identically.
|
|
89
|
+
*/
|
|
90
|
+
export declare function make<Parameters extends ToolSchema<any>, Success extends ToolSchema<any>>(config: {
|
|
91
|
+
readonly description: string;
|
|
92
|
+
readonly parameters: Parameters;
|
|
93
|
+
readonly success: Success;
|
|
94
|
+
readonly execute: ToolExecute<Parameters, Success>;
|
|
95
|
+
readonly toModelOutput?: ToolToModelOutput<Parameters, Success>;
|
|
96
|
+
readonly toStructuredOutput?: (output: Success["Encoded"]) => unknown;
|
|
97
|
+
}): ExecutableTool<Parameters, Success>;
|
|
98
|
+
export declare function make<Parameters extends ToolSchema<any>, Success extends ToolSchema<any>>(config: {
|
|
99
|
+
readonly description: string;
|
|
100
|
+
readonly parameters: Parameters;
|
|
101
|
+
readonly success: Success;
|
|
102
|
+
readonly execute?: undefined;
|
|
103
|
+
readonly toModelOutput?: ToolToModelOutput<Parameters, Success>;
|
|
104
|
+
readonly toStructuredOutput?: (output: Success["Encoded"]) => unknown;
|
|
105
|
+
}): Definition<Parameters, Success>;
|
|
106
|
+
export declare function make(config: {
|
|
107
|
+
readonly description: string;
|
|
108
|
+
readonly jsonSchema: JsonSchema.JsonSchema;
|
|
109
|
+
readonly outputSchema?: JsonSchema.JsonSchema;
|
|
110
|
+
readonly execute: (params: unknown, context?: ToolExecuteContext) => Effect.Effect<unknown, ToolFailure>;
|
|
111
|
+
readonly toModelOutput?: (input: ToolModelOutputInput<unknown, unknown>) => ReadonlyArray<Tool.Content>;
|
|
112
|
+
readonly toStructuredOutput?: (output: unknown) => unknown;
|
|
113
|
+
}): AnyExecutableTool;
|
|
114
|
+
export declare function make(config: {
|
|
115
|
+
readonly description: string;
|
|
116
|
+
readonly jsonSchema: JsonSchema.JsonSchema;
|
|
117
|
+
readonly outputSchema?: JsonSchema.JsonSchema;
|
|
118
|
+
readonly execute?: undefined;
|
|
119
|
+
readonly toModelOutput?: (input: ToolModelOutputInput<unknown, unknown>) => ReadonlyArray<Tool.Content>;
|
|
120
|
+
readonly toStructuredOutput?: (output: unknown) => unknown;
|
|
121
|
+
}): AnyTool;
|
|
122
|
+
/**
|
|
123
|
+
* A record of named tools. The record key becomes the tool name on the wire.
|
|
124
|
+
*/
|
|
125
|
+
export type Tools = Record<string, AnyTool>;
|
|
126
|
+
/**
|
|
127
|
+
* Convert a tools record into the `ToolDefinition[]` shape that
|
|
128
|
+
* `LLMRequest.tools` expects.
|
|
129
|
+
*
|
|
130
|
+
* Tool names come from the record keys, so the per-tool cached
|
|
131
|
+
* `_definition` is rebuilt with the correct name here. The JSON Schema body
|
|
132
|
+
* is reused.
|
|
133
|
+
*/
|
|
134
|
+
export declare const toDefinitions: (tools: Tools) => ReadonlyArray<ToolDefinitionClass>;
|
|
135
|
+
export { ToolFailure };
|
|
136
|
+
export * as Tool from "./tool.js";
|
package/dist/tool.js
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { Effect, JsonSchema, Schema } from "effect";
|
|
2
|
+
import { Tool } from "@opencode/schema/tool";
|
|
3
|
+
import { ToolDefinition, ToolFailure, ToolOutput } from "./schema/index.js";
|
|
4
|
+
export function make(config) {
|
|
5
|
+
if ("jsonSchema" in config) {
|
|
6
|
+
return {
|
|
7
|
+
description: config.description,
|
|
8
|
+
parameters: Schema.Unknown,
|
|
9
|
+
success: Schema.Unknown,
|
|
10
|
+
execute: config.execute,
|
|
11
|
+
toModelOutput: config.toModelOutput,
|
|
12
|
+
toStructuredOutput: config.toStructuredOutput,
|
|
13
|
+
_decode: Effect.succeed,
|
|
14
|
+
_encode: Effect.succeed,
|
|
15
|
+
_project: (parameters, id, output) => project(config.toModelOutput, config.toStructuredOutput, parameters, id, output),
|
|
16
|
+
_legacyResult: config.toModelOutput === undefined && config.toStructuredOutput === undefined,
|
|
17
|
+
_definition: new ToolDefinition({
|
|
18
|
+
name: "",
|
|
19
|
+
description: config.description,
|
|
20
|
+
inputSchema: config.jsonSchema,
|
|
21
|
+
outputSchema: config.outputSchema,
|
|
22
|
+
}),
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
return {
|
|
26
|
+
description: config.description,
|
|
27
|
+
parameters: config.parameters,
|
|
28
|
+
success: config.success,
|
|
29
|
+
execute: config.execute,
|
|
30
|
+
toModelOutput: config.toModelOutput,
|
|
31
|
+
toStructuredOutput: config.toStructuredOutput,
|
|
32
|
+
_decode: Schema.decodeUnknownEffect(config.parameters),
|
|
33
|
+
_encode: Schema.encodeEffect(config.success),
|
|
34
|
+
_project: (parameters, id, output) => project(config.toModelOutput, config.toStructuredOutput, parameters, id, output),
|
|
35
|
+
_legacyResult: false,
|
|
36
|
+
_definition: new ToolDefinition({
|
|
37
|
+
name: "",
|
|
38
|
+
description: config.description,
|
|
39
|
+
inputSchema: toJsonSchema(config.parameters),
|
|
40
|
+
outputSchema: toJsonSchema(config.success),
|
|
41
|
+
}),
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Convert a tools record into the `ToolDefinition[]` shape that
|
|
46
|
+
* `LLMRequest.tools` expects.
|
|
47
|
+
*
|
|
48
|
+
* Tool names come from the record keys, so the per-tool cached
|
|
49
|
+
* `_definition` is rebuilt with the correct name here. The JSON Schema body
|
|
50
|
+
* is reused.
|
|
51
|
+
*/
|
|
52
|
+
export const toDefinitions = (tools) => Object.entries(tools).map(([name, item]) => new ToolDefinition({
|
|
53
|
+
name,
|
|
54
|
+
description: item._definition.description,
|
|
55
|
+
inputSchema: item._definition.inputSchema,
|
|
56
|
+
outputSchema: item._definition.outputSchema,
|
|
57
|
+
}));
|
|
58
|
+
const toJsonSchema = (schema) => {
|
|
59
|
+
const document = Schema.toJsonSchemaDocument(schema);
|
|
60
|
+
if (Object.keys(document.definitions).length === 0)
|
|
61
|
+
return document.schema;
|
|
62
|
+
return { ...document.schema, $defs: document.definitions };
|
|
63
|
+
};
|
|
64
|
+
const project = (toModelOutput, toStructuredOutput, parameters, id, output) => ToolOutput.make(toStructuredOutput?.(output) ?? output, toModelOutput?.({ id, parameters, output }) ?? (typeof output === "string" ? [{ type: "text", text: output }] : []));
|
|
65
|
+
export { ToolFailure };
|
|
66
|
+
export * as Tool from "./tool.js";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const sanitizeSurrogates: <T>(value: T) => T;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { isRecord } from "./record.js";
|
|
2
|
+
export const sanitizeSurrogates = (value) => {
|
|
3
|
+
if (typeof value === "string")
|
|
4
|
+
return value.toWellFormed();
|
|
5
|
+
if (Array.isArray(value))
|
|
6
|
+
return value.map(sanitizeSurrogates);
|
|
7
|
+
if (value instanceof Uint8Array || value instanceof Error)
|
|
8
|
+
return value;
|
|
9
|
+
if (isRecord(value))
|
|
10
|
+
return Object.fromEntries(Object.entries(value).map(([key, entry]) => [key.toWellFormed(), sanitizeSurrogates(entry)]));
|
|
11
|
+
return value;
|
|
12
|
+
};
|
package/package.json
CHANGED
|
@@ -1,15 +1,48 @@
|
|
|
1
1
|
{
|
|
2
|
+
"$schema": "https://json.schemastore.org/package.json",
|
|
3
|
+
"version": "2.0.1",
|
|
2
4
|
"name": "@opencode/ai",
|
|
3
|
-
"
|
|
4
|
-
"description": "OpenCode package bootstrap — not a functional release",
|
|
5
|
+
"type": "module",
|
|
5
6
|
"license": "MIT",
|
|
6
|
-
"
|
|
7
|
-
"
|
|
8
|
-
"
|
|
7
|
+
"scripts": {
|
|
8
|
+
"setup:recording-env": "bun run script/setup-recording-env.ts",
|
|
9
|
+
"test": "bun test --timeout 30000 --only-failures",
|
|
10
|
+
"typecheck": "tsgo --noEmit && tsgo --noEmit -p tsconfig.types.json",
|
|
11
|
+
"build": "tsc -p tsconfig.build.json"
|
|
9
12
|
},
|
|
10
|
-
"
|
|
11
|
-
"
|
|
12
|
-
|
|
13
|
-
|
|
13
|
+
"files": [
|
|
14
|
+
"dist"
|
|
15
|
+
],
|
|
16
|
+
"exports": {
|
|
17
|
+
".": {
|
|
18
|
+
"import": "./dist/index.js",
|
|
19
|
+
"types": "./dist/index.d.ts"
|
|
20
|
+
},
|
|
21
|
+
"./testing": {
|
|
22
|
+
"import": "./dist/testing.js",
|
|
23
|
+
"types": "./dist/testing.d.ts"
|
|
24
|
+
},
|
|
25
|
+
"./*": {
|
|
26
|
+
"import": "./dist/*.js",
|
|
27
|
+
"types": "./dist/*.d.ts"
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
"devDependencies": {
|
|
31
|
+
"@clack/prompts": "1.0.0-alpha.1",
|
|
32
|
+
"@effect/platform-node": "4.0.0-rc.112",
|
|
33
|
+
"@opencode/http-recorder": "2.0.1",
|
|
34
|
+
"@tsconfig/bun": "1.0.9",
|
|
35
|
+
"@types/bun": "1.4.0",
|
|
36
|
+
"@typescript/native-preview": "7.0.0-dev.20251207.1",
|
|
37
|
+
"typescript": "5.8.2"
|
|
38
|
+
},
|
|
39
|
+
"dependencies": {
|
|
40
|
+
"@aws-sdk/credential-providers": "3.1057.0",
|
|
41
|
+
"@smithy/eventstream-codec": "4.2.14",
|
|
42
|
+
"@smithy/util-utf8": "4.2.2",
|
|
43
|
+
"@opencode/schema": "2.0.1",
|
|
44
|
+
"aws4fetch": "1.0.20",
|
|
45
|
+
"effect": "4.0.0-rc.112",
|
|
46
|
+
"google-auth-library": "10.5.0"
|
|
14
47
|
}
|
|
15
|
-
}
|
|
48
|
+
}
|