@usabledev/usable-chat 1.152.1 → 1.154.0
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/cli.js +432 -204
- package/package.json +1 -1
package/cli.js
CHANGED
|
@@ -77609,11 +77609,11 @@ var require_randomUUID = __commonJS({
|
|
|
77609
77609
|
var require_dist_cjs31 = __commonJS({
|
|
77610
77610
|
"node_modules/.pnpm/@smithy+uuid@1.1.2/node_modules/@smithy/uuid/dist-cjs/index.js"(exports) {
|
|
77611
77611
|
"use strict";
|
|
77612
|
-
var
|
|
77612
|
+
var randomUUID8 = require_randomUUID();
|
|
77613
77613
|
var decimalToHex = Array.from({ length: 256 }, (_16, i18) => i18.toString(16).padStart(2, "0"));
|
|
77614
77614
|
var v42 = () => {
|
|
77615
|
-
if (
|
|
77616
|
-
return
|
|
77615
|
+
if (randomUUID8.randomUUID) {
|
|
77616
|
+
return randomUUID8.randomUUID();
|
|
77617
77617
|
}
|
|
77618
77618
|
const rnds = new Uint8Array(16);
|
|
77619
77619
|
crypto.getRandomValues(rnds);
|
|
@@ -131517,7 +131517,7 @@ var require_gaxios = __commonJS({
|
|
|
131517
131517
|
var retry_js_1 = require_retry();
|
|
131518
131518
|
var stream_1 = __require("stream");
|
|
131519
131519
|
var interceptor_js_1 = require_interceptor();
|
|
131520
|
-
var
|
|
131520
|
+
var randomUUID8 = async () => globalThis.crypto?.randomUUID() || (await import("crypto")).randomUUID();
|
|
131521
131521
|
var HTTP_STATUS_NO_CONTENT = 204;
|
|
131522
131522
|
var Gaxios = class {
|
|
131523
131523
|
agentCache = /* @__PURE__ */ new Map();
|
|
@@ -131790,7 +131790,7 @@ var require_gaxios = __commonJS({
|
|
|
131790
131790
|
*/
|
|
131791
131791
|
["Blob", "File", "FormData"].includes(opts.data?.constructor?.name || "");
|
|
131792
131792
|
if (opts.multipart?.length) {
|
|
131793
|
-
const boundary = await
|
|
131793
|
+
const boundary = await randomUUID8();
|
|
131794
131794
|
preparedHeaders.set("content-type", `multipart/related; boundary=${boundary}`);
|
|
131795
131795
|
opts.body = stream_1.Readable.from(this.getMultipartRequest(opts.multipart, boundary));
|
|
131796
131796
|
} else if (shouldDirectlyPassData) {
|
|
@@ -162589,6 +162589,7 @@ async function adaptOpenRouterStream(stream, options2) {
|
|
|
162589
162589
|
let finishReason = null;
|
|
162590
162590
|
const toolCalls = [];
|
|
162591
162591
|
const completedToolCalls = [];
|
|
162592
|
+
const toolExecutionPromises = [];
|
|
162592
162593
|
const toolResults = [];
|
|
162593
162594
|
let capturedUsage;
|
|
162594
162595
|
let capturedModel;
|
|
@@ -162598,6 +162599,188 @@ async function adaptOpenRouterStream(stream, options2) {
|
|
|
162598
162599
|
let lastEmittedReasoning = "";
|
|
162599
162600
|
const accumulatedReasoningDetails = [];
|
|
162600
162601
|
const pendingToolCalls = /* @__PURE__ */ new Map();
|
|
162602
|
+
const executeParsedToolCall = async (id, name18, args) => {
|
|
162603
|
+
if (context.abortSignal?.aborted) {
|
|
162604
|
+
streamLogger.info("Abort signal detected before tool execution, returning cancelled result", {
|
|
162605
|
+
toolName: name18
|
|
162606
|
+
});
|
|
162607
|
+
const errorResult = { error: "Request cancelled by user", success: false };
|
|
162608
|
+
toolCalls.push({ toolName: name18, success: false });
|
|
162609
|
+
toolResults.push({ id, name: name18, result: errorResult, isError: true });
|
|
162610
|
+
const errorEvent = emitter.emit("tool-result", {
|
|
162611
|
+
id,
|
|
162612
|
+
toolName: name18,
|
|
162613
|
+
input: args,
|
|
162614
|
+
result: errorResult,
|
|
162615
|
+
success: false,
|
|
162616
|
+
error: errorResult.error
|
|
162617
|
+
});
|
|
162618
|
+
multiplexer.send(errorEvent);
|
|
162619
|
+
return;
|
|
162620
|
+
}
|
|
162621
|
+
streamLogger.debug("Executing tool", { toolName: name18, args });
|
|
162622
|
+
try {
|
|
162623
|
+
const toolResult = await executeTool3(name18, args, {
|
|
162624
|
+
...context,
|
|
162625
|
+
toolCallId: id
|
|
162626
|
+
});
|
|
162627
|
+
const success2 = !toolResult?.error;
|
|
162628
|
+
toolCalls.push({ toolName: name18, success: success2 });
|
|
162629
|
+
toolResults.push({ id, name: name18, result: toolResult, isError: !success2 });
|
|
162630
|
+
if (context.session) {
|
|
162631
|
+
if (!context.session.toolHistory) {
|
|
162632
|
+
context.session.toolHistory = [];
|
|
162633
|
+
}
|
|
162634
|
+
const toolHistory = context.session.toolHistory;
|
|
162635
|
+
toolHistory.push({
|
|
162636
|
+
toolName: name18,
|
|
162637
|
+
args,
|
|
162638
|
+
result: toolResult,
|
|
162639
|
+
success: success2,
|
|
162640
|
+
timestamp: Date.now()
|
|
162641
|
+
});
|
|
162642
|
+
if (toolHistory.length > 50) {
|
|
162643
|
+
toolHistory.shift();
|
|
162644
|
+
}
|
|
162645
|
+
}
|
|
162646
|
+
const toolResultEvent = emitter.emit("tool-result", {
|
|
162647
|
+
id,
|
|
162648
|
+
toolName: name18,
|
|
162649
|
+
input: args,
|
|
162650
|
+
result: toolResult,
|
|
162651
|
+
success: success2,
|
|
162652
|
+
error: toolResult?.error ? String(toolResult.error) : void 0
|
|
162653
|
+
});
|
|
162654
|
+
multiplexer.send(toolResultEvent);
|
|
162655
|
+
if (name18 === "check-context-window" && success2 && toolResult?.data) {
|
|
162656
|
+
const data2 = toolResult.data;
|
|
162657
|
+
const breakdown = data2.breakdown.systemPrompt !== void 0 ? {
|
|
162658
|
+
systemPrompts: data2.breakdown.systemPrompt + (data2.breakdown.systemTools || 0),
|
|
162659
|
+
contextTokens: data2.breakdown.memoryFiles || 0,
|
|
162660
|
+
toolCallsAndResponses: data2.breakdown.toolUseAndResults || 0,
|
|
162661
|
+
userQuestions: 0,
|
|
162662
|
+
assistantMessages: data2.breakdown.messages || 0
|
|
162663
|
+
} : data2.breakdown;
|
|
162664
|
+
const cardEvent = emitter.emit("card", {
|
|
162665
|
+
component: "context-window",
|
|
162666
|
+
data: {
|
|
162667
|
+
totalTokens: data2.totalTokens,
|
|
162668
|
+
maxTokens: data2.maxTokens,
|
|
162669
|
+
percentage: data2.percentage,
|
|
162670
|
+
breakdown,
|
|
162671
|
+
freeSpace: data2.freeSpace,
|
|
162672
|
+
contextItems: data2.contextItems || {
|
|
162673
|
+
workspaces: 0,
|
|
162674
|
+
fragments: 0,
|
|
162675
|
+
toolResults: 0
|
|
162676
|
+
}
|
|
162677
|
+
}
|
|
162678
|
+
});
|
|
162679
|
+
multiplexer.send(cardEvent);
|
|
162680
|
+
}
|
|
162681
|
+
if (isAskQuestionPause(toolResult)) {
|
|
162682
|
+
pauseDetected = true;
|
|
162683
|
+
if (onPauseDetected) {
|
|
162684
|
+
await onPauseDetected({
|
|
162685
|
+
resumeToken: toolResult.__pause.resumeToken,
|
|
162686
|
+
questions: toolResult.__pause.questions,
|
|
162687
|
+
context: toolResult.__pause.context
|
|
162688
|
+
});
|
|
162689
|
+
}
|
|
162690
|
+
return;
|
|
162691
|
+
}
|
|
162692
|
+
if (toolResult?.__parentToolCall === true) {
|
|
162693
|
+
const parentToolName = toolResult.toolName;
|
|
162694
|
+
const parentToolArgs = toolResult.args;
|
|
162695
|
+
const requestId = `parent_${id}_${Date.now()}`;
|
|
162696
|
+
streamLogger.info("Parent tool call detected - waiting for parent response", {
|
|
162697
|
+
toolName: name18,
|
|
162698
|
+
parentToolName,
|
|
162699
|
+
requestId
|
|
162700
|
+
});
|
|
162701
|
+
const parentToolEvent = emitter.emit("parent-tool-call", {
|
|
162702
|
+
requestId,
|
|
162703
|
+
toolName: parentToolName,
|
|
162704
|
+
args: parentToolArgs
|
|
162705
|
+
});
|
|
162706
|
+
multiplexer.send(parentToolEvent);
|
|
162707
|
+
try {
|
|
162708
|
+
const parentResponse = await waitForParentToolResponse(
|
|
162709
|
+
requestId,
|
|
162710
|
+
parentToolName,
|
|
162711
|
+
parentToolArgs,
|
|
162712
|
+
3e4
|
|
162713
|
+
);
|
|
162714
|
+
streamLogger.info("Parent tool response received", {
|
|
162715
|
+
requestId,
|
|
162716
|
+
parentToolName,
|
|
162717
|
+
hasResult: parentResponse !== void 0
|
|
162718
|
+
});
|
|
162719
|
+
const resultIndex = toolResults.findIndex((r17) => r17.id === id);
|
|
162720
|
+
if (resultIndex !== -1) {
|
|
162721
|
+
toolResults[resultIndex].result = parentResponse;
|
|
162722
|
+
}
|
|
162723
|
+
const updatedResultEvent = emitter.emit("tool-result", {
|
|
162724
|
+
id,
|
|
162725
|
+
toolName: name18,
|
|
162726
|
+
input: args,
|
|
162727
|
+
result: parentResponse,
|
|
162728
|
+
success: true
|
|
162729
|
+
});
|
|
162730
|
+
multiplexer.send(updatedResultEvent);
|
|
162731
|
+
} catch (parentError) {
|
|
162732
|
+
streamLogger.error("Parent tool call failed", {
|
|
162733
|
+
requestId,
|
|
162734
|
+
parentToolName,
|
|
162735
|
+
error: parentError instanceof Error ? parentError.message : String(parentError)
|
|
162736
|
+
});
|
|
162737
|
+
const errorResult = {
|
|
162738
|
+
error: parentError instanceof Error ? parentError.message : String(parentError),
|
|
162739
|
+
success: false
|
|
162740
|
+
};
|
|
162741
|
+
const resultIndex = toolResults.findIndex((r17) => r17.id === id);
|
|
162742
|
+
if (resultIndex !== -1) {
|
|
162743
|
+
toolResults[resultIndex].result = errorResult;
|
|
162744
|
+
toolResults[resultIndex].isError = true;
|
|
162745
|
+
}
|
|
162746
|
+
const errorEvent = emitter.emit("tool-result", {
|
|
162747
|
+
id,
|
|
162748
|
+
toolName: name18,
|
|
162749
|
+
input: args,
|
|
162750
|
+
result: errorResult,
|
|
162751
|
+
success: false,
|
|
162752
|
+
error: parentError instanceof Error ? parentError.message : String(parentError)
|
|
162753
|
+
});
|
|
162754
|
+
multiplexer.send(errorEvent);
|
|
162755
|
+
}
|
|
162756
|
+
}
|
|
162757
|
+
if (emitObservations && success2) {
|
|
162758
|
+
const observation = generateObservation2(name18, toolResult);
|
|
162759
|
+
const observationEvent = emitter.emit("observation", {
|
|
162760
|
+
observation,
|
|
162761
|
+
toolCallIds: [id]
|
|
162762
|
+
});
|
|
162763
|
+
multiplexer.send(observationEvent);
|
|
162764
|
+
}
|
|
162765
|
+
} catch (toolError) {
|
|
162766
|
+
console.error(`\u274C Tool execution error for ${name18}:`, toolError);
|
|
162767
|
+
toolCalls.push({ toolName: name18, success: false });
|
|
162768
|
+
const errorResult = {
|
|
162769
|
+
error: toolError instanceof Error ? toolError.message : String(toolError),
|
|
162770
|
+
success: false
|
|
162771
|
+
};
|
|
162772
|
+
toolResults.push({ id, name: name18, result: errorResult, isError: true });
|
|
162773
|
+
const errorEvent = emitter.emit("tool-result", {
|
|
162774
|
+
id,
|
|
162775
|
+
toolName: name18,
|
|
162776
|
+
input: args,
|
|
162777
|
+
result: null,
|
|
162778
|
+
success: false,
|
|
162779
|
+
error: toolError instanceof Error ? toolError.message : String(toolError)
|
|
162780
|
+
});
|
|
162781
|
+
multiplexer.send(errorEvent);
|
|
162782
|
+
}
|
|
162783
|
+
};
|
|
162601
162784
|
try {
|
|
162602
162785
|
while (true) {
|
|
162603
162786
|
if (context.abortSignal?.aborted) {
|
|
@@ -162855,187 +163038,9 @@ async function adaptOpenRouterStream(stream, options2) {
|
|
|
162855
163038
|
});
|
|
162856
163039
|
multiplexer.send(retrievalEvent);
|
|
162857
163040
|
}
|
|
162858
|
-
|
|
162859
|
-
|
|
162860
|
-
|
|
162861
|
-
});
|
|
162862
|
-
break;
|
|
162863
|
-
}
|
|
162864
|
-
streamLogger.debug("Executing tool", {
|
|
162865
|
-
toolName: pending.name,
|
|
162866
|
-
args
|
|
162867
|
-
});
|
|
162868
|
-
try {
|
|
162869
|
-
const toolResult = await executeTool3(pending.name, args, { ...context, toolCallId: pending.id });
|
|
162870
|
-
const success2 = !toolResult?.error;
|
|
162871
|
-
toolCalls.push({ toolName: pending.name, success: success2 });
|
|
162872
|
-
toolResults.push({
|
|
162873
|
-
id: pending.id,
|
|
162874
|
-
name: pending.name,
|
|
162875
|
-
result: toolResult,
|
|
162876
|
-
isError: !success2
|
|
162877
|
-
});
|
|
162878
|
-
if (context.session) {
|
|
162879
|
-
if (!context.session.toolHistory) {
|
|
162880
|
-
context.session.toolHistory = [];
|
|
162881
|
-
}
|
|
162882
|
-
const toolHistory = context.session.toolHistory;
|
|
162883
|
-
toolHistory.push({
|
|
162884
|
-
toolName: pending.name,
|
|
162885
|
-
args,
|
|
162886
|
-
result: toolResult,
|
|
162887
|
-
success: success2,
|
|
162888
|
-
timestamp: Date.now()
|
|
162889
|
-
});
|
|
162890
|
-
if (toolHistory.length > 50) {
|
|
162891
|
-
toolHistory.shift();
|
|
162892
|
-
}
|
|
162893
|
-
}
|
|
162894
|
-
const toolResultEvent = emitter.emit("tool-result", {
|
|
162895
|
-
id: pending.id,
|
|
162896
|
-
toolName: pending.name,
|
|
162897
|
-
input: args,
|
|
162898
|
-
// Include input arguments
|
|
162899
|
-
result: toolResult,
|
|
162900
|
-
success: success2,
|
|
162901
|
-
error: toolResult?.error ? String(toolResult.error) : void 0
|
|
162902
|
-
});
|
|
162903
|
-
multiplexer.send(toolResultEvent);
|
|
162904
|
-
if (pending.name === "check-context-window" && success2 && toolResult?.data) {
|
|
162905
|
-
const data2 = toolResult.data;
|
|
162906
|
-
const breakdown = data2.breakdown.systemPrompt !== void 0 ? {
|
|
162907
|
-
// New format - map to old format
|
|
162908
|
-
systemPrompts: data2.breakdown.systemPrompt + (data2.breakdown.systemTools || 0),
|
|
162909
|
-
contextTokens: data2.breakdown.memoryFiles || 0,
|
|
162910
|
-
toolCallsAndResponses: data2.breakdown.toolUseAndResults || 0,
|
|
162911
|
-
userQuestions: 0,
|
|
162912
|
-
// Will be calculated from messages
|
|
162913
|
-
assistantMessages: data2.breakdown.messages || 0
|
|
162914
|
-
} : data2.breakdown;
|
|
162915
|
-
const cardEvent = emitter.emit("card", {
|
|
162916
|
-
component: "context-window",
|
|
162917
|
-
data: {
|
|
162918
|
-
totalTokens: data2.totalTokens,
|
|
162919
|
-
maxTokens: data2.maxTokens,
|
|
162920
|
-
percentage: data2.percentage,
|
|
162921
|
-
breakdown,
|
|
162922
|
-
freeSpace: data2.freeSpace,
|
|
162923
|
-
contextItems: data2.contextItems || {
|
|
162924
|
-
workspaces: 0,
|
|
162925
|
-
fragments: 0,
|
|
162926
|
-
toolResults: 0
|
|
162927
|
-
}
|
|
162928
|
-
}
|
|
162929
|
-
});
|
|
162930
|
-
multiplexer.send(cardEvent);
|
|
162931
|
-
}
|
|
162932
|
-
if (isAskQuestionPause(toolResult)) {
|
|
162933
|
-
pauseDetected = true;
|
|
162934
|
-
if (onPauseDetected) {
|
|
162935
|
-
await onPauseDetected({
|
|
162936
|
-
resumeToken: toolResult.__pause.resumeToken,
|
|
162937
|
-
questions: toolResult.__pause.questions,
|
|
162938
|
-
context: toolResult.__pause.context
|
|
162939
|
-
});
|
|
162940
|
-
}
|
|
162941
|
-
break;
|
|
162942
|
-
}
|
|
162943
|
-
if (toolResult?.__parentToolCall === true) {
|
|
162944
|
-
const parentToolName = toolResult.toolName;
|
|
162945
|
-
const parentToolArgs = toolResult.args;
|
|
162946
|
-
const requestId = `parent_${pending.id}_${Date.now()}`;
|
|
162947
|
-
streamLogger.info("Parent tool call detected - waiting for parent response", {
|
|
162948
|
-
toolName: pending.name,
|
|
162949
|
-
parentToolName,
|
|
162950
|
-
requestId
|
|
162951
|
-
});
|
|
162952
|
-
const parentToolEvent = emitter.emit("parent-tool-call", {
|
|
162953
|
-
requestId,
|
|
162954
|
-
toolName: parentToolName,
|
|
162955
|
-
args: parentToolArgs
|
|
162956
|
-
});
|
|
162957
|
-
multiplexer.send(parentToolEvent);
|
|
162958
|
-
try {
|
|
162959
|
-
const parentResponse = await waitForParentToolResponse(
|
|
162960
|
-
requestId,
|
|
162961
|
-
parentToolName,
|
|
162962
|
-
parentToolArgs,
|
|
162963
|
-
3e4
|
|
162964
|
-
// 30 second timeout
|
|
162965
|
-
);
|
|
162966
|
-
streamLogger.info("Parent tool response received", {
|
|
162967
|
-
requestId,
|
|
162968
|
-
parentToolName,
|
|
162969
|
-
hasResult: parentResponse !== void 0
|
|
162970
|
-
});
|
|
162971
|
-
const resultIndex = toolResults.findIndex((r17) => r17.id === pending.id);
|
|
162972
|
-
if (resultIndex !== -1) {
|
|
162973
|
-
toolResults[resultIndex].result = parentResponse;
|
|
162974
|
-
}
|
|
162975
|
-
const updatedResultEvent = emitter.emit("tool-result", {
|
|
162976
|
-
id: pending.id,
|
|
162977
|
-
toolName: pending.name,
|
|
162978
|
-
input: args,
|
|
162979
|
-
result: parentResponse,
|
|
162980
|
-
success: true
|
|
162981
|
-
});
|
|
162982
|
-
multiplexer.send(updatedResultEvent);
|
|
162983
|
-
} catch (parentError) {
|
|
162984
|
-
streamLogger.error("Parent tool call failed", {
|
|
162985
|
-
requestId,
|
|
162986
|
-
parentToolName,
|
|
162987
|
-
error: parentError instanceof Error ? parentError.message : String(parentError)
|
|
162988
|
-
});
|
|
162989
|
-
const errorResult = {
|
|
162990
|
-
error: parentError instanceof Error ? parentError.message : String(parentError),
|
|
162991
|
-
success: false
|
|
162992
|
-
};
|
|
162993
|
-
const resultIndex = toolResults.findIndex((r17) => r17.id === pending.id);
|
|
162994
|
-
if (resultIndex !== -1) {
|
|
162995
|
-
toolResults[resultIndex].result = errorResult;
|
|
162996
|
-
toolResults[resultIndex].isError = true;
|
|
162997
|
-
}
|
|
162998
|
-
const errorEvent = emitter.emit("tool-result", {
|
|
162999
|
-
id: pending.id,
|
|
163000
|
-
toolName: pending.name,
|
|
163001
|
-
input: args,
|
|
163002
|
-
result: errorResult,
|
|
163003
|
-
success: false,
|
|
163004
|
-
error: parentError instanceof Error ? parentError.message : String(parentError)
|
|
163005
|
-
});
|
|
163006
|
-
multiplexer.send(errorEvent);
|
|
163007
|
-
}
|
|
163008
|
-
}
|
|
163009
|
-
if (emitObservations && success2) {
|
|
163010
|
-
const observation = generateObservation2(pending.name, toolResult);
|
|
163011
|
-
const observationEvent = emitter.emit("observation", {
|
|
163012
|
-
observation,
|
|
163013
|
-
toolCallIds: [pending.id]
|
|
163014
|
-
});
|
|
163015
|
-
multiplexer.send(observationEvent);
|
|
163016
|
-
}
|
|
163017
|
-
} catch (toolError) {
|
|
163018
|
-
console.error(`\u274C Tool execution error for ${pending.name}:`, toolError);
|
|
163019
|
-
toolCalls.push({ toolName: pending.name, success: false });
|
|
163020
|
-
const errorResult = {
|
|
163021
|
-
error: toolError instanceof Error ? toolError.message : String(toolError),
|
|
163022
|
-
success: false
|
|
163023
|
-
};
|
|
163024
|
-
toolResults.push({
|
|
163025
|
-
id: pending.id,
|
|
163026
|
-
name: pending.name,
|
|
163027
|
-
result: errorResult,
|
|
163028
|
-
isError: true
|
|
163029
|
-
});
|
|
163030
|
-
const errorEvent = emitter.emit("tool-result", {
|
|
163031
|
-
id: pending.id,
|
|
163032
|
-
toolName: pending.name,
|
|
163033
|
-
result: null,
|
|
163034
|
-
success: false,
|
|
163035
|
-
error: toolError instanceof Error ? toolError.message : String(toolError)
|
|
163036
|
-
});
|
|
163037
|
-
multiplexer.send(errorEvent);
|
|
163038
|
-
}
|
|
163041
|
+
toolExecutionPromises.push(
|
|
163042
|
+
executeParsedToolCall(pending.id, pending.name, args)
|
|
163043
|
+
);
|
|
163039
163044
|
pendingToolCalls.delete(index2);
|
|
163040
163045
|
} catch {
|
|
163041
163046
|
}
|
|
@@ -163048,6 +163053,13 @@ async function adaptOpenRouterStream(stream, options2) {
|
|
|
163048
163053
|
finishReason: finish_reason,
|
|
163049
163054
|
hasUsage: !!capturedUsage
|
|
163050
163055
|
});
|
|
163056
|
+
if (finish_reason === "tool_calls" && toolExecutionPromises.length > 0) {
|
|
163057
|
+
streamLogger.debug("Waiting for OpenRouter tool executions", {
|
|
163058
|
+
toolCount: toolExecutionPromises.length
|
|
163059
|
+
});
|
|
163060
|
+
await Promise.allSettled(toolExecutionPromises);
|
|
163061
|
+
toolExecutionPromises.length = 0;
|
|
163062
|
+
}
|
|
163051
163063
|
if (finish_reason !== "tool_calls" && !summaryEmitted) {
|
|
163052
163064
|
if (capturedUsage && (capturedUsage.completion_tokens ?? 0) > 0) {
|
|
163053
163065
|
const usageData = {
|
|
@@ -163082,6 +163094,13 @@ async function adaptOpenRouterStream(stream, options2) {
|
|
|
163082
163094
|
}
|
|
163083
163095
|
}
|
|
163084
163096
|
}
|
|
163097
|
+
if (toolExecutionPromises.length > 0) {
|
|
163098
|
+
streamLogger.debug("Waiting for pending OpenRouter tool executions after stream end", {
|
|
163099
|
+
toolCount: toolExecutionPromises.length
|
|
163100
|
+
});
|
|
163101
|
+
await Promise.allSettled(toolExecutionPromises);
|
|
163102
|
+
toolExecutionPromises.length = 0;
|
|
163103
|
+
}
|
|
163085
163104
|
if (finishReason && !summaryEmitted && finishReason !== "tool_calls") {
|
|
163086
163105
|
streamLogger.warn("Stream ended without usage chunk - emitting summary without usage", {
|
|
163087
163106
|
finishReason
|
|
@@ -175976,6 +175995,33 @@ data: ${JSON.stringify(envelope)}
|
|
|
175976
175995
|
});
|
|
175977
175996
|
|
|
175978
175997
|
// src/core/tools/spawn-subagent.ts
|
|
175998
|
+
async function maybeStartBackgroundRelay(redis, context, conversationId, taskId, success2) {
|
|
175999
|
+
if (!redis || !context.startBackgroundSubagentRelay) return false;
|
|
176000
|
+
try {
|
|
176001
|
+
const record2 = await spawnedTaskStore.get(redis, taskId);
|
|
176002
|
+
if (record2?.notifiedTurnAt) {
|
|
176003
|
+
return true;
|
|
176004
|
+
}
|
|
176005
|
+
const lockKey = `subagents:background-relay-lock:${conversationId}`;
|
|
176006
|
+
const acquired = await redis.set(lockKey, taskId, "NX", "EX", 180).catch(() => null);
|
|
176007
|
+
if (!acquired) {
|
|
176008
|
+
return true;
|
|
176009
|
+
}
|
|
176010
|
+
const started = await context.startBackgroundSubagentRelay({
|
|
176011
|
+
conversationId,
|
|
176012
|
+
taskId,
|
|
176013
|
+
reason: success2 ? "completed" : "failed"
|
|
176014
|
+
});
|
|
176015
|
+
return started;
|
|
176016
|
+
} catch (err) {
|
|
176017
|
+
logger.debug("SpawnSubagent", "Failed to start background relay job", {
|
|
176018
|
+
conversationId,
|
|
176019
|
+
taskId,
|
|
176020
|
+
error: err instanceof Error ? err.message : String(err)
|
|
176021
|
+
});
|
|
176022
|
+
return false;
|
|
176023
|
+
}
|
|
176024
|
+
}
|
|
175979
176025
|
async function maybeEmitPingPrompt(redis, conversationId, taskId) {
|
|
175980
176026
|
if (!redis) return;
|
|
175981
176027
|
try {
|
|
@@ -176276,7 +176322,10 @@ HOW TO USE THIS:
|
|
|
176276
176322
|
tokenUsage: response.tokenUsage
|
|
176277
176323
|
}
|
|
176278
176324
|
});
|
|
176279
|
-
await
|
|
176325
|
+
const relayed = await maybeStartBackgroundRelay(redis, context, conversationId, taskId, success2);
|
|
176326
|
+
if (!relayed) {
|
|
176327
|
+
await maybeEmitPingPrompt(redis, conversationId, taskId);
|
|
176328
|
+
}
|
|
176280
176329
|
} catch (err) {
|
|
176281
176330
|
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
176282
176331
|
const status = abort.signal.aborted ? "cancelled" : "failed";
|
|
@@ -176291,7 +176340,10 @@ HOW TO USE THIS:
|
|
|
176291
176340
|
taskId,
|
|
176292
176341
|
data: { error: errorMessage }
|
|
176293
176342
|
});
|
|
176294
|
-
await
|
|
176343
|
+
const relayed = await maybeStartBackgroundRelay(redis, context, conversationId, taskId, false);
|
|
176344
|
+
if (!relayed) {
|
|
176345
|
+
await maybeEmitPingPrompt(redis, conversationId, taskId);
|
|
176346
|
+
}
|
|
176295
176347
|
} finally {
|
|
176296
176348
|
spawnedTaskStore.unregisterLocalAbort(taskId);
|
|
176297
176349
|
}
|
|
@@ -249322,7 +249374,8 @@ async function orchestrate(request) {
|
|
|
249322
249374
|
chatMode,
|
|
249323
249375
|
imageGenModel: context.metadata?.imageGenModel,
|
|
249324
249376
|
imageGenThinking: context.metadata?.imageGenThinking,
|
|
249325
|
-
registeredParentToolSchemas: context.registeredParentToolSchemas
|
|
249377
|
+
registeredParentToolSchemas: context.registeredParentToolSchemas,
|
|
249378
|
+
startBackgroundSubagentRelay: context.startBackgroundSubagentRelay
|
|
249326
249379
|
});
|
|
249327
249380
|
if (!config3.localFilesystem) {
|
|
249328
249381
|
aiTools["spawn_subagent"] = {
|
|
@@ -252400,6 +252453,7 @@ function createOrchestratorRequest(messages4, context, config3, persona, apiKey,
|
|
|
252400
252453
|
metadata: context.metadata,
|
|
252401
252454
|
allowedWorkspaceIds: context.allowedWorkspaceIds,
|
|
252402
252455
|
registeredParentToolSchemas: context.registeredParentToolSchemas,
|
|
252456
|
+
startBackgroundSubagentRelay: context.startBackgroundSubagentRelay,
|
|
252403
252457
|
extensions: context.extensions
|
|
252404
252458
|
// Plugin hook seam (CLI only; undefined on web)
|
|
252405
252459
|
},
|
|
@@ -254193,6 +254247,7 @@ var init_skills3 = __esm({
|
|
|
254193
254247
|
// src/adapters/cli/subagent.ts
|
|
254194
254248
|
import { spawn as spawn5 } from "node:child_process";
|
|
254195
254249
|
import { basename as basename8 } from "node:path";
|
|
254250
|
+
import { randomUUID as randomUUID6 } from "node:crypto";
|
|
254196
254251
|
function currentSubagentDepth() {
|
|
254197
254252
|
const d21 = Number(process.env.USABLE_SUBAGENT_DEPTH ?? 0);
|
|
254198
254253
|
return Number.isFinite(d21) && d21 > 0 ? Math.floor(d21) : 0;
|
|
@@ -254277,11 +254332,73 @@ async function runCliSubagent(input) {
|
|
|
254277
254332
|
}
|
|
254278
254333
|
return { ok: true, text: res.out.trim() };
|
|
254279
254334
|
}
|
|
254335
|
+
function publicTask(task) {
|
|
254336
|
+
return {
|
|
254337
|
+
taskId: task.taskId,
|
|
254338
|
+
status: task.status,
|
|
254339
|
+
prompt: task.prompt,
|
|
254340
|
+
model: task.model,
|
|
254341
|
+
cwd: task.cwd,
|
|
254342
|
+
startedAt: task.startedAt,
|
|
254343
|
+
completedAt: task.completedAt,
|
|
254344
|
+
...task.text ? { text: task.text } : {},
|
|
254345
|
+
...task.error ? { error: task.error } : {}
|
|
254346
|
+
};
|
|
254347
|
+
}
|
|
254348
|
+
function startCliSubagentTask(input) {
|
|
254349
|
+
const taskId = input.taskId ?? randomUUID6();
|
|
254350
|
+
const abortController = new AbortController();
|
|
254351
|
+
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
254352
|
+
const run2 = input._run ?? runCliSubagent;
|
|
254353
|
+
const onParentAbort = () => abortController.abort();
|
|
254354
|
+
input.parentAbortSignal?.addEventListener("abort", onParentAbort, { once: true });
|
|
254355
|
+
const task = {
|
|
254356
|
+
taskId,
|
|
254357
|
+
prompt: input.prompt,
|
|
254358
|
+
model: input.model,
|
|
254359
|
+
cwd: input.cwd,
|
|
254360
|
+
status: "running",
|
|
254361
|
+
startedAt,
|
|
254362
|
+
abortController,
|
|
254363
|
+
promise: Promise.resolve(void 0)
|
|
254364
|
+
};
|
|
254365
|
+
task.promise = (async () => {
|
|
254366
|
+
try {
|
|
254367
|
+
const res = await run2({
|
|
254368
|
+
prompt: input.prompt,
|
|
254369
|
+
model: input.model,
|
|
254370
|
+
cwd: input.cwd,
|
|
254371
|
+
abortSignal: abortController.signal,
|
|
254372
|
+
timeoutMs: input.timeoutMs
|
|
254373
|
+
});
|
|
254374
|
+
task.completedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
254375
|
+
if (abortController.signal.aborted) {
|
|
254376
|
+
task.status = "cancelled";
|
|
254377
|
+
task.error = "subagent cancelled";
|
|
254378
|
+
} else if (res.ok) {
|
|
254379
|
+
task.status = "completed";
|
|
254380
|
+
task.text = res.text;
|
|
254381
|
+
} else {
|
|
254382
|
+
task.status = "failed";
|
|
254383
|
+
task.text = res.text;
|
|
254384
|
+
task.error = res.error ?? "subagent failed";
|
|
254385
|
+
}
|
|
254386
|
+
} catch (err) {
|
|
254387
|
+
task.completedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
254388
|
+
task.status = abortController.signal.aborted ? "cancelled" : "failed";
|
|
254389
|
+
task.error = err instanceof Error ? err.message : String(err);
|
|
254390
|
+
} finally {
|
|
254391
|
+
input.parentAbortSignal?.removeEventListener("abort", onParentAbort);
|
|
254392
|
+
}
|
|
254393
|
+
return task;
|
|
254394
|
+
})();
|
|
254395
|
+
cliSubagentTasks.set(taskId, task);
|
|
254396
|
+
return task;
|
|
254397
|
+
}
|
|
254280
254398
|
function buildCliSubagentTool(opts) {
|
|
254281
|
-
const run2 = opts._run ?? runCliSubagent;
|
|
254282
254399
|
return {
|
|
254283
254400
|
spawn_subagent: {
|
|
254284
|
-
description:
|
|
254401
|
+
description: ASYNC_SPAWN_DESCRIPTION,
|
|
254285
254402
|
parameters: {
|
|
254286
254403
|
type: "object",
|
|
254287
254404
|
properties: {
|
|
@@ -254300,31 +254417,89 @@ function buildCliSubagentTool(opts) {
|
|
|
254300
254417
|
const prompt = String(args.prompt ?? "").trim();
|
|
254301
254418
|
if (!prompt) return { error: "prompt is required" };
|
|
254302
254419
|
const model = typeof args.model === "string" && args.model.trim() ? args.model.trim() : opts.model;
|
|
254303
|
-
const
|
|
254420
|
+
const task = startCliSubagentTask({
|
|
254421
|
+
taskId: typeof args.taskId === "string" && args.taskId.trim() ? args.taskId.trim() : void 0,
|
|
254304
254422
|
prompt,
|
|
254305
254423
|
model,
|
|
254306
254424
|
cwd: process.cwd(),
|
|
254307
|
-
|
|
254425
|
+
parentAbortSignal: opts.abortSignal,
|
|
254426
|
+
_run: opts._run
|
|
254308
254427
|
});
|
|
254309
|
-
|
|
254310
|
-
|
|
254428
|
+
return publicTask(task);
|
|
254429
|
+
}
|
|
254430
|
+
},
|
|
254431
|
+
list_subagents: {
|
|
254432
|
+
description: "List background CLI subagents in this process. Use this before awaiting when you need task ids or status.",
|
|
254433
|
+
parameters: {
|
|
254434
|
+
type: "object",
|
|
254435
|
+
properties: {
|
|
254436
|
+
includeFinished: {
|
|
254437
|
+
type: "boolean",
|
|
254438
|
+
description: "Include completed, failed, and cancelled tasks. Defaults to true."
|
|
254439
|
+
}
|
|
254440
|
+
}
|
|
254441
|
+
},
|
|
254442
|
+
execute: async (args) => {
|
|
254443
|
+
const includeFinished = args.includeFinished !== false;
|
|
254444
|
+
const tasks = [...cliSubagentTasks.values()].filter(
|
|
254445
|
+
(task) => includeFinished || task.status === "running"
|
|
254446
|
+
);
|
|
254447
|
+
return { tasks: tasks.map(publicTask) };
|
|
254448
|
+
}
|
|
254449
|
+
},
|
|
254450
|
+
await_subagents: {
|
|
254451
|
+
description: "Wait for one or more background CLI subagents and collect their results. Omit taskIds to wait for all running tasks.",
|
|
254452
|
+
parameters: {
|
|
254453
|
+
type: "object",
|
|
254454
|
+
properties: {
|
|
254455
|
+
taskIds: {
|
|
254456
|
+
type: "array",
|
|
254457
|
+
items: { type: "string" },
|
|
254458
|
+
description: "Specific task ids to wait for. Defaults to all running tasks."
|
|
254459
|
+
},
|
|
254460
|
+
timeout: {
|
|
254461
|
+
type: "number",
|
|
254462
|
+
description: "Maximum seconds to wait. Defaults to 300."
|
|
254463
|
+
}
|
|
254464
|
+
}
|
|
254465
|
+
},
|
|
254466
|
+
execute: async (args) => {
|
|
254467
|
+
const ids = Array.isArray(args.taskIds) ? args.taskIds.filter((id) => typeof id === "string" && id.length > 0) : [...cliSubagentTasks.values()].filter((task) => task.status === "running").map((task) => task.taskId);
|
|
254468
|
+
const tasks = ids.map((id) => cliSubagentTasks.get(id));
|
|
254469
|
+
const missing = ids.filter((id, index2) => !tasks[index2]);
|
|
254470
|
+
const existing = tasks.filter((task) => !!task);
|
|
254471
|
+
if (missing.length > 0) return { error: `Unknown subagent task id(s): ${missing.join(", ")}` };
|
|
254472
|
+
if (existing.length === 0) return { results: [] };
|
|
254473
|
+
const timeoutS = typeof args.timeout === "number" && Number.isFinite(args.timeout) ? Math.max(1, args.timeout) : 300;
|
|
254474
|
+
const timeout = new Promise(
|
|
254475
|
+
(resolve8) => setTimeout(() => resolve8("timeout"), timeoutS * 1e3)
|
|
254476
|
+
);
|
|
254477
|
+
const settled = await Promise.race([
|
|
254478
|
+
Promise.all(existing.map((task) => task.promise)),
|
|
254479
|
+
timeout
|
|
254480
|
+
]);
|
|
254481
|
+
if (settled === "timeout") {
|
|
254482
|
+
return {
|
|
254483
|
+
error: `Timed out waiting for subagents after ${timeoutS}s`,
|
|
254484
|
+
pending: existing.filter((task) => task.status === "running").map(publicTask),
|
|
254485
|
+
results: existing.filter((task) => task.status !== "running").map(publicTask)
|
|
254486
|
+
};
|
|
254311
254487
|
}
|
|
254312
|
-
return {
|
|
254488
|
+
return { results: settled.map(publicTask) };
|
|
254313
254489
|
}
|
|
254314
254490
|
}
|
|
254315
254491
|
};
|
|
254316
254492
|
}
|
|
254317
|
-
var SUBAGENT_TIMEOUT_MS, SUBAGENT_MAX_DEPTH,
|
|
254493
|
+
var SUBAGENT_TIMEOUT_MS, SUBAGENT_MAX_DEPTH, cliSubagentTasks, ASYNC_SPAWN_DESCRIPTION;
|
|
254318
254494
|
var init_subagent = __esm({
|
|
254319
254495
|
"src/adapters/cli/subagent.ts"() {
|
|
254320
254496
|
"use strict";
|
|
254321
254497
|
SUBAGENT_TIMEOUT_MS = 5 * 60 * 1e3;
|
|
254322
254498
|
SUBAGENT_MAX_DEPTH = 2;
|
|
254323
|
-
|
|
254324
|
-
|
|
254325
|
-
The subagent is a fresh \`usable-chat\` agent running in YOUR current working directory, with the SAME model/provider and the same real filesystem + shell + MCP tools + skills. Use it to fan out isolated work \u2014 explore a folder, search, summarize a large file, draft something \u2014 without cluttering your own context. It returns its answer inline (no separate await step).
|
|
254499
|
+
cliSubagentTasks = /* @__PURE__ */ new Map();
|
|
254500
|
+
ASYNC_SPAWN_DESCRIPTION = `Start a background subagent for a self-contained task and return immediately.
|
|
254326
254501
|
|
|
254327
|
-
|
|
254502
|
+
The task runs in this CLI process. Use list_subagents to inspect status and await_subagents to collect results. The subagent is a fresh \`usable-chat\` agent running in the current working directory, with the same provider/config and local tools.`;
|
|
254328
254503
|
}
|
|
254329
254504
|
});
|
|
254330
254505
|
|
|
@@ -269592,7 +269767,7 @@ var init_tui_markdown = __esm({
|
|
|
269592
269767
|
// src/cli/warp-agent.ts
|
|
269593
269768
|
import { openSync, writeSync } from "node:fs";
|
|
269594
269769
|
import { basename as basename9 } from "node:path";
|
|
269595
|
-
import { randomUUID as
|
|
269770
|
+
import { randomUUID as randomUUID7 } from "node:crypto";
|
|
269596
269771
|
function isWarpAgentHost() {
|
|
269597
269772
|
return !!process.env.WARP_CLI_AGENT_PROTOCOL_VERSION && process.platform !== "win32";
|
|
269598
269773
|
}
|
|
@@ -269645,7 +269820,7 @@ var init_warp_agent = __esm({
|
|
|
269645
269820
|
"use strict";
|
|
269646
269821
|
WARP_AGENT_SLUG = "usable-chat";
|
|
269647
269822
|
APP_NAME = "usable-chat";
|
|
269648
|
-
sessionId =
|
|
269823
|
+
sessionId = randomUUID7();
|
|
269649
269824
|
}
|
|
269650
269825
|
});
|
|
269651
269826
|
|
|
@@ -269758,6 +269933,7 @@ async function launchTui(options2) {
|
|
|
269758
269933
|
const ui2 = new TUI(terminal);
|
|
269759
269934
|
const editor = new Editor(ui2, editorTheme, { paddingX: 1 });
|
|
269760
269935
|
editor.focused = true;
|
|
269936
|
+
const bangTools = createLocalTools();
|
|
269761
269937
|
let items = [...initialHistory];
|
|
269762
269938
|
let compactedPrefix = [];
|
|
269763
269939
|
let busy = false;
|
|
@@ -269768,6 +269944,7 @@ async function launchTui(options2) {
|
|
|
269768
269944
|
let exitArmed = false;
|
|
269769
269945
|
let toolsExpanded = false;
|
|
269770
269946
|
let abort = null;
|
|
269947
|
+
let lastBangCommand = null;
|
|
269771
269948
|
const extStatus = /* @__PURE__ */ new Map();
|
|
269772
269949
|
let picker = null;
|
|
269773
269950
|
const rebuildPicker = () => {
|
|
@@ -270021,6 +270198,56 @@ Edit it, then /verify-extension ${arg.trim()} to check it loads, /trust (project
|
|
|
270021
270198
|
async function submit(raw) {
|
|
270022
270199
|
const value = raw.trim();
|
|
270023
270200
|
if (!value || busy) return;
|
|
270201
|
+
if (value === "!!" || value.startsWith("!") && !value.startsWith("/")) {
|
|
270202
|
+
const command = value === "!!" ? lastBangCommand : value.slice(1).trim();
|
|
270203
|
+
if (!command) {
|
|
270204
|
+
sys(value === "!!" ? "No previous shell command." : "Usage: ! <command>", "error");
|
|
270205
|
+
return;
|
|
270206
|
+
}
|
|
270207
|
+
lastBangCommand = command;
|
|
270208
|
+
busy = true;
|
|
270209
|
+
push({ kind: "user", text: `! ${command}` });
|
|
270210
|
+
const toolId = `bang-${Date.now()}`;
|
|
270211
|
+
push({
|
|
270212
|
+
kind: "tool",
|
|
270213
|
+
id: toolId,
|
|
270214
|
+
name: "bash",
|
|
270215
|
+
args: short(JSON.stringify({ command })),
|
|
270216
|
+
argsFull: prettyJson({ command }),
|
|
270217
|
+
status: "running"
|
|
270218
|
+
});
|
|
270219
|
+
ui2.requestRender();
|
|
270220
|
+
try {
|
|
270221
|
+
const result = await bangTools.bash.execute({ command });
|
|
270222
|
+
const error41 = result && typeof result === "object" && "error" in result ? String(result.error) : void 0;
|
|
270223
|
+
items = items.map(
|
|
270224
|
+
(it7) => it7.kind === "tool" && it7.id === toolId ? {
|
|
270225
|
+
...it7,
|
|
270226
|
+
status: error41 ? "error" : "done",
|
|
270227
|
+
result: error41 ? void 0 : previewValue(result),
|
|
270228
|
+
error: error41
|
|
270229
|
+
} : it7
|
|
270230
|
+
);
|
|
270231
|
+
} catch (err) {
|
|
270232
|
+
items = items.map(
|
|
270233
|
+
(it7) => it7.kind === "tool" && it7.id === toolId ? {
|
|
270234
|
+
...it7,
|
|
270235
|
+
status: "error",
|
|
270236
|
+
error: err instanceof Error ? err.message : String(err)
|
|
270237
|
+
} : it7
|
|
270238
|
+
);
|
|
270239
|
+
} finally {
|
|
270240
|
+
busy = false;
|
|
270241
|
+
ui2.requestRender();
|
|
270242
|
+
if (autoSubmit) {
|
|
270243
|
+
setTimeout(() => {
|
|
270244
|
+
teardown();
|
|
270245
|
+
exit(0);
|
|
270246
|
+
}, 25);
|
|
270247
|
+
}
|
|
270248
|
+
}
|
|
270249
|
+
return;
|
|
270250
|
+
}
|
|
270024
270251
|
if (value.startsWith("/")) {
|
|
270025
270252
|
const [cmd, ...rest] = value.slice(1).split(/\s+/);
|
|
270026
270253
|
await runSlash(cmd.toLowerCase(), rest.join(" "));
|
|
@@ -270298,6 +270525,7 @@ var init_tui2 = __esm({
|
|
|
270298
270525
|
"use strict";
|
|
270299
270526
|
init_dist8();
|
|
270300
270527
|
init_tui_select();
|
|
270528
|
+
init_tools();
|
|
270301
270529
|
init_tui_markdown();
|
|
270302
270530
|
init_warp_agent();
|
|
270303
270531
|
BANNER_LINES = [
|
|
@@ -270948,7 +271176,7 @@ init_tui_select();
|
|
|
270948
271176
|
init_model_registry();
|
|
270949
271177
|
|
|
270950
271178
|
// package.json
|
|
270951
|
-
var version2 = "1.
|
|
271179
|
+
var version2 = "1.154.0";
|
|
270952
271180
|
|
|
270953
271181
|
// src/adapters/cli/model-catalog.ts
|
|
270954
271182
|
init_codex_auth();
|