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.
- package/dist/conversation-utility-host.bundle.cjs +159 -40
- package/dist/core/agent.d.ts +1 -1
- package/dist/core/agent.js +7 -1
- package/dist/core/agentKernelRunner.js +43 -8
- package/dist/core/emptyResponseRetry.d.ts +16 -0
- package/dist/core/emptyResponseRetry.js +25 -0
- package/dist/core/terminalOutputBuffer.d.ts +24 -0
- package/dist/core/terminalOutputBuffer.js +92 -0
- package/dist/llm/provider.d.ts +4 -4
- package/dist/llm/provider.js +55 -21
- package/dist/main.js +12 -3
- package/dist/providers/chat-completions.adapter.js +17 -4
- package/dist/providers/provider-events.d.ts +7 -0
- package/dist/providers/provider-events.js +40 -0
- package/dist/providers/responses.adapter.js +23 -9
- package/dist/ui/index.html +68 -17
- package/dist/wsl-agent-host.bundle.cjs +159 -40
- package/package.json +3 -1
package/dist/ui/index.html
CHANGED
|
@@ -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:
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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)
|
|
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
|
|
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
|
|
16938
|
+
window.setTerminalOutput(output, session.buffer);
|
|
16885
16939
|
} else if (payload.data) {
|
|
16886
|
-
output
|
|
16940
|
+
window.appendTerminalOutput(output, payload.data);
|
|
16887
16941
|
} else if (payload.type === 'stopped') {
|
|
16888
|
-
output
|
|
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
|
-
|
|
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
|
|
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]")
|
|
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
|
|
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)
|
|
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
|
|
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
|
-
|
|
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 || ""),
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
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 (
|
|
329282
|
-
yield { type: "tool_call.arguments.delta", id: call.id, delta:
|
|
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:
|
|
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]
|
|
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
|
-
*
|
|
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
|
|
330327
|
-
*
|
|
330328
|
-
*
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
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]")
|
|
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
|
-
|
|
330549
|
-
|
|
330550
|
-
|
|
330551
|
-
|
|
330552
|
-
|
|
330553
|
-
|
|
330554
|
-
|
|
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 (
|
|
330563
|
-
|
|
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
|
|
340873
|
-
|
|
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
|
-
|
|
340876
|
-
const notice = `[Model retry] Provider returned an empty response; retrying the same deployment (${
|
|
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.
|
|
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",
|