agent-worker 0.12.0 → 0.13.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.
@@ -0,0 +1,3 @@
1
+ import { C as CLAUDE_MODEL_MAP, D as SDK_MODEL_ALIASES, E as OPENCODE_MODEL_MAP, O as getModelForBackend, S as BACKEND_DEFAULT_MODELS, T as CURSOR_MODEL_MAP, _ as extractCodexResult, a as createMockBackend, b as execWithIdleTimeout, c as extractOpenCodeResult, d as CodexBackend, f as ClaudeCodeBackend, g as extractClaudeResult, h as createStreamParser, i as MockAIBackend, k as normalizeBackendType, l as opencodeAdapter, m as codexAdapter, n as createBackend, o as SdkBackend, p as claudeAdapter, r as listBackends, s as OpenCodeBackend, t as checkBackends, u as CursorBackend, v as formatEvent, w as CODEX_MODEL_MAP, x as DEFAULT_IDLE_TIMEOUT, y as IdleTimeoutError } from "./backends-CziIqKRg.mjs";
2
+
3
+ export { listBackends };
@@ -6,30 +6,45 @@ import { stringify } from "yaml";
6
6
 
7
7
  //#region src/agent/models.ts
8
8
  const providerCache = {};
9
+ /** Provider SDK package mapping */
10
+ const PROVIDER_PACKAGES = {
11
+ anthropic: {
12
+ package: "@ai-sdk/anthropic",
13
+ export: "anthropic"
14
+ },
15
+ openai: {
16
+ package: "@ai-sdk/openai",
17
+ export: "openai"
18
+ },
19
+ deepseek: {
20
+ package: "@ai-sdk/deepseek",
21
+ export: "deepseek"
22
+ },
23
+ google: {
24
+ package: "@ai-sdk/google",
25
+ export: "google"
26
+ },
27
+ groq: {
28
+ package: "@ai-sdk/groq",
29
+ export: "groq"
30
+ },
31
+ mistral: {
32
+ package: "@ai-sdk/mistral",
33
+ export: "mistral"
34
+ },
35
+ xai: {
36
+ package: "@ai-sdk/xai",
37
+ export: "xai"
38
+ }
39
+ };
9
40
  /**
10
- * Lazy load a provider, caching the result
11
- * Supports custom baseURL and apiKey for providers using compatible APIs (e.g., MiniMax using Claude API)
41
+ * Lazy load a provider SDK, caching the result.
42
+ * Only caches standard providers (no custom baseURL/apiKey).
12
43
  */
