nolo-cli 0.1.48 → 0.1.50
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/index.js +311 -50
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -1576,6 +1576,26 @@ function formatToolMessageContent(args2) {
|
|
|
1576
1576
|
[tool metadata]
|
|
1577
1577
|
${JSON.stringify(args2.metadata)}`;
|
|
1578
1578
|
}
|
|
1579
|
+
function buildUserAction(args2) {
|
|
1580
|
+
const rawAction = args2.metadata?.requiresUserAction;
|
|
1581
|
+
if (!rawAction || typeof rawAction !== "object" || Array.isArray(rawAction)) return null;
|
|
1582
|
+
const action = rawAction;
|
|
1583
|
+
if (action.type !== "terminal_command") return null;
|
|
1584
|
+
if (!Array.isArray(action.argv)) return null;
|
|
1585
|
+
const argv = action.argv.flatMap(
|
|
1586
|
+
(item) => typeof item === "string" && item.trim() ? [item.trim()] : []
|
|
1587
|
+
);
|
|
1588
|
+
if (argv.length === 0) return null;
|
|
1589
|
+
return {
|
|
1590
|
+
type: "terminal_command",
|
|
1591
|
+
toolName: args2.toolName,
|
|
1592
|
+
toolCallId: args2.toolCallId,
|
|
1593
|
+
argv,
|
|
1594
|
+
...typeof action.displayCommand === "string" ? { displayCommand: action.displayCommand } : {},
|
|
1595
|
+
...typeof action.reason === "string" ? { reason: action.reason, message: action.reason } : {},
|
|
1596
|
+
...typeof action.resumeHint === "string" ? { resumeHint: action.resumeHint } : {}
|
|
1597
|
+
};
|
|
1598
|
+
}
|
|
1579
1599
|
function summarizeHistoricalToolContent(content) {
|
|
1580
1600
|
if (typeof content !== "string") return content;
|
|
1581
1601
|
if (content.length <= MAX_HISTORICAL_TOOL_CONTENT_CHARS) return content;
|
|
@@ -1677,6 +1697,17 @@ async function runLocalAgentTurn(input2) {
|
|
|
1677
1697
|
arguments: toolCall.function.arguments,
|
|
1678
1698
|
...userInputText ? { userInput: userInputText } : {}
|
|
1679
1699
|
});
|
|
1700
|
+
const userAction = buildUserAction({
|
|
1701
|
+
toolName,
|
|
1702
|
+
toolCallId: toolCall.id,
|
|
1703
|
+
metadata: toolResult.metadata
|
|
1704
|
+
});
|
|
1705
|
+
if (userAction && input2.onUserAction) {
|
|
1706
|
+
const replacement = await input2.onUserAction(userAction);
|
|
1707
|
+
if (replacement) {
|
|
1708
|
+
toolResult = replacement;
|
|
1709
|
+
}
|
|
1710
|
+
}
|
|
1680
1711
|
emitToolEvent(input2, {
|
|
1681
1712
|
type: "tool-result",
|
|
1682
1713
|
round,
|
|
@@ -3436,6 +3467,35 @@ function accumulateToolCallDelta(accumulated, deltas) {
|
|
|
3436
3467
|
function finalizeAccumulatedToolCalls(accumulated) {
|
|
3437
3468
|
return Object.keys(accumulated).map((key2) => accumulated[Number(key2)]).filter((call) => call?.function?.name);
|
|
3438
3469
|
}
|
|
3470
|
+
function processOpenAiCompatibleSseEvent(event, state) {
|
|
3471
|
+
for (const line of event.split("\n")) {
|
|
3472
|
+
const trimmed = line.trim();
|
|
3473
|
+
if (!trimmed.startsWith("data:")) continue;
|
|
3474
|
+
const payload = trimmed.slice(5).trim();
|
|
3475
|
+
if (!payload || payload === "[DONE]") continue;
|
|
3476
|
+
let parsed;
|
|
3477
|
+
try {
|
|
3478
|
+
parsed = JSON.parse(payload);
|
|
3479
|
+
} catch {
|
|
3480
|
+
continue;
|
|
3481
|
+
}
|
|
3482
|
+
if (parsed?.usage && typeof parsed.usage === "object") {
|
|
3483
|
+
state.usage = parsed.usage;
|
|
3484
|
+
}
|
|
3485
|
+
const delta = parsed?.choices?.[0]?.delta;
|
|
3486
|
+
if (!delta || typeof delta !== "object") continue;
|
|
3487
|
+
const reasoningChunk = typeof delta.reasoning_content === "string" ? delta.reasoning_content : typeof delta.reasoning === "string" ? delta.reasoning : "";
|
|
3488
|
+
if (reasoningChunk) state.reasoning += reasoningChunk;
|
|
3489
|
+
if (Array.isArray(delta.tool_calls) && delta.tool_calls.length > 0) {
|
|
3490
|
+
accumulateToolCallDelta(state.accumulatedToolCalls, delta.tool_calls);
|
|
3491
|
+
}
|
|
3492
|
+
const textChunk = typeof delta.content === "string" ? delta.content : "";
|
|
3493
|
+
if (textChunk) {
|
|
3494
|
+
state.content += textChunk;
|
|
3495
|
+
state.onTextDelta?.(textChunk);
|
|
3496
|
+
}
|
|
3497
|
+
}
|
|
3498
|
+
}
|
|
3439
3499
|
async function readOpenAiCompatibleSseCompletion(args2) {
|
|
3440
3500
|
const reader = args2.response.body?.getReader();
|
|
3441
3501
|
if (!reader) {
|
|
@@ -3443,10 +3503,13 @@ async function readOpenAiCompatibleSseCompletion(args2) {
|
|
|
3443
3503
|
}
|
|
3444
3504
|
const decoder = new TextDecoder();
|
|
3445
3505
|
let buffer = "";
|
|
3446
|
-
|
|
3447
|
-
|
|
3448
|
-
|
|
3449
|
-
|
|
3506
|
+
const state = {
|
|
3507
|
+
content: "",
|
|
3508
|
+
reasoning: "",
|
|
3509
|
+
usage: void 0,
|
|
3510
|
+
accumulatedToolCalls: {},
|
|
3511
|
+
onTextDelta: args2.onTextDelta
|
|
3512
|
+
};
|
|
3450
3513
|
while (true) {
|
|
3451
3514
|
const { done, value } = await reader.read();
|
|
3452
3515
|
if (done) break;
|
|
@@ -3456,41 +3519,19 @@ async function readOpenAiCompatibleSseCompletion(args2) {
|
|
|
3456
3519
|
if (boundary === -1) break;
|
|
3457
3520
|
const event = buffer.slice(0, boundary);
|
|
3458
3521
|
buffer = buffer.slice(boundary + 2);
|
|
3459
|
-
|
|
3460
|
-
const trimmed = line.trim();
|
|
3461
|
-
if (!trimmed.startsWith("data:")) continue;
|
|
3462
|
-
const payload = trimmed.slice(5).trim();
|
|
3463
|
-
if (!payload || payload === "[DONE]") continue;
|
|
3464
|
-
let parsed;
|
|
3465
|
-
try {
|
|
3466
|
-
parsed = JSON.parse(payload);
|
|
3467
|
-
} catch {
|
|
3468
|
-
continue;
|
|
3469
|
-
}
|
|
3470
|
-
if (parsed?.usage && typeof parsed.usage === "object") {
|
|
3471
|
-
usage2 = parsed.usage;
|
|
3472
|
-
}
|
|
3473
|
-
const delta = parsed?.choices?.[0]?.delta;
|
|
3474
|
-
if (!delta || typeof delta !== "object") continue;
|
|
3475
|
-
const reasoningChunk = typeof delta.reasoning_content === "string" ? delta.reasoning_content : typeof delta.reasoning === "string" ? delta.reasoning : "";
|
|
3476
|
-
if (reasoningChunk) reasoning += reasoningChunk;
|
|
3477
|
-
if (Array.isArray(delta.tool_calls) && delta.tool_calls.length > 0) {
|
|
3478
|
-
accumulateToolCallDelta(accumulatedToolCalls, delta.tool_calls);
|
|
3479
|
-
}
|
|
3480
|
-
const textChunk = typeof delta.content === "string" ? delta.content : "";
|
|
3481
|
-
if (textChunk) {
|
|
3482
|
-
content += textChunk;
|
|
3483
|
-
args2.onTextDelta?.(textChunk);
|
|
3484
|
-
}
|
|
3485
|
-
}
|
|
3522
|
+
processOpenAiCompatibleSseEvent(event, state);
|
|
3486
3523
|
}
|
|
3487
3524
|
}
|
|
3488
|
-
|
|
3525
|
+
buffer += decoder.decode();
|
|
3526
|
+
if (buffer.trim()) {
|
|
3527
|
+
processOpenAiCompatibleSseEvent(buffer, state);
|
|
3528
|
+
}
|
|
3529
|
+
const tool_calls = finalizeAccumulatedToolCalls(state.accumulatedToolCalls);
|
|
3489
3530
|
return {
|
|
3490
|
-
content,
|
|
3491
|
-
...reasoning ? { reasoning_content: reasoning } : {},
|
|
3531
|
+
content: state.content,
|
|
3532
|
+
...state.reasoning ? { reasoning_content: state.reasoning } : {},
|
|
3492
3533
|
...tool_calls.length > 0 ? { tool_calls } : {},
|
|
3493
|
-
...
|
|
3534
|
+
...state.usage ? { usage: state.usage } : {}
|
|
3494
3535
|
};
|
|
3495
3536
|
}
|
|
3496
3537
|
async function executeOpenAiCompatibleChatCompletion(args2) {
|
|
@@ -5121,6 +5162,81 @@ function readTrimmedString(value) {
|
|
|
5121
5162
|
function readFiniteNumber(value) {
|
|
5122
5163
|
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
5123
5164
|
}
|
|
5165
|
+
function resolveExecShellTimeoutMs(override) {
|
|
5166
|
+
if (typeof override === "number" && Number.isFinite(override) && override > 0) {
|
|
5167
|
+
return override;
|
|
5168
|
+
}
|
|
5169
|
+
const raw = process.env[EXEC_SHELL_TIMEOUT_ENV];
|
|
5170
|
+
if (raw !== void 0) {
|
|
5171
|
+
const parsed = Number(raw);
|
|
5172
|
+
if (Number.isFinite(parsed) && parsed > 0) return parsed;
|
|
5173
|
+
}
|
|
5174
|
+
return void 0;
|
|
5175
|
+
}
|
|
5176
|
+
function tokenizeShellPrefix(command2) {
|
|
5177
|
+
const tokens = [];
|
|
5178
|
+
const pattern = /"([^"]*)"|'([^']*)'|([^\s"'|;&<>]+)/g;
|
|
5179
|
+
let match;
|
|
5180
|
+
while (match = pattern.exec(command2)) {
|
|
5181
|
+
tokens.push(match[1] ?? match[2] ?? match[3] ?? "");
|
|
5182
|
+
}
|
|
5183
|
+
return tokens;
|
|
5184
|
+
}
|
|
5185
|
+
function extractInteractiveGhAuthCommand(command2) {
|
|
5186
|
+
const tokens = tokenizeShellPrefix(command2);
|
|
5187
|
+
if (tokens[0] !== "gh" || tokens[1] !== "auth") return null;
|
|
5188
|
+
const subcommand = tokens[2];
|
|
5189
|
+
if (subcommand !== "login" && subcommand !== "refresh") return null;
|
|
5190
|
+
if (tokens.includes("--help")) return null;
|
|
5191
|
+
const result = ["gh", "auth", subcommand];
|
|
5192
|
+
for (let index = 3; index < tokens.length; index += 1) {
|
|
5193
|
+
const token = tokens[index];
|
|
5194
|
+
if (!token) continue;
|
|
5195
|
+
if (token === "-h" || token === "--hostname" || token === "-s" || token === "--scopes" || token === "--remove-scopes" || token === "-r") {
|
|
5196
|
+
const value = tokens[index + 1];
|
|
5197
|
+
if (value) {
|
|
5198
|
+
result.push(token, value);
|
|
5199
|
+
index += 1;
|
|
5200
|
+
}
|
|
5201
|
+
continue;
|
|
5202
|
+
}
|
|
5203
|
+
if (token === "--clipboard" || token === "-c" || token === "--insecure-storage" || token === "--reset-scopes") {
|
|
5204
|
+
result.push(token);
|
|
5205
|
+
continue;
|
|
5206
|
+
}
|
|
5207
|
+
if (token.startsWith("--hostname=") || token.startsWith("--scopes=") || token.startsWith("--remove-scopes=")) {
|
|
5208
|
+
result.push(token);
|
|
5209
|
+
continue;
|
|
5210
|
+
}
|
|
5211
|
+
}
|
|
5212
|
+
return result.join(" ");
|
|
5213
|
+
}
|
|
5214
|
+
function splitShellWords(command2) {
|
|
5215
|
+
return tokenizeShellPrefix(command2);
|
|
5216
|
+
}
|
|
5217
|
+
function buildInteractiveCommandBlockedResult(command2) {
|
|
5218
|
+
const argv = splitShellWords(command2);
|
|
5219
|
+
return {
|
|
5220
|
+
content: [
|
|
5221
|
+
"requires_user_action: terminal_command",
|
|
5222
|
+
`command: ${command2}`,
|
|
5223
|
+
"Run this in the current TUI terminal, then resume the agent turn.",
|
|
5224
|
+
"exitCode: 130"
|
|
5225
|
+
].join("\n"),
|
|
5226
|
+
metadata: {
|
|
5227
|
+
exitCode: 130,
|
|
5228
|
+
timedOut: false,
|
|
5229
|
+
requiresUserAction: {
|
|
5230
|
+
type: "terminal_command",
|
|
5231
|
+
argv,
|
|
5232
|
+
displayCommand: command2,
|
|
5233
|
+
reason: "This command requires an interactive terminal.",
|
|
5234
|
+
resumeHint: "Continue after the terminal command exits."
|
|
5235
|
+
},
|
|
5236
|
+
reason: "interactive-command-requires-terminal"
|
|
5237
|
+
}
|
|
5238
|
+
};
|
|
5239
|
+
}
|
|
5124
5240
|
function extractActivityRefs(rawRefs) {
|
|
5125
5241
|
if (!Array.isArray(rawRefs)) return void 0;
|
|
5126
5242
|
const refs = rawRefs.flatMap((entry) => {
|
|
@@ -6792,6 +6908,18 @@ async function previewLifecycleTool(args2) {
|
|
|
6792
6908
|
async function execShellTool(args2) {
|
|
6793
6909
|
const parsed = parseWorkspaceToolArguments(args2.call.arguments);
|
|
6794
6910
|
const command2 = requireShellCommand(parsed, args2.call.name);
|
|
6911
|
+
const interactiveAuthCommand = extractInteractiveGhAuthCommand(command2);
|
|
6912
|
+
if (interactiveAuthCommand) {
|
|
6913
|
+
const activity2 = extractActivity(parsed);
|
|
6914
|
+
const blocked = buildInteractiveCommandBlockedResult(interactiveAuthCommand);
|
|
6915
|
+
return {
|
|
6916
|
+
...blocked,
|
|
6917
|
+
metadata: {
|
|
6918
|
+
...blocked.metadata,
|
|
6919
|
+
...activity2 ? { activity: activity2 } : {}
|
|
6920
|
+
}
|
|
6921
|
+
};
|
|
6922
|
+
}
|
|
6795
6923
|
const result = await runWorkspaceCommand({
|
|
6796
6924
|
workspaceRoot: args2.workspaceRoot,
|
|
6797
6925
|
command: buildWorkspaceShellCommand({
|
|
@@ -6799,7 +6927,7 @@ async function execShellTool(args2) {
|
|
|
6799
6927
|
command: command2,
|
|
6800
6928
|
shell: parsed.shell
|
|
6801
6929
|
}),
|
|
6802
|
-
timeoutMs: args2.commandTimeoutMs,
|
|
6930
|
+
timeoutMs: resolveExecShellTimeoutMs(args2.commandTimeoutMs),
|
|
6803
6931
|
outputLimit: args2.commandOutputLimit,
|
|
6804
6932
|
commandPrefix: args2.commandPrefix
|
|
6805
6933
|
});
|
|
@@ -6877,11 +7005,12 @@ function createLocalWorkspaceToolExecutors(args2) {
|
|
|
6877
7005
|
})
|
|
6878
7006
|
};
|
|
6879
7007
|
}
|
|
6880
|
-
var WORKSPACE_TOOL_NAMES, DEFAULT_LOCAL_CODING_TOOL_NAMES, SHELL_TOOL_NAMES, WORKSPACE_TOOL_NAME_SET, REMOVED_WORKSPACE_TOOL_NAMES;
|
|
7008
|
+
var EXEC_SHELL_TIMEOUT_ENV, WORKSPACE_TOOL_NAMES, DEFAULT_LOCAL_CODING_TOOL_NAMES, SHELL_TOOL_NAMES, WORKSPACE_TOOL_NAME_SET, REMOVED_WORKSPACE_TOOL_NAMES;
|
|
6881
7009
|
var init_localWorkspaceTools = __esm({
|
|
6882
7010
|
"packages/agent-runtime/localWorkspaceTools.ts"() {
|
|
6883
7011
|
"use strict";
|
|
6884
7012
|
init_runtimeCompat();
|
|
7013
|
+
EXEC_SHELL_TIMEOUT_ENV = "NOLO_EXEC_SHELL_TIMEOUT_MS";
|
|
6885
7014
|
WORKSPACE_TOOL_NAMES = [
|
|
6886
7015
|
"listFiles",
|
|
6887
7016
|
"readFile",
|
|
@@ -7688,6 +7817,7 @@ function spawnProcess(options) {
|
|
|
7688
7817
|
} catch {
|
|
7689
7818
|
return {
|
|
7690
7819
|
exited: Promise.resolve(127),
|
|
7820
|
+
stdin: null,
|
|
7691
7821
|
stdout: null,
|
|
7692
7822
|
stderr: null
|
|
7693
7823
|
};
|
|
@@ -7698,6 +7828,7 @@ function spawnProcess(options) {
|
|
|
7698
7828
|
});
|
|
7699
7829
|
return {
|
|
7700
7830
|
exited,
|
|
7831
|
+
stdin: child.stdin,
|
|
7701
7832
|
stdout: child.stdout,
|
|
7702
7833
|
stderr: child.stderr
|
|
7703
7834
|
};
|
|
@@ -64754,7 +64885,9 @@ function createCliLocalRuntimeAdapter(deps) {
|
|
|
64754
64885
|
const data2 = parsePlatformChatCompletionData(raw2);
|
|
64755
64886
|
throw new Error(`platform provider failed: HTTP ${res.status} ${JSON.stringify(data2)}`);
|
|
64756
64887
|
}
|
|
64757
|
-
|
|
64888
|
+
const contentType = res.headers.get("content-type") ?? "";
|
|
64889
|
+
const shouldStream = Boolean(stream && options?.onTextDelta) && contentType.includes("text/event-stream");
|
|
64890
|
+
if (shouldStream && options?.onTextDelta) {
|
|
64758
64891
|
const streamed = await readOpenAiCompatibleSseCompletion({
|
|
64759
64892
|
response: res,
|
|
64760
64893
|
onTextDelta: options.onTextDelta
|
|
@@ -68114,6 +68247,64 @@ function formatAssistantDisplay(text, mode = "rich") {
|
|
|
68114
68247
|
}
|
|
68115
68248
|
return polished.split("\n").map((line) => styleRichMarkdownLine(line)).join("\n");
|
|
68116
68249
|
}
|
|
68250
|
+
function emitFormattedAssistantBlock(write2, text, renderMode, trailingNewline = false) {
|
|
68251
|
+
if (!text) return;
|
|
68252
|
+
write2(formatAssistantDisplay(text, renderMode));
|
|
68253
|
+
if (trailingNewline) write2("\n");
|
|
68254
|
+
}
|
|
68255
|
+
function createRenderAwareStreamWriter(args2) {
|
|
68256
|
+
let buffer = "";
|
|
68257
|
+
const flushCompleteBlocks = () => {
|
|
68258
|
+
if (args2.renderMode === "plain") {
|
|
68259
|
+
if (!buffer) return;
|
|
68260
|
+
args2.write(buffer);
|
|
68261
|
+
buffer = "";
|
|
68262
|
+
return;
|
|
68263
|
+
}
|
|
68264
|
+
while (buffer.includes("\n")) {
|
|
68265
|
+
const lines = buffer.split("\n");
|
|
68266
|
+
if (lines.length < 2) break;
|
|
68267
|
+
if (isTableRow(lines[0] ?? "") && isTableSeparator(lines[1] ?? "")) {
|
|
68268
|
+
let end = 2;
|
|
68269
|
+
while (end < lines.length && isTableRow(lines[end] ?? "") && !isTableSeparator(lines[end] ?? "")) {
|
|
68270
|
+
end += 1;
|
|
68271
|
+
}
|
|
68272
|
+
const tableComplete = end < lines.length || buffer.endsWith("\n");
|
|
68273
|
+
if (!tableComplete) break;
|
|
68274
|
+
emitFormattedAssistantBlock(
|
|
68275
|
+
args2.write,
|
|
68276
|
+
lines.slice(0, end).join("\n"),
|
|
68277
|
+
args2.renderMode,
|
|
68278
|
+
true
|
|
68279
|
+
);
|
|
68280
|
+
buffer = lines.slice(end).join("\n");
|
|
68281
|
+
continue;
|
|
68282
|
+
}
|
|
68283
|
+
emitFormattedAssistantBlock(args2.write, lines[0] ?? "", args2.renderMode, true);
|
|
68284
|
+
buffer = lines.slice(1).join("\n");
|
|
68285
|
+
}
|
|
68286
|
+
};
|
|
68287
|
+
return {
|
|
68288
|
+
push(chunk) {
|
|
68289
|
+
if (!chunk) return;
|
|
68290
|
+
if (args2.renderMode === "plain") {
|
|
68291
|
+
args2.write(chunk);
|
|
68292
|
+
return;
|
|
68293
|
+
}
|
|
68294
|
+
buffer += chunk;
|
|
68295
|
+
flushCompleteBlocks();
|
|
68296
|
+
},
|
|
68297
|
+
flush() {
|
|
68298
|
+
if (!buffer) return;
|
|
68299
|
+
if (args2.renderMode === "plain") {
|
|
68300
|
+
args2.write(buffer);
|
|
68301
|
+
} else {
|
|
68302
|
+
emitFormattedAssistantBlock(args2.write, buffer, args2.renderMode);
|
|
68303
|
+
}
|
|
68304
|
+
buffer = "";
|
|
68305
|
+
}
|
|
68306
|
+
};
|
|
68307
|
+
}
|
|
68117
68308
|
|
|
68118
68309
|
// packages/cli/client/thinkingOutput.ts
|
|
68119
68310
|
var THINK_OPEN = /<think>/i;
|
|
@@ -68357,8 +68548,18 @@ function clip4(value, max = 72) {
|
|
|
68357
68548
|
const compact2 = value.replace(/\s+/g, " ").trim();
|
|
68358
68549
|
return compact2.length > max ? `${compact2.slice(0, max - 1)}\u2026` : compact2;
|
|
68359
68550
|
}
|
|
68360
|
-
function compactResultHint(
|
|
68551
|
+
function compactResultHint(event, toolName) {
|
|
68552
|
+
const rawAction = event.metadata?.requiresUserAction;
|
|
68553
|
+
if (rawAction && typeof rawAction === "object" && !Array.isArray(rawAction)) {
|
|
68554
|
+
const action = rawAction;
|
|
68555
|
+
const command2 = typeof action.displayCommand === "string" ? action.displayCommand : Array.isArray(action.argv) ? action.argv.filter((item) => typeof item === "string").join(" ") : "";
|
|
68556
|
+
return command2.trim() ? `needs terminal: ${clip4(command2, 120)}` : "needs terminal";
|
|
68557
|
+
}
|
|
68558
|
+
if (event.metadata?.timedOut) return "timed out";
|
|
68559
|
+
const summary = event.summary;
|
|
68361
68560
|
if (!summary) return "";
|
|
68561
|
+
const exitMatch = summary.match(/exit=(\d+)/);
|
|
68562
|
+
if (exitMatch && exitMatch[1] !== "0") return `exit ${exitMatch[1]}`;
|
|
68362
68563
|
const linesMatch = summary.match(/(\d+)\s+lines?/);
|
|
68363
68564
|
if (linesMatch) {
|
|
68364
68565
|
if (toolName === "readFile" || toolName === "listFiles" || toolName === "globFiles") {
|
|
@@ -68368,10 +68569,13 @@ function compactResultHint(summary, toolName) {
|
|
|
68368
68569
|
return `${linesMatch[1]} lines`;
|
|
68369
68570
|
}
|
|
68370
68571
|
}
|
|
68371
|
-
const exitMatch = summary.match(/exit=(\d+)/);
|
|
68372
|
-
if (exitMatch && exitMatch[1] !== "0") return `exit ${exitMatch[1]}`;
|
|
68373
68572
|
return "";
|
|
68374
68573
|
}
|
|
68574
|
+
function isFailedToolResult(event) {
|
|
68575
|
+
const exitCode2 = event.metadata?.exitCode;
|
|
68576
|
+
if (typeof exitCode2 === "number" && exitCode2 !== 0) return true;
|
|
68577
|
+
return Boolean(event.metadata?.timedOut || event.metadata?.requiresUserAction);
|
|
68578
|
+
}
|
|
68375
68579
|
function formatToolTraceLine(text, colorEnabled, accent = "none") {
|
|
68376
68580
|
if (!colorEnabled) return `${text}
|
|
68377
68581
|
`;
|
|
@@ -68413,10 +68617,12 @@ function formatCompactToolLine(event, pending, colorEnabled) {
|
|
|
68413
68617
|
const timing2 = ms ? ` \xB7 ${ms}` : "";
|
|
68414
68618
|
return formatToolTraceLine(` \u25B8 ${label} \u2717 ${message}${timing2}`, colorEnabled, "error");
|
|
68415
68619
|
}
|
|
68416
|
-
const hint = compactResultHint(event
|
|
68620
|
+
const hint = compactResultHint(event, toolName);
|
|
68417
68621
|
const timing = ms ? ` ${ms}` : "";
|
|
68418
68622
|
const suffix = hint ? ` \xB7 ${hint}` : "";
|
|
68419
|
-
|
|
68623
|
+
const marker = isFailedToolResult(event) ? "\u2717" : "\u2713";
|
|
68624
|
+
const accent = isFailedToolResult(event) ? "error" : "none";
|
|
68625
|
+
return formatToolTraceLine(` \u25B8 ${label} ${marker}${timing}${suffix}`, colorEnabled, accent);
|
|
68420
68626
|
}
|
|
68421
68627
|
function createToolEventFormatter(mode, colorEnabled = resolveCliColorEnabled()) {
|
|
68422
68628
|
const pending = /* @__PURE__ */ new Map();
|
|
@@ -68920,12 +69126,16 @@ async function runLocalAgentTurnForCli(options, settings) {
|
|
|
68920
69126
|
const traceLocalTools = shouldEmitToolEvents(toolDisplayMode);
|
|
68921
69127
|
const formatToolEvent = createToolEventFormatter(toolDisplayMode);
|
|
68922
69128
|
const eventMode = resolveAgentEventMode(options);
|
|
68923
|
-
let wroteToolTrace = false;
|
|
68924
69129
|
let streamedAssistantText = false;
|
|
68925
69130
|
let printedAssistantLabel = false;
|
|
68926
69131
|
const thinkingMode = resolveThinkingDisplayMode(options.env);
|
|
69132
|
+
const renderMode = resolveRenderDisplayMode(options.env);
|
|
69133
|
+
const renderWriter = createRenderAwareStreamWriter({
|
|
69134
|
+
write: (chunk) => options.output.write(chunk),
|
|
69135
|
+
renderMode
|
|
69136
|
+
});
|
|
68927
69137
|
const thinkingFilter = createThinkingAwareStreamFilter(
|
|
68928
|
-
(chunk) =>
|
|
69138
|
+
(chunk) => renderWriter.push(chunk),
|
|
68929
69139
|
thinkingMode
|
|
68930
69140
|
);
|
|
68931
69141
|
const subjectRefs = buildSubjectRefs(options);
|
|
@@ -68954,12 +69164,12 @@ async function runLocalAgentTurnForCli(options, settings) {
|
|
|
68954
69164
|
}
|
|
68955
69165
|
} : {},
|
|
68956
69166
|
...typeof options.timeoutMs === "number" ? { timeoutMs: options.timeoutMs } : {},
|
|
69167
|
+
...options.userActionHandler ? { onUserAction: options.userActionHandler } : {},
|
|
68957
69168
|
...traceLocalTools ? {
|
|
68958
69169
|
onToolEvent: (event) => {
|
|
68959
69170
|
spinner.stop();
|
|
68960
69171
|
const chunk = eventMode === "jsonl" ? formatToolJsonEvent(event) : formatToolEvent(event);
|
|
68961
69172
|
if (chunk) {
|
|
68962
|
-
wroteToolTrace = true;
|
|
68963
69173
|
options.output.write(chunk);
|
|
68964
69174
|
}
|
|
68965
69175
|
}
|
|
@@ -68980,6 +69190,7 @@ ${options.agentName} > `);
|
|
|
68980
69190
|
spinner.stop();
|
|
68981
69191
|
if (streamedAssistantText) {
|
|
68982
69192
|
thinkingFilter.flush();
|
|
69193
|
+
renderWriter.flush();
|
|
68983
69194
|
options.output.write("\n");
|
|
68984
69195
|
} else {
|
|
68985
69196
|
const content = formatAssistantResponseForCli(result.content.trim(), options);
|
|
@@ -69017,8 +69228,13 @@ async function readStreamingAgentRun(options, res) {
|
|
|
69017
69228
|
}
|
|
69018
69229
|
const decoder = new TextDecoder();
|
|
69019
69230
|
const thinkingMode = resolveThinkingDisplayMode(options.env);
|
|
69231
|
+
const renderMode = resolveRenderDisplayMode(options.env);
|
|
69232
|
+
const renderWriter = createRenderAwareStreamWriter({
|
|
69233
|
+
write: (chunk) => options.output.write(chunk),
|
|
69234
|
+
renderMode
|
|
69235
|
+
});
|
|
69020
69236
|
const writer = createStreamingTextWriter({
|
|
69021
|
-
write: (chunk) =>
|
|
69237
|
+
write: (chunk) => renderWriter.push(chunk)
|
|
69022
69238
|
});
|
|
69023
69239
|
const thinkingFilter = createThinkingAwareStreamFilter(
|
|
69024
69240
|
(chunk) => writer.push(chunk),
|
|
@@ -69091,6 +69307,7 @@ ${options.agentName} > `);
|
|
|
69091
69307
|
} finally {
|
|
69092
69308
|
writer.flushAll();
|
|
69093
69309
|
thinkingFilter.flush();
|
|
69310
|
+
renderWriter.flush();
|
|
69094
69311
|
}
|
|
69095
69312
|
if (!content) {
|
|
69096
69313
|
options.output.write(`
|
|
@@ -74970,7 +75187,7 @@ ${renderTuiHelp()}`
|
|
|
74970
75187
|
}
|
|
74971
75188
|
|
|
74972
75189
|
// packages/cli/tui/readlineWorkspace.ts
|
|
74973
|
-
async function runAgentChat(scriptDir, state, message, env, output2, agentRunner = runAgentTurn) {
|
|
75190
|
+
async function runAgentChat(scriptDir, state, message, env, output2, agentRunner = runAgentTurn, userActionHandler) {
|
|
74974
75191
|
const result = await agentRunner({
|
|
74975
75192
|
agentName: state.agentName,
|
|
74976
75193
|
agentKey: state.agentKey,
|
|
@@ -74986,10 +75203,52 @@ async function runAgentChat(scriptDir, state, message, env, output2, agentRunner
|
|
|
74986
75203
|
NOLO_CLI_TOOLS: state.toolDisplay,
|
|
74987
75204
|
NOLO_CLI_RENDER: state.renderDisplay
|
|
74988
75205
|
},
|
|
74989
|
-
output: output2
|
|
75206
|
+
output: output2,
|
|
75207
|
+
...userActionHandler ? { userActionHandler } : {}
|
|
74990
75208
|
});
|
|
74991
75209
|
return result;
|
|
74992
75210
|
}
|
|
75211
|
+
function waitForManualUserAction(rl, input2, output2, action, spawnRunner) {
|
|
75212
|
+
const displayCommand = action.displayCommand ?? action.argv.join(" ");
|
|
75213
|
+
output2.write("\n[nolo] Action needed in your terminal\n");
|
|
75214
|
+
if (action.reason) output2.write(`[nolo] ${action.reason}
|
|
75215
|
+
`);
|
|
75216
|
+
output2.write(` ${displayCommand}
|
|
75217
|
+
`);
|
|
75218
|
+
output2.write("[nolo] Press Enter to run it now. Follow any prompts below, or Ctrl+C to cancel.\n");
|
|
75219
|
+
return new Promise((resolve6) => {
|
|
75220
|
+
rl.question("", async () => {
|
|
75221
|
+
const rawInput = input2;
|
|
75222
|
+
const restoreRawMode = Boolean(rawInput.isRaw);
|
|
75223
|
+
rl.pause();
|
|
75224
|
+
rawInput.setRawMode?.(false);
|
|
75225
|
+
let exitCode2 = 1;
|
|
75226
|
+
try {
|
|
75227
|
+
const proc = spawnRunner({
|
|
75228
|
+
cmd: action.argv,
|
|
75229
|
+
stdin: "inherit",
|
|
75230
|
+
stdout: "inherit",
|
|
75231
|
+
stderr: "inherit"
|
|
75232
|
+
});
|
|
75233
|
+
exitCode2 = await proc.exited;
|
|
75234
|
+
} finally {
|
|
75235
|
+
if (restoreRawMode) rawInput.setRawMode?.(true);
|
|
75236
|
+
rl.resume();
|
|
75237
|
+
}
|
|
75238
|
+
resolve6({
|
|
75239
|
+
content: exitCode2 === 0 ? `user action completed: ${displayCommand}` : `user action failed with exit code ${exitCode2}: ${displayCommand}`,
|
|
75240
|
+
metadata: {
|
|
75241
|
+
exitCode: exitCode2,
|
|
75242
|
+
userActionCompleted: exitCode2 === 0,
|
|
75243
|
+
userActionFailed: exitCode2 !== 0,
|
|
75244
|
+
requiresUserActionCompleted: true,
|
|
75245
|
+
argv: action.argv,
|
|
75246
|
+
displayCommand
|
|
75247
|
+
}
|
|
75248
|
+
});
|
|
75249
|
+
});
|
|
75250
|
+
});
|
|
75251
|
+
}
|
|
74993
75252
|
async function pipeReadableToOutput(stream, output2) {
|
|
74994
75253
|
const text = await readPipeText(stream);
|
|
74995
75254
|
if (text) output2.write(text);
|
|
@@ -75030,6 +75289,7 @@ async function startTuiWorkspace(options) {
|
|
|
75030
75289
|
const output2 = options.output ?? defaultOutput;
|
|
75031
75290
|
const cliEntrypointPath = options.cliEntrypointPath ?? resolveDefaultCliEntrypoint(options.scriptDir);
|
|
75032
75291
|
const cliCommandRunner = options.cliCommandRunner ?? runCliCommandInChildProcess;
|
|
75292
|
+
const spawnRunner = options.spawnRunner ?? spawnProcess;
|
|
75033
75293
|
const selfUpdater = options.selfUpdater ?? ((target) => runSelfUpdate({ output: target }));
|
|
75034
75294
|
const rl = createInterface2({ input: input2, output: output2 });
|
|
75035
75295
|
output2.write(renderWelcome(state));
|
|
@@ -75172,7 +75432,8 @@ async function startTuiWorkspace(options) {
|
|
|
75172
75432
|
result.action.message,
|
|
75173
75433
|
options.env ?? process.env,
|
|
75174
75434
|
output2,
|
|
75175
|
-
options.agentRunner
|
|
75435
|
+
options.agentRunner,
|
|
75436
|
+
(action) => waitForManualUserAction(rl, input2, output2, action, spawnRunner)
|
|
75176
75437
|
);
|
|
75177
75438
|
if (runResult.dialogId || runResult.turnTokens) {
|
|
75178
75439
|
state = {
|