newmark-agent 0.5.7 → 0.5.8

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.
@@ -2537,9 +2537,12 @@ button.left-ws-item {
2537
2537
  text-align: left;
2538
2538
  cursor: pointer;
2539
2539
  font: 600 12px var(--font);
2540
+ transition: none !important;
2541
+ animation: none !important;
2542
+ transform: none !important;
2540
2543
  }
2541
2544
 
2542
- .conversation-work-run-head:hover { color: var(--text-bright); }
2545
+ .conversation-work-run-head:hover { color: var(--text-bright); filter: none; }
2543
2546
 
2544
2547
  .conversation-work-run-chevron {
2545
2548
  width: 8px;
@@ -2548,7 +2551,7 @@ button.left-ws-item {
2548
2551
  border-right: 1px solid var(--text-dim);
2549
2552
  border-bottom: 1px solid var(--text-dim);
2550
2553
  transform: rotate(45deg);
2551
- transition: transform 150ms ease;
2554
+ transition: none;
2552
2555
  }
2553
2556
 
2554
2557
  .conversation-work-run.collapsed .conversation-work-run-chevron { transform: rotate(-45deg); }
@@ -16524,6 +16527,57 @@ var _terminalCounter = 0;
16524
16527
  var _terminalTakeoverTabs = {};
16525
16528
  var _terminalTakeoverRefreshToken = 0;
16526
16529
  var _terminalTakeoverDetached = {};
16530
+ var _terminalOutputLimit = 256 * 1024;
16531
+
16532
+ window.appendTerminalOutput = function(output, text, style) {
16533
+ if (!output || !text) return;
16534
+ var state = output._terminalRenderState;
16535
+ if (!state) state = output._terminalRenderState = { pending: [], frame: 0 };
16536
+ state.pending.push({ text: String(text), style: style || null });
16537
+ if (state.frame) return;
16538
+ state.frame = requestAnimationFrame(function() {
16539
+ state.frame = 0;
16540
+ if (!output.isConnected) { state.pending.length = 0; return; }
16541
+ var nearBottom = output.scrollHeight - output.scrollTop - output.clientHeight <= 32;
16542
+ var fragment = document.createDocumentFragment();
16543
+ for (var i = 0; i < state.pending.length; i++) {
16544
+ var item = state.pending[i];
16545
+ if (item.style) {
16546
+ var span = document.createElement('span');
16547
+ span.textContent = item.text;
16548
+ if (item.style.color) span.style.color = item.style.color;
16549
+ if (item.style.opacity) span.style.opacity = item.style.opacity;
16550
+ fragment.appendChild(span);
16551
+ } else {
16552
+ fragment.appendChild(document.createTextNode(item.text));
16553
+ }
16554
+ }
16555
+ state.pending.length = 0;
16556
+ output.appendChild(fragment);
16557
+ var overflow = output.textContent.length - _terminalOutputLimit;
16558
+ while (overflow > 0 && output.firstChild) {
16559
+ var first = output.firstChild;
16560
+ var length = first.textContent.length;
16561
+ if (length <= overflow) {
16562
+ output.removeChild(first);
16563
+ overflow -= length;
16564
+ } else {
16565
+ first.textContent = first.textContent.slice(overflow);
16566
+ overflow = 0;
16567
+ }
16568
+ }
16569
+ if (nearBottom) output.scrollTop = output.scrollHeight;
16570
+ });
16571
+ };
16572
+
16573
+ window.setTerminalOutput = function(output, text, style) {
16574
+ if (!output) return;
16575
+ var state = output._terminalRenderState;
16576
+ if (state && state.frame) cancelAnimationFrame(state.frame);
16577
+ output._terminalRenderState = { pending: [], frame: 0 };
16578
+ output.textContent = '';
16579
+ window.appendTerminalOutput(output, text, style);
16580
+ };
16527
16581
 
16528
16582
  window.portableTerminalWorkspacePath = function(input) {
16529
16583
  var raw = String(input || '').trim().replace(/\\/g, '/').replace(/\/+$/g, '');
@@ -16606,14 +16660,14 @@ window.terminalSend = function() {
16606
16660
  if (takeoverSession) {
16607
16661
  input.value = '';
16608
16662
  if (!api.terminalTakeoverWrite) {
16609
- if (output) output.innerHTML += '\r\n<span style="color:#ff6666;">[' + esc(t('terminal.notConnected')) + ']</span>';
16663
+ if (output) window.appendTerminalOutput(output, '\r\n[' + t('terminal.notConnected') + ']', { color:'#ff6666' });
16610
16664
  return;
16611
16665
  }
16612
16666
  var takeoverConversation = pane.getAttribute('data-takeover-conversation') || activeConversationId();
16613
16667
  api.terminalTakeoverWrite(takeoverSession, cmd, takeoverConversation).then(function(result) {
16614
16668
  if (result && result.ok === false) throw new Error(result.error || 'Terminal write failed');
16615
16669
  }).catch(function(err) {
16616
- if (output) output.innerHTML += '\r\n<span style="color:#ff6666;">[' + esc(t('common.error')) + '] ' + esc(err.message) + '</span>';
16670
+ if (output) window.appendTerminalOutput(output, '\r\n[' + t('common.error') + '] ' + err.message, { color:'#ff6666' });
16617
16671
  });
16618
16672
  return;
16619
16673
  }
@@ -16632,12 +16686,12 @@ window.terminalSend = function() {
16632
16686
  input.disabled = false;
16633
16687
  var currentPane = window.getActiveTerminalPane();
16634
16688
  var currentOutput = (currentPane && currentPane.querySelector('.terminal-output')) || output;
16635
- if (currentOutput) currentOutput.innerHTML += '\r\n<span style="color:#ff6666;">[' + esc(t('common.error')) + '] ' + esc(err.message || String(err)) + '</span>';
16689
+ if (currentOutput) window.appendTerminalOutput(currentOutput, '\r\n[' + t('common.error') + '] ' + (err.message || String(err)), { color:'#ff6666' });
16636
16690
  });
16637
16691
  }
16638
16692
  input.value = '';
16639
16693
  api.terminalWrite(sessionId, cmd + '\r').catch(function(err) {
16640
- if (output) output.innerHTML += '\r\n<span style="color:#ff6666;">[' + esc(t('common.error')) + '] ' + esc(err.message) + '</span>';
16694
+ if (output) window.appendTerminalOutput(output, '\r\n[' + t('common.error') + '] ' + err.message, { color:'#ff6666' });
16641
16695
  });
16642
16696
  };
