open-agents-ai 0.184.44 → 0.184.46

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 +208 -100
  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) {
@@ -28266,6 +28266,19 @@ ${transcript}`
28266
28266
  }
28267
28267
  if (kind === "content") {
28268
28268
  content += fragment;
28269
+ if (content.length > 400 && content.length % 200 < fragment.length) {
28270
+ const half = Math.floor(content.length / 2);
28271
+ const firstHalf = content.slice(half - 150, half);
28272
+ const secondHalf = content.slice(-150);
28273
+ if (firstHalf.length >= 100 && firstHalf === secondHalf) {
28274
+ this.emit({
28275
+ type: "status",
28276
+ content: "Aborting generation \u2014 intra-response repetition detected (model stuck in text loop)",
28277
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
28278
+ });
28279
+ break;
28280
+ }
28281
+ }
28269
28282
  }
28270
28283
  this.emit({
28271
28284
  type: "stream_token",
@@ -28349,13 +28362,13 @@ ${transcript}`
28349
28362
  }
28350
28363
  return headers;
28351
28364
  }
28352
- async chatCompletion(request2) {
28365
+ async chatCompletion(request) {
28353
28366
  const body = {
28354
28367
  model: this.model,
28355
- messages: request2.messages,
28356
- tools: request2.tools,
28357
- temperature: request2.temperature,
28358
- max_tokens: request2.maxTokens,
28368
+ messages: request.messages,
28369
+ tools: request.tools,
28370
+ temperature: request.temperature,
28371
+ max_tokens: request.maxTokens,
28359
28372
  think: this.thinking
28360
28373
  };
28361
28374
  const fetchOpts = {
@@ -28412,13 +28425,13 @@ ${transcript}`
28412
28425
  * Uses `stream: true` and the current thinking setting.
28413
28426
  * The existing chatCompletion() method is completely unmodified.
28414
28427
  */
28415
- async *chatCompletionStream(request2) {
28428
+ async *chatCompletionStream(request) {
28416
28429
  const body = {
28417
28430
  model: this.model,
28418
- messages: request2.messages,
28419
- tools: request2.tools,
28420
- temperature: request2.temperature,
28421
- max_tokens: request2.maxTokens,
28431
+ messages: request.messages,
28432
+ tools: request.tools,
28433
+ temperature: request.temperature,
28434
+ max_tokens: request.maxTokens,
28422
28435
  stream: true,
28423
28436
  stream_options: { include_usage: true },
28424
28437
  think: this.thinking
@@ -28459,8 +28472,9 @@ ${transcript}`
28459
28472
  continue;
28460
28473
  const delta = choice.delta;
28461
28474
  const finishReason = choice.finish_reason;
28462
- if (delta?.reasoning) {
28463
- yield { type: "content", content: delta.reasoning, thinking: true };
28475
+ const reasoningToken = delta?.reasoning ?? delta?.reasoning_content;
28476
+ if (reasoningToken) {
28477
+ yield { type: "content", content: reasoningToken, thinking: true };
28464
28478
  }
28465
28479
  if (delta?.content) {
28466
28480
  yield { type: "content", content: delta.content };
@@ -28538,7 +28552,7 @@ var init_nexusBackend = __esm({
28538
28552
  this.targetPeer = peerId;
28539
28553
  this.consecutiveFailures = 0;
28540
28554
  }
28541
- async chatCompletion(request2) {
28555
+ async chatCompletion(request) {
28542
28556
  if (this.consecutiveFailures >= _NexusAgenticBackend.MAX_CONSECUTIVE_FAILURES) {
28543
28557
  const err = new Error(`Remote peer unreachable after ${this.consecutiveFailures} attempts. Use /endpoint to switch to a local model or reconnect.`);
28544
28558
  err.fatal = true;
@@ -28546,10 +28560,10 @@ var init_nexusBackend = __esm({
28546
28560
  }
28547
28561
  const daemonArgs = {
28548
28562
  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)
28563
+ messages: JSON.stringify(request.messages),
28564
+ tools: JSON.stringify(request.tools),
28565
+ temperature: String(request.temperature),
28566
+ max_tokens: String(request.maxTokens)
28553
28567
  };
28554
28568
  if (this.targetPeer) {
28555
28569
  daemonArgs.target_peer = this.targetPeer;
@@ -28560,7 +28574,7 @@ var init_nexusBackend = __esm({
28560
28574
  daemonArgs.think = String(this.thinking);
28561
28575
  let rawResult;
28562
28576
  try {
28563
- rawResult = await this.sendFn("remote_infer", daemonArgs, request2.timeoutMs || 12e4);
28577
+ rawResult = await this.sendFn("remote_infer", daemonArgs, request.timeoutMs || 12e4);
28564
28578
  } catch (sendErr) {
28565
28579
  this.consecutiveFailures++;
28566
28580
  throw sendErr;
@@ -28653,15 +28667,15 @@ var init_nexusBackend = __esm({
28653
28667
  * the file for token-by-token delivery from the remote peer.
28654
28668
  * Falls back to unary + word-split if streaming setup fails.
28655
28669
  */
28656
- async *chatCompletionStream(request2) {
28670
+ async *chatCompletionStream(request) {
28657
28671
  const streamFile = join47(tmpdir7(), `nexus-stream-${randomBytes11(6).toString("hex")}.jsonl`);
28658
28672
  writeFileSync11(streamFile, "", "utf8");
28659
28673
  const daemonArgs = {
28660
28674
  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),
28675
+ messages: JSON.stringify(request.messages),
28676
+ tools: JSON.stringify(request.tools),
28677
+ temperature: String(request.temperature),
28678
+ max_tokens: String(request.maxTokens),
28665
28679
  stream_file: streamFile
28666
28680
  };
28667
28681
  if (this.targetPeer)
@@ -28671,7 +28685,7 @@ var init_nexusBackend = __esm({
28671
28685
  daemonArgs.think = String(this.thinking);
28672
28686
  let rawResult;
28673
28687
  try {
28674
- rawResult = await this.sendFn("remote_infer", daemonArgs, request2.timeoutMs || 12e4);
28688
+ rawResult = await this.sendFn("remote_infer", daemonArgs, request.timeoutMs || 12e4);
28675
28689
  } catch (sendErr) {
28676
28690
  this.consecutiveFailures++;
28677
28691
  try {
@@ -28744,7 +28758,7 @@ var init_nexusBackend = __esm({
28744
28758
  this.consecutiveFailures = 0;
28745
28759
  let position = 0;
28746
28760
  let done = false;
28747
- const deadline = Date.now() + (request2.timeoutMs ?? 12e4);
28761
+ const deadline = Date.now() + (request.timeoutMs ?? 12e4);
28748
28762
  try {
28749
28763
  while (!done && Date.now() < deadline) {
28750
28764
  await new Promise((resolve36) => {
@@ -28917,10 +28931,10 @@ var init_cascadeBackend = __esm({
28917
28931
  get fallbackCount() {
28918
28932
  return this.endpoints.filter((ep, i) => i !== this.activeIndex && ep.modelAvailable !== false).length;
28919
28933
  }
28920
- async chatCompletion(request2) {
28934
+ async chatCompletion(request) {
28921
28935
  const backend = this.getActiveBackend();
28922
28936
  try {
28923
- const result = await backend.chatCompletion(request2);
28937
+ const result = await backend.chatCompletion(request);
28924
28938
  this.consecutiveFailures = 0;
28925
28939
  return result;
28926
28940
  } catch (err) {
@@ -28936,7 +28950,7 @@ var init_cascadeBackend = __esm({
28936
28950
  this.onSwitch?.(from, to, reason);
28937
28951
  const newBackend = this.getActiveBackend();
28938
28952
  try {
28939
- const result = await newBackend.chatCompletion(request2);
28953
+ const result = await newBackend.chatCompletion(request);
28940
28954
  return result;
28941
28955
  } catch (cascadeErr) {
28942
28956
  this.consecutiveFailures++;
@@ -28950,11 +28964,11 @@ var init_cascadeBackend = __esm({
28950
28964
  /**
28951
28965
  * Streaming support — delegates to active backend's stream method if available.
28952
28966
  */
28953
- async *chatCompletionStream(request2) {
28967
+ async *chatCompletionStream(request) {
28954
28968
  const backend = this.getActiveBackend();
28955
28969
  const streamable = backend;
28956
28970
  if (typeof streamable.chatCompletionStream !== "function") {
28957
- const result = await this.chatCompletion(request2);
28971
+ const result = await this.chatCompletion(request);
28958
28972
  const choice = result.choices[0];
28959
28973
  if (choice?.message.content) {
28960
28974
  yield { type: "content", content: choice.message.content };
@@ -28985,7 +28999,7 @@ var init_cascadeBackend = __esm({
28985
28999
  return;
28986
29000
  }
28987
29001
  try {
28988
- for await (const chunk of streamable.chatCompletionStream(request2)) {
29002
+ for await (const chunk of streamable.chatCompletionStream(request)) {
28989
29003
  yield chunk;
28990
29004
  }
28991
29005
  this.consecutiveFailures = 0;
@@ -28999,7 +29013,7 @@ var init_cascadeBackend = __esm({
28999
29013
  this.activeIndex = nextIdx;
29000
29014
  this.consecutiveFailures = 0;
29001
29015
  this.onSwitch?.(from, to, err instanceof Error ? err.message : String(err));
29002
- const result = await this.chatCompletion(request2);
29016
+ const result = await this.chatCompletion(request);
29003
29017
  const choice = result.choices[0];
29004
29018
  if (choice?.message.content) {
29005
29019
  yield { type: "content", content: choice.message.content };
@@ -32596,7 +32610,7 @@ var require_websocket = __commonJS({
32596
32610
  "node_modules/.pnpm/ws@8.19.0/node_modules/ws/lib/websocket.js"(exports, module) {
32597
32611
  "use strict";
32598
32612
  var EventEmitter7 = __require("events");
32599
- var https = __require("https");
32613
+ var https2 = __require("https");
32600
32614
  var http2 = __require("http");
32601
32615
  var net = __require("net");
32602
32616
  var tls = __require("tls");
@@ -33131,7 +33145,7 @@ var require_websocket = __commonJS({
33131
33145
  }
33132
33146
  const defaultPort = isSecure ? 443 : 80;
33133
33147
  const key = randomBytes18(16).toString("base64");
33134
- const request2 = isSecure ? https.request : http2.request;
33148
+ const request = isSecure ? https2.request : http2.request;
33135
33149
  const protocolSet = /* @__PURE__ */ new Set();
33136
33150
  let perMessageDeflate;
33137
33151
  opts.createConnection = opts.createConnection || (isSecure ? tlsConnect : netConnect);
@@ -33208,12 +33222,12 @@ var require_websocket = __commonJS({
33208
33222
  if (opts.auth && !options.headers.authorization) {
33209
33223
  options.headers.authorization = "Basic " + Buffer.from(opts.auth).toString("base64");
33210
33224
  }
33211
- req = websocket._req = request2(opts);
33225
+ req = websocket._req = request(opts);
33212
33226
  if (websocket._redirects) {
33213
33227
  websocket.emit("redirect", websocket.url, req);
33214
33228
  }
33215
33229
  } else {
33216
- req = websocket._req = request2(opts);
33230
+ req = websocket._req = request(opts);
33217
33231
  }
33218
33232
  if (opts.timeout) {
33219
33233
  req.on("timeout", () => {
@@ -37601,7 +37615,7 @@ var init_peer_mesh = __esm({
37601
37615
  * Send an inference request to a specific peer.
37602
37616
  * Returns a promise that resolves when the response arrives.
37603
37617
  */
37604
- async requestInference(peerId, request2, timeoutMs = 12e4) {
37618
+ async requestInference(peerId, request, timeoutMs = 12e4) {
37605
37619
  const ws = this.connections.get(peerId);
37606
37620
  if (!ws || ws.readyState !== import_websocket.default.OPEN) {
37607
37621
  throw new Error(`Peer ${peerId} not connected`);
@@ -37613,7 +37627,7 @@ var init_peer_mesh = __esm({
37613
37627
  reject(new Error(`Inference timeout (${timeoutMs}ms)`));
37614
37628
  }, timeoutMs);
37615
37629
  this.pendingRequests.set(msgId, { resolve: resolve36, reject, timeout, chunks: [] });
37616
- this.sendMsg(ws, "infer_request", request2, msgId);
37630
+ this.sendMsg(ws, "infer_request", request, msgId);
37617
37631
  });
37618
37632
  }
37619
37633
  // ── Inbound connection handling ─────────────────────────────────────────
@@ -37955,7 +37969,7 @@ var init_inference_router = __esm({
37955
37969
  }
37956
37970
  }));
37957
37971
  }
37958
- const request2 = {
37972
+ const request = {
37959
37973
  model,
37960
37974
  messages: redactedMessages,
37961
37975
  tools: redactedTools,
@@ -37968,7 +37982,7 @@ var init_inference_router = __esm({
37968
37982
  if (redactedCount > 0)
37969
37983
  this._totalRedacted++;
37970
37984
  try {
37971
- const response = await this.mesh.requestInference(peer.peerId, request2, options?.timeoutMs ?? this.defaultTimeoutMs);
37985
+ const response = await this.mesh.requestInference(peer.peerId, request, options?.timeoutMs ?? this.defaultTimeoutMs);
37972
37986
  const injectedContent = this.vault.inject(response.content);
37973
37987
  const injectedToolCalls = response.toolCalls?.map((tc) => ({
37974
37988
  ...tc,
@@ -38001,8 +38015,8 @@ var init_inference_router = __esm({
38001
38015
  * The response text may contain placeholders from the requester's vault,
38002
38016
  * which is fine — we don't have their secrets and don't need them.
38003
38017
  */
38004
- async handleInboundRequest(request2, inferFn) {
38005
- for (const msg of request2.messages) {
38018
+ async handleInboundRequest(request, inferFn) {
38019
+ for (const msg of request.messages) {
38006
38020
  const found = this.vault.scan(msg.content);
38007
38021
  if (found.length > 0) {
38008
38022
  this.emit("leaked_secrets_detected", {
@@ -38011,7 +38025,7 @@ var init_inference_router = __esm({
38011
38025
  });
38012
38026
  }
38013
38027
  }
38014
- return inferFn(request2);
38028
+ return inferFn(request);
38015
38029
  }
38016
38030
  // ── Scoring ────────────────────────────────────────────────────────────
38017
38031
  scoreRoute(peer, model) {
@@ -53593,6 +53607,7 @@ var init_stream_renderer = __esm({
53593
53607
  process.stdout.write(`\x1B[1A\x1B[2K${dimText(" \u23BF ")}${dimItalic(`thought for ${this.thinkingTokenCount} tokens`)}
53594
53608
  `);
53595
53609
  this.thinkingTokenCount = 0;
53610
+ this.lineStarted = false;
53596
53611
  }
53597
53612
  if (kind === "tool_args" && !this.inToolArgs) {
53598
53613
  this.flushPartial(kind);
@@ -53645,8 +53660,6 @@ var init_stream_renderer = __esm({
53645
53660
  if (this.lineStarted) {
53646
53661
  process.stdout.write("\n");
53647
53662
  this.lineStarted = false;
53648
- } else {
53649
- process.stdout.write("\n");
53650
53663
  }
53651
53664
  return;
53652
53665
  }
@@ -61892,6 +61905,7 @@ __export(serve_exports, {
61892
61905
  startApiServer: () => startApiServer
61893
61906
  });
61894
61907
  import * as http from "node:http";
61908
+ import * as https from "node:https";
61895
61909
  import { createRequire as createRequire2 } from "node:module";
61896
61910
  import { fileURLToPath as fileURLToPath12 } from "node:url";
61897
61911
  import { dirname as dirname20, join as join69, resolve as resolve31 } from "node:path";
@@ -61977,20 +61991,29 @@ async function parseJsonBody(req) {
61977
61991
  return null;
61978
61992
  }
61979
61993
  }
61994
+ function backendAuthHeaders() {
61995
+ const config = loadConfig();
61996
+ if (config.apiKey)
61997
+ return { Authorization: `Bearer ${config.apiKey}` };
61998
+ return {};
61999
+ }
61980
62000
  function ollamaRequest(ollamaUrl, path, method, body) {
61981
62001
  return new Promise((resolve36, reject) => {
61982
62002
  const url = new URL(path, ollamaUrl);
62003
+ const isHttps = url.protocol === "https:";
61983
62004
  const options = {
61984
62005
  hostname: url.hostname,
61985
- port: url.port,
62006
+ port: url.port || (isHttps ? 443 : 80),
61986
62007
  path: url.pathname + url.search,
61987
62008
  method,
61988
62009
  headers: {
61989
62010
  "Content-Type": "application/json",
61990
- ...body ? { "Content-Length": Buffer.byteLength(body) } : {}
62011
+ ...body ? { "Content-Length": Buffer.byteLength(body) } : {},
62012
+ ...backendAuthHeaders()
61991
62013
  }
61992
62014
  };
61993
- const proxyReq = http.request(options, (proxyRes) => {
62015
+ const transport = isHttps ? https : http;
62016
+ const proxyReq = transport.request(options, (proxyRes) => {
61994
62017
  const chunks = [];
61995
62018
  proxyRes.on("data", (chunk) => chunks.push(chunk));
61996
62019
  proxyRes.on("end", () => {
@@ -62012,17 +62035,20 @@ function ollamaRequest(ollamaUrl, path, method, body) {
62012
62035
  }
62013
62036
  function ollamaStream(ollamaUrl, path, method, body, onData, onEnd, onError) {
62014
62037
  const url = new URL(path, ollamaUrl);
62038
+ const isHttps = url.protocol === "https:";
62015
62039
  const options = {
62016
62040
  hostname: url.hostname,
62017
- port: url.port,
62041
+ port: url.port || (isHttps ? 443 : 80),
62018
62042
  path: url.pathname + url.search,
62019
62043
  method,
62020
62044
  headers: {
62021
62045
  "Content-Type": "application/json",
62022
- ...body ? { "Content-Length": Buffer.byteLength(body) } : {}
62046
+ ...body ? { "Content-Length": Buffer.byteLength(body) } : {},
62047
+ ...backendAuthHeaders()
62023
62048
  }
62024
62049
  };
62025
- const proxyReq = http.request(options, (proxyRes) => {
62050
+ const transport = isHttps ? https : http;
62051
+ const proxyReq = transport.request(options, (proxyRes) => {
62026
62052
  proxyRes.setEncoding("utf-8");
62027
62053
  proxyRes.on("data", onData);
62028
62054
  proxyRes.on("end", onEnd);
@@ -62117,14 +62143,17 @@ function handleHealth(res) {
62117
62143
  }
62118
62144
  async function handleHealthReady(res, ollamaUrl) {
62119
62145
  try {
62120
- const result = await ollamaRequest(ollamaUrl, "/api/tags", "GET");
62146
+ const config = loadConfig();
62147
+ const isVllm = config.backendType === "vllm";
62148
+ const checkPath = isVllm ? "/v1/models" : "/api/tags";
62149
+ const result = await ollamaRequest(ollamaUrl, checkPath, "GET");
62121
62150
  if (result.status === 200) {
62122
- jsonResponse(res, 200, { status: "ready", ollama: "reachable" });
62151
+ jsonResponse(res, 200, { status: "ready", backend: "reachable", type: config.backendType || "ollama" });
62123
62152
  } else {
62124
- jsonResponse(res, 503, { status: "not_ready", ollama: "unreachable", code: result.status });
62153
+ jsonResponse(res, 503, { status: "not_ready", backend: "unreachable", code: result.status });
62125
62154
  }
62126
62155
  } catch {
62127
- jsonResponse(res, 503, { status: "not_ready", ollama: "unreachable" });
62156
+ jsonResponse(res, 503, { status: "not_ready", backend: "unreachable" });
62128
62157
  }
62129
62158
  }
62130
62159
  function handleHealthStartup(res) {
@@ -62143,9 +62172,20 @@ function handleMetrics(res) {
62143
62172
  }
62144
62173
  async function handleV1Models(res, ollamaUrl) {
62145
62174
  try {
62175
+ const config = loadConfig();
62176
+ const isVllm = config.backendType === "vllm";
62177
+ if (isVllm) {
62178
+ const result2 = await ollamaRequest(ollamaUrl, "/v1/models", "GET");
62179
+ if (result2.status !== 200) {
62180
+ jsonResponse(res, result2.status, { error: "Failed to fetch models from backend" });
62181
+ return;
62182
+ }
62183
+ jsonResponse(res, 200, JSON.parse(result2.body));
62184
+ return;
62185
+ }
62146
62186
  const result = await ollamaRequest(ollamaUrl, "/api/tags", "GET");
62147
62187
  if (result.status !== 200) {
62148
- jsonResponse(res, result.status, { error: "Failed to fetch models from Ollama" });
62188
+ jsonResponse(res, result.status, { error: "Failed to fetch models from backend" });
62149
62189
  return;
62150
62190
  }
62151
62191
  const ollamaBody = JSON.parse(result.body);
@@ -62158,7 +62198,7 @@ async function handleV1Models(res, ollamaUrl) {
62158
62198
  jsonResponse(res, 200, { object: "list", data: models });
62159
62199
  } catch (err) {
62160
62200
  jsonResponse(res, 502, {
62161
- error: "Failed to proxy to Ollama",
62201
+ error: "Failed to proxy to backend",
62162
62202
  message: err instanceof Error ? err.message : String(err)
62163
62203
  });
62164
62204
  }
@@ -62172,6 +62212,51 @@ async function handleV1ChatCompletions(req, res, ollamaUrl) {
62172
62212
  const requestBody = body;
62173
62213
  const stream = requestBody["stream"] === true;
62174
62214
  const model = requestBody["model"] || "unknown";
62215
+ const config = loadConfig();
62216
+ const isVllm = config.backendType === "vllm";
62217
+ if (isVllm) {
62218
+ const payload = JSON.stringify(requestBody);
62219
+ if (stream) {
62220
+ res.writeHead(200, {
62221
+ "Content-Type": "text/event-stream",
62222
+ "Cache-Control": "no-cache",
62223
+ "Connection": "keep-alive",
62224
+ "Access-Control-Allow-Origin": "*",
62225
+ "Access-Control-Allow-Methods": "GET, POST, PUT, PATCH, DELETE, OPTIONS",
62226
+ "Access-Control-Allow-Headers": "Content-Type, Authorization"
62227
+ });
62228
+ ollamaStream(ollamaUrl, "/v1/chat/completions", "POST", payload, (chunk) => {
62229
+ res.write(chunk);
62230
+ }, () => {
62231
+ res.end();
62232
+ }, (err) => {
62233
+ try {
62234
+ res.write(`data: ${JSON.stringify({ error: err.message })}
62235
+
62236
+ `);
62237
+ } catch {
62238
+ }
62239
+ res.end();
62240
+ });
62241
+ } else {
62242
+ try {
62243
+ const result = await ollamaRequest(ollamaUrl, "/v1/chat/completions", "POST", payload);
62244
+ if (result.status !== 200) {
62245
+ jsonResponse(res, result.status, { error: "Backend request failed", details: result.body });
62246
+ return;
62247
+ }
62248
+ const parsed = JSON.parse(result.body);
62249
+ if (parsed.usage) {
62250
+ metrics.totalTokensIn += parsed.usage.prompt_tokens ?? 0;
62251
+ metrics.totalTokensOut += parsed.usage.completion_tokens ?? 0;
62252
+ }
62253
+ jsonResponse(res, 200, parsed);
62254
+ } catch (err) {
62255
+ jsonResponse(res, 502, { error: "Failed to proxy to backend", message: err instanceof Error ? err.message : String(err) });
62256
+ }
62257
+ }
62258
+ return;
62259
+ }
62175
62260
  const ollamaPayload = JSON.stringify({
62176
62261
  ...requestBody,
62177
62262
  stream,
@@ -62329,6 +62414,22 @@ async function handleV1Embeddings(req, res, ollamaUrl) {
62329
62414
  return;
62330
62415
  }
62331
62416
  const requestBody = body;
62417
+ const config = loadConfig();
62418
+ const isVllm = config.backendType === "vllm";
62419
+ if (isVllm) {
62420
+ try {
62421
+ const payload = JSON.stringify(requestBody);
62422
+ const result = await ollamaRequest(ollamaUrl, "/v1/embeddings", "POST", payload);
62423
+ if (result.status !== 200) {
62424
+ jsonResponse(res, result.status, { error: "Backend embeddings request failed", details: result.body });
62425
+ return;
62426
+ }
62427
+ jsonResponse(res, 200, JSON.parse(result.body));
62428
+ } catch (err) {
62429
+ jsonResponse(res, 502, { error: "Failed to proxy to backend", message: err instanceof Error ? err.message : String(err) });
62430
+ }
62431
+ return;
62432
+ }
62332
62433
  const ollamaPayload = JSON.stringify({
62333
62434
  model: requestBody["model"],
62334
62435
  input: requestBody["input"]
@@ -62337,7 +62438,7 @@ async function handleV1Embeddings(req, res, ollamaUrl) {
62337
62438
  const result = await ollamaRequest(ollamaUrl, "/api/embed", "POST", ollamaPayload);
62338
62439
  if (result.status !== 200) {
62339
62440
  jsonResponse(res, result.status, {
62340
- error: "Ollama embeddings request failed",
62441
+ error: "Backend embeddings request failed",
62341
62442
  details: result.body
62342
62443
  });
62343
62444
  return;
@@ -62356,7 +62457,7 @@ async function handleV1Embeddings(req, res, ollamaUrl) {
62356
62457
  });
62357
62458
  } catch (err) {
62358
62459
  jsonResponse(res, 502, {
62359
- error: "Failed to proxy to Ollama",
62460
+ error: "Failed to proxy to backend",
62360
62461
  message: err instanceof Error ? err.message : String(err)
62361
62462
  });
62362
62463
  }
@@ -62564,10 +62665,16 @@ function handleV1RunsDelete(res, id) {
62564
62665
  }
62565
62666
  jsonResponse(res, 200, { run_id: id, status: "aborted" });
62566
62667
  }
62668
+ function redactSecrets(obj) {
62669
+ const copy = { ...obj };
62670
+ if (typeof copy.apiKey === "string" && copy.apiKey)
62671
+ copy.apiKey = "[redacted]";
62672
+ return copy;
62673
+ }
62567
62674
  function handleGetConfig(res) {
62568
62675
  const config = loadConfig();
62569
62676
  const settings = loadGlobalSettings();
62570
- jsonResponse(res, 200, { config, settings });
62677
+ jsonResponse(res, 200, { config: redactSecrets(config), settings: redactSecrets(settings) });
62571
62678
  }
62572
62679
  async function handlePatchConfig(req, res) {
62573
62680
  const body = await parseJsonBody(req);
@@ -62595,7 +62702,7 @@ async function handlePatchConfig(req, res) {
62595
62702
  settingsUpdate.timeoutMs = updates["timeoutMs"];
62596
62703
  saveGlobalSettings(settingsUpdate);
62597
62704
  const updatedConfig = loadConfig();
62598
- jsonResponse(res, 200, { config: updatedConfig, updated: Object.keys(settingsUpdate) });
62705
+ jsonResponse(res, 200, { config: redactSecrets(updatedConfig), updated: Object.keys(settingsUpdate) });
62599
62706
  }
62600
62707
  function handleGetConfigModel(res) {
62601
62708
  const config = loadConfig();
@@ -62936,7 +63043,8 @@ function startApiServer(options = {}) {
62936
63043
  `);
62937
63044
  process.stderr.write(` Listening on http://${host}:${port}
62938
63045
  `);
62939
- process.stderr.write(` Ollama proxy: ${ollamaUrl}
63046
+ const beType = config.backendType || "ollama";
63047
+ process.stderr.write(` Backend (${beType}): ${ollamaUrl}
62940
63048
  `);
62941
63049
  if (process.env["OA_API_KEYS"]) {
62942
63050
  const keyCount = process.env["OA_API_KEYS"].split(",").length;
@@ -68616,17 +68724,17 @@ async function evalCommand(opts, config) {
68616
68724
  rawBackend = new FakeBackend();
68617
68725
  }
68618
68726
  const backend = {
68619
- async complete(request2) {
68727
+ async complete(request) {
68620
68728
  const result = await rawBackend.complete({
68621
- messages: request2.messages.map((m) => ({
68729
+ messages: request.messages.map((m) => ({
68622
68730
  id: globalThis.crypto.randomUUID(),
68623
68731
  sessionId: globalThis.crypto.randomUUID(),
68624
68732
  role: m.role,
68625
68733
  content: m.content,
68626
68734
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
68627
68735
  })),
68628
- maxTokens: request2.maxTokens,
68629
- temperature: request2.temperature
68736
+ maxTokens: request.maxTokens,
68737
+ temperature: request.temperature
68630
68738
  });
68631
68739
  return { content: result.content ?? "" };
68632
68740
  }
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.46",
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",