open-agents-ai 0.184.44 → 0.184.45

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 +191 -96
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -672,20 +672,20 @@ var init_VllmBackend = __esm({
672
672
  // -------------------------------------------------------------------------
673
673
  // LLMBackend — complete
674
674
  // -------------------------------------------------------------------------
675
- async complete(request2) {
676
- const messages = request2.messages.map(agentMessageToChatMessage);
675
+ async complete(request) {
676
+ const messages = request.messages.map(agentMessageToChatMessage);
677
677
  const body = {
678
678
  model: this.model,
679
679
  messages,
680
- temperature: request2.temperature ?? 0,
681
- max_tokens: request2.maxTokens ?? 4096
680
+ temperature: request.temperature ?? 0,
681
+ max_tokens: request.maxTokens ?? 4096
682
682
  };
683
- if (request2.tools && request2.tools.length > 0) {
684
- body.tools = request2.tools;
683
+ if (request.tools && request.tools.length > 0) {
684
+ body.tools = request.tools;
685
685
  body.tool_choice = "auto";
686
686
  }
687
- if (request2.responseFormat) {
688
- body.response_format = request2.responseFormat;
687
+ if (request.responseFormat) {
688
+ body.response_format = request.responseFormat;
689
689
  }
690
690
  const start = Date.now();
691
691
  const rawResponse = await this.postWithRetry("/v1/chat/completions", body);
@@ -822,24 +822,24 @@ var init_OllamaBackend = __esm({
822
822
  // -------------------------------------------------------------------------
823
823
  // LLMBackend — complete
824
824
  // -------------------------------------------------------------------------
825
- async complete(request2) {
826
- const messages = request2.messages.map(agentMessageToChatMessage2);
825
+ async complete(request) {
826
+ const messages = request.messages.map(agentMessageToChatMessage2);
827
827
  const body = {
828
828
  model: this.model,
829
829
  messages,
830
- temperature: request2.temperature ?? 0,
831
- max_tokens: request2.maxTokens ?? 4096,
830
+ temperature: request.temperature ?? 0,
831
+ max_tokens: request.maxTokens ?? 4096,
832
832
  // Disable Qwen thinking mode by default so tokens go to content,
833
833
  // not reasoning. The model still produces good output without it
834
834
  // and coding agents need structured JSON in the content field.
835
835
  think: false
836
836
  };
837
- if (request2.tools && request2.tools.length > 0) {
838
- body.tools = request2.tools;
837
+ if (request.tools && request.tools.length > 0) {
838
+ body.tools = request.tools;
839
839
  body.tool_choice = "auto";
840
840
  }
841
- if (request2.responseFormat) {
842
- body.response_format = request2.responseFormat;
841
+ if (request.responseFormat) {
842
+ body.response_format = request.responseFormat;
843
843
  }
844
844
  const start = Date.now();
845
845
  const rawResponse = await this.postWithRetry("/v1/chat/completions", body);
@@ -1003,19 +1003,19 @@ var init_FakeBackend = __esm({
1003
1003
  this._healthCheckCount++;
1004
1004
  return this.options.healthy;
1005
1005
  }
1006
- async complete(request2) {
1006
+ async complete(request) {
1007
1007
  this._completeCount++;
1008
1008
  if (this.options.shouldThrow) {
1009
1009
  throw new Error(this.options.errorMessage);
1010
1010
  }
1011
- const lastMessage = request2.messages[request2.messages.length - 1];
1011
+ const lastMessage = request.messages[request.messages.length - 1];
1012
1012
  const inputKey = lastMessage ? `${lastMessage.role}:${lastMessage.content}` : "empty";
1013
1013
  const hash = fnv1a32(inputKey);
1014
1014
  const deterministicContent = `fake-response-${hash.toString(16).padStart(8, "0")}`;
1015
1015
  const usage = {
1016
- prompt_tokens: request2.messages.length * 10,
1016
+ prompt_tokens: request.messages.length * 10,
1017
1017
  completion_tokens: 20,
1018
- total_tokens: request2.messages.length * 10 + 20
1018
+ total_tokens: request.messages.length * 10 + 20
1019
1019
  };
1020
1020
  if (this.options.toolCallResponses.length > 0) {
1021
1021
  return {
@@ -11711,13 +11711,13 @@ Validation: ${validation.pass ? "PASS" : "NEEDS IMPROVEMENT"} (${validation.over
11711
11711
  // -------------------------------------------------------------------------
11712
11712
  // Phase 1: Seed Analysis
11713
11713
  // -------------------------------------------------------------------------
11714
- async analyzeSeed(request2) {
11714
+ async analyzeSeed(request) {
11715
11715
  const prompt = loadBuilderPrompt("seed-analysis.md", {
11716
- skill_request: request2
11716
+ skill_request: request
11717
11717
  });
11718
11718
  const response = await this.llmCall([
11719
11719
  { role: "system", content: prompt },
11720
- { role: "user", content: `Analyze this skill request: "${request2}"` }
11720
+ { role: "user", content: `Analyze this skill request: "${request}"` }
11721
11721
  ]);
11722
11722
  const jsonStr = extractJSON(response);
11723
11723
  const seed = JSON.parse(jsonStr);
@@ -24287,22 +24287,22 @@ var init_snippetPacker = __esm({
24287
24287
  });
24288
24288
 
24289
24289
  // packages/retrieval/dist/contextAssembler.js
24290
- async function assembleContext(request2, opts) {
24290
+ async function assembleContext(request, opts) {
24291
24291
  const { repoRoot, graph, semanticEngine, summaryMap, maxFiles = 8, maxSnippets = 20, maxLogs = 3, maxTokens = 24e3, expandNeighbors = true, architectureNote, priorAttemptNote } = opts;
24292
24292
  const lexOpts = {
24293
24293
  rootDir: repoRoot,
24294
24294
  includePatterns: [],
24295
- includeTests: request2.includeTests,
24296
- includeConfigs: request2.includeConfigs,
24295
+ includeTests: request.includeTests,
24296
+ includeConfigs: request.includeConfigs,
24297
24297
  maxMatches: 30
24298
24298
  };
24299
24299
  const [pathMatches, symbolMatches, errorMatches, semanticResults] = await Promise.all([
24300
- Promise.all(request2.pathsHint.map((p) => searchByPath(p, { rootDir: repoRoot, maxMatches: 10 }))).then((res) => res.flat()),
24301
- Promise.all(request2.symbolHint.map((s) => searchBySymbol(s, { rootDir: repoRoot, maxMatches: 10 }))).then((res) => res.flat()),
24302
- Promise.all(request2.errorHint.slice(0, maxLogs).map((e) => searchByError(e, { rootDir: repoRoot, maxMatches: 5 }))).then((res) => res.flat()),
24303
- semanticEngine?.isAvailable ? semanticEngine.search(request2.query, maxFiles) : Promise.resolve([])
24300
+ Promise.all(request.pathsHint.map((p) => searchByPath(p, { rootDir: repoRoot, maxMatches: 10 }))).then((res) => res.flat()),
24301
+ Promise.all(request.symbolHint.map((s) => searchBySymbol(s, { rootDir: repoRoot, maxMatches: 10 }))).then((res) => res.flat()),
24302
+ Promise.all(request.errorHint.slice(0, maxLogs).map((e) => searchByError(e, { rootDir: repoRoot, maxMatches: 5 }))).then((res) => res.flat()),
24303
+ semanticEngine?.isAvailable ? semanticEngine.search(request.query, maxFiles) : Promise.resolve([])
24304
24304
  ]);
24305
- const queryType = classifyQuery(request2.query);
24305
+ const queryType = classifyQuery(request.query);
24306
24306
  const rrfK = adaptiveK(queryType, opts.rrfConfig?.k);
24307
24307
  const wFts = opts.rrfConfig?.weightFts ?? 1;
24308
24308
  const wSem = opts.rrfConfig?.weightSemantic ?? 1;
@@ -24373,7 +24373,7 @@ async function assembleContext(request2, opts) {
24373
24373
  }
24374
24374
  }
24375
24375
  return {
24376
- request: request2,
24376
+ request,
24377
24377
  files,
24378
24378
  snippets: packed.slice(0, maxSnippets),
24379
24379
  architectureNote,
@@ -28232,14 +28232,14 @@ ${transcript}`
28232
28232
  * assembles and returns the same response format as chatCompletion().
28233
28233
  * The non-streaming chatCompletion path is NEVER touched by this code.
28234
28234
  */
28235
- async streamingRequest(request2, turn) {
28235
+ async streamingRequest(request, turn) {
28236
28236
  const backend = this.backend;
28237
28237
  let content = "";
28238
28238
  let inThinkTag = false;
28239
28239
  const toolCallAccumulators = /* @__PURE__ */ new Map();
28240
28240
  let streamUsage;
28241
28241
  this.emit({ type: "stream_start", turn, timestamp: (/* @__PURE__ */ new Date()).toISOString() });
28242
- for await (const chunk of backend.chatCompletionStream(request2)) {
28242
+ for await (const chunk of backend.chatCompletionStream(request)) {
28243
28243
  if (this.aborted)
28244
28244
  break;
28245
28245
  if (chunk.type === "usage" && chunk.usage) {
@@ -28349,13 +28349,13 @@ ${transcript}`
28349
28349
  }
28350
28350
  return headers;
28351
28351
  }
28352
- async chatCompletion(request2) {
28352
+ async chatCompletion(request) {
28353
28353
  const body = {
28354
28354
  model: this.model,
28355
- messages: request2.messages,
28356
- tools: request2.tools,
28357
- temperature: request2.temperature,
28358
- max_tokens: request2.maxTokens,
28355
+ messages: request.messages,
28356
+ tools: request.tools,
28357
+ temperature: request.temperature,
28358
+ max_tokens: request.maxTokens,
28359
28359
  think: this.thinking
28360
28360
  };
28361
28361
  const fetchOpts = {
@@ -28412,13 +28412,13 @@ ${transcript}`
28412
28412
  * Uses `stream: true` and the current thinking setting.
28413
28413
  * The existing chatCompletion() method is completely unmodified.
28414
28414
  */
28415
- async *chatCompletionStream(request2) {
28415
+ async *chatCompletionStream(request) {
28416
28416
  const body = {
28417
28417
  model: this.model,
28418
- messages: request2.messages,
28419
- tools: request2.tools,
28420
- temperature: request2.temperature,
28421
- max_tokens: request2.maxTokens,
28418
+ messages: request.messages,
28419
+ tools: request.tools,
28420
+ temperature: request.temperature,
28421
+ max_tokens: request.maxTokens,
28422
28422
  stream: true,
28423
28423
  stream_options: { include_usage: true },
28424
28424
  think: this.thinking
@@ -28538,7 +28538,7 @@ var init_nexusBackend = __esm({
28538
28538
  this.targetPeer = peerId;
28539
28539
  this.consecutiveFailures = 0;
28540
28540
  }
28541
- async chatCompletion(request2) {
28541
+ async chatCompletion(request) {
28542
28542
  if (this.consecutiveFailures >= _NexusAgenticBackend.MAX_CONSECUTIVE_FAILURES) {
28543
28543
  const err = new Error(`Remote peer unreachable after ${this.consecutiveFailures} attempts. Use /endpoint to switch to a local model or reconnect.`);
28544
28544
  err.fatal = true;
@@ -28546,10 +28546,10 @@ var init_nexusBackend = __esm({
28546
28546
  }
28547
28547
  const daemonArgs = {
28548
28548
  model: this.model,
28549
- messages: JSON.stringify(request2.messages),
28550
- tools: JSON.stringify(request2.tools),
28551
- temperature: String(request2.temperature),
28552
- max_tokens: String(request2.maxTokens)
28549
+ messages: JSON.stringify(request.messages),
28550
+ tools: JSON.stringify(request.tools),
28551
+ temperature: String(request.temperature),
28552
+ max_tokens: String(request.maxTokens)
28553
28553
  };
28554
28554
  if (this.targetPeer) {
28555
28555
  daemonArgs.target_peer = this.targetPeer;
@@ -28560,7 +28560,7 @@ var init_nexusBackend = __esm({
28560
28560
  daemonArgs.think = String(this.thinking);
28561
28561
  let rawResult;
28562
28562
  try {
28563
- rawResult = await this.sendFn("remote_infer", daemonArgs, request2.timeoutMs || 12e4);
28563
+ rawResult = await this.sendFn("remote_infer", daemonArgs, request.timeoutMs || 12e4);
28564
28564
  } catch (sendErr) {
28565
28565
  this.consecutiveFailures++;
28566
28566
  throw sendErr;
@@ -28653,15 +28653,15 @@ var init_nexusBackend = __esm({
28653
28653
  * the file for token-by-token delivery from the remote peer.
28654
28654
  * Falls back to unary + word-split if streaming setup fails.
28655
28655
  */
28656
- async *chatCompletionStream(request2) {
28656
+ async *chatCompletionStream(request) {
28657
28657
  const streamFile = join47(tmpdir7(), `nexus-stream-${randomBytes11(6).toString("hex")}.jsonl`);
28658
28658
  writeFileSync11(streamFile, "", "utf8");
28659
28659
  const daemonArgs = {
28660
28660
  model: this.model,
28661
- messages: JSON.stringify(request2.messages),
28662
- tools: JSON.stringify(request2.tools),
28663
- temperature: String(request2.temperature),
28664
- max_tokens: String(request2.maxTokens),
28661
+ messages: JSON.stringify(request.messages),
28662
+ tools: JSON.stringify(request.tools),
28663
+ temperature: String(request.temperature),
28664
+ max_tokens: String(request.maxTokens),
28665
28665
  stream_file: streamFile
28666
28666
  };
28667
28667
  if (this.targetPeer)
@@ -28671,7 +28671,7 @@ var init_nexusBackend = __esm({
28671
28671
  daemonArgs.think = String(this.thinking);
28672
28672
  let rawResult;
28673
28673
  try {
28674
- rawResult = await this.sendFn("remote_infer", daemonArgs, request2.timeoutMs || 12e4);
28674
+ rawResult = await this.sendFn("remote_infer", daemonArgs, request.timeoutMs || 12e4);
28675
28675
  } catch (sendErr) {
28676
28676
  this.consecutiveFailures++;
28677
28677
  try {
@@ -28744,7 +28744,7 @@ var init_nexusBackend = __esm({
28744
28744
  this.consecutiveFailures = 0;
28745
28745
  let position = 0;
28746
28746
  let done = false;
28747
- const deadline = Date.now() + (request2.timeoutMs ?? 12e4);
28747
+ const deadline = Date.now() + (request.timeoutMs ?? 12e4);
28748
28748
  try {
28749
28749
  while (!done && Date.now() < deadline) {
28750
28750
  await new Promise((resolve36) => {
@@ -28917,10 +28917,10 @@ var init_cascadeBackend = __esm({
28917
28917
  get fallbackCount() {
28918
28918
  return this.endpoints.filter((ep, i) => i !== this.activeIndex && ep.modelAvailable !== false).length;
28919
28919
  }
28920
- async chatCompletion(request2) {
28920
+ async chatCompletion(request) {
28921
28921
  const backend = this.getActiveBackend();
28922
28922
  try {
28923
- const result = await backend.chatCompletion(request2);
28923
+ const result = await backend.chatCompletion(request);
28924
28924
  this.consecutiveFailures = 0;
28925
28925
  return result;
28926
28926
  } catch (err) {
@@ -28936,7 +28936,7 @@ var init_cascadeBackend = __esm({
28936
28936
  this.onSwitch?.(from, to, reason);
28937
28937
  const newBackend = this.getActiveBackend();
28938
28938
  try {
28939
- const result = await newBackend.chatCompletion(request2);
28939
+ const result = await newBackend.chatCompletion(request);
28940
28940
  return result;
28941
28941
  } catch (cascadeErr) {
28942
28942
  this.consecutiveFailures++;
@@ -28950,11 +28950,11 @@ var init_cascadeBackend = __esm({
28950
28950
  /**
28951
28951
  * Streaming support — delegates to active backend's stream method if available.
28952
28952
  */
28953
- async *chatCompletionStream(request2) {
28953
+ async *chatCompletionStream(request) {
28954
28954
  const backend = this.getActiveBackend();
28955
28955
  const streamable = backend;
28956
28956
  if (typeof streamable.chatCompletionStream !== "function") {
28957
- const result = await this.chatCompletion(request2);
28957
+ const result = await this.chatCompletion(request);
28958
28958
  const choice = result.choices[0];
28959
28959
  if (choice?.message.content) {
28960
28960
  yield { type: "content", content: choice.message.content };
@@ -28985,7 +28985,7 @@ var init_cascadeBackend = __esm({
28985
28985
  return;
28986
28986
  }
28987
28987
  try {
28988
- for await (const chunk of streamable.chatCompletionStream(request2)) {
28988
+ for await (const chunk of streamable.chatCompletionStream(request)) {
28989
28989
  yield chunk;
28990
28990
  }
28991
28991
  this.consecutiveFailures = 0;
@@ -28999,7 +28999,7 @@ var init_cascadeBackend = __esm({
28999
28999
  this.activeIndex = nextIdx;
29000
29000
  this.consecutiveFailures = 0;
29001
29001
  this.onSwitch?.(from, to, err instanceof Error ? err.message : String(err));
29002
- const result = await this.chatCompletion(request2);
29002
+ const result = await this.chatCompletion(request);
29003
29003
  const choice = result.choices[0];
29004
29004
  if (choice?.message.content) {
29005
29005
  yield { type: "content", content: choice.message.content };
@@ -32596,7 +32596,7 @@ var require_websocket = __commonJS({
32596
32596
  "node_modules/.pnpm/ws@8.19.0/node_modules/ws/lib/websocket.js"(exports, module) {
32597
32597
  "use strict";
32598
32598
  var EventEmitter7 = __require("events");
32599
- var https = __require("https");
32599
+ var https2 = __require("https");
32600
32600
  var http2 = __require("http");
32601
32601
  var net = __require("net");
32602
32602
  var tls = __require("tls");
@@ -33131,7 +33131,7 @@ var require_websocket = __commonJS({
33131
33131
  }
33132
33132
  const defaultPort = isSecure ? 443 : 80;
33133
33133
  const key = randomBytes18(16).toString("base64");
33134
- const request2 = isSecure ? https.request : http2.request;
33134
+ const request = isSecure ? https2.request : http2.request;
33135
33135
  const protocolSet = /* @__PURE__ */ new Set();
33136
33136
  let perMessageDeflate;
33137
33137
  opts.createConnection = opts.createConnection || (isSecure ? tlsConnect : netConnect);
@@ -33208,12 +33208,12 @@ var require_websocket = __commonJS({
33208
33208
  if (opts.auth && !options.headers.authorization) {
33209
33209
  options.headers.authorization = "Basic " + Buffer.from(opts.auth).toString("base64");
33210
33210
  }
33211
- req = websocket._req = request2(opts);
33211
+ req = websocket._req = request(opts);
33212
33212
  if (websocket._redirects) {
33213
33213
  websocket.emit("redirect", websocket.url, req);
33214
33214
  }
33215
33215
  } else {
33216
- req = websocket._req = request2(opts);
33216
+ req = websocket._req = request(opts);
33217
33217
  }
33218
33218
  if (opts.timeout) {
33219
33219
  req.on("timeout", () => {
@@ -37601,7 +37601,7 @@ var init_peer_mesh = __esm({
37601
37601
  * Send an inference request to a specific peer.
37602
37602
  * Returns a promise that resolves when the response arrives.
37603
37603
  */
37604
- async requestInference(peerId, request2, timeoutMs = 12e4) {
37604
+ async requestInference(peerId, request, timeoutMs = 12e4) {
37605
37605
  const ws = this.connections.get(peerId);
37606
37606
  if (!ws || ws.readyState !== import_websocket.default.OPEN) {
37607
37607
  throw new Error(`Peer ${peerId} not connected`);
@@ -37613,7 +37613,7 @@ var init_peer_mesh = __esm({
37613
37613
  reject(new Error(`Inference timeout (${timeoutMs}ms)`));
37614
37614
  }, timeoutMs);
37615
37615
  this.pendingRequests.set(msgId, { resolve: resolve36, reject, timeout, chunks: [] });
37616
- this.sendMsg(ws, "infer_request", request2, msgId);
37616
+ this.sendMsg(ws, "infer_request", request, msgId);
37617
37617
  });
37618
37618
  }
37619
37619
  // ── Inbound connection handling ─────────────────────────────────────────
@@ -37955,7 +37955,7 @@ var init_inference_router = __esm({
37955
37955
  }
37956
37956
  }));
37957
37957
  }
37958
- const request2 = {
37958
+ const request = {
37959
37959
  model,
37960
37960
  messages: redactedMessages,
37961
37961
  tools: redactedTools,
@@ -37968,7 +37968,7 @@ var init_inference_router = __esm({
37968
37968
  if (redactedCount > 0)
37969
37969
  this._totalRedacted++;
37970
37970
  try {
37971
- const response = await this.mesh.requestInference(peer.peerId, request2, options?.timeoutMs ?? this.defaultTimeoutMs);
37971
+ const response = await this.mesh.requestInference(peer.peerId, request, options?.timeoutMs ?? this.defaultTimeoutMs);
37972
37972
  const injectedContent = this.vault.inject(response.content);
37973
37973
  const injectedToolCalls = response.toolCalls?.map((tc) => ({
37974
37974
  ...tc,
@@ -38001,8 +38001,8 @@ var init_inference_router = __esm({
38001
38001
  * The response text may contain placeholders from the requester's vault,
38002
38002
  * which is fine — we don't have their secrets and don't need them.
38003
38003
  */
38004
- async handleInboundRequest(request2, inferFn) {
38005
- for (const msg of request2.messages) {
38004
+ async handleInboundRequest(request, inferFn) {
38005
+ for (const msg of request.messages) {
38006
38006
  const found = this.vault.scan(msg.content);
38007
38007
  if (found.length > 0) {
38008
38008
  this.emit("leaked_secrets_detected", {
@@ -38011,7 +38011,7 @@ var init_inference_router = __esm({
38011
38011
  });
38012
38012
  }
38013
38013
  }
38014
- return inferFn(request2);
38014
+ return inferFn(request);
38015
38015
  }
38016
38016
  // ── Scoring ────────────────────────────────────────────────────────────
38017
38017
  scoreRoute(peer, model) {
@@ -61892,6 +61892,7 @@ __export(serve_exports, {
61892
61892
  startApiServer: () => startApiServer
61893
61893
  });
61894
61894
  import * as http from "node:http";
61895
+ import * as https from "node:https";
61895
61896
  import { createRequire as createRequire2 } from "node:module";
61896
61897
  import { fileURLToPath as fileURLToPath12 } from "node:url";
61897
61898
  import { dirname as dirname20, join as join69, resolve as resolve31 } from "node:path";
@@ -61977,20 +61978,29 @@ async function parseJsonBody(req) {
61977
61978
  return null;
61978
61979
  }
61979
61980
  }
61981
+ function backendAuthHeaders() {
61982
+ const config = loadConfig();
61983
+ if (config.apiKey)
61984
+ return { Authorization: `Bearer ${config.apiKey}` };
61985
+ return {};
61986
+ }
61980
61987
  function ollamaRequest(ollamaUrl, path, method, body) {
61981
61988
  return new Promise((resolve36, reject) => {
61982
61989
  const url = new URL(path, ollamaUrl);
61990
+ const isHttps = url.protocol === "https:";
61983
61991
  const options = {
61984
61992
  hostname: url.hostname,
61985
- port: url.port,
61993
+ port: url.port || (isHttps ? 443 : 80),
61986
61994
  path: url.pathname + url.search,
61987
61995
  method,
61988
61996
  headers: {
61989
61997
  "Content-Type": "application/json",
61990
- ...body ? { "Content-Length": Buffer.byteLength(body) } : {}
61998
+ ...body ? { "Content-Length": Buffer.byteLength(body) } : {},
61999
+ ...backendAuthHeaders()
61991
62000
  }
61992
62001
  };
61993
- const proxyReq = http.request(options, (proxyRes) => {
62002
+ const transport = isHttps ? https : http;
62003
+ const proxyReq = transport.request(options, (proxyRes) => {
61994
62004
  const chunks = [];
61995
62005
  proxyRes.on("data", (chunk) => chunks.push(chunk));
61996
62006
  proxyRes.on("end", () => {
@@ -62012,17 +62022,20 @@ function ollamaRequest(ollamaUrl, path, method, body) {
62012
62022
  }
62013
62023
  function ollamaStream(ollamaUrl, path, method, body, onData, onEnd, onError) {
62014
62024
  const url = new URL(path, ollamaUrl);
62025
+ const isHttps = url.protocol === "https:";
62015
62026
  const options = {
62016
62027
  hostname: url.hostname,
62017
- port: url.port,
62028
+ port: url.port || (isHttps ? 443 : 80),
62018
62029
  path: url.pathname + url.search,
62019
62030
  method,
62020
62031
  headers: {
62021
62032
  "Content-Type": "application/json",
62022
- ...body ? { "Content-Length": Buffer.byteLength(body) } : {}
62033
+ ...body ? { "Content-Length": Buffer.byteLength(body) } : {},
62034
+ ...backendAuthHeaders()
62023
62035
  }
62024
62036
  };
62025
- const proxyReq = http.request(options, (proxyRes) => {
62037
+ const transport = isHttps ? https : http;
62038
+ const proxyReq = transport.request(options, (proxyRes) => {
62026
62039
  proxyRes.setEncoding("utf-8");
62027
62040
  proxyRes.on("data", onData);
62028
62041
  proxyRes.on("end", onEnd);
@@ -62117,14 +62130,17 @@ function handleHealth(res) {
62117
62130
  }
62118
62131
  async function handleHealthReady(res, ollamaUrl) {
62119
62132
  try {
62120
- const result = await ollamaRequest(ollamaUrl, "/api/tags", "GET");
62133
+ const config = loadConfig();
62134
+ const isVllm = config.backendType === "vllm";
62135
+ const checkPath = isVllm ? "/v1/models" : "/api/tags";
62136
+ const result = await ollamaRequest(ollamaUrl, checkPath, "GET");
62121
62137
  if (result.status === 200) {
62122
- jsonResponse(res, 200, { status: "ready", ollama: "reachable" });
62138
+ jsonResponse(res, 200, { status: "ready", backend: "reachable", type: config.backendType || "ollama" });
62123
62139
  } else {
62124
- jsonResponse(res, 503, { status: "not_ready", ollama: "unreachable", code: result.status });
62140
+ jsonResponse(res, 503, { status: "not_ready", backend: "unreachable", code: result.status });
62125
62141
  }
62126
62142
  } catch {
62127
- jsonResponse(res, 503, { status: "not_ready", ollama: "unreachable" });
62143
+ jsonResponse(res, 503, { status: "not_ready", backend: "unreachable" });
62128
62144
  }
62129
62145
  }
62130
62146
  function handleHealthStartup(res) {
@@ -62143,9 +62159,20 @@ function handleMetrics(res) {
62143
62159
  }
62144
62160
  async function handleV1Models(res, ollamaUrl) {
62145
62161
  try {
62162
+ const config = loadConfig();
62163
+ const isVllm = config.backendType === "vllm";
62164
+ if (isVllm) {
62165
+ const result2 = await ollamaRequest(ollamaUrl, "/v1/models", "GET");
62166
+ if (result2.status !== 200) {
62167
+ jsonResponse(res, result2.status, { error: "Failed to fetch models from backend" });
62168
+ return;
62169
+ }
62170
+ jsonResponse(res, 200, JSON.parse(result2.body));
62171
+ return;
62172
+ }
62146
62173
  const result = await ollamaRequest(ollamaUrl, "/api/tags", "GET");
62147
62174
  if (result.status !== 200) {
62148
- jsonResponse(res, result.status, { error: "Failed to fetch models from Ollama" });
62175
+ jsonResponse(res, result.status, { error: "Failed to fetch models from backend" });
62149
62176
  return;
62150
62177
  }
62151
62178
  const ollamaBody = JSON.parse(result.body);
@@ -62158,7 +62185,7 @@ async function handleV1Models(res, ollamaUrl) {
62158
62185
  jsonResponse(res, 200, { object: "list", data: models });
62159
62186
  } catch (err) {
62160
62187
  jsonResponse(res, 502, {
62161
- error: "Failed to proxy to Ollama",
62188
+ error: "Failed to proxy to backend",
62162
62189
  message: err instanceof Error ? err.message : String(err)
62163
62190
  });
62164
62191
  }
@@ -62172,6 +62199,51 @@ async function handleV1ChatCompletions(req, res, ollamaUrl) {
62172
62199
  const requestBody = body;
62173
62200
  const stream = requestBody["stream"] === true;
62174
62201
  const model = requestBody["model"] || "unknown";
62202
+ const config = loadConfig();
62203
+ const isVllm = config.backendType === "vllm";
62204
+ if (isVllm) {
62205
+ const payload = JSON.stringify(requestBody);
62206
+ if (stream) {
62207
+ res.writeHead(200, {
62208
+ "Content-Type": "text/event-stream",
62209
+ "Cache-Control": "no-cache",
62210
+ "Connection": "keep-alive",
62211
+ "Access-Control-Allow-Origin": "*",
62212
+ "Access-Control-Allow-Methods": "GET, POST, PUT, PATCH, DELETE, OPTIONS",
62213
+ "Access-Control-Allow-Headers": "Content-Type, Authorization"
62214
+ });
62215
+ ollamaStream(ollamaUrl, "/v1/chat/completions", "POST", payload, (chunk) => {
62216
+ res.write(chunk);
62217
+ }, () => {
62218
+ res.end();
62219
+ }, (err) => {
62220
+ try {
62221
+ res.write(`data: ${JSON.stringify({ error: err.message })}
62222
+
62223
+ `);
62224
+ } catch {
62225
+ }
62226
+ res.end();
62227
+ });
62228
+ } else {
62229
+ try {
62230
+ const result = await ollamaRequest(ollamaUrl, "/v1/chat/completions", "POST", payload);
62231
+ if (result.status !== 200) {
62232
+ jsonResponse(res, result.status, { error: "Backend request failed", details: result.body });
62233
+ return;
62234
+ }
62235
+ const parsed = JSON.parse(result.body);
62236
+ if (parsed.usage) {
62237
+ metrics.totalTokensIn += parsed.usage.prompt_tokens ?? 0;
62238
+ metrics.totalTokensOut += parsed.usage.completion_tokens ?? 0;
62239
+ }
62240
+ jsonResponse(res, 200, parsed);
62241
+ } catch (err) {
62242
+ jsonResponse(res, 502, { error: "Failed to proxy to backend", message: err instanceof Error ? err.message : String(err) });
62243
+ }
62244
+ }
62245
+ return;
62246
+ }
62175
62247
  const ollamaPayload = JSON.stringify({
62176
62248
  ...requestBody,
62177
62249
  stream,
@@ -62329,6 +62401,22 @@ async function handleV1Embeddings(req, res, ollamaUrl) {
62329
62401
  return;
62330
62402
  }
62331
62403
  const requestBody = body;
62404
+ const config = loadConfig();
62405
+ const isVllm = config.backendType === "vllm";
62406
+ if (isVllm) {
62407
+ try {
62408
+ const payload = JSON.stringify(requestBody);
62409
+ const result = await ollamaRequest(ollamaUrl, "/v1/embeddings", "POST", payload);
62410
+ if (result.status !== 200) {
62411
+ jsonResponse(res, result.status, { error: "Backend embeddings request failed", details: result.body });
62412
+ return;
62413
+ }
62414
+ jsonResponse(res, 200, JSON.parse(result.body));
62415
+ } catch (err) {
62416
+ jsonResponse(res, 502, { error: "Failed to proxy to backend", message: err instanceof Error ? err.message : String(err) });
62417
+ }
62418
+ return;
62419
+ }
62332
62420
  const ollamaPayload = JSON.stringify({
62333
62421
  model: requestBody["model"],
62334
62422
  input: requestBody["input"]
@@ -62337,7 +62425,7 @@ async function handleV1Embeddings(req, res, ollamaUrl) {
62337
62425
  const result = await ollamaRequest(ollamaUrl, "/api/embed", "POST", ollamaPayload);
62338
62426
  if (result.status !== 200) {
62339
62427
  jsonResponse(res, result.status, {
62340
- error: "Ollama embeddings request failed",
62428
+ error: "Backend embeddings request failed",
62341
62429
  details: result.body
62342
62430
  });
62343
62431
  return;
@@ -62356,7 +62444,7 @@ async function handleV1Embeddings(req, res, ollamaUrl) {
62356
62444
  });
62357
62445
  } catch (err) {
62358
62446
  jsonResponse(res, 502, {
62359
- error: "Failed to proxy to Ollama",
62447
+ error: "Failed to proxy to backend",
62360
62448
  message: err instanceof Error ? err.message : String(err)
62361
62449
  });
62362
62450
  }
@@ -62564,10 +62652,16 @@ function handleV1RunsDelete(res, id) {
62564
62652
  }
62565
62653
  jsonResponse(res, 200, { run_id: id, status: "aborted" });
62566
62654
  }
62655
+ function redactSecrets(obj) {
62656
+ const copy = { ...obj };
62657
+ if (typeof copy.apiKey === "string" && copy.apiKey)
62658
+ copy.apiKey = "[redacted]";
62659
+ return copy;
62660
+ }
62567
62661
  function handleGetConfig(res) {
62568
62662
  const config = loadConfig();
62569
62663
  const settings = loadGlobalSettings();
62570
- jsonResponse(res, 200, { config, settings });
62664
+ jsonResponse(res, 200, { config: redactSecrets(config), settings: redactSecrets(settings) });
62571
62665
  }
62572
62666
  async function handlePatchConfig(req, res) {
62573
62667
  const body = await parseJsonBody(req);
@@ -62595,7 +62689,7 @@ async function handlePatchConfig(req, res) {
62595
62689
  settingsUpdate.timeoutMs = updates["timeoutMs"];
62596
62690
  saveGlobalSettings(settingsUpdate);
62597
62691
  const updatedConfig = loadConfig();
62598
- jsonResponse(res, 200, { config: updatedConfig, updated: Object.keys(settingsUpdate) });
62692
+ jsonResponse(res, 200, { config: redactSecrets(updatedConfig), updated: Object.keys(settingsUpdate) });
62599
62693
  }
62600
62694
  function handleGetConfigModel(res) {
62601
62695
  const config = loadConfig();
@@ -62936,7 +63030,8 @@ function startApiServer(options = {}) {
62936
63030
  `);
62937
63031
  process.stderr.write(` Listening on http://${host}:${port}
62938
63032
  `);
62939
- process.stderr.write(` Ollama proxy: ${ollamaUrl}
63033
+ const beType = config.backendType || "ollama";
63034
+ process.stderr.write(` Backend (${beType}): ${ollamaUrl}
62940
63035
  `);
62941
63036
  if (process.env["OA_API_KEYS"]) {
62942
63037
  const keyCount = process.env["OA_API_KEYS"].split(",").length;
@@ -68616,17 +68711,17 @@ async function evalCommand(opts, config) {
68616
68711
  rawBackend = new FakeBackend();
68617
68712
  }
68618
68713
  const backend = {
68619
- async complete(request2) {
68714
+ async complete(request) {
68620
68715
  const result = await rawBackend.complete({
68621
- messages: request2.messages.map((m) => ({
68716
+ messages: request.messages.map((m) => ({
68622
68717
  id: globalThis.crypto.randomUUID(),
68623
68718
  sessionId: globalThis.crypto.randomUUID(),
68624
68719
  role: m.role,
68625
68720
  content: m.content,
68626
68721
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
68627
68722
  })),
68628
- maxTokens: request2.maxTokens,
68629
- temperature: request2.temperature
68723
+ maxTokens: request.maxTokens,
68724
+ temperature: request.temperature
68630
68725
  });
68631
68726
  return { content: result.content ?? "" };
68632
68727
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "open-agents-ai",
3
- "version": "0.184.44",
3
+ "version": "0.184.45",
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",