agent-worker 0.3.0 → 0.5.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-worker",
3
- "version": "0.3.0",
3
+ "version": "0.5.0",
4
4
  "description": "SDK and CLI for creating and testing agent workers with Vercel AI SDK",
5
5
  "type": "module",
6
6
  "main": "./dist/index.mjs",
@@ -30,10 +30,16 @@
30
30
  "prepublishOnly": "bun run build"
31
31
  },
32
32
  "dependencies": {
33
+ "@modelcontextprotocol/sdk": "^1.26.0",
33
34
  "ai": "^6.0.69",
34
35
  "bash-tool": "^1.3.12",
36
+ "chalk": "^5.6.2",
35
37
  "commander": "^14.0.3",
38
+ "execa": "^9.6.1",
36
39
  "just-bash": "^2.8.0",
40
+ "nanoid": "^5.1.6",
41
+ "string-width": "^8.1.1",
42
+ "wrap-ansi": "^9.0.2",
37
43
  "yaml": "^2.7.0",
38
44
  "zod": "^3.24.1"
39
45
  },
@@ -52,14 +58,14 @@
52
58
  "tsdown": "^0.20.1"
53
59
  },
54
60
  "peerDependencies": {
55
- "typescript": "^5",
56
61
  "@ai-sdk/anthropic": "^3.0.0",
57
- "@ai-sdk/openai": "^3.0.0",
58
62
  "@ai-sdk/deepseek": "^1.0.0",
59
63
  "@ai-sdk/google": "^1.0.0",
60
64
  "@ai-sdk/groq": "^1.0.0",
61
65
  "@ai-sdk/mistral": "^1.0.0",
62
- "@ai-sdk/xai": "^1.0.0"
66
+ "@ai-sdk/openai": "^3.0.0",
67
+ "@ai-sdk/xai": "^1.0.0",
68
+ "typescript": "^5"
63
69
  },