13
- async function loadProvider(name, packageName, exportName, options) {
44
+ async function loadProvider(name, packageName, exportName) {
14
45
  if (name in providerCache) return providerCache[name] ?? null;
15
46
  try {
16
- const module = await import(packageName);
17
- if (options?.baseURL || options?.apiKeyEnvVar) {
18
- const createProvider = module[`create${exportName.charAt(0).toUpperCase() + exportName.slice(1)}`];
19
- if (createProvider) {
20
- const providerOptions = {};
21
- if (options.baseURL) providerOptions.baseURL = options.baseURL;
22
- if (options.apiKeyEnvVar) {
23
- const apiKey = process.env[options.apiKeyEnvVar];
24
- if (!apiKey) throw new Error(`Environment variable ${options.apiKeyEnvVar} is not set (required for ${name} provider)`);
25
- providerOptions.apiKey = apiKey;
26
- }
27
- const provider = createProvider(providerOptions);
28
- providerCache[name] = provider;
29
- return provider;
30
- }
31
- }
32
- const exportedProvider = module[exportName];
47
+ const exportedProvider = (await import(packageName))[exportName];
33
48
  providerCache[name] = exportedProvider;
34
49
  return exportedProvider;
35
50
  } catch {
@@ -38,6 +53,55 @@ async function loadProvider(name, packageName, exportName, options) {
38
53
  }
39
54
  }
40
55
  /**
56
+ * Create a provider instance with custom baseURL and/or apiKey.
57
+ * Not cached — each call creates a fresh instance.
58
+ */
59
+ async function createCustomProvider(packageName, exportName, options) {
60
+ const createFn = (await import(packageName))[`create${exportName.charAt(0).toUpperCase() + exportName.slice(1)}`];
61
+ if (!createFn) throw new Error(`Package ${packageName} does not export create${exportName.charAt(0).toUpperCase() + exportName.slice(1)}`);
62
+ return createFn(options);
63
+ }
64
+ /**
65
+ * Resolve api_key field: '$ENV_VAR' → process.env.ENV_VAR, literal → as-is
66
+ */
67
+ function resolveApiKey(apiKey) {
68
+ if (apiKey.startsWith("$")) {
69
+ const envVar = apiKey.slice(1);
70
+ const value = process.env[envVar];
71
+ if (!value) throw new Error(`Environment variable ${envVar} is not set`);
72
+ return value;
73
+ }
74
+ return apiKey;
75
+ }
76
+ /**
77
+ * Create a model using explicit provider configuration.
78
+ * Use this when provider details (base_url, api_key) are specified separately from the model name.
79
+ *
80
+ * Example:
81
+ * createModelWithProvider("MiniMax-M2.5", { name: "anthropic", base_url: "https://api.minimax.io/anthropic/v1", api_key: "$MINIMAX_API_KEY" })
82
+ */
83
+ async function createModelWithProvider(modelName, provider) {
84
+ if (typeof provider === "string") {
85
+ const pkg = PROVIDER_PACKAGES[provider];
86
+ if (!pkg) throw new Error(`Unknown provider: ${provider}. Supported: ${Object.keys(PROVIDER_PACKAGES).join(", ")}`);
87
+ const providerFn = await loadProvider(provider, pkg.package, pkg.export);
88
+ if (!providerFn) throw new Error(`Install ${pkg.package} to use ${provider} models directly`);
89
+ return providerFn(modelName);
90
+ }
91
+ const { name, base_url, api_key } = provider;
92
+ const pkg = PROVIDER_PACKAGES[name];
93
+ if (!pkg) throw new Error(`Unknown provider: ${name}. Supported: ${Object.keys(PROVIDER_PACKAGES).join(", ")}`);
94
+ if (!base_url && !api_key) {
95
+ const providerFn = await loadProvider(name, pkg.package, pkg.export);
96
+ if (!providerFn) throw new Error(`Install ${pkg.package} to use ${name} models directly`);
97
+ return providerFn(modelName);
98
+ }
99
+ const opts = {};
100
+ if (base_url) opts.baseURL = base_url;
101
+ if (api_key) opts.apiKey = resolveApiKey(api_key);
102
+ return (await createCustomProvider(pkg.package, pkg.export, opts))(modelName);
103
+ }
104
+ /**
41
105
  * Parse model identifier and return the appropriate provider model
42
106
  *
43
107
  * Supports three formats:
@@ -89,61 +153,14 @@ async function createModelAsync(modelId) {
89
153
  const provider = modelId.slice(0, colonIndex);
90
154
  const modelName = modelId.slice(colonIndex + 1);
91
155
  if (!modelName) throw new Error(`Invalid model identifier: ${modelId}. Model name is required.`);
92
- const providerConfigs = {
93
- anthropic: {
94
- package: "@ai-sdk/anthropic",
95
- export: "anthropic"
96
- },
97
- openai: {
98
- package: "@ai-sdk/openai",
99
- export: "openai"
100
- },
101
- deepseek: {
102
- package: "@ai-sdk/deepseek",
103
- export: "deepseek"
104
- },
105
- google: {
106
- package: "@ai-sdk/google",
107
- export: "google"
108
- },
109
- groq: {
110
- package: "@ai-sdk/groq",
111
- export: "groq"
112
- },
113
- mistral: {
114
- package: "@ai-sdk/mistral",
115
- export: "mistral"
116
- },
117
- xai: {
118
- package: "@ai-sdk/xai",
119
- export: "xai"
120
- },
121
- minimax: {
122
- package: "@ai-sdk/anthropic",
123
- export: "anthropic",
124
- options: {
125
- baseURL: "https://api.minimax.io/anthropic/v1",
126
- apiKeyEnvVar: "MINIMAX_API_KEY"
127
- }
128
- },
129
- minimax_cn: {
130
- package: "@ai-sdk/anthropic",
131
- export: "anthropic",
132
- options: {
133
- baseURL: "https://api.minimaxi.com/anthropic/v1",
134
- apiKeyEnvVar: "MINIMAX_CN_API_KEY"
135
- }
136
- }
137
- };
138
- const config = providerConfigs[provider];
139
- 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)`);
140
- const providerFn = await loadProvider(provider, config.package, config.export, config.options);
156
+ const config = PROVIDER_PACKAGES[provider];
157
+ if (!config) throw new Error(`Unknown provider: ${provider}. Supported: ${Object.keys(PROVIDER_PACKAGES).join(", ")}. Or use gateway format: provider/model (e.g., openai/gpt-5.2)`);
158
+ const providerFn = await loadProvider(provider, config.package, config.export);
141
159
  if (!providerFn) throw new Error(`Install ${config.package} to use ${provider} models directly`);
142
160
  return providerFn(modelName);
143
161
  }
144
162
  /**
145
163
  * List of supported providers for direct access
146
- * Note: minimax uses Claude-compatible API via @ai-sdk/anthropic with custom baseURL
147
164
  */
148
165
  const SUPPORTED_PROVIDERS = [
149
166
  "anthropic",
@@ -152,8 +169,7 @@ const SUPPORTED_PROVIDERS = [
152
169
  "google",
153
170
  "groq",
154
171
  "mistral",
155
- "xai",
156
- "minimax"
172
+ "xai"
157
173
  ];
158
174
  /**
159
175
  * Default provider when none specified
@@ -192,8 +208,7 @@ const FRONTIER_MODELS = {
192
208
  "pixtral-large-latest",
193
209
  "magistral-medium-2506"
194
210
  ],
195
- xai: ["grok-4", "grok-4-fast-reasoning"],
196
- minimax: ["MiniMax-M2"]
211
+ xai: ["grok-4", "grok-4-fast-reasoning"]
197
212
  };
198
213
 
199
214
  //#endregion
@@ -209,7 +224,8 @@ const BACKEND_DEFAULT_MODELS = {
209
224
  default: "claude-sonnet-4-5",
210
225
  claude: "sonnet",
211
226
  cursor: "sonnet-4.5",
212
- codex: "gpt-5.2-codex"
227
+ codex: "gpt-5.2-codex",
228
+ opencode: "deepseek/deepseek-chat"
213
229
  };
214
230
  /**
215
231
  * Model aliases for SDK (Anthropic format)
@@ -272,6 +288,22 @@ const CODEX_MODEL_MAP = {
272
288
  "o3-mini": "o3-mini"
273
289
  };
274
290
  /**
291
+ * Model translation for OpenCode CLI backend
292
+ * OpenCode uses provider/model format natively
293
+ */
294
+ const OPENCODE_MODEL_MAP = {
295
+ "deepseek-chat": "deepseek/deepseek-chat",
296
+ "deepseek-reasoner": "deepseek/deepseek-reasoner",
297
+ "deepseek/deepseek-chat": "deepseek/deepseek-chat",
298
+ "deepseek/deepseek-reasoner": "deepseek/deepseek-reasoner",
299
+ sonnet: "anthropic/claude-sonnet-4-5-20250514",
300
+ opus: "anthropic/claude-opus-4-20250514",
301
+ "claude-sonnet-4-5": "anthropic/claude-sonnet-4-5-20250514",
302
+ "claude-opus-4": "anthropic/claude-opus-4-20250514",
303
+ "gpt-5.2": "openai/gpt-5.2",
304
+ o3: "openai/o3"
305
+ };
306
+ /**
275
307
  * Get the model name for a specific backend
276
308
  * Translates generic model names to backend-specific format
277
309
  */
@@ -283,6 +315,7 @@ function getModelForBackend(model, backend) {
283
315
  case "cursor": return CURSOR_MODEL_MAP[model] || CURSOR_MODEL_MAP[normalizedModel] || normalizedModel;
284
316
  case "claude": return CLAUDE_MODEL_MAP[model] || CLAUDE_MODEL_MAP[normalizedModel] || normalizedModel;
285
317
  case "codex": return CODEX_MODEL_MAP[model] || CODEX_MODEL_MAP[normalizedModel] || normalizedModel;
318
+ case "opencode": return OPENCODE_MODEL_MAP[model] || OPENCODE_MODEL_MAP[normalizedModel] || model;
286
319
  default: return normalizedModel;
287
320
  }
288
321
  }
@@ -931,8 +964,7 @@ var CodexBackend = class {
931
964
  //#region src/backends/cursor.ts
932
965
  /**
933
966
  * Cursor CLI backend
934
- * Uses `cursor agent -p` (preferred) or `cursor-agent -p` (fallback)
935
- * for non-interactive mode with stream-json output
967
+ * Uses `cursor agent -p` for non-interactive mode with stream-json output
936
968
  *
937
969
  * MCP Configuration:
938
970
  * Cursor uses project-level MCP config via .cursor/mcp.json in the workspace.
@@ -943,7 +975,7 @@ var CodexBackend = class {
943
975
  var CursorBackend = class {
944
976
  type = "cursor";
945
977
  options;
946
- /** Resolved command: "cursor" (subcommand style) or "cursor-agent" (standalone) */
978
+ /** Resolved command: "cursor" (subcommand style) */
947
979
  resolvedCommand = null;
948
980
  constructor(options = {}) {
949
981
  this.options = {
@@ -994,7 +1026,7 @@ var CursorBackend = class {
994
1026
  }
995
1027
  /**
996
1028
  * Resolve which cursor command is available.
997
- * Prefers `cursor agent` (subcommand), falls back to `cursor-agent` (standalone).
1029
+ * Checks `cursor agent --version` (subcommand style).
998
1030
  * Result is cached after first resolution.
999
1031
  */
1000
1032
  async resolveCommand() {
@@ -1007,14 +1039,6 @@ var CursorBackend = class {
1007
1039
  this.resolvedCommand = "cursor";
1008
1040
  return "cursor";
1009
1041
  } catch {}
1010
- try {
1011
- await execa("cursor-agent", ["--version"], {
1012
- stdin: "ignore",
1013
- timeout: 2e3
1014
- });
1015
- this.resolvedCommand = "cursor-agent";
1016
- return "cursor-agent";
1017
- } catch {}
1018
1042
  return null;
1019
1043
  }
1020
1044
  async buildCommand(message) {
@@ -1027,16 +1051,161 @@ var CursorBackend = class {
1027
1051
  message
1028
1052
  ];
1029
1053
  if (this.options.model) agentArgs.push("--model", this.options.model);
1030
- if (cmd === "cursor") return {
1054
+ if (!cmd) throw new Error("cursor agent CLI not found");
1055
+ return {
1031
1056
  command: "cursor",
1032
1057
  args: ["agent", ...agentArgs]
1033
1058
  };
1059
+ }
1060
+ };
1061
+
1062
+ //#endregion
1063
+ //#region src/backends/opencode.ts
1064
+ /**
1065
+ * OpenCode CLI backend
1066
+ * Uses `opencode run` for non-interactive mode with JSON event output
1067
+ *
1068
+ * MCP Configuration:
1069
+ * OpenCode uses project-level MCP config via opencode.json in the workspace.
1070
+ * Use setWorkspace() to set up a dedicated workspace with MCP config.
1071
+ *
1072
+ * @see https://opencode.ai/docs/
1073
+ */
1074
+ var OpenCodeBackend = class {
1075
+ type = "opencode";
1076
+ options;
1077
+ constructor(options = {}) {
1078
+ this.options = {
1079
+ timeout: DEFAULT_IDLE_TIMEOUT,
1080
+ ...options
1081
+ };
1082
+ }
1083
+ /**
1084
+ * Set up workspace directory with MCP config
1085
+ * Creates opencode.json in the workspace with MCP server config
1086
+ */
1087
+ setWorkspace(workspaceDir, mcpConfig) {
1088
+ this.options.workspace = workspaceDir;
1089
+ if (!existsSync(workspaceDir)) mkdirSync(workspaceDir, { recursive: true });
1090
+ const opencodeMcp = {};
1091
+ for (const [name, config] of Object.entries(mcpConfig.mcpServers)) {
1092
+ const serverConfig = config;
1093
+ opencodeMcp[name] = {
1094
+ type: "local",
1095
+ command: [serverConfig.command, ...serverConfig.args || []],
1096
+ enabled: true,
1097
+ ...serverConfig.env ? { environment: serverConfig.env } : {}
1098
+ };
1099
+ }
1100
+ const opencodeConfig = {
1101
+ $schema: "https://opencode.ai/config.json",
1102
+ mcp: opencodeMcp
1103
+ };
1104
+ writeFileSync(join(workspaceDir, "opencode.json"), JSON.stringify(opencodeConfig, null, 2));
1105
+ }
1106
+ async send(message, _options) {
1107
+ const args = this.buildArgs(message);
1108
+ const cwd = this.options.workspace || this.options.cwd;
1109
+ const timeout = this.options.timeout ?? DEFAULT_IDLE_TIMEOUT;
1110
+ try {
1111
+ const { stdout } = await execWithIdleTimeout({
1112
+ command: "opencode",
1113
+ args,
1114
+ cwd,
1115
+ timeout,
1116
+ onStdout: this.options.streamCallbacks ? createStreamParser(this.options.streamCallbacks, "OpenCode", opencodeAdapter) : void 0
1117
+ });
1118
+ return extractOpenCodeResult(stdout);
1119
+ } catch (error) {
1120
+ if (error instanceof IdleTimeoutError) throw new Error(`opencode timed out after ${timeout}ms of inactivity`);
1121
+ if (error && typeof error === "object" && "exitCode" in error) {
1122
+ const execError = error;
1123
+ throw new Error(`opencode failed (exit ${execError.exitCode}): ${execError.stderr || execError.shortMessage}`);
1124
+ }
1125
+ throw error;
1126
+ }
1127
+ }
1128
+ async isAvailable() {
1129
+ try {
1130
+ await execa("opencode", ["--version"], {
1131
+ stdin: "ignore",
1132
+ timeout: 5e3
1133
+ });
1134
+ return true;
1135
+ } catch {
1136
+ return false;
1137
+ }
1138
+ }
1139
+ getInfo() {
1140
+ return {
1141
+ name: "OpenCode CLI",
1142
+ model: this.options.model
1143
+ };
1144
+ }
1145
+ buildArgs(message) {
1146
+ const args = [
1147
+ "run",
1148
+ "--format",
1149
+ "json",
1150
+ message
1151
+ ];
1152
+ if (this.options.model) args.push("--model", this.options.model);
1153
+ return args;
1154
+ }
1155
+ };
1156
+ /**
1157
+ * Adapter for OpenCode --format json output.
1158
+ *
1159
+ * Events:
1160
+ * { type: "step_start", sessionID: "..." }
1161
+ * { type: "tool_use", part: { tool: "bash", state: { input: {...} } } }
1162
+ * { type: "text", part: { text: "..." } } → skipped (result only)
1163
+ * { type: "step_finish", part: { cost, tokens } }
1164
+ */
1165
+ const opencodeAdapter = (raw) => {
1166
+ const event = raw;
1167
+ if (event.type === "step_start") return {
1168
+ kind: "init",
1169
+ sessionId: event.sessionID
1170
+ };
1171
+ if (event.type === "tool_use") {
1172
+ const { tool, state } = event.part;
1173
+ const args = state.input ? JSON.stringify(state.input) : "";
1034
1174
  return {
1035
- command: "cursor-agent",
1036
- args: agentArgs
1175
+ kind: "tool_call",
1176
+ name: tool,
1177
+ args: args.length > 100 ? args.slice(0, 100) + "..." : args
1178
+ };
1179
+ }
1180
+ if (event.type === "text") return { kind: "skip" };
1181
+ if (event.type === "step_finish") {
1182
+ const { cost, tokens } = event.part;
1183
+ return {
1184
+ kind: "completed",
1185
+ costUsd: cost,
1186
+ usage: tokens ? {
1187
+ input: tokens.input,
1188
+ output: tokens.output
1189
+ } : void 0
1037
1190
  };
1038
1191
  }
1192
+ return null;
1039
1193
  };
1194
+ /**
1195
+ * Extract final result from OpenCode --format json output.
1196
+ *
1197
+ * Priority:
1198
+ * 1. Last text event
1199
+ * 2. Raw stdout fallback
1200
+ */
1201
+ function extractOpenCodeResult(stdout) {
1202
+ const lines = stdout.trim().split("\n");
1203
+ for (let i = lines.length - 1; i >= 0; i--) try {
1204
+ const event = JSON.parse(lines[i]);
1205
+ if (event.type === "text" && event.part?.text) return { content: event.part.text };
1206
+ } catch {}
1207
+ return { content: stdout.trim() };
1208
+ }
1040
1209
 
1041
1210
  //#endregion
1042
1211
  //#region src/backends/sdk.ts
@@ -1049,15 +1218,17 @@ var SdkBackend = class {
1049
1218
  modelId;
1050
1219
  model = null;
1051
1220
  maxTokens;
1221
+ provider;
1052
1222
  constructor(options) {
1053
1223
  this.modelId = options.model;
1054
1224
  this.maxTokens = options.maxTokens ?? 4096;
1055
- try {
1225
+ this.provider = options.provider;
1226
+ if (!this.provider) try {
1056
1227
  this.model = createModel(this.modelId);
1057
1228
  } catch {}
1058
1229
  }
1059
1230
  async send(message, options) {
1060
- if (!this.model) this.model = await createModelAsync(this.modelId);
1231
+ if (!this.model) this.model = this.provider ? await createModelWithProvider(this.modelId, this.provider) : await createModelAsync(this.modelId);
1061
1232
  const result = await generateText({
1062
1233
  model: this.model,
1063
1234
  system: options?.system,
@@ -1075,7 +1246,7 @@ var SdkBackend = class {
1075
1246
  }
1076
1247
  async isAvailable() {
1077
1248
  try {
1078
- if (!this.model) this.model = await createModelAsync(this.modelId);
1249
+ if (!this.model) this.model = this.provider ? await createModelWithProvider(this.modelId, this.provider) : await createModelAsync(this.modelId);
1079
1250
  return true;
1080
1251
  } catch {
1081
1252
  return false;
@@ -1133,10 +1304,14 @@ function createBackend(config) {
1133
1304
  };
1134
1305
  const model = getModelForBackend(normalized.model, normalized.type);
1135
1306
  switch (normalized.type) {
1136
- case "default": return new SdkBackend({
1137
- model,
1138
- maxTokens: normalized.maxTokens
1139
- });
1307
+ case "default": {
1308
+ const provider = normalized.provider;
1309
+ return new SdkBackend({
1310
+ model,
1311
+ maxTokens: normalized.maxTokens,
1312
+ provider
1313
+ });
1314
+ }
1140
1315
  case "claude": return new ClaudeCodeBackend({
1141
1316
  ...normalized.options,
1142
1317
  model
@@ -1149,6 +1324,10 @@ function createBackend(config) {
1149
1324
  ...normalized.options,
1150
1325
  model
1151
1326
  });
1327
+ case "opencode": return new OpenCodeBackend({
1328
+ ...normalized.options,
1329
+ model
1330
+ });
1152
1331
  default: throw new Error(`Unknown backend type: ${normalized.type}`);
1153
1332
  }
1154
1333
  }
@@ -1163,16 +1342,19 @@ async function checkBackends() {
1163
1342
  const claude = new ClaudeCodeBackend();
1164
1343
  const codex = new CodexBackend();
1165
1344
  const cursor = new CursorBackend();
1166
- const [claudeAvailable, codexAvailable, cursorAvailable] = await Promise.all([
1345
+ const opencode = new OpenCodeBackend();
1346
+ const [claudeAvailable, codexAvailable, cursorAvailable, opencodeAvailable] = await Promise.all([
1167
1347
  withTimeout(claude.isAvailable(), 3e3),
1168
1348
  withTimeout(codex.isAvailable(), 3e3),
1169
- withTimeout(cursor.isAvailable(), 3e3)
1349
+ withTimeout(cursor.isAvailable(), 3e3),
1350
+ withTimeout(opencode.isAvailable(), 3e3)
1170
1351
  ]);
1171
1352
  return {
1172
1353
  default: true,
1173
1354
  claude: claudeAvailable,
1174
1355
  codex: codexAvailable,
1175
1356
  cursor: cursorAvailable,
1357
+ opencode: opencodeAvailable,
1176
1358
  mock: true
1177
1359
  };
1178
1360
  }
@@ -1201,9 +1383,14 @@ async function listBackends() {
1201
1383
  type: "cursor",
1202
1384
  available: availability.cursor,
1203
1385
  name: "Cursor Agent CLI"
1386
+ },
1387
+ {
1388
+ type: "opencode",
1389
+ available: availability.opencode,
1390
+ name: "OpenCode CLI"
1204
1391
  }
1205
1392
  ];
1206
1393
  }
1207
1394
 
1208
1395
  //#endregion
1209
- export { createModelAsync as A, SDK_MODEL_ALIASES as C, FRONTIER_MODELS as D, parseModel as E, SUPPORTED_PROVIDERS as O, CURSOR_MODEL_MAP as S, normalizeBackendType as T, execWithIdleTimeout as _, createMockBackend as a, CLAUDE_MODEL_MAP as b, CodexBackend as c, codexAdapter as d, createStreamParser as f, IdleTimeoutError as g, formatEvent as h, MockAIBackend as i, getDefaultModel as j, createModel as k, ClaudeCodeBackend as l, extractCodexResult as m, createBackend as n, SdkBackend as o, extractClaudeResult as p, listBackends as r, CursorBackend as s, checkBackends as t, claudeAdapter as u, DEFAULT_IDLE_TIMEOUT as v, getModelForBackend as w, CODEX_MODEL_MAP as x, BACKEND_DEFAULT_MODELS as y };
1396
+ export { parseModel as A, CLAUDE_MODEL_MAP as C, SDK_MODEL_ALIASES as D, OPENCODE_MODEL_MAP as E, createModelWithProvider as F, getDefaultModel as I, SUPPORTED_PROVIDERS as M, createModel as N, getModelForBackend as O, createModelAsync as P, BACKEND_DEFAULT_MODELS as S, CURSOR_MODEL_MAP as T, extractCodexResult as _, createMockBackend as a, execWithIdleTimeout as b, extractOpenCodeResult as c, CodexBackend as d, ClaudeCodeBackend as f, extractClaudeResult as g, createStreamParser as h, MockAIBackend as i, FRONTIER_MODELS as j, normalizeBackendType as k, opencodeAdapter as l, codexAdapter as m, createBackend as n, SdkBackend as o, claudeAdapter as p, listBackends as r, OpenCodeBackend as s, checkBackends as t, CursorBackend as u, formatEvent as v, CODEX_MODEL_MAP as w, DEFAULT_IDLE_TIMEOUT as x, IdleTimeoutError as y };
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
- import { D as FRONTIER_MODELS, T as normalizeBackendType, j as getDefaultModel, n as createBackend } from "../backends-DLaP0rMW.mjs";
3
- import { t as AgentWorker } from "../worker-CJ5_b2_q.mjs";
2
+ import { I as getDefaultModel, j as FRONTIER_MODELS, k as normalizeBackendType, n as createBackend } from "../backends-CziIqKRg.mjs";
3
+ import { t as AgentWorker } from "../worker-DBJ8136Q.mjs";
4
4
  import { existsSync, mkdirSync, readFileSync, readdirSync, unlinkSync, writeFileSync } from "node:fs";
5
5
  import { dirname, isAbsolute, join, relative } from "node:path";
6
6
  import { appendFile, mkdir, open, readFile, readdir, stat, unlink, writeFile } from "node:fs/promises";
@@ -97,7 +97,8 @@ var LocalWorker = class {
97
97
  model: config.model,
98
98
  system: config.system,
99
99
  tools: {},
100
- backend
100
+ backend,
101
+ provider: config.provider
101
102
  }, restore);
102
103
  }
103
104
  send(input, options) {
@@ -1545,7 +1546,7 @@ function createDaemonApp(optionsOrGetState) {
1545
1546
  if (!s) return c.json({ error: "Not ready" }, 503);
1546
1547
  const body = await parseJsonBody(c);
1547
1548
  if (!body || typeof body !== "object") return c.json({ error: "Invalid JSON body" }, 400);
1548
- const { name, model, system, backend = "default", workflow = "global", tag = "main" } = body;
1549
+ const { name, model, system, backend = "default", provider, workflow = "global", tag = "main" } = body;
1549
1550
  if (!name || !model || !system) return c.json({ error: "name, model, system required" }, 400);
1550
1551
  if (s.configs.has(name)) return c.json({ error: `Agent already exists: ${name}` }, 409);
1551
1552
  const agentConfig = {
@@ -1553,6 +1554,7 @@ function createDaemonApp(optionsOrGetState) {
1553
1554
  model,
1554
1555
  system,
1555
1556
  backend,
1557
+ provider,
1556
1558
  workflow,
1557
1559
  tag,
1558
1560
  createdAt: (/* @__PURE__ */ new Date()).toISOString()
@@ -1718,7 +1720,7 @@ function createDaemonApp(optionsOrGetState) {
1718
1720
  const key = `${workflowName}:${tag}`;
1719
1721
  if (s.workflows.has(key)) return c.json({ error: `Workflow already running: ${key}` }, 409);
1720
1722
  try {
1721
- const { runWorkflowWithControllers } = await import("../runner-CQJYnM7D.mjs");
1723
+ const { runWorkflowWithControllers } = await import("../runner-CnxROIev.mjs");
1722
1724
  const result = await runWorkflowWithControllers({
1723
1725
  workflow,
1724
1726
  workflowName,
@@ -2069,23 +2071,33 @@ function registerAgentCommands(program) {
2069
2071
  "claude",
2070
2072
  "codex",
2071
2073
  "cursor",
2074
+ "opencode",
2072
2075
  "mock"
2073
- ]).default("default")).option("-s, --system <prompt>", "System prompt", "You are a helpful assistant.").option("-f, --system-file <file>", "Read system prompt from file").option("--workflow <name>", "Workflow name (default: global)").option("--tag <tag>", "Workflow instance tag (default: main)").option("--port <port>", `Daemon port if starting new daemon (default: ${DEFAULT_PORT})`).option("--host <host>", "Daemon host (default: 127.0.0.1)").option("--json", "Output as JSON").addHelpText("after", `
2076
+ ]).default("default")).option("--provider <name>", "Provider SDK name (e.g., anthropic, openai)").option("--base-url <url>", "Override provider base URL").option("--api-key <ref>", "API key env var (e.g., $MINIMAX_API_KEY)").option("-s, --system <prompt>", "System prompt", "You are a helpful assistant.").option("-f, --system-file <file>", "Read system prompt from file").option("--workflow <name>", "Workflow name (default: global)").option("--tag <tag>", "Workflow instance tag (default: main)").option("--port <port>", `Daemon port if starting new daemon (default: ${DEFAULT_PORT})`).option("--host <host>", "Daemon host (default: 127.0.0.1)").option("--json", "Output as JSON").addHelpText("after", `
2074
2077
  Examples:
2075
2078
  $ agent-worker new alice -m anthropic/claude-sonnet-4-5
2076
2079
  $ agent-worker new bot -b mock
2077
2080
  $ agent-worker new reviewer --workflow review --tag pr-123
2081
+ $ agent-worker new coder -m MiniMax-M2.5 --provider anthropic --base-url https://api.minimax.io/anthropic/v1 --api-key '$MINIMAX_API_KEY'
2078
2082
  `).action(async (name, options) => {
2079
2083
  let system = options.system;
2080
2084
  if (options.systemFile) system = readFileSync(options.systemFile, "utf-8");
2081
2085
  const backend = normalizeBackendType(options.backend ?? "default");
2082
2086
  const model = options.model || getDefaultModel();
2087
+ let provider;
2088
+ if (options.provider) if (options.baseUrl || options.apiKey) provider = {
2089
+ name: options.provider,
2090
+ base_url: options.baseUrl,
2091
+ api_key: options.apiKey
2092
+ };
2093
+ else provider = options.provider;
2083
2094
  await ensureDaemon(options.port ? parseInt(options.port, 10) : void 0, options.host);
2084
2095
  const res = await createAgent({
2085
2096
  name,
2086
2097
  model,
2087
2098
  system,
2088
2099
  backend,
2100
+ provider,
2089
2101
  workflow: options.workflow,
2090
2102
  tag: options.tag
2091
2103
  });
@@ -2339,7 +2351,7 @@ Examples:
2339
2351
 
2340
2352
  Note: Workflow name is inferred from YAML 'name' field or filename
2341
2353
  `).action(async (file, options) => {
2342
- const { parseWorkflowFile, runWorkflowWithControllers } = await import("../workflow-CNlUyGit.mjs");
2354
+ const { parseWorkflowFile, runWorkflowWithControllers } = await import("../workflow-CIE3WPNx.mjs");
2343
2355
  const tag = options.tag || DEFAULT_TAG;
2344
2356
  const parsedWorkflow = await parseWorkflowFile(file, { tag });
2345
2357
  const workflowName = parsedWorkflow.name;
@@ -2350,7 +2362,7 @@ Note: Workflow name is inferred from YAML 'name' field or filename
2350
2362
  isCleaningUp = true;
2351
2363
  console.log("\nInterrupted, cleaning up...");
2352
2364
  if (controllers) {
2353
- const { shutdownControllers } = await import("../workflow-CNlUyGit.mjs");
2365
+ const { shutdownControllers } = await import("../workflow-CIE3WPNx.mjs");
2354
2366
  const { createSilentLogger } = await import("../logger-Bfdo83xL.mjs");
2355
2367
  await shutdownControllers(controllers, createSilentLogger());
2356
2368
  }
@@ -2423,7 +2435,7 @@ Workflow runs inside the daemon. Use ls/stop to manage:
2423
2435
 
2424
2436
  Note: Workflow name is inferred from YAML 'name' field or filename
2425
2437
  `).action(async (file, options) => {
2426
- const { parseWorkflowFile } = await import("../workflow-CNlUyGit.mjs");
2438
+ const { parseWorkflowFile } = await import("../workflow-CIE3WPNx.mjs");
2427
2439
  const { ensureDaemon } = await Promise.resolve().then(() => agent_exports);
2428
2440
  const tag = options.tag || DEFAULT_TAG;
2429
2441
  const parsedWorkflow = await parseWorkflowFile(file, { tag });
@@ -2574,10 +2586,6 @@ const PROVIDER_API_KEYS = {
2574
2586
  xai: {
2575
2587
  envVar: "XAI_API_KEY",
2576
2588
  description: "xAI Grok"
2577
- },
2578
- minimax: {
2579
- envVar: "MINIMAX_API_KEY",
2580
- description: "MiniMax"
2581
2589
  }
2582
2590
  };
2583
2591
  function registerInfoCommands(program) {
@@ -2598,10 +2606,11 @@ function registerInfoCommands(program) {
2598
2606
  console.log(` Provider only: provider (e.g., openai → ${gatewayExample})`);
2599
2607
  console.log(` Gateway format: provider/model (e.g., ${gatewayExample})`);
2600
2608
  console.log(` Direct format: provider:model (e.g., ${directExample})`);
2609
+ console.log(` Custom endpoint: --provider anthropic --base-url <url> --api-key '$KEY'`);
2601
2610
  console.log(`\nDefault: ${defaultModel} (when no model specified)`);
2602
2611
  });
2603
2612
  program.command("backends").description("Check available backends (SDK, CLI tools)").action(async () => {
2604
- const { listBackends } = await import("../backends-DG5igQii.mjs");
2613
+ const { listBackends } = await import("../backends-BWzhErjT.mjs");
2605
2614
  const backends = await listBackends();
2606
2615
  console.log("Backend Status:\n");
2607
2616
  for (const backend of backends) {
@@ -2681,7 +2690,7 @@ async function resolveDir(targetInput) {
2681
2690
 
2682
2691
  //#endregion
2683
2692
  //#region package.json
2684
- var version = "0.12.0";
2693
+ var version = "0.13.0";
2685
2694
 
2686
2695
  //#endregion
2687
2696
  //#region src/cli/index.ts
package/dist/index.d.mts CHANGED
@@ -1,6 +1,27 @@
1
1
  import { LanguageModel } from "ai";
2
2
  import { BashToolkit, CreateBashToolOptions, createBashTool } from "bash-tool";
3
3
 
4
+ //#region src/workflow/types.d.ts
5
+ /**
6
+ * Custom provider configuration for API endpoint overrides.
7
+ * Allows pointing any compatible SDK at a different base URL.
8
+ *
9
+ * Examples:
10
+ * provider: anthropic # string → built-in provider
11
+ * provider: # object → custom endpoint
12
+ * name: anthropic
13
+ * base_url: https://api.minimax.io/anthropic/v1
14
+ * api_key: $MINIMAX_API_KEY
15
+ */
16
+ interface ProviderConfig {
17
+ /** Provider SDK name (e.g., 'anthropic', 'openai') */
18
+ name: string;
19
+ /** Override base URL for the provider */
20
+ base_url?: string;
21
+ /** API key — env var reference with '$' prefix (e.g., '$MINIMAX_API_KEY') or literal value */
22
+ api_key?: string;
23
+ }
24
+ //#endregion
4
25
  //#region src/agent/types.d.ts
5
26
  /**
6
27
  * Message status for streaming/response tracking
@@ -132,7 +153,7 @@ interface Transcript {
132
153
  * Both `backends/types.ts` and `workflow/controller/types.ts` re-export from this module.
133
154
  */
134
155
  /** Backend type (union of all supported backends) */
135
- type BackendType = "default" | "claude" | "cursor" | "codex" | "mock";
156
+ type BackendType = "default" | "claude" | "cursor" | "codex" | "opencode" | "mock";
136
157
  //#endregion
137
158
  //#region src/backends/types.d.ts
138
159
  interface BackendConfig {
@@ -188,6 +209,8 @@ interface Backend {
188
209
  interface AgentWorkerConfig extends SessionConfig {
189
210
  /** CLI backend - when provided, send() delegates to this backend */
190
211
  backend?: Backend;
212
+ /** Provider configuration — when set, model is resolved via createModelWithProvider */
213
+ provider?: string | ProviderConfig;
191
214
  }
192
215
  /**
193
216
  * Step finish callback info
@@ -230,6 +253,7 @@ declare class AgentWorker {
230
253
  private totalUsage;
231
254
  private pendingApprovals;
232
255
  private backend;
256
+ private provider;
233
257
  private cachedAgent;
234
258
  private toolsChanged;
235
259
  /**
@@ -325,9 +349,8 @@ declare function createModel(modelId: string): LanguageModel;
325
349
  declare function createModelAsync(modelId: string): Promise<LanguageModel>;
326
350
  /**
327
351
  * List of supported providers for direct access
328
- * Note: minimax uses Claude-compatible API via @ai-sdk/anthropic with custom baseURL
329
352
  */
330
- declare const SUPPORTED_PROVIDERS: readonly ["anthropic", "openai", "deepseek", "google", "groq", "mistral", "xai", "minimax"];
353
+ declare const SUPPORTED_PROVIDERS: readonly ["anthropic", "openai", "deepseek", "google", "groq", "mistral", "xai"];
331
354
  type SupportedProvider = (typeof SUPPORTED_PROVIDERS)[number];
332
355
  /**
333
356
  * Frontier models for each provider (as of 2026-02)
@@ -344,7 +367,6 @@ declare const FRONTIER_MODELS: {
344
367
  readonly groq: readonly ["meta-llama/llama-4-scout-17b-16e-instruct", "deepseek-r1-distill-llama-70b"];
345
368
  readonly mistral: readonly ["mistral-large-latest", "pixtral-large-latest", "magistral-medium-2506"];
346
369
  readonly xai: readonly ["grok-4", "grok-4-fast-reasoning"];
347
- readonly minimax: readonly ["MiniMax-M2"];
348
370
  };
349
371
  //#endregion
350
372
  //#region src/agent/tools/bash.d.ts
@@ -575,7 +597,7 @@ interface CursorOptions {
575
597
  declare class CursorBackend implements Backend {
576
598
  readonly type: "cursor";
577
599
  private options;
578
- /** Resolved command: "cursor" (subcommand style) or "cursor-agent" (standalone) */
600
+ /** Resolved command: "cursor" (subcommand style) */
579
601
  private resolvedCommand;
580
602
  constructor(options?: CursorOptions);
581
603
  /**
@@ -596,7 +618,7 @@ declare class CursorBackend implements Backend {
596
618
  };
597
619
  /**
598
620
  * Resolve which cursor command is available.
599
- * Prefers `cursor agent` (subcommand), falls back to `cursor-agent` (standalone).
621
+ * Checks `cursor agent --version` (subcommand style).
600
622
  * Result is cached after first resolution.
601
623
  */
602
624
  private resolveCommand;
@@ -606,18 +628,35 @@ declare class CursorBackend implements Backend {
606
628
  }>;
607
629
  }
608
630
  //#endregion
631
+ //#region src/backends/opencode.d.ts
632
+ interface OpenCodeOptions {
633
+ /** Model to use in provider/model format (e.g., 'deepseek/deepseek-chat') */
634
+ model?: string;
635
+ /** Working directory (defaults to workspace if set) */
636
+ cwd?: string;
637
+ /** Workspace directory for agent isolation */
638
+ workspace?: string;
639
+ /** Idle timeout in milliseconds — kills process if no output for this duration */
640
+ timeout?: number;
641
+ /** Stream parser callbacks (structured event output) */
642
+ streamCallbacks?: StreamParserCallbacks;
643
+ }
644
+ //#endregion
609
645
  //#region src/backends/sdk.d.ts
610
646
  interface SdkBackendOptions {
611
- /** Model identifier (e.g., 'openai/gpt-5.2' or 'anthropic:claude-sonnet-4-5') */
647
+ /** Model identifier (e.g., 'openai/gpt-5.2' or 'anthropic:claude-sonnet-4-5' or just 'MiniMax-M2.5' with provider) */
612
648
  model: string;
613
649
  /** Maximum tokens to generate */
614
650
  maxTokens?: number;
651
+ /** Provider configuration — when set, model is a plain name resolved via the provider */
652
+ provider?: string | ProviderConfig;
615
653
  }
616
654
  declare class SdkBackend implements Backend {
617
655
  readonly type: "default";
618
656
  private modelId;
619
657
  private model;
620
658
  private maxTokens;
659
+ private provider;
621
660
  constructor(options: SdkBackendOptions);
622
661
  send(message: string, options?: {
623
662
  system?: string;
@@ -656,6 +695,7 @@ type BackendOptions = {
656
695
  type: "default";
657
696
  model?: string;
658
697
  maxTokens?: number;
698
+ provider?: string | ProviderConfig;
659
699
  } | {
660
700
  type: "claude";
661
701
  model?: string;
@@ -668,6 +708,10 @@ type BackendOptions = {
668
708
  type: "cursor";
669
709
  model?: string;
670
710
  options?: Omit<CursorOptions, "model">;
711
+ } | {
712
+ type: "opencode";
713
+ model?: string;
714
+ options?: Omit<OpenCodeOptions, "model">;
671
715
  };
672
716
  /**
673
717
  * Create a backend instance
package/dist/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
- import { A as createModelAsync, D as FRONTIER_MODELS, O as SUPPORTED_PROVIDERS, a as createMockBackend, c as CodexBackend, i as MockAIBackend, k as createModel, l as ClaudeCodeBackend, n as createBackend, o as SdkBackend, r as listBackends, s as CursorBackend, t as checkBackends } from "./backends-DLaP0rMW.mjs";
2
- import { t as AgentWorker } from "./worker-CJ5_b2_q.mjs";
1
+ import { M as SUPPORTED_PROVIDERS, N as createModel, P as createModelAsync, a as createMockBackend, d as CodexBackend, f as ClaudeCodeBackend, i as MockAIBackend, j as FRONTIER_MODELS, n as createBackend, o as SdkBackend, r as listBackends, t as checkBackends, u as CursorBackend } from "./backends-CziIqKRg.mjs";
2
+ import { t as AgentWorker } from "./worker-DBJ8136Q.mjs";
3
3
  import { jsonSchema, tool } from "ai";
4
4
  import { createBashTool } from "bash-tool";
5
5
  import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
@@ -1,4 +1,4 @@
1
- import { A as createModelAsync, E as parseModel, a as createMockBackend, n as createBackend } from "./backends-DLaP0rMW.mjs";
1
+ import { A as parseModel, P as createModelAsync, a as createMockBackend, n as createBackend } from "./backends-CziIqKRg.mjs";
2
2
  import { T as EventLog, n as createFileContextProvider, t as FileContextProvider, v as createContextMCPServer } from "./cli/index.mjs";
3
3
  import { n as createMemoryContextProvider } from "./memory-provider-BtLYtdQH.mjs";
4
4
  import { createChannelLogger, createSilentLogger } from "./logger-Bfdo83xL.mjs";
@@ -825,6 +825,7 @@ function getBackendByType(backendType, options) {
825
825
  return createBackend({
826
826
  type: backendType,
827
827
  model: options?.model,
828
+ ...backendType === "default" && options?.provider ? { provider: options.provider } : {},
828
829
  ...Object.keys(backendOptions).length > 0 ? { options: backendOptions } : {}
829
830
  });
830
831
  }
@@ -835,6 +836,10 @@ function getBackendByType(backendType, options) {
835
836
  * Prefer using getBackendByType with explicit backend field in workflow configs.
836
837
  */
837
838
  function getBackendForModel(model, options) {
839
+ if (options?.provider) return getBackendByType("default", {
840
+ ...options,
841
+ model
842
+ });
838
843
  const { provider } = parseModel(model);
839
844
  switch (provider) {
840
845
  case "anthropic": return getBackendByType("default", {
@@ -1344,11 +1349,13 @@ async function runWorkflowWithControllers(config) {
1344
1349
  if (createBackend) backend = createBackend(agentName, agentDef);
1345
1350
  else if (agentDef.backend) backend = getBackendByType(agentDef.backend, {
1346
1351
  model: agentDef.model,
1352
+ provider: agentDef.provider,
1347
1353
  debugLog: (msg) => agentLogger.debug(msg),
1348
1354
  streamCallbacks,
1349
1355
  timeout: agentDef.timeout
1350
1356
  });
1351
1357
  else if (agentDef.model) backend = getBackendForModel(agentDef.model, {
1358
+ provider: agentDef.provider,
1352
1359
  debugLog: (msg) => agentLogger.debug(msg),
1353
1360
  streamCallbacks
1354
1361
  });
@@ -1,4 +1,4 @@
1
- import { A as createModelAsync } from "./backends-DLaP0rMW.mjs";
1
+ import { F as createModelWithProvider, P as createModelAsync } from "./backends-CziIqKRg.mjs";
2
2
  import { ToolLoopAgent, stepCountIs } from "ai";
3
3
 
4
4
  //#region src/agent/worker.ts
@@ -30,6 +30,7 @@ var AgentWorker = class {
30
30
  };
31
31
  pendingApprovals = [];
32
32
  backend;
33
+ provider;
33
34
  cachedAgent = null;
34
35
  toolsChanged = false;
35
36
  /**
@@ -65,6 +66,7 @@ var AgentWorker = class {
65
66
  this.maxTokens = config.maxTokens ?? 4096;
66
67
  this.maxSteps = config.maxSteps ?? 200;
67
68
  this.backend = config.backend ?? null;
69
+ this.provider = config.provider;
68
70
  }
69
71
  /**
70
72
  * Check if a tool needs approval for given arguments
@@ -117,7 +119,7 @@ var AgentWorker = class {
117
119
  async getAgent(autoApprove) {
118
120
  if (!this.cachedAgent || this.toolsChanged || !autoApprove) {
119
121
  this.cachedAgent = new ToolLoopAgent({
120
- model: await createModelAsync(this.model),
122
+ model: this.provider ? await createModelWithProvider(this.model, this.provider) : await createModelAsync(this.model),
121
123
  instructions: this.system,
122
124
  tools: this.buildTools(autoApprove),
123
125
  maxOutputTokens: this.maxTokens,
@@ -1,8 +1,8 @@
1
- import "./backends-DLaP0rMW.mjs";
1
+ import "./backends-CziIqKRg.mjs";
2
2
  import { c as CONTEXT_DEFAULTS, i as resolveContextDir } from "./cli/index.mjs";
3
3
  import "./memory-provider-BtLYtdQH.mjs";
4
4
  import { createChannelLogger, createSilentLogger } from "./logger-Bfdo83xL.mjs";
5
- import { a as runSdkAgent, c as buildAgentPrompt, createWorkflowProvider, d as createContext, f as interpolate, i as createAgentController, initWorkflow, l as formatInbox, n as getBackendForModel, o as runMockAgent, r as checkWorkflowIdle, runWorkflowWithControllers, s as generateWorkflowMCPConfig, shutdownControllers, t as getBackendByType, u as CONTROLLER_DEFAULTS } from "./runner-CQJYnM7D.mjs";
5
+ import { a as runSdkAgent, c as buildAgentPrompt, createWorkflowProvider, d as createContext, f as interpolate, i as createAgentController, initWorkflow, l as formatInbox, n as getBackendForModel, o as runMockAgent, r as checkWorkflowIdle, runWorkflowWithControllers, s as generateWorkflowMCPConfig, shutdownControllers, t as getBackendByType, u as CONTROLLER_DEFAULTS } from "./runner-CnxROIev.mjs";
6
6
  import { existsSync, readFileSync } from "node:fs";
7
7
  import { basename, dirname, join, resolve } from "node:path";
8
8
  import { parse } from "yaml";
@@ -212,6 +212,7 @@ const CLI_BACKENDS = [
212
212
  "claude",
213
213
  "cursor",
214
214
  "codex",
215
+ "opencode",
215
216
  "mock"
216
217
  ];
217
218
  function validateAgent(name, agent, errors) {
@@ -241,6 +242,30 @@ function validateAgent(name, agent, errors) {
241
242
  path: `${path}.tools`,
242
243
  message: "Optional field \"tools\" must be an array"
243
244
  });
245
+ if (a.provider !== void 0) {
246
+ if (typeof a.provider === "string") {} else if (typeof a.provider === "object" && a.provider !== null && !Array.isArray(a.provider)) {
247
+ const p = a.provider;
248
+ if (!p.name || typeof p.name !== "string") errors.push({
249
+ path: `${path}.provider.name`,
250
+ message: "Field \"provider.name\" is required and must be a string"
251
+ });
252
+ if (p.base_url !== void 0 && typeof p.base_url !== "string") errors.push({
253
+ path: `${path}.provider.base_url`,
254
+ message: "Field \"provider.base_url\" must be a string"
255
+ });
256
+ if (p.api_key !== void 0 && typeof p.api_key !== "string") errors.push({
257
+ path: `${path}.provider.api_key`,
258
+ message: "Field \"provider.api_key\" must be a string"
259
+ });
260
+ } else errors.push({
261
+ path: `${path}.provider`,
262
+ message: "Field \"provider\" must be a string or object with { name, base_url?, api_key? }"
263
+ });
264
+ if (CLI_BACKENDS.includes(backend) && backend !== "mock") errors.push({
265
+ path: `${path}.provider`,
266
+ message: `Field "provider" is ignored for CLI backend "${backend}" (only works with default backend)`
267
+ });
268
+ }
244
269
  }
245
270
 
246
271
  //#endregion
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-worker",
3
- "version": "0.12.0",
3
+ "version": "0.13.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",
@@ -21,7 +21,9 @@
21
21
  "scripts": {
22
22
  "dev": "tsdown --watch",
23
23
  "build": "tsdown",
24
- "test": "bun test",
24
+ "test": "bun test test/*.test.ts test/unit/",
25
+ "test:e2e": "bun test test/e2e/",
26
+ "e2e:setup": "bash scripts/e2e-setup.sh",
25
27
  "lint": "oxlint src",
26
28
  "lint:fix": "oxlint src --fix",
27
29
  "format": "oxfmt src",
@@ -1,3 +0,0 @@
1
- import { C as SDK_MODEL_ALIASES, S as CURSOR_MODEL_MAP, T as normalizeBackendType, _ as execWithIdleTimeout, a as createMockBackend, b as CLAUDE_MODEL_MAP, c as CodexBackend, d as codexAdapter, f as createStreamParser, g as IdleTimeoutError, h as formatEvent, i as MockAIBackend, l as ClaudeCodeBackend, m as extractCodexResult, n as createBackend, o as SdkBackend, p as extractClaudeResult, r as listBackends, s as CursorBackend, t as checkBackends, u as claudeAdapter, v as DEFAULT_IDLE_TIMEOUT, w as getModelForBackend, x as CODEX_MODEL_MAP, y as BACKEND_DEFAULT_MODELS } from "./backends-DLaP0rMW.mjs";
2
-
3
- export { listBackends };