16643
16697
 
@@ -16651,7 +16705,7 @@ window.clearTerminal = function() {
16651
16705
  api.terminalKill(sessionId, state.terminalInterruptTimeoutMs || 0).catch(function(){});
16652
16706
  pane.setAttribute('data-session', '');
16653
16707
  }
16654
- if (output) output.innerHTML = '';
16708
+ if (output) window.setTerminalOutput(output, '');
16655
16709
  if (prompt) prompt.textContent = '>';
16656
16710
  };
16657
16711
 
@@ -16761,12 +16815,12 @@ window.addTerminalTab = function(shellId, options) {
16761
16815
  var terminalReadyPromise = terminalSpawnPromise.then(function(resp) {
16762
16816
  pane.setAttribute('data-session', resp.sessionId);
16763
16817
  var out = pane.querySelector('.terminal-output');
16764
- if (out) out.innerHTML = '<span style="color:var(--accent2);">' + esc(t('terminal.connected')) + ' (' + esc(shellId) + ')</span>\r\n';
16818
+ if (out) window.setTerminalOutput(out, t('terminal.connected') + ' (' + shellId + ')\r\n', { color:'var(--accent2)' });
16765
16819
  state._terminalActiveSession = resp.sessionId;
16766
16820
  return resp;
16767
16821
  }).catch(function(err) {
16768
16822
  var out = pane.querySelector('.terminal-output');
16769
- if (out) out.innerHTML = '<span style="color:#ff6666;">' + esc(t('terminal.failed')) + ': ' + esc(err.message) + '</span>';
16823
+ if (out) window.setTerminalOutput(out, t('terminal.failed') + ': ' + err.message, { color:'#ff6666' });
16770
16824
  if (options.required === true) throw err;
16771
16825
  return null;
16772
16826
  });
@@ -16881,13 +16935,12 @@ window.applyTerminalTakeoverEvent = function(payload) {
16881
16935
  var output = pane.querySelector('.terminal-output');
16882
16936
  if (!output) return;
16883
16937
  if (payload.type === 'started' && session.buffer) {
16884
- output.innerHTML = esc(session.buffer);
16938
+ window.setTerminalOutput(output, session.buffer);
16885
16939
  } else if (payload.data) {
16886
- output.innerHTML += esc(payload.data);
16940
+ window.appendTerminalOutput(output, payload.data);
16887
16941
  } else if (payload.type === 'stopped') {
16888
- output.innerHTML += '\r\n<span style="color:var(--text-dim);opacity:0.65;">[Agent takeover stopped]</span>';
16942
+ window.appendTerminalOutput(output, '\r\n[Agent takeover stopped]', { color:'var(--text-dim)', opacity:'0.65' });
16889
16943
  }
16890
- output.scrollTop = output.scrollHeight;
16891
16944
  if (!active) window.pruneTerminalTakeoverEndedPanes(session.id);
16892
16945
  };
16893
16946
 
@@ -17007,16 +17060,14 @@ if (api.onTerminalData) {
17007
17060
  .replace(/\x1b\][^\x07]*(?:\x07|\x1b\\)/g, '')
17008
17061
  .replace(/\x1b\[[0-?]*[ -\/]*[@-~]/g, '')
17009
17062
  .replace(/\r(?!\n)/g, '');
17010
- var text = clean.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
17011
- output.innerHTML += text;
17012
- output.scrollTop = output.scrollHeight;
17063
+ window.appendTerminalOutput(output, clean);
17013
17064
  });
