open-agents-ai 0.187.222 → 0.187.224
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/index.js +304 -11
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -317843,6 +317843,117 @@ function appendExpandableContent(parent, fullText, opts) {
|
|
|
317843
317843
|
return wrapper;
|
|
317844
317844
|
}
|
|
317845
317845
|
|
|
317846
|
+
// WO-CHAT-RESUME-TOOLS — render a single tool_call event into a parent
|
|
317847
|
+
// container. Used by BOTH the live SSE handler AND the chat history
|
|
317848
|
+
// restore path so reloads see the same intermediate-state dropdowns
|
|
317849
|
+
// they saw during the original turn. The chunkLike object accepts the
|
|
317850
|
+
// SSE chunk shape ({type:'tool_call', tool, args}) OR the persisted
|
|
317851
|
+
// session message shape ({role:'tool_call', tool, args}).
|
|
317852
|
+
function renderToolCallEvent(parent, chunkLike) {
|
|
317853
|
+
const details = document.createElement('details');
|
|
317854
|
+
details.style.cssText = 'background:#1e1e22;border-left:2px solid #b2920a;margin:2px 0;font-size:0.7rem';
|
|
317855
|
+
const summary = document.createElement('summary');
|
|
317856
|
+
summary.style.cssText = 'padding:4px 8px;color:#b2920a;cursor:pointer';
|
|
317857
|
+
|
|
317858
|
+
const toolName = chunkLike.tool || 'tool';
|
|
317859
|
+
let a = (chunkLike.args && typeof chunkLike.args === 'object') ? chunkLike.args : {};
|
|
317860
|
+
if (typeof chunkLike.args === 'string') {
|
|
317861
|
+
try { a = JSON.parse(chunkLike.args); } catch { a = { _raw: chunkLike.args }; }
|
|
317862
|
+
}
|
|
317863
|
+
if (toolName === 'todo_write' && typeof a.todos === 'string') {
|
|
317864
|
+
try {
|
|
317865
|
+
const parsed = JSON.parse(a.todos);
|
|
317866
|
+
if (Array.isArray(parsed)) a = Object.assign({}, a, { todos: parsed });
|
|
317867
|
+
} catch {}
|
|
317868
|
+
}
|
|
317869
|
+
|
|
317870
|
+
let inlineSummary = '';
|
|
317871
|
+
if (toolName === 'todo_write' && Array.isArray(a.todos)) {
|
|
317872
|
+
const todos = a.todos;
|
|
317873
|
+
let p = 0, ip = 0, c = 0, b = 0;
|
|
317874
|
+
for (const t of todos) {
|
|
317875
|
+
const s = (t && typeof t === 'object') ? t.status : '';
|
|
317876
|
+
if (s === 'completed') c++;
|
|
317877
|
+
else if (s === 'in_progress') ip++;
|
|
317878
|
+
else if (s === 'blocked') b++;
|
|
317879
|
+
else p++;
|
|
317880
|
+
}
|
|
317881
|
+
inlineSummary = ' \\u2014 ' + todos.length + ' items (' + c + '\\u25C9 ' + ip + '\\u25D0 ' + p + '\\u25CB' + (b > 0 ? ' ' + b + '\\u25CD' : '') + ')';
|
|
317882
|
+
} else {
|
|
317883
|
+
const previewParts = [];
|
|
317884
|
+
for (const [k, v] of Object.entries(a)) {
|
|
317885
|
+
let vs;
|
|
317886
|
+
if (v === null || v === undefined) vs = String(v);
|
|
317887
|
+
else if (typeof v === 'string') vs = v;
|
|
317888
|
+
else if (typeof v === 'number' || typeof v === 'boolean') vs = String(v);
|
|
317889
|
+
else { try { vs = JSON.stringify(v); } catch { vs = '[object]'; } }
|
|
317890
|
+
if (vs.length > 60) vs = vs.slice(0, 57) + '\\u2026';
|
|
317891
|
+
previewParts.push(k + '=' + vs);
|
|
317892
|
+
if (previewParts.length >= 3) break;
|
|
317893
|
+
}
|
|
317894
|
+
if (previewParts.length) inlineSummary = ' \\u2014 ' + previewParts.join(', ');
|
|
317895
|
+
}
|
|
317896
|
+
summary.textContent = '\\u25B8 ' + toolName + inlineSummary;
|
|
317897
|
+
details.appendChild(summary);
|
|
317898
|
+
|
|
317899
|
+
if (a && typeof a === 'object') {
|
|
317900
|
+
const argsDiv = document.createElement('div');
|
|
317901
|
+
argsDiv.style.cssText = 'padding:4px 8px 6px 16px;color:#888;font-size:0.65rem;border-top:1px solid #2a2a30';
|
|
317902
|
+
if (toolName === 'todo_write' && Array.isArray(a.todos)) {
|
|
317903
|
+
for (const t of a.todos) {
|
|
317904
|
+
if (!t || typeof t !== 'object') continue;
|
|
317905
|
+
const row = document.createElement('div');
|
|
317906
|
+
row.style.cssText = 'padding:2px 0';
|
|
317907
|
+
let mark = '\\u25CB';
|
|
317908
|
+
let color = '#666';
|
|
317909
|
+
if (t.status === 'completed') { mark = '\\u25C9'; color = '#4a7a4a'; }
|
|
317910
|
+
else if (t.status === 'in_progress') { mark = '\\u25D0'; color = '#b2920a'; }
|
|
317911
|
+
else if (t.status === 'blocked') { mark = '\\u25CD'; color = '#b25f5f'; }
|
|
317912
|
+
row.innerHTML = '<span style="color:' + color + '">' + mark + '</span> <span style="color:#b0b0b0">' + escHtml(String(t.content || '').slice(0, 300)) + '</span>' + (t.blocker ? ' <span style="color:#b25f5f">(blocked: ' + escHtml(String(t.blocker).slice(0, 100)) + ')</span>' : '');
|
|
317913
|
+
argsDiv.appendChild(row);
|
|
317914
|
+
}
|
|
317915
|
+
} else {
|
|
317916
|
+
for (const [k, v] of Object.entries(a)) {
|
|
317917
|
+
const row = document.createElement('div');
|
|
317918
|
+
row.style.cssText = 'padding:2px 0;display:flex;gap:8px;align-items:flex-start';
|
|
317919
|
+
let vs;
|
|
317920
|
+
if (v === null || v === undefined) vs = String(v);
|
|
317921
|
+
else if (typeof v === 'string') vs = v;
|
|
317922
|
+
else if (typeof v === 'number' || typeof v === 'boolean') vs = String(v);
|
|
317923
|
+
else { try { vs = JSON.stringify(v, null, 2); } catch { vs = '[object]'; } }
|
|
317924
|
+
|
|
317925
|
+
const keyEl = document.createElement('span');
|
|
317926
|
+
keyEl.style.cssText = 'color:#b2920a;min-width:60px;flex-shrink:0';
|
|
317927
|
+
keyEl.textContent = k;
|
|
317928
|
+
row.appendChild(keyEl);
|
|
317929
|
+
|
|
317930
|
+
const valWrap = document.createElement('div');
|
|
317931
|
+
valWrap.style.cssText = 'flex:1;min-width:0;color:#b0b0b0';
|
|
317932
|
+
appendExpandableContent(valWrap, vs, { truncateAt: 500, baseStyle: 'color:#b0b0b0;' });
|
|
317933
|
+
row.appendChild(valWrap);
|
|
317934
|
+
|
|
317935
|
+
argsDiv.appendChild(row);
|
|
317936
|
+
}
|
|
317937
|
+
}
|
|
317938
|
+
details.appendChild(argsDiv);
|
|
317939
|
+
}
|
|
317940
|
+
parent.appendChild(details);
|
|
317941
|
+
return details;
|
|
317942
|
+
}
|
|
317943
|
+
|
|
317944
|
+
// WO-CHAT-RESUME-TOOLS — render a single tool_result event with the
|
|
317945
|
+
// show-more/hide expandable helper. Used by both live SSE and restore.
|
|
317946
|
+
function renderToolResultEvent(parent, chunkLike) {
|
|
317947
|
+
const resultEl = document.createElement('div');
|
|
317948
|
+
const errStyle = chunkLike.success === false
|
|
317949
|
+
? 'background:#2a1e1e;border-left:2px solid #b25f5f;color:#b25f5f;'
|
|
317950
|
+
: 'background:#1e1e22;color:#888;';
|
|
317951
|
+
resultEl.style.cssText = errStyle + 'padding:4px 8px 4px 18px;margin:0 0 2px 0;font-size:0.65rem';
|
|
317952
|
+
appendExpandableContent(resultEl, chunkLike.output || '', { truncateAt: 150, baseStyle: 'color:inherit;' });
|
|
317953
|
+
parent.appendChild(resultEl);
|
|
317954
|
+
return resultEl;
|
|
317955
|
+
}
|
|
317956
|
+
|
|
317846
317957
|
async function sendMessage() {
|
|
317847
317958
|
const text = input.value.trim();
|
|
317848
317959
|
if (!text || streaming) return;
|
|
@@ -319835,12 +319946,54 @@ async function restoreChatSession() {
|
|
|
319835
319946
|
if (!data || !data.id) return;
|
|
319836
319947
|
chatSessionId = data.id;
|
|
319837
319948
|
window.currentSessionId = data.id;
|
|
319838
|
-
|
|
319949
|
+
// Keep the in-memory messages array clean (only user/assistant) so
|
|
319950
|
+
// the next inference call sees a valid conversation. Tool events
|
|
319951
|
+
// are still rendered into the DOM via the dropdown helpers below.
|
|
319952
|
+
const allMessages = data.messages || [];
|
|
319953
|
+
messages = allMessages
|
|
319954
|
+
.filter(m => m.role === 'user' || m.role === 'assistant')
|
|
319955
|
+
.map(m => ({ role: m.role, content: m.content }));
|
|
319839
319956
|
const conv = document.getElementById('conversation');
|
|
319840
319957
|
if (conv) {
|
|
319841
319958
|
conv.innerHTML = '';
|
|
319842
|
-
|
|
319843
|
-
|
|
319959
|
+
// WO-CHAT-RESUME-TOOLS — replay the FULL intermediate flow on
|
|
319960
|
+
// restore: user/assistant text bubbles AND tool_call/tool_result
|
|
319961
|
+
// dropdowns interleaved in original order. We track the current
|
|
319962
|
+
// assistant bubble's tools container so consecutive tool events
|
|
319963
|
+
// attach to the same bubble (the live SSE handler does the same).
|
|
319964
|
+
let currentAssistantTools = null;
|
|
319965
|
+
for (const m of allMessages) {
|
|
319966
|
+
if (m.role === 'user') {
|
|
319967
|
+
addMessage('user', m.content);
|
|
319968
|
+
currentAssistantTools = null;
|
|
319969
|
+
} else if (m.role === 'assistant') {
|
|
319970
|
+
const msgDiv = addMessage('assistant', m.content);
|
|
319971
|
+
// Stage a tools container for any tool events that come AFTER
|
|
319972
|
+
// this assistant turn (small models sometimes emit text first
|
|
319973
|
+
// then tool calls — we attach those tools to the most recent
|
|
319974
|
+
// assistant bubble for visual coherence).
|
|
319975
|
+
currentAssistantTools = document.createElement('div');
|
|
319976
|
+
currentAssistantTools.style.cssText = 'margin:4px 0;font-size:0.7rem';
|
|
319977
|
+
msgDiv.appendChild(currentAssistantTools);
|
|
319978
|
+
} else if (m.role === 'tool_call') {
|
|
319979
|
+
// If no assistant bubble yet (tool fired before any text),
|
|
319980
|
+
// create one with empty content so the dropdown has a parent.
|
|
319981
|
+
if (!currentAssistantTools) {
|
|
319982
|
+
const msgDiv = addMessage('assistant', '');
|
|
319983
|
+
currentAssistantTools = document.createElement('div');
|
|
319984
|
+
currentAssistantTools.style.cssText = 'margin:4px 0;font-size:0.7rem';
|
|
319985
|
+
msgDiv.appendChild(currentAssistantTools);
|
|
319986
|
+
}
|
|
319987
|
+
renderToolCallEvent(currentAssistantTools, { tool: m.tool, args: m.args });
|
|
319988
|
+
} else if (m.role === 'tool_result') {
|
|
319989
|
+
if (!currentAssistantTools) {
|
|
319990
|
+
const msgDiv = addMessage('assistant', '');
|
|
319991
|
+
currentAssistantTools = document.createElement('div');
|
|
319992
|
+
currentAssistantTools.style.cssText = 'margin:4px 0;font-size:0.7rem';
|
|
319993
|
+
msgDiv.appendChild(currentAssistantTools);
|
|
319994
|
+
}
|
|
319995
|
+
renderToolResultEvent(currentAssistantTools, { output: m.output, success: m.success });
|
|
319996
|
+
}
|
|
319844
319997
|
}
|
|
319845
319998
|
}
|
|
319846
319999
|
try { refreshTodos(data.id); } catch {}
|
|
@@ -319884,11 +320037,31 @@ function attachInFlightPoller(sessionId, initialJob) {
|
|
|
319884
320037
|
const data = await r.json();
|
|
319885
320038
|
const job = data.in_flight;
|
|
319886
320039
|
if (!job) {
|
|
319887
|
-
|
|
320040
|
+
// WO-CHAT-RESUME-TOOLS — re-run the same restore logic so the
|
|
320041
|
+
// poller's terminal repaint also includes tool dropdowns.
|
|
320042
|
+
const allMessages = data.messages || [];
|
|
320043
|
+
messages = allMessages
|
|
320044
|
+
.filter(m => m.role === 'user' || m.role === 'assistant')
|
|
320045
|
+
.map(m => ({ role: m.role, content: m.content }));
|
|
319888
320046
|
const conv2 = document.getElementById('conversation');
|
|
319889
320047
|
if (conv2) {
|
|
319890
320048
|
conv2.innerHTML = '';
|
|
319891
|
-
|
|
320049
|
+
let curTools = null;
|
|
320050
|
+
for (const m of allMessages) {
|
|
320051
|
+
if (m.role === 'user') { addMessage('user', m.content); curTools = null; }
|
|
320052
|
+
else if (m.role === 'assistant') {
|
|
320053
|
+
const md = addMessage('assistant', m.content);
|
|
320054
|
+
curTools = document.createElement('div');
|
|
320055
|
+
curTools.style.cssText = 'margin:4px 0;font-size:0.7rem';
|
|
320056
|
+
md.appendChild(curTools);
|
|
320057
|
+
} else if (m.role === 'tool_call') {
|
|
320058
|
+
if (!curTools) { const md = addMessage('assistant', ''); curTools = document.createElement('div'); curTools.style.cssText = 'margin:4px 0;font-size:0.7rem'; md.appendChild(curTools); }
|
|
320059
|
+
renderToolCallEvent(curTools, { tool: m.tool, args: m.args });
|
|
320060
|
+
} else if (m.role === 'tool_result') {
|
|
320061
|
+
if (!curTools) { const md = addMessage('assistant', ''); curTools = document.createElement('div'); curTools.style.cssText = 'margin:4px 0;font-size:0.7rem'; md.appendChild(curTools); }
|
|
320062
|
+
renderToolResultEvent(curTools, { output: m.output, success: m.success });
|
|
320063
|
+
}
|
|
320064
|
+
}
|
|
319892
320065
|
}
|
|
319893
320066
|
stopPolling();
|
|
319894
320067
|
return;
|
|
@@ -319903,11 +320076,30 @@ function attachInFlightPoller(sessionId, initialJob) {
|
|
|
319903
320076
|
} else if (job.error) {
|
|
319904
320077
|
placeholder.textContent = 'Error: ' + job.error;
|
|
319905
320078
|
}
|
|
319906
|
-
|
|
320079
|
+
// WO-CHAT-RESUME-TOOLS — same tool-replay logic on terminal repaint
|
|
320080
|
+
const allMessages = data.messages || [];
|
|
320081
|
+
messages = allMessages
|
|
320082
|
+
.filter(m => m.role === 'user' || m.role === 'assistant')
|
|
320083
|
+
.map(m => ({ role: m.role, content: m.content }));
|
|
319907
320084
|
const conv3 = document.getElementById('conversation');
|
|
319908
320085
|
if (conv3) {
|
|
319909
320086
|
conv3.innerHTML = '';
|
|
319910
|
-
|
|
320087
|
+
let curTools = null;
|
|
320088
|
+
for (const m of allMessages) {
|
|
320089
|
+
if (m.role === 'user') { addMessage('user', m.content); curTools = null; }
|
|
320090
|
+
else if (m.role === 'assistant') {
|
|
320091
|
+
const md = addMessage('assistant', m.content);
|
|
320092
|
+
curTools = document.createElement('div');
|
|
320093
|
+
curTools.style.cssText = 'margin:4px 0;font-size:0.7rem';
|
|
320094
|
+
md.appendChild(curTools);
|
|
320095
|
+
} else if (m.role === 'tool_call') {
|
|
320096
|
+
if (!curTools) { const md = addMessage('assistant', ''); curTools = document.createElement('div'); curTools.style.cssText = 'margin:4px 0;font-size:0.7rem'; md.appendChild(curTools); }
|
|
320097
|
+
renderToolCallEvent(curTools, { tool: m.tool, args: m.args });
|
|
320098
|
+
} else if (m.role === 'tool_result') {
|
|
320099
|
+
if (!curTools) { const md = addMessage('assistant', ''); curTools = document.createElement('div'); curTools.style.cssText = 'margin:4px 0;font-size:0.7rem'; md.appendChild(curTools); }
|
|
320100
|
+
renderToolResultEvent(curTools, { output: m.output, success: m.success });
|
|
320101
|
+
}
|
|
320102
|
+
}
|
|
319911
320103
|
}
|
|
319912
320104
|
stopPolling();
|
|
319913
320105
|
}
|
|
@@ -320610,13 +320802,57 @@ function addUserMessage(session, content) {
|
|
|
320610
320802
|
session.messages.push({ role: "user", content });
|
|
320611
320803
|
session.lastActivity = Date.now();
|
|
320612
320804
|
persistSession(session);
|
|
320613
|
-
return
|
|
320805
|
+
return session.messages.filter((m2) => m2.role === "system" || m2.role === "user" || m2.role === "assistant");
|
|
320614
320806
|
}
|
|
320615
320807
|
function addAssistantMessage(session, content) {
|
|
320616
320808
|
session.messages.push({ role: "assistant", content });
|
|
320617
320809
|
session.lastActivity = Date.now();
|
|
320618
320810
|
persistSession(session);
|
|
320619
320811
|
}
|
|
320812
|
+
function addToolCallMessage(session, tool, args) {
|
|
320813
|
+
let cappedArgs = args;
|
|
320814
|
+
try {
|
|
320815
|
+
const json = JSON.stringify(args);
|
|
320816
|
+
if (json && json.length > 4e3) {
|
|
320817
|
+
if (args && typeof args === "object" && !Array.isArray(args)) {
|
|
320818
|
+
const pruned = {};
|
|
320819
|
+
for (const [k, v] of Object.entries(args)) {
|
|
320820
|
+
if (typeof v === "string" && v.length > 1e3) {
|
|
320821
|
+
pruned[k] = v.slice(0, 800) + `… [+${v.length - 800} chars truncated for storage]`;
|
|
320822
|
+
} else {
|
|
320823
|
+
pruned[k] = v;
|
|
320824
|
+
}
|
|
320825
|
+
}
|
|
320826
|
+
cappedArgs = pruned;
|
|
320827
|
+
} else {
|
|
320828
|
+
cappedArgs = { _truncated: true, _bytes: json.length };
|
|
320829
|
+
}
|
|
320830
|
+
}
|
|
320831
|
+
} catch {
|
|
320832
|
+
}
|
|
320833
|
+
session.messages.push({
|
|
320834
|
+
role: "tool_call",
|
|
320835
|
+
content: "",
|
|
320836
|
+
tool,
|
|
320837
|
+
args: cappedArgs,
|
|
320838
|
+
ts: Date.now()
|
|
320839
|
+
});
|
|
320840
|
+
session.lastActivity = Date.now();
|
|
320841
|
+
persistSession(session);
|
|
320842
|
+
}
|
|
320843
|
+
function addToolResultMessage(session, tool, output, success) {
|
|
320844
|
+
const capped = output && output.length > 2048 ? output.slice(0, 2e3) + `… [+${output.length - 2e3} chars truncated for storage]` : output;
|
|
320845
|
+
session.messages.push({
|
|
320846
|
+
role: "tool_result",
|
|
320847
|
+
content: "",
|
|
320848
|
+
tool,
|
|
320849
|
+
output: capped,
|
|
320850
|
+
success,
|
|
320851
|
+
ts: Date.now()
|
|
320852
|
+
});
|
|
320853
|
+
session.lastActivity = Date.now();
|
|
320854
|
+
persistSession(session);
|
|
320855
|
+
}
|
|
320620
320856
|
function listSessions2() {
|
|
320621
320857
|
return Array.from(sessions.values()).map((s2) => ({
|
|
320622
320858
|
id: s2.id,
|
|
@@ -323759,7 +323995,9 @@ async function handleRequest(req2, res, ollamaUrl, verbose) {
|
|
|
323759
323995
|
try {
|
|
323760
323996
|
const ans = await directChatBackend({
|
|
323761
323997
|
model,
|
|
323762
|
-
|
|
323998
|
+
// Filter out tool_call/tool_result messages — the upstream
|
|
323999
|
+
// model only accepts standard system/user/assistant turns.
|
|
324000
|
+
messages: session.messages.filter((m2) => m2.role === "system" || m2.role === "user" || m2.role === "assistant").map((m2) => ({ role: m2.role, content: m2.content })),
|
|
323763
324001
|
stream: streamMode,
|
|
323764
324002
|
res,
|
|
323765
324003
|
sessionId: session.id,
|
|
@@ -323789,7 +324027,7 @@ async function handleRequest(req2, res, ollamaUrl, verbose) {
|
|
|
323789
324027
|
}
|
|
323790
324028
|
return;
|
|
323791
324029
|
}
|
|
323792
|
-
const historyLines = session.messages.filter((m2) => m2.role
|
|
324030
|
+
const historyLines = session.messages.filter((m2) => m2.role === "user" || m2.role === "assistant").slice(-10).map((m2) => m2.role === "user" ? `User: ${m2.content}` : `Assistant: ${m2.content}`).join("\n");
|
|
323793
324031
|
const memCtx = await retrieveMemoryContext(
|
|
323794
324032
|
chatBody.message,
|
|
323795
324033
|
session.id,
|
|
@@ -323885,6 +324123,10 @@ ${historyLines}
|
|
|
323885
324123
|
tool: evt.tool,
|
|
323886
324124
|
args: evt.args
|
|
323887
324125
|
}) + "\n\n");
|
|
324126
|
+
try {
|
|
324127
|
+
addToolCallMessage(session, evt.tool, evt.args);
|
|
324128
|
+
} catch {
|
|
324129
|
+
}
|
|
323888
324130
|
try {
|
|
323889
324131
|
const evtType = evt.tool === "sub_agent" || evt.tool === "full_sub_agent" ? "run.started" : "tool.called";
|
|
323890
324132
|
publishEvent(evtType, {
|
|
@@ -323895,6 +324137,17 @@ ${historyLines}
|
|
|
323895
324137
|
}, { subject: session.id });
|
|
323896
324138
|
} catch {
|
|
323897
324139
|
}
|
|
324140
|
+
} else if (evt.type === "tool_result") {
|
|
324141
|
+
res.write("data: " + JSON.stringify({
|
|
324142
|
+
type: "tool_result",
|
|
324143
|
+
tool: evt.tool,
|
|
324144
|
+
output: evt.output ?? evt.content ?? "",
|
|
324145
|
+
success: evt.success
|
|
324146
|
+
}) + "\n\n");
|
|
324147
|
+
try {
|
|
324148
|
+
addToolResultMessage(session, evt.tool, evt.output ?? evt.content ?? "", evt.success);
|
|
324149
|
+
} catch {
|
|
324150
|
+
}
|
|
323898
324151
|
} else {
|
|
323899
324152
|
finalLines.push(line);
|
|
323900
324153
|
}
|
|
@@ -324906,7 +325159,7 @@ function createTaskCompleteTool(modelTier) {
|
|
|
324906
325159
|
const summaryDesc = modelTier === "small" || modelTier === "medium" ? "Your complete response to the user. For questions/chat: put your FULL answer here (this is what the user will see). For coding tasks: brief summary of what was accomplished." : "Brief summary of what was accomplished";
|
|
324907
325160
|
return {
|
|
324908
325161
|
name: "task_complete",
|
|
324909
|
-
description: "Signal that the task is complete.",
|
|
325162
|
+
description: "Signal that the task is complete. GUARDED: cannot fire while the active todo list (todo_write) has pending, in_progress, or blocked items. If you're truly done, first call todo_write to mark every remaining item completed. If you're not done, continue working down the list and call this only after the last item flips to completed.",
|
|
324910
325163
|
parameters: {
|
|
324911
325164
|
type: "object",
|
|
324912
325165
|
properties: {
|
|
@@ -324915,6 +325168,35 @@ function createTaskCompleteTool(modelTier) {
|
|
|
324915
325168
|
required: ["summary"]
|
|
324916
325169
|
},
|
|
324917
325170
|
async execute(args) {
|
|
325171
|
+
try {
|
|
325172
|
+
const sessionId = getTodoSessionId();
|
|
325173
|
+
const todos = readTodos(sessionId);
|
|
325174
|
+
if (todos.length > 0) {
|
|
325175
|
+
const incomplete = todos.filter(
|
|
325176
|
+
(t2) => t2.status === "pending" || t2.status === "in_progress" || t2.status === "blocked"
|
|
325177
|
+
);
|
|
325178
|
+
if (incomplete.length > 0) {
|
|
325179
|
+
const incompleteList = incomplete.slice(0, 10).map((t2) => ` - [${t2.status}] ${t2.content}${t2.blocker ? ` (blocked: ${t2.blocker})` : ""}`).join("\n");
|
|
325180
|
+
const more = incomplete.length > 10 ? `
|
|
325181
|
+
... +${incomplete.length - 10} more` : "";
|
|
325182
|
+
return {
|
|
325183
|
+
success: false,
|
|
325184
|
+
output: "",
|
|
325185
|
+
error: `task_complete BLOCKED — ${incomplete.length} todo item(s) still incomplete. You must either:
|
|
325186
|
+
1. Continue working on the remaining items, OR
|
|
325187
|
+
2. If they're actually done, call todo_write with status='completed' for each one, THEN call task_complete again.
|
|
325188
|
+
|
|
325189
|
+
Incomplete items:
|
|
325190
|
+
${incompleteList}${more}`
|
|
325191
|
+
};
|
|
325192
|
+
}
|
|
325193
|
+
try {
|
|
325194
|
+
writeTodos(sessionId, []);
|
|
325195
|
+
} catch {
|
|
325196
|
+
}
|
|
325197
|
+
}
|
|
325198
|
+
} catch {
|
|
325199
|
+
}
|
|
324918
325200
|
return { success: true, output: args["summary"] || "Task completed." };
|
|
324919
325201
|
}
|
|
324920
325202
|
};
|
|
@@ -326215,6 +326497,13 @@ ${entry.fullContent}`
|
|
|
326215
326497
|
}
|
|
326216
326498
|
break;
|
|
326217
326499
|
case "tool_result": {
|
|
326500
|
+
if (_apiCallbacks?.onToolResult) {
|
|
326501
|
+
_apiCallbacks.onToolResult(
|
|
326502
|
+
event.toolName ?? "unknown",
|
|
326503
|
+
String(event.content ?? ""),
|
|
326504
|
+
event.success ?? false
|
|
326505
|
+
);
|
|
326506
|
+
}
|
|
326218
326507
|
if (lastToolCall) {
|
|
326219
326508
|
editHistory.logToolCall(lastToolCall.name, lastToolCall.args, event.success ?? false);
|
|
326220
326509
|
lastToolCall = null;
|
|
@@ -330716,6 +331005,10 @@ async function runJson(task, config, repoPath) {
|
|
|
330716
331005
|
onToolCall: (tool, args) => {
|
|
330717
331006
|
toolCallLog.push({ tool, args });
|
|
330718
331007
|
origWrite(JSON.stringify({ type: "tool_call", tool, args }) + "\n");
|
|
331008
|
+
},
|
|
331009
|
+
onToolResult: (tool, output, success) => {
|
|
331010
|
+
const capped = output && output.length > 4e3 ? output.slice(0, 3900) + `… [+${output.length - 3900} chars]` : output;
|
|
331011
|
+
origWrite(JSON.stringify({ type: "tool_result", tool, output: capped, success }) + "\n");
|
|
330719
331012
|
}
|
|
330720
331013
|
});
|
|
330721
331014
|
result = {
|
package/package.json
CHANGED