open-agents-ai 0.184.71 → 0.184.73

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.
Files changed (2) hide show
  1. package/dist/index.js +193 -12
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -560,6 +560,10 @@ var init_normalizeUrl = __esm({
560
560
  "use strict";
561
561
  PROVIDERS = [
562
562
  // --- Cloud providers (specific domains) ---
563
+ {
564
+ match: (u) => /api\.anthropic\.com/i.test(u),
565
+ info: { id: "anthropic", label: "Anthropic (Claude)", local: false, authRequired: true, keyPrefix: "sk-ant-", modelsPath: "/v1/models" }
566
+ },
563
567
  {
564
568
  match: (u) => /api\.openai\.com/i.test(u),
565
569
  info: { id: "openai", label: "OpenAI", local: false, authRequired: true, keyPrefix: "sk-", modelsPath: "/v1/models" }
@@ -28487,11 +28491,14 @@ ${transcript}`
28487
28491
  /** Multi-key pool — round-robin rotation per request for load distribution */
28488
28492
  _keyPool = [];
28489
28493
  _keyIndex = 0;
28494
+ /** Whether this backend targets Anthropic's Messages API */
28495
+ _isAnthropic = false;
28490
28496
  constructor(baseUrl, model, apiKey, thinking) {
28491
28497
  this.baseUrl = normalizeBaseUrl(baseUrl);
28492
28498
  this.model = model;
28493
28499
  this.apiKey = apiKey ?? "";
28494
28500
  this.thinking = thinking ?? true;
28501
+ this._isAnthropic = /api\.anthropic\.com/i.test(baseUrl);
28495
28502
  }
28496
28503
  /** Set multiple API keys for round-robin rotation per request */
28497
28504
  setKeyPool(keys) {
@@ -28502,7 +28509,7 @@ ${transcript}`
28502
28509
  setAbortSignal(signal) {
28503
28510
  this._abortSignal = signal;
28504
28511
  }
28505
- /** Build auth headers — all providers use standard Bearer token auth.
28512
+ /** Build auth headers — adapts to provider (Bearer for most, x-api-key for Anthropic).
28506
28513
  * When a key pool is set, round-robins through keys per request. */
28507
28514
  authHeaders() {
28508
28515
  const headers = { "Content-Type": "application/json" };
@@ -28512,11 +28519,19 @@ ${transcript}`
28512
28519
  this._keyIndex++;
28513
28520
  }
28514
28521
  if (key) {
28515
- headers["Authorization"] = `Bearer ${key}`;
28522
+ if (this._isAnthropic) {
28523
+ headers["x-api-key"] = key;
28524
+ headers["anthropic-version"] = "2023-06-01";
28525
+ } else {
28526
+ headers["Authorization"] = `Bearer ${key}`;
28527
+ }
28516
28528
  }
28517
28529
  return headers;
28518
28530
  }
28519
28531
  async chatCompletion(request) {
28532
+ if (this._isAnthropic) {
28533
+ return this._anthropicChatCompletion(request);
28534
+ }
28520
28535
  const body = {
28521
28536
  model: this.model,
28522
28537
  messages: request.messages,
@@ -28574,6 +28589,106 @@ ${transcript}`
28574
28589
  } : void 0
28575
28590
  };
28576
28591
  }
28592
+ /** Anthropic Messages API translation — converts our standard format to/from Anthropic's. */
28593
+ async _anthropicChatCompletion(request) {
28594
+ const systemMsgs = request.messages.filter((m) => m.role === "system");
28595
+ const nonSystemMsgs = request.messages.filter((m) => m.role !== "system");
28596
+ const systemText = systemMsgs.map((m) => typeof m.content === "string" ? m.content : "").join("\n\n");
28597
+ const anthropicMessages = [];
28598
+ for (const msg of nonSystemMsgs) {
28599
+ if (msg.role === "tool") {
28600
+ anthropicMessages.push({
28601
+ role: "user",
28602
+ content: [{
28603
+ type: "tool_result",
28604
+ tool_use_id: msg.tool_call_id ?? "",
28605
+ content: typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content)
28606
+ }]
28607
+ });
28608
+ } else if (msg.role === "assistant" && msg.tool_calls?.length > 0) {
28609
+ const blocks = [];
28610
+ if (typeof msg.content === "string" && msg.content) {
28611
+ blocks.push({ type: "text", text: msg.content });
28612
+ }
28613
+ for (const tc of msg.tool_calls) {
28614
+ blocks.push({
28615
+ type: "tool_use",
28616
+ id: tc.id,
28617
+ name: tc.function?.name ?? tc.name,
28618
+ input: typeof tc.function?.arguments === "string" ? (() => {
28619
+ try {
28620
+ return JSON.parse(tc.function.arguments);
28621
+ } catch {
28622
+ return {};
28623
+ }
28624
+ })() : tc.function?.arguments ?? {}
28625
+ });
28626
+ }
28627
+ anthropicMessages.push({ role: "assistant", content: blocks });
28628
+ } else {
28629
+ anthropicMessages.push({
28630
+ role: msg.role === "user" ? "user" : "assistant",
28631
+ content: typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content ?? "")
28632
+ });
28633
+ }
28634
+ }
28635
+ const anthropicTools = (request.tools ?? []).filter((t) => t.type === "function" && t.function).map((t) => ({
28636
+ name: t.function.name,
28637
+ description: t.function.description,
28638
+ input_schema: t.function.parameters
28639
+ }));
28640
+ const body = {
28641
+ model: this.model,
28642
+ messages: anthropicMessages,
28643
+ max_tokens: request.maxTokens ?? 8192,
28644
+ temperature: request.temperature
28645
+ };
28646
+ if (systemText)
28647
+ body.system = systemText;
28648
+ if (anthropicTools.length > 0)
28649
+ body.tools = anthropicTools;
28650
+ const fetchOpts = {
28651
+ method: "POST",
28652
+ headers: this.authHeaders(),
28653
+ body: JSON.stringify(body)
28654
+ };
28655
+ if (this._abortSignal)
28656
+ fetchOpts.signal = this._abortSignal;
28657
+ const resp = await fetch(`${this.baseUrl}/v1/messages`, fetchOpts);
28658
+ if (!resp.ok) {
28659
+ const text = await resp.text().catch(() => "");
28660
+ throw new Error(`Anthropic HTTP ${resp.status}: ${text.slice(0, 300)}`);
28661
+ }
28662
+ const data = await resp.json();
28663
+ const contentBlocks = data.content ?? [];
28664
+ const usage = data.usage;
28665
+ let textContent = "";
28666
+ const toolCalls = [];
28667
+ for (const block of contentBlocks) {
28668
+ if (block.type === "text") {
28669
+ textContent += block.text ?? "";
28670
+ } else if (block.type === "tool_use") {
28671
+ toolCalls.push({
28672
+ id: block.id || crypto.randomUUID(),
28673
+ name: block.name,
28674
+ arguments: block.input ?? {}
28675
+ });
28676
+ }
28677
+ }
28678
+ return {
28679
+ choices: [{
28680
+ message: {
28681
+ content: textContent || null,
28682
+ toolCalls: toolCalls.length > 0 ? toolCalls : void 0
28683
+ }
28684
+ }],
28685
+ usage: usage ? {
28686
+ totalTokens: (usage.input_tokens ?? 0) + (usage.output_tokens ?? 0),
28687
+ promptTokens: usage.input_tokens,
28688
+ completionTokens: usage.output_tokens
28689
+ } : void 0
28690
+ };
28691
+ }
28577
28692
  /**
28578
28693
  * SSE streaming variant — yields StreamChunks as tokens arrive.
28579
28694
  * Uses `stream: true` and the current thinking setting.
@@ -38613,9 +38728,15 @@ async function fetchOllamaModels(baseUrl) {
38613
38728
  async function fetchOpenAIModels(baseUrl, apiKey) {
38614
38729
  const normalized = normalizeBaseUrl(baseUrl);
38615
38730
  const url = `${normalized}/v1/models`;
38731
+ const isAnthropic = /api\.anthropic\.com/i.test(baseUrl);
38616
38732
  const headers = {};
38617
38733
  if (apiKey) {
38618
- headers["Authorization"] = `Bearer ${apiKey}`;
38734
+ if (isAnthropic) {
38735
+ headers["x-api-key"] = apiKey;
38736
+ headers["anthropic-version"] = "2023-06-01";
38737
+ } else {
38738
+ headers["Authorization"] = `Bearer ${apiKey}`;
38739
+ }
38619
38740
  }
38620
38741
  const resp = await fetch(url, {
38621
38742
  headers,
@@ -46047,17 +46168,31 @@ Error: ${err2 instanceof Error ? err2.message : String(err2)}`);
46047
46168
  const isMac = process.platform === "darwin";
46048
46169
  try {
46049
46170
  const { spawnSync } = await import("node:child_process");
46171
+ if (process.stdout.isTTY) {
46172
+ process.stdout.write("\x1B[?1002l\x1B[?1003l\x1B[?1006l");
46173
+ }
46050
46174
  if (isMac) {
46051
46175
  renderInfo(" macOS ARM detected \u2014 installing build deps via Homebrew...");
46052
46176
  const brewCheck = spawnSync("which", ["brew"], { stdio: "pipe", timeout: 5e3 });
46053
46177
  if (brewCheck.status === 0) {
46054
- spawnSync("brew", ["install", "llvm", "gcc", "openblas", "libsndfile"], {
46055
- stdio: "inherit",
46178
+ const brewResult = spawnSync("brew", ["install", "llvm", "gcc", "openblas", "libsndfile"], {
46179
+ stdio: "pipe",
46056
46180
  timeout: 3e5
46057
46181
  });
46182
+ if (brewResult.stdout)
46183
+ process.stdout.write(brewResult.stdout);
46184
+ if (brewResult.stderr)
46185
+ process.stderr.write(brewResult.stderr);
46058
46186
  const llvmPrefix = spawnSync("brew", ["--prefix", "llvm"], { stdio: "pipe", timeout: 5e3 });
46059
46187
  if (llvmPrefix.stdout) {
46060
- process.env.LLVM_CONFIG = `${llvmPrefix.stdout.toString().trim()}/bin/llvm-config`;
46188
+ const prefix = llvmPrefix.stdout.toString().trim();
46189
+ process.env.LLVM_CONFIG = `${prefix}/bin/llvm-config`;
46190
+ const openblas = spawnSync("brew", ["--prefix", "openblas"], { stdio: "pipe", timeout: 5e3 });
46191
+ if (openblas.stdout) {
46192
+ const obPrefix = openblas.stdout.toString().trim();
46193
+ process.env.LDFLAGS = `${process.env.LDFLAGS ?? ""} -L${obPrefix}/lib`.trim();
46194
+ process.env.CPPFLAGS = `${process.env.CPPFLAGS ?? ""} -I${obPrefix}/include`.trim();
46195
+ }
46061
46196
  }
46062
46197
  } else {
46063
46198
  renderWarning(" Homebrew not found. Install it: https://brew.sh");
@@ -46068,7 +46203,7 @@ Error: ${err2 instanceof Error ? err2.message : String(err2)}`);
46068
46203
  const sudoCheck = spawnSync("sudo", ["-v"], { stdio: "inherit", timeout: 6e4 });
46069
46204
  if (sudoCheck.status === 0) {
46070
46205
  renderInfo(" Installing system build dependencies (this may take a minute)...");
46071
- spawnSync("sudo", [
46206
+ const aptResult = spawnSync("sudo", [
46072
46207
  "apt-get",
46073
46208
  "install",
46074
46209
  "-y",
@@ -46079,11 +46214,18 @@ Error: ${err2 instanceof Error ? err2.message : String(err2)}`);
46079
46214
  "gfortran",
46080
46215
  "libopenblas-dev",
46081
46216
  "libsndfile1-dev"
46082
- ], { stdio: "inherit", timeout: 12e4 });
46217
+ ], { stdio: "pipe", timeout: 12e4 });
46218
+ if (aptResult.stdout)
46219
+ process.stdout.write(aptResult.stdout);
46220
+ if (aptResult.stderr)
46221
+ process.stderr.write(aptResult.stderr);
46083
46222
  } else {
46084
46223
  renderWarning(" sudo not available \u2014 skipping system build deps. librosa/lhotse may fail to compile.");
46085
46224
  }
46086
46225
  }
46226
+ if (process.stdout.isTTY) {
46227
+ process.stdout.write("\x1B[?1002h\x1B[?1006h");
46228
+ }
46087
46229
  } catch (err) {
46088
46230
  renderWarning(` Could not install system build deps: ${err instanceof Error ? err.message : String(err)}`);
46089
46231
  }
@@ -46119,8 +46261,10 @@ Error: ${err2 instanceof Error ? err2.message : String(err2)}`);
46119
46261
  // Non-fatal (not hard-imported by LuxTTS):
46120
46262
  { cmd: `${pipCmd} -m pip install --quiet piper-phonemize --find-links https://k2-fsa.github.io/icefall/piper_phonemize.html`, fatal: false, label: "piper-phonemize (optional)" },
46121
46263
  { cmd: `${pipCmd} -m pip install --quiet jieba pypinyin cn2an`, fatal: true, label: "Chinese text processing" },
46122
- // LuxTTS itself:
46123
- { cmd: `${pipCmd} -m pip install --quiet -e ${JSON.stringify(repoDir)}`, fatal: true, label: "LuxTTS (editable install)" }
46264
+ // LuxTTS itself: use --no-deps on ARM because we installed deps individually above.
46265
+ // Without --no-deps, pip tries to resolve zipvoice's full dependency tree which
46266
+ // includes piper-phonemize — and that has no macOS ARM wheel, causing fatal failure.
46267
+ { cmd: `${pipCmd} -m pip install --quiet --no-deps -e ${JSON.stringify(repoDir)}`, fatal: true, label: "LuxTTS (editable install)" }
46124
46268
  ] : [
46125
46269
  // x86_64: all-in-one (fast, all wheels available)
46126
46270
  {
@@ -50202,7 +50346,12 @@ async function handleEndpoint(arg, ctx, local = false) {
50202
50346
  const healthUrl = `${normalizedUrl}${provider.modelsPath}`;
50203
50347
  const headers = {};
50204
50348
  if (apiKey) {
50205
- headers["Authorization"] = `Bearer ${apiKey}`;
50349
+ if (provider.id === "anthropic") {
50350
+ headers["x-api-key"] = apiKey;
50351
+ headers["anthropic-version"] = "2023-06-01";
50352
+ } else {
50353
+ headers["Authorization"] = `Bearer ${apiKey}`;
50354
+ }
50206
50355
  }
50207
50356
  const resp = await fetch(healthUrl, {
50208
50357
  headers,
@@ -65099,6 +65248,7 @@ async function startInteractive(config, repoPath) {
65099
65248
  initOaDirectory(repoRoot);
65100
65249
  const savedSettings = resolveSettings(repoRoot);
65101
65250
  if (process.stdout.isTTY) {
65251
+ process.stdout.write("\x1B[2J\x1B[3J\x1B[H");
65102
65252
  process.stdout.write("\x1B[?1002l\x1B[?1003l\x1B[?1006l\x1B[?1015l\x1B[?1049h\x1B[48;5;233m\x1B[2J\x1B[3J\x1B[H\x1B[?25l");
65103
65253
  const restoreScreen = () => {
65104
65254
  process.stdout.write("\x1B[?1002l\x1B[?1003l\x1B[?1006l\x1B[?1015l\x1B[?25h\x1B[?1049l");
@@ -69957,9 +70107,40 @@ async function main() {
69957
70107
  }
69958
70108
  var scriptPath = process.argv[1] ?? "";
69959
70109
  var isMain = scriptPath.endsWith("index.js") || scriptPath.endsWith("index.ts") || scriptPath.endsWith("open-agents-bin") || scriptPath.includes("open-agents") || scriptPath.includes("/oa");
70110
+ function crashLog(label, err) {
70111
+ const msg = err instanceof Error ? err.stack ?? err.message : String(err);
70112
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString();
70113
+ const logLine = `[${timestamp}] ${label}: ${msg}
70114
+ `;
70115
+ try {
70116
+ const { appendFileSync: appendFileSync5, mkdirSync: mkdirSync30 } = __require("node:fs");
70117
+ const { join: join76 } = __require("node:path");
70118
+ const { homedir: homedir20 } = __require("node:os");
70119
+ const logDir = join76(homedir20(), ".open-agents");
70120
+ mkdirSync30(logDir, { recursive: true });
70121
+ appendFileSync5(join76(logDir, "crash.log"), logLine);
70122
+ } catch {
70123
+ }
70124
+ try {
70125
+ process.stdout.write("\x1B[?1002l\x1B[?1003l\x1B[?1006l\x1B[?25h\x1B[?1049l\x1B[0m\n");
70126
+ process.stderr.write(`\x1B[31m\u2716 ${label}:\x1B[0m ${msg}
70127
+ `);
70128
+ process.stderr.write(`\x1B[2m Crash log: ~/.open-agents/crash.log\x1B[0m
70129
+ `);
70130
+ } catch {
70131
+ }
70132
+ }
70133
+ process.on("uncaughtException", (err) => {
70134
+ crashLog("Uncaught exception", err);
70135
+ process.exit(1);
70136
+ });
70137
+ process.on("unhandledRejection", (reason) => {
70138
+ crashLog("Unhandled rejection", reason);
70139
+ process.exit(1);
70140
+ });
69960
70141
  if (isMain && !process.env.__OPEN_AGENTS_NO_AUTO_RUN) {
69961
70142
  await main().catch((err) => {
69962
- printError(err instanceof Error ? err.message : String(err));
70143
+ crashLog("Fatal error", err);
69963
70144
  process.exit(1);
69964
70145
  });
69965
70146
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "open-agents-ai",
3
- "version": "0.184.71",
3
+ "version": "0.184.73",
4
4
  "description": "AI coding agent powered by open-source models (Ollama/vLLM) — interactive TUI with agentic tool-calling loop",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",