64
70
  "peerDependenciesMeta": {
65
71
  "@ai-sdk/anthropic": {
@@ -1,564 +0,0 @@
1
- import { gateway, generateText } from "ai";
2
- import { spawn } from "node:child_process";
3
-
4
- //#region src/models.ts
5
- const providerCache = {};
6
- /**
7
- * Lazy load a provider, caching the result
8
- * Supports custom baseURL and apiKey for providers using compatible APIs (e.g., MiniMax using Claude API)
9
- */
10
- async function loadProvider(name, packageName, exportName, options) {
11
- if (name in providerCache) return providerCache[name];
12
- try {
13
- const module = await import(packageName);
14
- if (options?.baseURL || options?.apiKeyEnvVar) {
15
- const createProvider = module[`create${exportName.charAt(0).toUpperCase() + exportName.slice(1)}`];
16
- if (createProvider) {
17
- const providerOptions = {};
18
- if (options.baseURL) providerOptions.baseURL = options.baseURL;
19
- if (options.apiKeyEnvVar) providerOptions.apiKey = process.env[options.apiKeyEnvVar];
20
- providerCache[name] = createProvider(providerOptions);
21
- return providerCache[name];
22
- }
23
- }
24
- providerCache[name] = module[exportName];
25
- return providerCache[name];
26
- } catch {
27
- providerCache[name] = null;
28
- return null;
29
- }
30
- }
31
- /**
32
- * Parse model identifier and return the appropriate provider model
33
- *
34
- * Supports three formats:
35
- *
36
- * 1. Provider-only format: provider
37
- * Uses first model from FRONTIER_MODELS via gateway
38
- * Examples: anthropic → anthropic/claude-sonnet-4-5, openai → openai/gpt-5.2
39
- *
40
- * 2. Gateway format: provider/model-name
41
- * Uses Vercel AI Gateway (requires AI_GATEWAY_API_KEY)
42
- * Examples: anthropic/claude-sonnet-4-5, openai/gpt-5.2, deepseek/deepseek-chat
43
- *
44
- * 3. Direct provider format: provider:model-name
45
- * Requires installing the specific @ai-sdk/provider package
46
- * Examples: anthropic:claude-sonnet-4-5, openai:gpt-5.2, deepseek:deepseek-chat
47
- */
48
- function createModel(modelId) {
49
- if (modelId.includes("/")) return gateway(modelId);
50
- if (!modelId.includes(":")) {
51
- const provider = modelId;
52
- if (provider in FRONTIER_MODELS) {
53
- const defaultModel = FRONTIER_MODELS[provider][0];
54
- return gateway(`${provider}/${defaultModel}`);
55
- }
56
- throw new Error(`Unknown provider: ${modelId}. Supported: ${Object.keys(FRONTIER_MODELS).join(", ")}`);
57
- }
58
- const colonIndex = modelId.indexOf(":");
59
- const provider = modelId.slice(0, colonIndex);
60
- const modelName = modelId.slice(colonIndex + 1);
61
- if (!modelName) throw new Error(`Invalid model identifier: ${modelId}. Model name is required.`);
62
- if (provider in providerCache && providerCache[provider]) return providerCache[provider](modelName);
63
- throw new Error(`Provider '${provider}' not loaded. Use gateway format (${provider}/${modelName}) or call createModelAsync() for direct provider access.`);
64
- }
65
- /**
66
- * Async version of createModel - supports lazy loading of direct providers
67
- * Use this when you need direct provider access (provider:model format)
68
- */
69
- async function createModelAsync(modelId) {
70
- if (modelId.includes("/")) return gateway(modelId);
71
- if (!modelId.includes(":")) {
72
- const provider = modelId;
73
- if (provider in FRONTIER_MODELS) {
74
- const defaultModel = FRONTIER_MODELS[provider][0];
75
- return gateway(`${provider}/${defaultModel}`);
76
- }
77
- throw new Error(`Unknown provider: ${modelId}. Supported: ${Object.keys(FRONTIER_MODELS).join(", ")}`);
78
- }
79
- const colonIndex = modelId.indexOf(":");
80
- const provider = modelId.slice(0, colonIndex);
81
- const modelName = modelId.slice(colonIndex + 1);
82
- if (!modelName) throw new Error(`Invalid model identifier: ${modelId}. Model name is required.`);
83
- const providerConfigs = {
84
- anthropic: {
85
- package: "@ai-sdk/anthropic",
86
- export: "anthropic"
87
- },
88
- openai: {
89
- package: "@ai-sdk/openai",
90
- export: "openai"
91
- },
92
- deepseek: {
93
- package: "@ai-sdk/deepseek",
94
- export: "deepseek"
95
- },
96
- google: {
97
- package: "@ai-sdk/google",
98
- export: "google"
99
- },
100
- groq: {
101
- package: "@ai-sdk/groq",
102
- export: "groq"
103
- },
104
- mistral: {
105
- package: "@ai-sdk/mistral",
106
- export: "mistral"
107
- },
108
- xai: {
109
- package: "@ai-sdk/xai",
110
- export: "xai"
111
- },
112
- minimax: {
113
- package: "@ai-sdk/anthropic",
114
- export: "anthropic",
115
- options: {
116
- baseURL: "https://api.minimax.chat/v1",
117
- apiKeyEnvVar: "MINIMAX_API_KEY"
118
- }
119
- }
120
- };
121
- const config = providerConfigs[provider];
122
- if (!config) throw new Error(`Unknown provider: ${provider}. Supported: ${Object.keys(providerConfigs).join(", ")}. Or use gateway format: provider/model (e.g., openai/gpt-5.2)`);
123
- const providerFn = await loadProvider(provider, config.package, config.export, config.options);
124
- if (!providerFn) throw new Error(`Install ${config.package} to use ${provider} models directly`);
125
- return providerFn(modelName);
126
- }
127
- /**
128
- * List of supported providers for direct access
129
- * Note: minimax uses Claude-compatible API via @ai-sdk/anthropic with custom baseURL
130
- */
131
- const SUPPORTED_PROVIDERS = [
132
- "anthropic",
133
- "openai",
134
- "deepseek",
135
- "google",
136
- "groq",
137
- "mistral",
138
- "xai",
139
- "minimax"
140
- ];
141
- /**
142
- * Default provider when none specified
143
- */
144
- const DEFAULT_PROVIDER = "anthropic";
145
- /**
146
- * Get the default model identifier (provider/model format)
147
- * Uses the first model from the default provider
148
- */
149
- function getDefaultModel() {
150
- return `${DEFAULT_PROVIDER}/${FRONTIER_MODELS[DEFAULT_PROVIDER][0]}`;
151
- }
152
- /**
153
- * Frontier models for each provider (as of 2026-02)
154
- * Only includes the latest/best models, no legacy versions
155
- *
156
- * Note: Some models may be placeholders for testing or future releases.
157
- * Always verify model availability with the provider before production use.
158
- */
159
- const FRONTIER_MODELS = {
160
- anthropic: [
161
- "claude-sonnet-4-5",
162
- "claude-haiku-4-5",
163
- "claude-opus-4-5"
164
- ],
165
- openai: ["gpt-5.2", "gpt-5.2-codex"],
166
- google: [
167
- "gemini-3-pro-preview",
168
- "gemini-2.5-flash",
169
- "gemini-2.5-pro"
170
- ],
171
- deepseek: ["deepseek-chat", "deepseek-reasoner"],
172
- groq: ["meta-llama/llama-4-scout-17b-16e-instruct", "deepseek-r1-distill-llama-70b"],
173
- mistral: [
174
- "mistral-large-latest",
175
- "pixtral-large-latest",
176
- "magistral-medium-2506"
177
- ],
178
- xai: ["grok-4", "grok-4-fast-reasoning"],
179
- minimax: ["MiniMax-M2"]
180
- };
181
-
182
- //#endregion
183
- //#region src/backends/claude-cli.ts
184
- /**
185
- * Claude Code CLI backend
186
- * Uses `claude -p` for non-interactive mode
187
- *
188
- * @see https://code.claude.com/docs/en/headless
189
- */
190
- var ClaudeCliBackend = class {
191
- type = "claude";
192
- options;
193
- constructor(options = {}) {
194
- this.options = options;
195
- }
196
- async send(message, options) {
197
- const args = this.buildArgs(message, options);
198
- return new Promise((resolve, reject) => {
199
- const proc = spawn("claude", args, {
200
- cwd: this.options.cwd,
201
- env: process.env,
202
- stdio: [
203
- "ignore",
204
- "pipe",
205
- "pipe"
206
- ]
207
- });
208
- let stdout = "";
209
- let stderr = "";
210
- proc.stdout.on("data", (data) => {
211
- stdout += data.toString();
212
- });
213
- proc.stderr.on("data", (data) => {
214
- stderr += data.toString();
215
- });
216
- proc.on("close", (code) => {
217
- if (code !== 0) {
218
- reject(/* @__PURE__ */ new Error(`claude exited with code ${code}: ${stderr}`));
219
- return;
220
- }
221
- if (this.options.outputFormat === "json") try {
222
- const parsed = JSON.parse(stdout);
223
- resolve({
224
- content: parsed.content || parsed.result || stdout,
225
- toolCalls: parsed.toolCalls,
226
- usage: parsed.usage
227
- });
228
- } catch {
229
- resolve({ content: stdout.trim() });
230
- }
231
- else resolve({ content: stdout.trim() });
232
- });
233
- proc.on("error", (err) => {
234
- reject(/* @__PURE__ */ new Error(`Failed to spawn claude: ${err.message}`));
235
- });
236
- });
237
- }
238
- async isAvailable() {
239
- return new Promise((resolve) => {
240
- const proc = spawn("claude", ["--version"], { stdio: "pipe" });
241
- proc.on("close", (code) => resolve(code === 0));
242
- proc.on("error", () => resolve(false));
243
- });
244
- }
245
- getInfo() {
246
- return {
247
- name: "Claude Code CLI",
248
- model: this.options.model
249
- };
250
- }
251
- buildArgs(message, options) {
252
- const args = ["-p", message];
253
- if (this.options.model) args.push("--model", this.options.model);
254
- if (options?.system || this.options.appendSystemPrompt) {
255
- const system = options?.system || this.options.appendSystemPrompt;
256
- args.push("--append-system-prompt", system);
257
- }
258
- if (this.options.allowedTools?.length) args.push("--allowed-tools", this.options.allowedTools.join(","));
259
- if (this.options.outputFormat) args.push("--output-format", this.options.outputFormat);
260
- if (this.options.continue) args.push("--continue");
261
- if (this.options.resume) args.push("--resume", this.options.resume);
262
- return args;
263
- }
264
- };
265
-
266
- //#endregion
267
- //#region src/backends/codex-cli.ts
268
- /**
269
- * OpenAI Codex CLI backend
270
- * Uses `codex exec` for non-interactive mode
271
- *
272
- * @see https://developers.openai.com/codex/noninteractive/
273
- */
274
- var CodexCliBackend = class {
275
- type = "codex";
276
- options;
277
- constructor(options = {}) {
278
- this.options = options;
279
- }
280
- async send(message, _options) {
281
- const args = this.buildArgs(message);
282
- return new Promise((resolve, reject) => {
283
- const proc = spawn("codex", args, {
284
- cwd: this.options.cwd,
285
- env: process.env
286
- });
287
- let stdout = "";
288
- let stderr = "";
289
- proc.stdout.on("data", (data) => {
290
- stdout += data.toString();
291
- });
292
- proc.stderr.on("data", (data) => {
293
- stderr += data.toString();
294
- });
295
- proc.on("close", (code) => {
296
- if (code !== 0) {
297
- reject(/* @__PURE__ */ new Error(`codex exited with code ${code}: ${stderr}`));
298
- return;
299
- }
300
- if (this.options.json) try {
301
- const lines = stdout.trim().split("\n");
302
- const lastEvent = JSON.parse(lines[lines.length - 1]);
303
- resolve({
304
- content: lastEvent.message || lastEvent.content || stdout,
305
- toolCalls: lastEvent.toolCalls,
306
- usage: lastEvent.usage
307
- });
308
- } catch {
309
- resolve({ content: stdout.trim() });
310
- }
311
- else resolve({ content: stdout.trim() });
312
- });
313
- proc.on("error", (err) => {
314
- reject(/* @__PURE__ */ new Error(`Failed to spawn codex: ${err.message}`));
315
- });
316
- });
317
- }
318
- async isAvailable() {
319
- return new Promise((resolve) => {
320
- const proc = spawn("codex", ["--version"], { stdio: "pipe" });
321
- proc.on("close", (code) => resolve(code === 0));
322
- proc.on("error", () => resolve(false));
323
- });
324
- }
325
- getInfo() {
326
- return {
327
- name: "OpenAI Codex CLI",
328
- model: this.options.model
329
- };
330
- }
331
- buildArgs(message) {
332
- const args = ["exec", message];
333
- if (this.options.model) args.push("--model", this.options.model);
334
- if (this.options.json) args.push("--json");
335
- if (this.options.skipGitRepoCheck) args.push("--skip-git-repo-check");
336
- if (this.options.approvalMode) args.push("--approval-mode", this.options.approvalMode);
337
- if (this.options.resume) args.push("--resume", this.options.resume);
338
- return args;
339
- }
340
- };
341
-
342
- //#endregion
343
- //#region src/backends/cursor-cli.ts
344
- /**
345
- * Cursor CLI backend
346
- * Uses `cursor-agent -p` or `agent chat` for non-interactive mode
347
- *
348
- * @see https://cursor.com/docs/cli/headless
349
- */
350
- var CursorCliBackend = class {
351
- type = "cursor";
352
- options;
353
- constructor(options = {}) {
354
- this.options = {
355
- timeout: 12e4,
356
- ...options
357
- };
358
- }
359
- async send(message, _options) {
360
- const { command, args } = this.buildCommand(message);
361
- return new Promise((resolve, reject) => {
362
- const proc = spawn(command, args, {
363
- cwd: this.options.cwd,
364
- env: process.env
365
- });
366
- let stdout = "";
367
- let stderr = "";
368
- let timedOut = false;
369
- const timer = setTimeout(() => {
370
- timedOut = true;
371
- proc.kill("SIGTERM");
372
- }, this.options.timeout);
373
- proc.stdout.on("data", (data) => {
374
- stdout += data.toString();
375
- });
376
- proc.stderr.on("data", (data) => {
377
- stderr += data.toString();
378
- });
379
- proc.on("close", (code) => {
380
- clearTimeout(timer);
381
- if (timedOut) {
382
- reject(/* @__PURE__ */ new Error(`cursor-agent timed out after ${this.options.timeout}ms`));
383
- return;
384
- }
385
- if (code !== 0 && code !== null) {
386
- reject(/* @__PURE__ */ new Error(`cursor-agent exited with code ${code}: ${stderr}`));
387
- return;
388
- }
389
- resolve({ content: stdout.trim() });
390
- });
391
- proc.on("error", (err) => {
392
- clearTimeout(timer);
393
- reject(/* @__PURE__ */ new Error(`Failed to spawn cursor-agent: ${err.message}`));
394
- });
395
- });
396
- }
397
- async isAvailable() {
398
- for (const cmd of ["cursor-agent", "agent"]) if (await this.checkCommand(cmd)) return true;
399
- return false;
400
- }
401
- async checkCommand(command) {
402
- return new Promise((resolve) => {
403
- const proc = spawn(command, ["--version"], { stdio: "pipe" });
404
- const timer = setTimeout(() => {
405
- proc.kill();
406
- resolve(false);
407
- }, 5e3);
408
- proc.on("close", (code) => {
409
- clearTimeout(timer);
410
- resolve(code === 0);
411
- });
412
- proc.on("error", () => {
413
- clearTimeout(timer);
414
- resolve(false);
415
- });
416
- });
417
- }
418
- getInfo() {
419
- return {
420
- name: "Cursor Agent CLI",
421
- model: this.options.model
422
- };
423
- }
424
- buildCommand(message) {
425
- if (this.options.useAgentCommand) return {
426
- command: "agent",
427
- args: ["chat", message]
428
- };
429
- const args = ["-p", message];
430
- if (this.options.model) args.push("--model", this.options.model);
431
- return {
432
- command: "cursor-agent",
433
- args
434
- };
435
- }
436
- };
437
-
438
- //#endregion
439
- //#region src/backends/sdk.ts
440
- /**
441
- * Vercel AI SDK backend
442
- * Uses the AI SDK for direct API access
443
- */
444
- var SdkBackend = class {
445
- type = "sdk";
446
- modelId;
447
- model = null;
448
- maxTokens;
449
- constructor(options) {
450
- this.modelId = options.model;
451
- this.maxTokens = options.maxTokens ?? 4096;
452
- try {
453
- this.model = createModel(this.modelId);
454
- } catch {}
455
- }
456
- async send(message, options) {
457
- if (!this.model) this.model = await createModelAsync(this.modelId);
458
- const result = await generateText({
459
- model: this.model,
460
- system: options?.system,
461
- prompt: message,
462
- maxTokens: this.maxTokens
463
- });
464
- return {
465
- content: result.text,
466
- usage: {
467
- input: result.usage.promptTokens,
468
- output: result.usage.completionTokens,
469
- total: result.usage.totalTokens
470
- }
471
- };
472
- }
473
- async isAvailable() {
474
- try {
475
- if (!this.model) this.model = await createModelAsync(this.modelId);
476
- return true;
477
- } catch {
478
- return false;
479
- }
480
- }
481
- getInfo() {
482
- return {
483
- name: "Vercel AI SDK",
484
- model: this.modelId
485
- };
486
- }
487
- };
488
-
489
- //#endregion
490
- //#region src/backends/index.ts
491
- /**
492
- * Create a backend instance
493
- */
494
- function createBackend(config) {
495
- switch (config.type) {
496
- case "sdk": return new SdkBackend({
497
- model: config.model,
498
- maxTokens: config.maxTokens
499
- });
500
- case "claude": return new ClaudeCliBackend({
501
- ...config.options,
502
- model: config.model
503
- });
504
- case "codex": return new CodexCliBackend({
505
- ...config.options,
506
- model: config.model
507
- });
508
- case "cursor": return new CursorCliBackend({
509
- ...config.options,
510
- model: config.model
511
- });
512
- default: throw new Error(`Unknown backend type: ${config.type}`);
513
- }
514
- }
515
- /**
516
- * Check which backends are available
517
- */
518
- async function checkBackends() {
519
- const claude = new ClaudeCliBackend();
520
- const codex = new CodexCliBackend();
521
- const cursor = new CursorCliBackend();
522
- const [claudeAvailable, codexAvailable, cursorAvailable] = await Promise.all([
523
- claude.isAvailable(),
524
- codex.isAvailable(),
525
- cursor.isAvailable()
526
- ]);
527
- return {
528
- sdk: true,
529
- claude: claudeAvailable,
530
- codex: codexAvailable,
531
- cursor: cursorAvailable
532
- };
533
- }
534
- /**
535
- * List available backends with info
536
- */
537
- async function listBackends() {
538
- const availability = await checkBackends();
539
- return [
540
- {
541
- type: "sdk",
542
- available: availability.sdk,
543
- name: "Vercel AI SDK"
544
- },
545
- {
546
- type: "claude",
547
- available: availability.claude,
548
- name: "Claude Code CLI"
549
- },
550
- {
551
- type: "codex",
552
- available: availability.codex,
553
- name: "OpenAI Codex CLI"
554
- },
555
- {
556
- type: "cursor",
557
- available: availability.cursor,
558
- name: "Cursor Agent CLI"
559
- }
560
- ];
561
- }
562
-
563
- //#endregion
564
- export { CursorCliBackend as a, FRONTIER_MODELS as c, createModelAsync as d, getDefaultModel as f, SdkBackend as i, SUPPORTED_PROVIDERS as l, createBackend as n, CodexCliBackend as o, listBackends as r, ClaudeCliBackend as s, checkBackends as t, createModel as u };
@@ -1,3 +0,0 @@
1
- import { a as CursorCliBackend, i as SdkBackend, n as createBackend, o as CodexCliBackend, r as listBackends, s as ClaudeCliBackend, t as checkBackends } from "./backends-BZ866Ij9.mjs";
2
-
3
- export { listBackends };