17014
17065
  api.onTerminalExit(function(event, sessionId, code) {
17015
17066
  var pane = document.querySelector('.terminal-pane[data-session="' + sessionId + '"]');
17016
17067
  if (!pane) return;
17017
17068
  pane.setAttribute('data-session', '');
17018
17069
  var output = pane.querySelector('.terminal-output');
17019
- if (output) output.innerHTML += '\r\n<span style="color:var(--text-dim);opacity:0.5;">[Process exited code=' + code + ']</span>';
17070
+ if (output) window.appendTerminalOutput(output, '\r\n[Process exited code=' + code + ']', { color:'var(--text-dim)', opacity:'0.5' });
17020
17071
  });
17021
17072
  }
17022
17073
 
@@ -328713,6 +328713,30 @@ function parseProviderSse(raw) {
328713
328713
  }
328714
328714
  return events;
328715
328715
  }
328716
+ function assembleCompatibleToolArguments(parts) {
328717
+ const nonEmpty = (parts || []).map(String).filter((part) => part && part !== "null");
328718
+ if (!nonEmpty.length) return "{}";
328719
+ const isJsonObject = (value) => {
328720
+ try {
328721
+ const parsed = JSON.parse(value);
328722
+ return !!parsed && typeof parsed === "object" && !Array.isArray(parsed);
328723
+ } catch {
328724
+ return false;
328725
+ }
328726
+ };
328727
+ const incremental = nonEmpty.join("");
328728
+ if (isJsonObject(incremental)) return incremental;
328729
+ let compatible = "";
328730
+ for (const incoming of nonEmpty) {
328731
+ if (!compatible) compatible = incoming;
328732
+ else if (incoming === compatible) continue;
328733
+ else if (incoming.startsWith(compatible)) compatible = incoming;
328734
+ else if (compatible.startsWith(incoming)) continue;
328735
+ else compatible += incoming;
328736
+ }
328737
+ if (isJsonObject(compatible)) return compatible;
328738
+ return [...nonEmpty].reverse().find(isJsonObject) || compatible;
328739
+ }
328716
328740
  function isContentPolicyBlocked(json) {
328717
328741
  const choices = Array.isArray(json.choices) ? json.choices : [];
328718
328742
  const choice = choices[0] || {};
@@ -328941,6 +328965,8 @@ var ChatCompletionsAdapter = class {
328941
328965
  let contentPolicyBlocked = false;
328942
328966
  let emittedContent = false;
328943
328967
  let emittedTool = false;
328968
+ let emittedReasoning = false;
328969
+ let explicitCompletion = false;
328944
328970
  try {
328945
328971
  while (true) {
328946
328972
  const { done, value } = await readProviderStreamChunk(reader, signal);
@@ -328952,7 +328978,10 @@ var ChatCompletionsAdapter = class {
328952
328978
  const trimmed = line.trim();
328953
328979
  if (!trimmed.startsWith("data: ")) continue;
328954
328980
  const data = trimmed.slice(6);
328955
- if (data === "[DONE]") continue;
328981
+ if (data === "[DONE]") {
328982
+ explicitCompletion = true;
328983
+ continue;
328984
+ }
328956
328985
  let json;
328957
328986
  try {
328958
328987
  json = JSON.parse(data);
@@ -328965,11 +328994,16 @@ var ChatCompletionsAdapter = class {
328965
328994
  }
328966
328995
  if (isContentPolicyBlocked(json)) contentPolicyBlocked = true;
328967
328996
  const choices = Array.isArray(json.choices) ? json.choices : [];
328968
- const delta = choices[0]?.delta;
328997
+ const choice = choices[0];
328998
+ if (choice?.finish_reason !== void 0 && choice.finish_reason !== null) explicitCompletion = true;
328999
+ const delta = choice?.delta;
328969
329000
  if (!delta) continue;
328970
329001
  if (delta.reasoning_content) {
328971
329002
  const reasoning = this.extractText(delta.reasoning_content);
328972
- if (reasoning) yield { type: "reasoning.summary.delta", delta: reasoning };
329003
+ if (reasoning) {
329004
+ emittedReasoning = true;
329005
+ yield { type: "reasoning.summary.delta", delta: reasoning };
329006
+ }
328973
329007
  }
328974
329008
  const textDelta = this.extractText(delta.content);
328975
329009
  if (textDelta) {
@@ -329014,13 +329048,17 @@ var ChatCompletionsAdapter = class {
329014
329048
  type: "tool_call.completed",
329015
329049
  id: currentToolCall.id,
329016
329050
  name: currentToolCall.name,
329017
- arguments: currentToolCall.argumentParts.join("")
329051
+ arguments: assembleCompatibleToolArguments(currentToolCall.argumentParts)
329018
329052
  };
329019
329053
  }
329020
329054
  } else if (!emittedContent && !emittedTool && contentPolicyBlocked) {
329021
329055
  yield { type: "response.failed", error: "[Error] Content policy refusal (content_filter)." };
329022
329056
  return;
329023
329057
  }
329058
+ if (!explicitCompletion && !emittedContent && !emittedTool && !emittedReasoning) {
329059
+ yield { type: "response.failed", error: "[LLM Error] Chat stream ended before an explicit completion." };
329060
+ return;
329061
+ }
329024
329062
  yield { type: "response.completed" };
329025
329063
  } finally {
329026
329064
  reader.releaseLock();
@@ -329217,6 +329255,7 @@ var ResponsesAdapter = class {
329217
329255
  const key3 = `${String(payload.item_id || "")}:${String(payload.summary_index || 0)}`;
329218
329256
  const delta = this.extractText(payload.delta);
329219
329257
  if (delta) {
329258
+ emittedContent = true;
329220
329259
  reasoningSummaries.set(key3, (reasoningSummaries.get(key3) || "") + delta);
329221
329260
  yield { type: "reasoning.summary.delta", delta };
329222
329261
  }
@@ -329247,7 +329286,7 @@ var ResponsesAdapter = class {
329247
329286
  calls.set(key3, {
329248
329287
  id: String(item.call_id || item.id || key3),
329249
329288
  name: String(item.name || ""),
329250
- arguments: String(item.arguments || ""),
329289
+ argumentParts: item.arguments ? [String(item.arguments)] : [],
329251
329290
  emitted: false
329252
329291
  });
329253
329292
  }
@@ -329255,9 +329294,9 @@ var ResponsesAdapter = class {
329255
329294
  }
329256
329295
  if (eventType === "response.function_call_arguments.delta") {
329257
329296
  const key3 = String(payload.item_id || payload.call_id || payload.output_index || "");
329258
- const call = calls.get(key3) || { id: String(payload.call_id || key3), name: String(payload.name || ""), arguments: "", emitted: false };
329297
+ const call = calls.get(key3) || { id: String(payload.call_id || key3), name: String(payload.name || ""), argumentParts: [], emitted: false };
329259
329298
  const delta = String(payload.delta || "");
329260
- call.arguments += delta;
329299
+ if (delta) call.argumentParts.push(delta);
329261
329300
  calls.set(key3, call);
329262
329301
  yield { type: "tool_call.arguments.delta", id: call.id, delta };
329263
329302
  continue;
@@ -329269,19 +329308,20 @@ var ResponsesAdapter = class {
329269
329308
  const call = calls.get(key3) || {
329270
329309
  id: String(item.call_id || item.id || key3),
329271
329310
  name: String(item.name || ""),
329272
- arguments: String(item.arguments || ""),
329311
+ argumentParts: item.arguments ? [String(item.arguments)] : [],
329273
329312
  emitted: false
329274
329313
  };
329275
329314
  call.id = String(item.call_id || call.id);
329276
329315
  call.name = String(item.name || call.name);
329277
- call.arguments = typeof item.arguments === "string" ? item.arguments : call.arguments;
329316
+ if (typeof item.arguments === "string" && item.arguments) call.argumentParts.push(item.arguments);
329278
329317
  if (!call.emitted) {
329279
329318
  call.emitted = true;
329319
+ const argumentsJson = assembleCompatibleToolArguments(call.argumentParts);
329280
329320
  yield { type: "tool_call.started", id: call.id, name: call.name };
329281
- if (call.arguments && call.arguments !== "{}") {
329282
- yield { type: "tool_call.arguments.delta", id: call.id, delta: call.arguments };
329321
+ if (argumentsJson !== "{}") {
329322
+ yield { type: "tool_call.arguments.delta", id: call.id, delta: argumentsJson };
329283
329323
  }
329284
- yield { type: "tool_call.completed", id: call.id, name: call.name, arguments: call.arguments };
329324
+ yield { type: "tool_call.completed", id: call.id, name: call.name, arguments: argumentsJson };
329285
329325
  }
329286
329326
  calls.set(key3, call);
329287
329327
  }
@@ -329306,8 +329346,14 @@ var ResponsesAdapter = class {
329306
329346
  } else if (!completed) {
329307
329347
  yield { type: "response.failed", error: "[LLM Error] Responses stream ended before response.completed." };
329308
329348
  } else if (!emittedContent && calls.size === 0) {
329309
- yield { type: "response.failed", error: "[Error] Empty Responses stream." };
329349
+ yield { type: "response.failed", error: "[Error] Provider returned an empty response." };
329310
329350
  } else {
329351
+ for (const call of calls.values()) {
329352
+ if (call.emitted) continue;
329353
+ const argumentsJson = assembleCompatibleToolArguments(call.argumentParts);
329354
+ yield { type: "tool_call.started", id: call.id, name: call.name };
329355
+ yield { type: "tool_call.completed", id: call.id, name: call.name, arguments: argumentsJson };
329356
+ }
329311
329357
  yield { type: "response.completed" };
329312
329358
  }
329313
329359
  } finally {
@@ -330186,7 +330232,7 @@ ${responsePath}
330186
330232
  * `provider_adapters_v2` context flag. Request serialization and SSE
330187
330233
  * normalization are delegated to the shared provider adapters while the
330188
330234
  * transport orchestration (loopback node-http, fetch -> node-http fallback,
330189
- * 120s/30s timeouts) and the 4xx Chat -> Responses downgrade stay here.
330235
+ * cancellation-only streaming and the 4xx Chat -> Responses downgrade) stay here.
330190
330236
  * The emitted request body and StreamToken stream are byte-equivalent to
330191
330237
  * the legacy inlined path.
330192
330238
  */
@@ -330323,9 +330369,9 @@ ${responsePath}
330323
330369
  }
330324
330370
  /**
330325
330371
  * Loopback-aware transport injected into adapter `execute`. Streaming
330326
- * requests retain the fetch-to-node fallback for transport failures, while
330327
- * a local deadline is returned directly so one request cannot become a
330328
- * second Windows fallback request.
330372
+ * requests retain the fetch-to-node fallback for transport failures. They
330373
+ * have no response deadline; only caller cancellation or a concrete
330374
+ * transport/provider failure may end the request.
330329
330375
  */
330330
330376
  buildProviderAdapterTransport() {
330331
330377
  return async (request, signal) => {
@@ -330335,7 +330381,7 @@ ${responsePath}
330335
330381
  const forwardAbort = () => abort.abort(signal?.reason);
330336
330382
  if (signal?.aborted) forwardAbort();
330337
330383
  else signal?.addEventListener("abort", forwardAbort, { once: true });
330338
- const effectiveTimeout = this.effectiveRequestTimeout(12e4);
330384
+ const effectiveTimeout = 0;
330339
330385
  const timer = effectiveTimeout > 0 ? setTimeout(() => abort.abort(providerTimeoutError(effectiveTimeout)), effectiveTimeout) : void 0;
330340
330386
  try {
330341
330387
  try {
@@ -330480,7 +330526,7 @@ ${responsePath}
330480
330526
  const forwardAbort = () => abort.abort(signal?.reason);
330481
330527
  if (signal?.aborted) forwardAbort();
330482
330528
  else signal?.addEventListener("abort", forwardAbort, { once: true });
330483
- const effectiveTimeout = this.effectiveRequestTimeout(12e4);
330529
+ const effectiveTimeout = 0;
330484
330530
  const timeout = effectiveTimeout > 0 ? setTimeout(() => abort.abort(providerTimeoutError(effectiveTimeout)), effectiveTimeout) : void 0;
330485
330531
  let reader = null;
330486
330532
  try {
@@ -330512,10 +330558,14 @@ ${responsePath}
330512
330558
  }
330513
330559
  const decoder = new TextDecoder();
330514
330560
  let buffer = "";
330515
- let currentToolCall = null;
330561
+ const toolCalls = /* @__PURE__ */ new Map();
330562
+ const toolCallOrder = [];
330563
+ let syntheticToolIndex = 0;
330564
+ let lastToolIndex = 0;
330516
330565
  let currentReasoningContent = "";
330517
330566
  let contentPolicyBlocked = false;
330518
330567
  let emittedContent = false;
330568
+ let explicitCompletion = false;
330519
330569
  const streamSignal = signal || new AbortController().signal;
330520
330570
  while (true) {
330521
330571
  const { done, value } = await readProviderStreamChunk(reader, streamSignal);
@@ -330527,7 +330577,10 @@ ${responsePath}
330527
330577
  const trimmed = line.trim();
330528
330578
  if (!trimmed.startsWith("data: ")) continue;
330529
330579
  const data = trimmed.slice(6);
330530
- if (data === "[DONE]") continue;
330580
+ if (data === "[DONE]") {
330581
+ explicitCompletion = true;
330582
+ continue;
330583
+ }
330531
330584
  try {
330532
330585
  const json = JSON.parse(data);
330533
330586
  if (json.usage) yield { type: "usage", text: "", usage: extractProviderUsage(json) };
@@ -330545,24 +330598,44 @@ ${responsePath}
330545
330598
  }
330546
330599
  if (delta.tool_calls) {
330547
330600
  for (const tc of delta.tool_calls) {
330548
- if (tc.id) {
330549
- if (currentToolCall) {
330550
- yield { type: "tool_call", text: "", toolCall: currentToolCall, reasoningContent: currentReasoningContent || void 0 };
330551
- }
330552
- currentToolCall = { id: tc.id, name: tc.function?.name || "", arguments: tc.function?.arguments || "" };
330553
- } else if (tc.function?.arguments && currentToolCall) {
330554
- currentToolCall.arguments += tc.function.arguments;
330601
+ const rawIndex = Number(tc.index);
330602
+ const index = Number.isInteger(rawIndex) && rawIndex >= 0 ? rawIndex : tc.id ? syntheticToolIndex++ : lastToolIndex;
330603
+ lastToolIndex = index;
330604
+ let call = toolCalls.get(index);
330605
+ if (!call && (tc.id || tc.function?.name)) {
330606
+ call = { id: tc.id || "", name: tc.function?.name || "", argumentParts: [] };
330607
+ toolCalls.set(index, call);
330608
+ toolCallOrder.push(index);
330555
330609
  }
330610
+ if (!call) continue;
330611
+ if (tc.id && !call.id) call.id = tc.id;
330612
+ if (tc.function?.name && !call.name) call.name = tc.function.name;
330613
+ if (tc.function?.arguments) call.argumentParts.push(tc.function.arguments);
330556
330614
  }
330557
330615
  }
330558
330616
  } catch {
330559
330617
  }
330560
330618
  }
330561
330619
  }
330562
- if (currentToolCall && currentToolCall.arguments) {
330563
- yield { type: "tool_call", text: "", toolCall: currentToolCall, reasoningContent: currentReasoningContent || void 0 };
330620
+ if (toolCallOrder.length) {
330621
+ for (const index of toolCallOrder) {
330622
+ const call = toolCalls.get(index);
330623
+ if (!call) continue;
330624
+ yield {
330625
+ type: "tool_call",
330626
+ text: "",
330627
+ toolCall: {
330628
+ id: call.id,
330629
+ name: call.name,
330630
+ arguments: assembleCompatibleToolArguments(call.argumentParts)
330631
+ },
330632
+ reasoningContent: currentReasoningContent || void 0
330633
+ };
330634
+ }
330564
330635
  } else if (!emittedContent && contentPolicyBlocked) {
330565
330636
  yield { type: "text", text: "[Error] Content policy refusal (content_filter)." };
330637
+ } else if (!explicitCompletion && !emittedContent && !currentReasoningContent) {
330638
+ yield { type: "text", text: "[LLM Error] GitHub Models stream ended before an explicit completion." };
330566
330639
  }
330567
330640
  } finally {
330568
330641
  reader?.releaseLock();
@@ -340515,6 +340588,22 @@ function createToolchainCore() {
340515
340588
  return { registry: new ToolRegistry(), catalog: new CapabilityCatalog() };
340516
340589
  }
340517
340590
 
340591
+ // src/core/emptyResponseRetry.ts
340592
+ var EMPTY_RESPONSE_RETRY_DELAYS_MS = [200, 800, 2e3, 1e4, 6e4];
340593
+ var MAX_EMPTY_RESPONSE_RETRIES = EMPTY_RESPONSE_RETRY_DELAYS_MS.length;
340594
+ var MAX_CONSECUTIVE_EMPTY_RESPONSES = MAX_EMPTY_RESPONSE_RETRIES + 1;
340595
+ function emptyResponseRetryDelayMs(consecutiveEmptyResponses) {
340596
+ return EMPTY_RESPONSE_RETRY_DELAYS_MS[Math.max(0, consecutiveEmptyResponses - 1)] ?? 0;
340597
+ }
340598
+ function observeEmptyResponseOutcome(consecutiveEmptyResponses, emptyResponse) {
340599
+ const nextCount = emptyResponse ? consecutiveEmptyResponses + 1 : 0;
340600
+ return {
340601
+ consecutiveEmptyResponses: nextCount,
340602
+ retry: emptyResponse && nextCount <= MAX_EMPTY_RESPONSE_RETRIES,
340603
+ terminate: emptyResponse && nextCount > MAX_EMPTY_RESPONSE_RETRIES
340604
+ };
340605
+ }
340606
+
340518
340607
  // src/core/agentKernelRunner.ts
340519
340608
  var publicStreamFilters = /* @__PURE__ */ new WeakMap();
340520
340609
  var brokerOnlyAssistantBuffers = /* @__PURE__ */ new WeakMap();
@@ -340666,7 +340755,7 @@ function kernelTurnFailed(agent, turn) {
340666
340755
  return turn.stopReason === "error" || agent.isLlmErrorText(turn.text);
340667
340756
  }
340668
340757
  function providerTurnIsEmpty(turn) {
340669
- return /provider returned an empty response/i.test(`${turn.errorMessage}
340758
+ return !turn.activity && /provider returned an empty response/i.test(`${turn.errorMessage}
340670
340759
  ${turn.text}`);
340671
340760
  }
340672
340761
  function removeTrailingFailedAssistant(agent, messages) {
@@ -340675,6 +340764,12 @@ function removeTrailingFailedAssistant(agent, messages) {
340675
340764
  const text = KernelMessageText(last);
340676
340765
  if (last.stopReason === "error" || agent.isLlmErrorText(text)) messages.pop();
340677
340766
  }
340767
+ function removeTrailingThoughtOnlyAssistant(messages) {
340768
+ const last = messages[messages.length - 1];
340769
+ if (last?.role !== "assistant") return;
340770
+ const hasToolCall = last.content.some((content) => content.type === "toolCall");
340771
+ if (!KernelMessageText(last).trim() && !hasToolCall) messages.pop();
340772
+ }
340678
340773
  function normalizePublicProviderError(error, secrets = []) {
340679
340774
  let raw = "";
340680
340775
  if (error instanceof Error) {
@@ -340800,8 +340895,17 @@ async function runAgentKernel(agent) {
340800
340895
  const tokens = [];
340801
340896
  const runOnce = async (promptMessages, appendPromptToAgentHistory) => {
340802
340897
  let lastAssistant = null;
340898
+ let observedActivity = false;
340899
+ let observedThought = false;
340803
340900
  const unsubscribe = kernel2.subscribe(async (event) => {
340804
340901
  await handleKernelEvent(agent, event, tokens);
340902
+ if (event.type === "message_update") {
340903
+ const delta = event.assistantMessageEvent;
340904
+ const deltaText = typeof delta.delta === "string" ? delta.delta : "";
340905
+ const thoughtDelta = delta.type === "thinking_delta" && !!deltaText.trim();
340906
+ observedThought = observedThought || thoughtDelta;
340907
+ observedActivity = observedActivity || thoughtDelta || delta.type === "text_delta" && !!deltaText.trim() || delta.type === "toolcall_end";
340908
+ }
340805
340909
  if (event.type === "message_end" && event.message.role === "assistant") {
340806
340910
  lastAssistant = event.message;
340807
340911
  }
@@ -340819,11 +340923,13 @@ async function runAgentKernel(agent) {
340819
340923
  const assistant = lastAssistant;
340820
340924
  const text = assistant ? KernelMessageText(assistant) : "";
340821
340925
  const hasToolCall = !!assistant?.content?.some((content) => content.type === "toolCall");
340822
- const emptyResponse = !assistant || !text.trim() && !hasToolCall && String(assistant?.stopReason || "") !== "aborted";
340926
+ const emptyResponse = !assistant || !text.trim() && !hasToolCall && !observedActivity && String(assistant?.stopReason || "") !== "aborted";
340823
340927
  return {
340824
340928
  text: emptyResponse ? "[Error] Provider returned an empty response." : text,
340825
340929
  stopReason: String(assistant?.stopReason || ""),
340826
- errorMessage: String(assistant?.errorMessage || (emptyResponse ? "Provider returned an empty response." : ""))
340930
+ errorMessage: String(assistant?.errorMessage || (emptyResponse ? "Provider returned an empty response." : "")),
340931
+ activity: observedActivity || !!text.trim() || hasToolCall,
340932
+ thoughtOnly: observedThought && !text.trim() && !hasToolCall && !["error", "aborted"].includes(String(assistant?.stopReason || ""))
340827
340933
  };
340828
340934
  } finally {
340829
340935
  unsubscribe();
@@ -340869,14 +340975,22 @@ async function runAgentKernel(agent) {
340869
340975
  fallback: { from: modelBeforeKernelRun, to: agent.model, providerId: agent.activeDeployment()?.providerId }
340870
340976
  });
340871
340977
  }
340872
- let emptyResponseRetries = 0;
340873
- while (providerTurnIsEmpty(lastTurn) && emptyResponseRetries < 2) {
340978
+ let consecutiveEmptyResponses = 0;
340979
+ for (; ; ) {
340980
+ const emptyResponseState = observeEmptyResponseOutcome(consecutiveEmptyResponses, providerTurnIsEmpty(lastTurn));
340981
+ consecutiveEmptyResponses = emptyResponseState.consecutiveEmptyResponses;
340982
+ if (lastTurn.thoughtOnly) {
340983
+ removeTrailingThoughtOnlyAssistant(kernel2.state.messages);
340984
+ lastTurn = await runWithCompressionResume([], false);
340985
+ continue;
340986
+ }
340987
+ if (!emptyResponseState.retry) break;
340874
340988
  removeTrailingFailedAssistant(agent, kernel2.state.messages);
340875
- emptyResponseRetries += 1;
340876
- const notice = `[Model retry] Provider returned an empty response; retrying the same deployment (${emptyResponseRetries}/2).`;
340989
+ const retryNumber = consecutiveEmptyResponses;
340990
+ const notice = `[Model retry] Provider returned an empty response; retrying the same deployment (${retryNumber}/${MAX_EMPTY_RESPONSE_RETRIES}) after ${emptyResponseRetryDelayMs(consecutiveEmptyResponses)}ms.`;
340877
340991
  tokens.push({ type: "text", text: notice });
340878
340992
  agent.recordWorkStatus(notice);
340879
- await agent.waitForPlannedRouteRetry();
340993
+ await agent.waitForPlannedRouteRetry(emptyResponseRetryDelayMs(consecutiveEmptyResponses));
340880
340994
  lastTurn = await runWithCompressionResume([], false);
340881
340995
  }
340882
340996
  let routeRetries = 0;
@@ -341072,7 +341186,7 @@ async function runAgentKernel(agent) {
341072
341186
  return;
341073
341187
  }
341074
341188
  if (textStarted) finalContent.push({ type: "text", text });
341075
- if (!finalContent.length) {
341189
+ if (!finalContent.length && !thinking.trim()) {
341076
341190
  text = "[Error] Provider returned an empty response.";
341077
341191
  finalContent.push({ type: "text", text });
341078
341192
  }
@@ -345656,7 +345770,12 @@ var Agent4 = class _Agent {
345656
345770
  beginRouteAttempt() {
345657
345771
  this.routeAttemptStartedAt = Date.now();
345658
345772
  }
345659
- async waitForPlannedRouteRetry() {
345773
+ async waitForPlannedRouteRetry(explicitDelayMs) {
345774
+ if (explicitDelayMs !== void 0) {
345775
+ if (explicitDelayMs <= 0) return;
345776
+ await new Promise((resolve16) => setTimeout(resolve16, explicitDelayMs));
345777
+ return;
345778
+ }
345660
345779
  const waitBudgetMs = Math.max(0, Math.min(15e3, this.lastRouteDecision?.retryBudgetMs ?? 5e3));
345661
345780
  const delay = Math.max(0, Math.min(waitBudgetMs, this.lastRouteRetryDelayMs));
345662
345781
  this.lastRouteRetryDelayMs = 0;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "newmark-agent",
3
3
  "productName": "Newmark Agent",
4
- "version": "0.5.7",
4
+ "version": "0.5.8",
5
5
  "description": "Newmark Agent — Portable AI coding agent with rich GUI and CLI (TypeScript)",
6
6
  "homepage": "https://github.com/positer/Newmark-Agent",
7
7
  "repository": {
@@ -41,6 +41,7 @@
41
41
  "test:cli:built": "node dist/tests/cliToolContractVerify.js && node dist/tests/openAIHubAnthropicSmokeContractVerify.js && node dist/tests/cliComputerUseAccuracyVerify.js",
42
42
  "test:gui-tui-cli-stress": "npm run build && npm run test:gui-tui-cli-stress:built",
43
43
  "test:gui-tui-cli-stress:built": "node dist/tests/guiTuiCliSharedBackendStressVerify.js",
44
+ "test:empty-response-retry": "npm run build && node dist/tests/emptyResponseRetryVerify.js",
44
45
  "typecheck": "tsc --noEmit",
45
46
  "lint": "oxlint src",
46
47
  "test": "npm run test:full-release",
@@ -94,6 +95,7 @@
94
95
  "test:dev012": "npm run build && node dist/tests/performanceOptimizationVerify.js && node dist/tests/compressionFidelityVerify.js && node dist/tests/responseTrajectoryVerify.js && node dist/tests/normalChatRegressionVerify.js && node dist/tests/toolProvisioningVerify.js && node dist/tests/guideWorkRunVerify.js && node dist/tests/runtimeIsolationVerify.js && node dist/tests/openAIHubAnthropicSmokeContractVerify.js",
95
96
  "test:computer-use-performance": "npm run build && node dist/tests/computerUsePerformanceVerify.js",
96
97
  "test:terminal-takeover": "npm run build && node dist/tests/terminalTakeoverVerify.js",
98
+ "test:terminal-output-stress": "npm run build && node dist/tests/terminalOutputBackpressureVerify.js",
97
99
  "test:native-bash": "npm run build && node dist/tests/nativeBashVerify.js",
98
100
  "test:automation-bash-stress": "npm run build && node dist/tests/automationBashStressVerify.js",
99
101
  "dist:win": "npm run build:clean && node scripts/dist-portable.cjs",