@terminaluse/vercel-ai-sdk-provider 0.0.3 → 0.1.0
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 +49 -5
- package/dist/index.d.mts +51 -1
- package/dist/index.mjs +410 -3
- package/package.json +24 -8
package/README.md
CHANGED
|
@@ -1,15 +1,59 @@
|
|
|
1
1
|
# @terminaluse/vercel-ai-sdk-provider
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Vercel AI SDK provider for TerminalUse agents. Implements `LanguageModelV3`.
|
|
4
|
+
|
|
5
|
+
Use [`@terminaluse/sdk`](https://www.npmjs.com/package/@terminaluse/sdk) to create projects, filesystems, tasks, and manage your TerminalUse resources.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
4
8
|
|
|
5
9
|
```bash
|
|
6
|
-
|
|
10
|
+
npm install @terminaluse/vercel-ai-sdk-provider
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Usage
|
|
14
|
+
|
|
15
|
+
```typescript
|
|
16
|
+
import { createTerminalUseProvider } from '@terminaluse/vercel-ai-sdk-provider';
|
|
17
|
+
import { streamText } from 'ai';
|
|
18
|
+
|
|
19
|
+
const terminaluse = createTerminalUseProvider({
|
|
20
|
+
apiKey: process.env.TERMINALUSE_API_KEY!,
|
|
21
|
+
baseURL: process.env.TERMINALUSE_BASE_URL!,
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
// Stream messages to an existing task
|
|
25
|
+
const result = await streamText({
|
|
26
|
+
model: terminaluse.agent('namespace/agent-name'),
|
|
27
|
+
messages: [{ role: 'user', content: 'Hello' }],
|
|
28
|
+
providerOptions: {
|
|
29
|
+
terminaluse: { taskId: 'existing-task-id' },
|
|
30
|
+
},
|
|
31
|
+
});
|
|
7
32
|
```
|
|
8
33
|
|
|
9
|
-
|
|
34
|
+
## Provider Options
|
|
35
|
+
|
|
36
|
+
| Option | Type | Description |
|
|
37
|
+
|--------|------|-------------|
|
|
38
|
+
| `taskId` | `string` | Task ID to send messages to (required) |
|
|
39
|
+
|
|
40
|
+
Tasks must be created separately using the [`@terminaluse/sdk`](https://www.npmjs.com/package/@terminaluse/sdk).
|
|
41
|
+
|
|
42
|
+
## Requirements
|
|
43
|
+
|
|
44
|
+
- Node.js 18+
|
|
45
|
+
- ESM projects (this package is ESM-only)
|
|
46
|
+
- `ai` package v6.0.0+ (peer dependency)
|
|
47
|
+
|
|
48
|
+
## Development
|
|
10
49
|
|
|
11
50
|
```bash
|
|
12
|
-
bun
|
|
51
|
+
bun install
|
|
52
|
+
bun run dev # watch mode
|
|
53
|
+
bun test # run tests
|
|
54
|
+
bun run build # production build
|
|
13
55
|
```
|
|
14
56
|
|
|
15
|
-
|
|
57
|
+
## License
|
|
58
|
+
|
|
59
|
+
Apache-2.0
|
package/dist/index.d.mts
CHANGED
|
@@ -1 +1,51 @@
|
|
|
1
|
-
|
|
1
|
+
import { LanguageModel } from "ai";
|
|
2
|
+
|
|
3
|
+
//#region src/provider.d.ts
|
|
4
|
+
interface TerminalUseProviderConfig {
|
|
5
|
+
/** API key for authentication (Bearer token) */
|
|
6
|
+
apiKey: string;
|
|
7
|
+
/** TerminalUse API base URL */
|
|
8
|
+
baseURL: string;
|
|
9
|
+
}
|
|
10
|
+
interface TerminalUseProvider {
|
|
11
|
+
/** Create a language model for the specified agent */
|
|
12
|
+
agent(agentName: string): LanguageModel;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Provider options for TerminalUse agents.
|
|
16
|
+
* Task must be created separately via the /api/tasks endpoint.
|
|
17
|
+
*/
|
|
18
|
+
interface TerminalUseProviderOptions {
|
|
19
|
+
/** Task ID to send messages to (required) */
|
|
20
|
+
taskId: string;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Creates a custom AI SDK provider for TerminalUse agents.
|
|
24
|
+
*
|
|
25
|
+
* This provider implements the LanguageModelV3 interface, bridging
|
|
26
|
+
* Vercel AI SDK's useChat/streamText to TerminalUse's RPC-based API.
|
|
27
|
+
*
|
|
28
|
+
* Note: Tasks must be created separately via the /api/tasks endpoint
|
|
29
|
+
* before sending messages.
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* ```ts
|
|
33
|
+
* const terminaluse = createTerminalUseProvider({
|
|
34
|
+
* apiKey: process.env.TERMINALUSE_API_KEY!,
|
|
35
|
+
* baseURL: process.env.TERMINALUSE_API_BASE_URL!,
|
|
36
|
+
* });
|
|
37
|
+
*
|
|
38
|
+
* const result = await streamText({
|
|
39
|
+
* model: terminaluse.agent('namespace/agent-name'),
|
|
40
|
+
* messages,
|
|
41
|
+
* providerOptions: {
|
|
42
|
+
* terminaluse: {
|
|
43
|
+
* taskId: 'existing-task-id',
|
|
44
|
+
* },
|
|
45
|
+
* },
|
|
46
|
+
* });
|
|
47
|
+
* ```
|
|
48
|
+
*/
|
|
49
|
+
declare function createTerminalUseProvider(config: TerminalUseProviderConfig): TerminalUseProvider;
|
|
50
|
+
//#endregion
|
|
51
|
+
export { type TerminalUseProvider, type TerminalUseProviderConfig, type TerminalUseProviderOptions, createTerminalUseProvider };
|
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,412 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import { UnsupportedFunctionalityError } from "ai";
|
|
2
|
+
import { createEventSourceResponseHandler, getFromApi } from "@ai-sdk/provider-utils";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
|
|
5
|
+
//#region src/http.ts
|
|
6
|
+
/**
|
|
7
|
+
* Fetch wrapper with TerminalUse authentication headers.
|
|
8
|
+
*/
|
|
9
|
+
async function terminaluseFetch(config, path, options = {}) {
|
|
10
|
+
const headers = new Headers(options.headers);
|
|
11
|
+
if (config.apiKey) headers.set("Authorization", `Bearer ${config.apiKey}`);
|
|
12
|
+
if (!headers.has("Content-Type") && options.body) headers.set("Content-Type", "application/json");
|
|
13
|
+
return fetch(`${config.baseURL}${path}`, {
|
|
14
|
+
...options,
|
|
15
|
+
headers
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
//#endregion
|
|
20
|
+
//#region src/ndjson-stream.ts
|
|
21
|
+
/**
|
|
22
|
+
* SSE streaming utility for TerminalUse task events.
|
|
23
|
+
* Uses @ai-sdk/provider-utils for clean SSE parsing.
|
|
24
|
+
*
|
|
25
|
+
* This module handles the v2 TextStreamPart format from the backend,
|
|
26
|
+
* which is based on Vercel AI SDK's TextStreamPart with extensions.
|
|
27
|
+
*/
|
|
28
|
+
const streamMetadataSchema = z.object({
|
|
29
|
+
costUsd: z.number().optional(),
|
|
30
|
+
durationMs: z.number().optional(),
|
|
31
|
+
cacheCreationInputTokens: z.number().optional(),
|
|
32
|
+
cacheReadInputTokens: z.number().optional(),
|
|
33
|
+
parentToolUseId: z.string().optional()
|
|
34
|
+
}).optional();
|
|
35
|
+
const streamUsageSchema = z.object({
|
|
36
|
+
inputTokens: z.number(),
|
|
37
|
+
outputTokens: z.number(),
|
|
38
|
+
totalTokens: z.number().optional()
|
|
39
|
+
});
|
|
40
|
+
const finishReasonSchema = z.enum([
|
|
41
|
+
"stop",
|
|
42
|
+
"tool-calls",
|
|
43
|
+
"length",
|
|
44
|
+
"content-filter",
|
|
45
|
+
"error",
|
|
46
|
+
"other"
|
|
47
|
+
]);
|
|
48
|
+
const startPartSchema = z.object({ type: z.literal("start") });
|
|
49
|
+
const startStepPartSchema = z.object({
|
|
50
|
+
type: z.literal("start-step"),
|
|
51
|
+
request: z.record(z.unknown()).optional(),
|
|
52
|
+
warnings: z.array(z.unknown()).optional(),
|
|
53
|
+
metadata: streamMetadataSchema
|
|
54
|
+
});
|
|
55
|
+
const finishStepPartSchema = z.object({
|
|
56
|
+
type: z.literal("finish-step"),
|
|
57
|
+
finishReason: finishReasonSchema,
|
|
58
|
+
usage: streamUsageSchema,
|
|
59
|
+
metadata: streamMetadataSchema
|
|
60
|
+
});
|
|
61
|
+
const finishPartSchema = z.object({
|
|
62
|
+
type: z.literal("finish"),
|
|
63
|
+
finishReason: finishReasonSchema,
|
|
64
|
+
totalUsage: streamUsageSchema,
|
|
65
|
+
metadata: streamMetadataSchema
|
|
66
|
+
});
|
|
67
|
+
const textStartPartSchema = z.object({
|
|
68
|
+
type: z.literal("text-start"),
|
|
69
|
+
id: z.string(),
|
|
70
|
+
metadata: streamMetadataSchema
|
|
71
|
+
});
|
|
72
|
+
const textDeltaPartSchema = z.object({
|
|
73
|
+
type: z.literal("text-delta"),
|
|
74
|
+
id: z.string(),
|
|
75
|
+
text: z.string(),
|
|
76
|
+
metadata: streamMetadataSchema
|
|
77
|
+
});
|
|
78
|
+
const textEndPartSchema = z.object({
|
|
79
|
+
type: z.literal("text-end"),
|
|
80
|
+
id: z.string(),
|
|
81
|
+
metadata: streamMetadataSchema
|
|
82
|
+
});
|
|
83
|
+
const reasoningStartPartSchema = z.object({
|
|
84
|
+
type: z.literal("reasoning-start"),
|
|
85
|
+
id: z.string(),
|
|
86
|
+
metadata: streamMetadataSchema
|
|
87
|
+
});
|
|
88
|
+
const reasoningDeltaPartSchema = z.object({
|
|
89
|
+
type: z.literal("reasoning-delta"),
|
|
90
|
+
id: z.string(),
|
|
91
|
+
text: z.string(),
|
|
92
|
+
metadata: streamMetadataSchema
|
|
93
|
+
});
|
|
94
|
+
const reasoningEndPartSchema = z.object({
|
|
95
|
+
type: z.literal("reasoning-end"),
|
|
96
|
+
id: z.string(),
|
|
97
|
+
metadata: streamMetadataSchema
|
|
98
|
+
});
|
|
99
|
+
const toolInputStartPartSchema = z.object({
|
|
100
|
+
type: z.literal("tool-input-start"),
|
|
101
|
+
id: z.string(),
|
|
102
|
+
toolName: z.string(),
|
|
103
|
+
metadata: streamMetadataSchema
|
|
104
|
+
});
|
|
105
|
+
const toolInputDeltaPartSchema = z.object({
|
|
106
|
+
type: z.literal("tool-input-delta"),
|
|
107
|
+
id: z.string(),
|
|
108
|
+
delta: z.string(),
|
|
109
|
+
metadata: streamMetadataSchema
|
|
110
|
+
});
|
|
111
|
+
const toolInputEndPartSchema = z.object({
|
|
112
|
+
type: z.literal("tool-input-end"),
|
|
113
|
+
id: z.string(),
|
|
114
|
+
metadata: streamMetadataSchema
|
|
115
|
+
});
|
|
116
|
+
const toolCallPartSchema = z.object({
|
|
117
|
+
type: z.literal("tool-call"),
|
|
118
|
+
toolCallId: z.string(),
|
|
119
|
+
toolName: z.string(),
|
|
120
|
+
input: z.record(z.unknown()),
|
|
121
|
+
metadata: streamMetadataSchema
|
|
122
|
+
});
|
|
123
|
+
const toolResultPartSchema = z.object({
|
|
124
|
+
type: z.literal("tool-result"),
|
|
125
|
+
toolCallId: z.string(),
|
|
126
|
+
toolName: z.string(),
|
|
127
|
+
output: z.unknown(),
|
|
128
|
+
metadata: streamMetadataSchema
|
|
129
|
+
});
|
|
130
|
+
const errorPartSchema = z.object({
|
|
131
|
+
type: z.literal("error"),
|
|
132
|
+
error: z.string(),
|
|
133
|
+
rawContent: z.unknown().optional()
|
|
134
|
+
});
|
|
135
|
+
/**
|
|
136
|
+
* Zod schema for v2 TextStreamPart events from the backend.
|
|
137
|
+
* Based on Vercel AI SDK's TextStreamPart with extensions for reasoning and tool streaming.
|
|
138
|
+
*/
|
|
139
|
+
const textStreamPartSchema = z.discriminatedUnion("type", [
|
|
140
|
+
startPartSchema,
|
|
141
|
+
startStepPartSchema,
|
|
142
|
+
finishStepPartSchema,
|
|
143
|
+
finishPartSchema,
|
|
144
|
+
textStartPartSchema,
|
|
145
|
+
textDeltaPartSchema,
|
|
146
|
+
textEndPartSchema,
|
|
147
|
+
reasoningStartPartSchema,
|
|
148
|
+
reasoningDeltaPartSchema,
|
|
149
|
+
reasoningEndPartSchema,
|
|
150
|
+
toolInputStartPartSchema,
|
|
151
|
+
toolInputDeltaPartSchema,
|
|
152
|
+
toolInputEndPartSchema,
|
|
153
|
+
toolCallPartSchema,
|
|
154
|
+
toolResultPartSchema,
|
|
155
|
+
errorPartSchema
|
|
156
|
+
]);
|
|
157
|
+
/**
|
|
158
|
+
* Creates an async generator that yields v2 TextStreamPart events.
|
|
159
|
+
* Uses @ai-sdk/provider-utils for SSE parsing.
|
|
160
|
+
*/
|
|
161
|
+
async function* createTaskEventGenerator(config, taskId, options = {}) {
|
|
162
|
+
const { signal } = options;
|
|
163
|
+
const { value: responseStream } = await getFromApi({
|
|
164
|
+
url: `${config.baseURL}/tasks/${encodeURIComponent(taskId)}/stream`,
|
|
165
|
+
headers: config.apiKey ? { Authorization: `Bearer ${config.apiKey}` } : {},
|
|
166
|
+
abortSignal: signal,
|
|
167
|
+
successfulResponseHandler: createEventSourceResponseHandler(textStreamPartSchema),
|
|
168
|
+
failedResponseHandler: async ({ response }) => {
|
|
169
|
+
const text = await response.text();
|
|
170
|
+
return { value: /* @__PURE__ */ new Error(`Stream request failed: ${response.status} - ${text}`) };
|
|
171
|
+
}
|
|
172
|
+
});
|
|
173
|
+
const reader = responseStream.getReader();
|
|
174
|
+
try {
|
|
175
|
+
while (true) {
|
|
176
|
+
const { done, value } = await reader.read();
|
|
177
|
+
if (done) break;
|
|
178
|
+
const parseResult = value;
|
|
179
|
+
if (parseResult.success) yield parseResult.value;
|
|
180
|
+
}
|
|
181
|
+
} finally {
|
|
182
|
+
reader.releaseLock();
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
//#endregion
|
|
187
|
+
//#region src/stream.ts
|
|
188
|
+
/**
|
|
189
|
+
* Creates a ReadableStream that transforms v2 TextStreamPart events to AI SDK v3 format.
|
|
190
|
+
* Implements a thin passthrough adapter with minimal transformation logic.
|
|
191
|
+
*/
|
|
192
|
+
function createTerminalUseTransformStream(config, taskId, signal) {
|
|
193
|
+
const eventGenerator = createTaskEventGenerator(config, taskId, { signal });
|
|
194
|
+
let emittedStreamStart = false;
|
|
195
|
+
return new ReadableStream({
|
|
196
|
+
async pull(controller) {
|
|
197
|
+
if (!emittedStreamStart) {
|
|
198
|
+
emittedStreamStart = true;
|
|
199
|
+
controller.enqueue({
|
|
200
|
+
type: "stream-start",
|
|
201
|
+
warnings: []
|
|
202
|
+
});
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
try {
|
|
206
|
+
while (true) {
|
|
207
|
+
const { value, done } = await eventGenerator.next();
|
|
208
|
+
if (done || signal?.aborted) {
|
|
209
|
+
controller.close();
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
const event = value;
|
|
213
|
+
const transformed = transformEvent(event, taskId);
|
|
214
|
+
if (event.type === "finish" || event.type === "error") {
|
|
215
|
+
if (transformed) controller.enqueue(transformed);
|
|
216
|
+
controller.close();
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
if (transformed) {
|
|
220
|
+
controller.enqueue(transformed);
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
} catch (error) {
|
|
225
|
+
eventGenerator.return?.(void 0);
|
|
226
|
+
controller.error(error);
|
|
227
|
+
}
|
|
228
|
+
},
|
|
229
|
+
cancel() {
|
|
230
|
+
eventGenerator.return?.(void 0);
|
|
231
|
+
}
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* Transforms a single v2 TextStreamPart event to AI SDK v3 LanguageModelV3StreamPart.
|
|
236
|
+
* Returns null for events that should be skipped (e.g., finish-step, start).
|
|
237
|
+
*/
|
|
238
|
+
function transformEvent(event, taskId) {
|
|
239
|
+
switch (event.type) {
|
|
240
|
+
case "start": return null;
|
|
241
|
+
case "start-step": return {
|
|
242
|
+
type: "response-metadata",
|
|
243
|
+
id: taskId,
|
|
244
|
+
timestamp: /* @__PURE__ */ new Date(),
|
|
245
|
+
modelId: "terminaluse-agent"
|
|
246
|
+
};
|
|
247
|
+
case "finish-step": return null;
|
|
248
|
+
case "finish": return {
|
|
249
|
+
type: "finish",
|
|
250
|
+
finishReason: {
|
|
251
|
+
unified: event.finishReason,
|
|
252
|
+
raw: event.finishReason
|
|
253
|
+
},
|
|
254
|
+
usage: {
|
|
255
|
+
inputTokens: {
|
|
256
|
+
total: event.totalUsage.inputTokens,
|
|
257
|
+
noCache: void 0,
|
|
258
|
+
cacheRead: void 0,
|
|
259
|
+
cacheWrite: void 0
|
|
260
|
+
},
|
|
261
|
+
outputTokens: {
|
|
262
|
+
total: event.totalUsage.outputTokens,
|
|
263
|
+
text: void 0,
|
|
264
|
+
reasoning: void 0
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
};
|
|
268
|
+
case "text-start": return {
|
|
269
|
+
type: "text-start",
|
|
270
|
+
id: event.id
|
|
271
|
+
};
|
|
272
|
+
case "text-delta": return {
|
|
273
|
+
type: "text-delta",
|
|
274
|
+
id: event.id,
|
|
275
|
+
delta: event.text
|
|
276
|
+
};
|
|
277
|
+
case "text-end": return {
|
|
278
|
+
type: "text-end",
|
|
279
|
+
id: event.id
|
|
280
|
+
};
|
|
281
|
+
case "reasoning-start": return {
|
|
282
|
+
type: "reasoning-start",
|
|
283
|
+
id: event.id
|
|
284
|
+
};
|
|
285
|
+
case "reasoning-delta": return {
|
|
286
|
+
type: "reasoning-delta",
|
|
287
|
+
id: event.id,
|
|
288
|
+
delta: event.text
|
|
289
|
+
};
|
|
290
|
+
case "reasoning-end": return {
|
|
291
|
+
type: "reasoning-end",
|
|
292
|
+
id: event.id
|
|
293
|
+
};
|
|
294
|
+
case "tool-input-start": return {
|
|
295
|
+
type: "tool-input-start",
|
|
296
|
+
id: event.id,
|
|
297
|
+
toolName: event.toolName,
|
|
298
|
+
providerExecuted: true,
|
|
299
|
+
dynamic: true
|
|
300
|
+
};
|
|
301
|
+
case "tool-input-delta": return {
|
|
302
|
+
type: "tool-input-delta",
|
|
303
|
+
id: event.id,
|
|
304
|
+
delta: event.delta
|
|
305
|
+
};
|
|
306
|
+
case "tool-input-end": return {
|
|
307
|
+
type: "tool-input-end",
|
|
308
|
+
id: event.id
|
|
309
|
+
};
|
|
310
|
+
case "tool-call": return {
|
|
311
|
+
type: "tool-call",
|
|
312
|
+
toolCallId: event.toolCallId,
|
|
313
|
+
toolName: event.toolName,
|
|
314
|
+
input: JSON.stringify(event.input),
|
|
315
|
+
providerExecuted: true,
|
|
316
|
+
dynamic: true
|
|
317
|
+
};
|
|
318
|
+
case "tool-result": {
|
|
319
|
+
const result = event.output ?? "";
|
|
320
|
+
return {
|
|
321
|
+
type: "tool-result",
|
|
322
|
+
toolCallId: event.toolCallId,
|
|
323
|
+
toolName: event.toolName,
|
|
324
|
+
result
|
|
325
|
+
};
|
|
326
|
+
}
|
|
327
|
+
case "error": return {
|
|
328
|
+
type: "error",
|
|
329
|
+
error: event.error
|
|
330
|
+
};
|
|
331
|
+
default: return null;
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
//#endregion
|
|
336
|
+
//#region src/provider.ts
|
|
337
|
+
/**
|
|
338
|
+
* Creates a custom AI SDK provider for TerminalUse agents.
|
|
339
|
+
*
|
|
340
|
+
* This provider implements the LanguageModelV3 interface, bridging
|
|
341
|
+
* Vercel AI SDK's useChat/streamText to TerminalUse's RPC-based API.
|
|
342
|
+
*
|
|
343
|
+
* Note: Tasks must be created separately via the /api/tasks endpoint
|
|
344
|
+
* before sending messages.
|
|
345
|
+
*
|
|
346
|
+
* @example
|
|
347
|
+
* ```ts
|
|
348
|
+
* const terminaluse = createTerminalUseProvider({
|
|
349
|
+
* apiKey: process.env.TERMINALUSE_API_KEY!,
|
|
350
|
+
* baseURL: process.env.TERMINALUSE_API_BASE_URL!,
|
|
351
|
+
* });
|
|
352
|
+
*
|
|
353
|
+
* const result = await streamText({
|
|
354
|
+
* model: terminaluse.agent('namespace/agent-name'),
|
|
355
|
+
* messages,
|
|
356
|
+
* providerOptions: {
|
|
357
|
+
* terminaluse: {
|
|
358
|
+
* taskId: 'existing-task-id',
|
|
359
|
+
* },
|
|
360
|
+
* },
|
|
361
|
+
* });
|
|
362
|
+
* ```
|
|
363
|
+
*/
|
|
364
|
+
function createTerminalUseProvider(config) {
|
|
365
|
+
const tuConfig = {
|
|
366
|
+
baseURL: config.baseURL,
|
|
367
|
+
apiKey: config.apiKey
|
|
368
|
+
};
|
|
369
|
+
return { agent(agentName) {
|
|
370
|
+
return {
|
|
371
|
+
specificationVersion: "v3",
|
|
372
|
+
provider: "terminaluse",
|
|
373
|
+
modelId: agentName,
|
|
374
|
+
supportedUrls: {},
|
|
375
|
+
async doGenerate() {
|
|
376
|
+
throw new UnsupportedFunctionalityError({
|
|
377
|
+
functionality: "non-streaming generation",
|
|
378
|
+
message: "TerminalUse provider only supports streaming. Use streamText() instead of generateText()."
|
|
379
|
+
});
|
|
380
|
+
},
|
|
381
|
+
async doStream(options) {
|
|
382
|
+
const { prompt, providerOptions, abortSignal } = options;
|
|
383
|
+
const tuOptions = providerOptions?.terminaluse;
|
|
384
|
+
if (!tuOptions?.taskId) throw new Error("taskId is required. Create a task via /api/tasks first.");
|
|
385
|
+
const { taskId } = tuOptions;
|
|
386
|
+
const userContent = prompt.at(-1)?.content;
|
|
387
|
+
let textContent;
|
|
388
|
+
if (Array.isArray(userContent)) {
|
|
389
|
+
const textPart = userContent.find((c) => c.type === "text");
|
|
390
|
+
if (textPart && textPart.type === "text") textContent = textPart.text;
|
|
391
|
+
} else if (typeof userContent === "string") textContent = userContent;
|
|
392
|
+
const sendResponse = await terminaluseFetch(tuConfig, `/tasks/${encodeURIComponent(taskId)}/events`, {
|
|
393
|
+
method: "POST",
|
|
394
|
+
body: JSON.stringify({ content: {
|
|
395
|
+
type: "text",
|
|
396
|
+
content: textContent || "",
|
|
397
|
+
author: "user",
|
|
398
|
+
format: "plain"
|
|
399
|
+
} })
|
|
400
|
+
});
|
|
401
|
+
if (!sendResponse.ok) {
|
|
402
|
+
const errorText = await sendResponse.text();
|
|
403
|
+
throw new Error(`Failed to send message: ${sendResponse.status} - ${errorText}`);
|
|
404
|
+
}
|
|
405
|
+
return { stream: createTerminalUseTransformStream(tuConfig, taskId, abortSignal) };
|
|
406
|
+
}
|
|
407
|
+
};
|
|
408
|
+
} };
|
|
409
|
+
}
|
|
3
410
|
|
|
4
411
|
//#endregion
|
|
5
|
-
export {
|
|
412
|
+
export { createTerminalUseProvider };
|
package/package.json
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@terminaluse/vercel-ai-sdk-provider",
|
|
3
|
-
"version": "0.0
|
|
4
|
-
"description": "Vercel AI SDK provider for
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Vercel AI SDK provider for TerminalUse agents",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
7
|
-
"url": "git+https://github.com/
|
|
7
|
+
"url": "git+https://github.com/terminal-use/monorepo.git",
|
|
8
|
+
"directory": "packages/vercel-ai-sdk-provider"
|
|
8
9
|
},
|
|
10
|
+
"license": "Apache-2.0",
|
|
9
11
|
"type": "module",
|
|
10
12
|
"main": "./dist/index.mjs",
|
|
11
13
|
"module": "./dist/index.mjs",
|
|
@@ -23,16 +25,30 @@
|
|
|
23
25
|
"scripts": {
|
|
24
26
|
"build": "tsdown",
|
|
25
27
|
"dev": "tsdown --watch",
|
|
26
|
-
"test": "
|
|
28
|
+
"test": "vitest run",
|
|
29
|
+
"test:watch": "vitest",
|
|
27
30
|
"typecheck": "tsc --noEmit",
|
|
31
|
+
"lint": "biome lint .",
|
|
32
|
+
"format": "biome format --write .",
|
|
33
|
+
"check": "biome check --write .",
|
|
28
34
|
"prepublishOnly": "bun run build"
|
|
29
35
|
},
|
|
36
|
+
"dependencies": {
|
|
37
|
+
"@ai-sdk/provider": "^3.0.0",
|
|
38
|
+
"@ai-sdk/provider-utils": "^4.0.0",
|
|
39
|
+
"zod": "^3.24.0"
|
|
40
|
+
},
|
|
41
|
+
"peerDependencies": {
|
|
42
|
+
"ai": "^6.0.0"
|
|
43
|
+
},
|
|
30
44
|
"devDependencies": {
|
|
31
|
-
"@
|
|
45
|
+
"@biomejs/biome": "^2.3.11",
|
|
46
|
+
"@types/node": "^20",
|
|
32
47
|
"tsdown": "^0.16.0",
|
|
33
|
-
"typescript": "^5
|
|
48
|
+
"typescript": "^5",
|
|
49
|
+
"vitest": "^4.0.16"
|
|
34
50
|
},
|
|
35
|
-
"
|
|
36
|
-
"
|
|
51
|
+
"engines": {
|
|
52
|
+
"node": ">=18.0.0"
|
|
37
53
|
}
|
|
38
54
|
}
|