llmist 2.0.0 → 2.2.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/dist/{chunk-LBHWVCZ2.js → chunk-GANXNBIZ.js} +123 -1
- package/dist/chunk-GANXNBIZ.js.map +1 -0
- package/dist/{chunk-LFSIEPAE.js → chunk-ZDNV7DDO.js} +2 -2
- package/dist/cli.cjs +283 -96
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +163 -98
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +122 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +4 -2
- package/dist/index.d.ts +4 -2
- package/dist/index.js +2 -2
- package/dist/{mock-stream-BQHut0lQ.d.cts → mock-stream-wRfUqXx4.d.cts} +96 -6
- package/dist/{mock-stream-BQHut0lQ.d.ts → mock-stream-wRfUqXx4.d.ts} +96 -6
- package/dist/testing/index.cjs +122 -0
- package/dist/testing/index.cjs.map +1 -1
- package/dist/testing/index.d.cts +2 -2
- package/dist/testing/index.d.ts +2 -2
- package/dist/testing/index.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-LBHWVCZ2.js.map +0 -1
- /package/dist/{chunk-LFSIEPAE.js.map → chunk-ZDNV7DDO.js.map} +0 -0
package/dist/cli.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import "./chunk-
|
|
2
|
+
import "./chunk-ZDNV7DDO.js";
|
|
3
3
|
import {
|
|
4
4
|
AgentBuilder,
|
|
5
5
|
BaseGadget,
|
|
@@ -27,7 +27,7 @@ import {
|
|
|
27
27
|
resolveModel,
|
|
28
28
|
schemaToJSONSchema,
|
|
29
29
|
validateGadgetSchema
|
|
30
|
-
} from "./chunk-
|
|
30
|
+
} from "./chunk-GANXNBIZ.js";
|
|
31
31
|
|
|
32
32
|
// src/cli/constants.ts
|
|
33
33
|
var CLI_NAME = "llmist";
|
|
@@ -51,7 +51,6 @@ var OPTION_FLAGS = {
|
|
|
51
51
|
logFile: "--log-file <path>",
|
|
52
52
|
logReset: "--log-reset",
|
|
53
53
|
logLlmRequests: "--log-llm-requests [dir]",
|
|
54
|
-
logLlmResponses: "--log-llm-responses [dir]",
|
|
55
54
|
noBuiltins: "--no-builtins",
|
|
56
55
|
noBuiltinInteraction: "--no-builtin-interaction",
|
|
57
56
|
quiet: "-q, --quiet",
|
|
@@ -70,8 +69,7 @@ var OPTION_DESCRIPTIONS = {
|
|
|
70
69
|
logLevel: "Log level: silly, trace, debug, info, warn, error, fatal.",
|
|
71
70
|
logFile: "Path to log file. When set, logs are written to file instead of stderr.",
|
|
72
71
|
logReset: "Reset (truncate) the log file at session start instead of appending.",
|
|
73
|
-
logLlmRequests: "Save
|
|
74
|
-
logLlmResponses: "Save raw LLM responses as plain text. Optional dir, defaults to ~/.llmist/logs/responses/",
|
|
72
|
+
logLlmRequests: "Save LLM requests/responses to session directories. Optional dir, defaults to ~/.llmist/logs/requests/",
|
|
75
73
|
noBuiltins: "Disable built-in gadgets (AskUser, TellUser).",
|
|
76
74
|
noBuiltinInteraction: "Disable interactive gadgets (AskUser) while keeping TellUser.",
|
|
77
75
|
quiet: "Suppress all output except content (text and TellUser messages).",
|
|
@@ -88,7 +86,7 @@ import { Command, InvalidArgumentError as InvalidArgumentError2 } from "commande
|
|
|
88
86
|
// package.json
|
|
89
87
|
var package_default = {
|
|
90
88
|
name: "llmist",
|
|
91
|
-
version: "1.
|
|
89
|
+
version: "2.1.0",
|
|
92
90
|
description: "Universal TypeScript LLM client with streaming-first agent framework. Works with any model - no structured outputs or native tool calling required. Implements its own flexible grammar for function calling.",
|
|
93
91
|
type: "module",
|
|
94
92
|
main: "dist/index.cjs",
|
|
@@ -261,6 +259,14 @@ ${addedLines}`;
|
|
|
261
259
|
}
|
|
262
260
|
|
|
263
261
|
// src/cli/approval/context-providers.ts
|
|
262
|
+
function formatGadgetSummary(gadgetName, params) {
|
|
263
|
+
const paramEntries = Object.entries(params);
|
|
264
|
+
if (paramEntries.length === 0) {
|
|
265
|
+
return `${gadgetName}()`;
|
|
266
|
+
}
|
|
267
|
+
const paramStr = paramEntries.map(([k, v]) => `${k}=${JSON.stringify(v)}`).join(", ");
|
|
268
|
+
return `${gadgetName}(${paramStr})`;
|
|
269
|
+
}
|
|
264
270
|
var WriteFileContextProvider = class {
|
|
265
271
|
gadgetName = "WriteFile";
|
|
266
272
|
async getContext(params) {
|
|
@@ -269,14 +275,14 @@ var WriteFileContextProvider = class {
|
|
|
269
275
|
const resolvedPath = resolve(process.cwd(), filePath);
|
|
270
276
|
if (!existsSync(resolvedPath)) {
|
|
271
277
|
return {
|
|
272
|
-
summary:
|
|
278
|
+
summary: formatGadgetSummary(this.gadgetName, params),
|
|
273
279
|
details: formatNewFileDiff(filePath, newContent)
|
|
274
280
|
};
|
|
275
281
|
}
|
|
276
282
|
const oldContent = readFileSync(resolvedPath, "utf-8");
|
|
277
283
|
const diff = createPatch(filePath, oldContent, newContent, "original", "modified");
|
|
278
284
|
return {
|
|
279
|
-
summary:
|
|
285
|
+
summary: formatGadgetSummary(this.gadgetName, params),
|
|
280
286
|
details: diff
|
|
281
287
|
};
|
|
282
288
|
}
|
|
@@ -290,37 +296,27 @@ var EditFileContextProvider = class {
|
|
|
290
296
|
const newContent = String(params.content);
|
|
291
297
|
if (!existsSync(resolvedPath)) {
|
|
292
298
|
return {
|
|
293
|
-
summary:
|
|
299
|
+
summary: formatGadgetSummary(this.gadgetName, params),
|
|
294
300
|
details: formatNewFileDiff(filePath, newContent)
|
|
295
301
|
};
|
|
296
302
|
}
|
|
297
303
|
const oldContent = readFileSync(resolvedPath, "utf-8");
|
|
298
304
|
const diff = createPatch(filePath, oldContent, newContent, "original", "modified");
|
|
299
305
|
return {
|
|
300
|
-
summary:
|
|
306
|
+
summary: formatGadgetSummary(this.gadgetName, params),
|
|
301
307
|
details: diff
|
|
302
308
|
};
|
|
303
309
|
}
|
|
304
310
|
if ("commands" in params) {
|
|
305
311
|
const commands = String(params.commands);
|
|
306
312
|
return {
|
|
307
|
-
summary:
|
|
313
|
+
summary: formatGadgetSummary(this.gadgetName, params),
|
|
308
314
|
details: `Commands:
|
|
309
315
|
${commands}`
|
|
310
316
|
};
|
|
311
317
|
}
|
|
312
318
|
return {
|
|
313
|
-
summary:
|
|
314
|
-
};
|
|
315
|
-
}
|
|
316
|
-
};
|
|
317
|
-
var RunCommandContextProvider = class {
|
|
318
|
-
gadgetName = "RunCommand";
|
|
319
|
-
async getContext(params) {
|
|
320
|
-
const command = String(params.command ?? "");
|
|
321
|
-
const cwd = params.cwd ? ` (in ${params.cwd})` : "";
|
|
322
|
-
return {
|
|
323
|
-
summary: `Execute: ${command}${cwd}`
|
|
319
|
+
summary: formatGadgetSummary(this.gadgetName, params)
|
|
324
320
|
};
|
|
325
321
|
}
|
|
326
322
|
};
|
|
@@ -329,27 +325,15 @@ var DefaultContextProvider = class {
|
|
|
329
325
|
this.gadgetName = gadgetName;
|
|
330
326
|
}
|
|
331
327
|
async getContext(params) {
|
|
332
|
-
const paramEntries = Object.entries(params);
|
|
333
|
-
if (paramEntries.length === 0) {
|
|
334
|
-
return {
|
|
335
|
-
summary: `${this.gadgetName}()`
|
|
336
|
-
};
|
|
337
|
-
}
|
|
338
|
-
const formatValue = (value) => {
|
|
339
|
-
const MAX_LEN = 50;
|
|
340
|
-
const str = JSON.stringify(value);
|
|
341
|
-
return str.length > MAX_LEN ? `${str.slice(0, MAX_LEN - 3)}...` : str;
|
|
342
|
-
};
|
|
343
|
-
const paramStr = paramEntries.map(([k, v]) => `${k}=${formatValue(v)}`).join(", ");
|
|
344
328
|
return {
|
|
345
|
-
summary:
|
|
329
|
+
summary: formatGadgetSummary(this.gadgetName, params)
|
|
346
330
|
};
|
|
347
331
|
}
|
|
348
332
|
};
|
|
349
333
|
var builtinContextProviders = [
|
|
350
334
|
new WriteFileContextProvider(),
|
|
351
|
-
new EditFileContextProvider()
|
|
352
|
-
|
|
335
|
+
new EditFileContextProvider()
|
|
336
|
+
// Note: RunCommand uses DefaultContextProvider - no custom details needed
|
|
353
337
|
];
|
|
354
338
|
|
|
355
339
|
// src/cli/approval/manager.ts
|
|
@@ -360,11 +344,13 @@ var ApprovalManager = class {
|
|
|
360
344
|
* @param config - Approval configuration with per-gadget modes
|
|
361
345
|
* @param env - CLI environment for I/O operations
|
|
362
346
|
* @param progress - Optional progress indicator to pause during prompts
|
|
347
|
+
* @param keyboard - Optional keyboard coordinator to disable ESC listener during prompts
|
|
363
348
|
*/
|
|
364
|
-
constructor(config, env, progress) {
|
|
349
|
+
constructor(config, env, progress, keyboard) {
|
|
365
350
|
this.config = config;
|
|
366
351
|
this.env = env;
|
|
367
352
|
this.progress = progress;
|
|
353
|
+
this.keyboard = keyboard;
|
|
368
354
|
for (const provider of builtinContextProviders) {
|
|
369
355
|
this.registerProvider(provider);
|
|
370
356
|
}
|
|
@@ -433,26 +419,34 @@ var ApprovalManager = class {
|
|
|
433
419
|
const provider = this.providers.get(gadgetName.toLowerCase()) ?? new DefaultContextProvider(gadgetName);
|
|
434
420
|
const context = await provider.getContext(params);
|
|
435
421
|
this.progress?.pause();
|
|
436
|
-
this.
|
|
422
|
+
if (this.keyboard?.cleanupEsc) {
|
|
423
|
+
this.keyboard.cleanupEsc();
|
|
424
|
+
this.keyboard.cleanupEsc = null;
|
|
425
|
+
}
|
|
426
|
+
try {
|
|
427
|
+
this.env.stderr.write(`
|
|
437
428
|
${chalk2.yellow("\u{1F512} Approval required:")} ${context.summary}
|
|
438
429
|
`);
|
|
439
|
-
|
|
440
|
-
|
|
430
|
+
if (context.details) {
|
|
431
|
+
this.env.stderr.write(`
|
|
441
432
|
${renderColoredDiff(context.details)}
|
|
442
433
|
`);
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
434
|
+
}
|
|
435
|
+
const response = await this.prompt(" \u23CE approve, or type to reject: ");
|
|
436
|
+
const isApproved = response === "" || response.toLowerCase() === "y";
|
|
437
|
+
if (isApproved) {
|
|
438
|
+
this.env.stderr.write(` ${chalk2.green("\u2713 Approved")}
|
|
448
439
|
|
|
449
440
|
`);
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
441
|
+
return { approved: true };
|
|
442
|
+
}
|
|
443
|
+
this.env.stderr.write(` ${chalk2.red("\u2717 Denied")}
|
|
453
444
|
|
|
454
445
|
`);
|
|
455
|
-
|
|
446
|
+
return { approved: false, reason: response || "Rejected by user" };
|
|
447
|
+
} finally {
|
|
448
|
+
this.keyboard?.restore();
|
|
449
|
+
}
|
|
456
450
|
}
|
|
457
451
|
/**
|
|
458
452
|
* Prompts for user input.
|
|
@@ -922,6 +916,22 @@ var runCommand = createGadget({
|
|
|
922
916
|
params: { argv: ["gh", "pr", "review", "123", "--comment", "--body", "Review with `backticks` and 'quotes'"], timeout: 3e4 },
|
|
923
917
|
output: "status=0\n\n(no output)",
|
|
924
918
|
comment: "Complex arguments with special characters - no escaping needed"
|
|
919
|
+
},
|
|
920
|
+
{
|
|
921
|
+
params: {
|
|
922
|
+
argv: [
|
|
923
|
+
"gh",
|
|
924
|
+
"pr",
|
|
925
|
+
"review",
|
|
926
|
+
"123",
|
|
927
|
+
"--approve",
|
|
928
|
+
"--body",
|
|
929
|
+
"## Review Summary\n\n**Looks good!**\n\n- Clean code\n- Tests pass"
|
|
930
|
+
],
|
|
931
|
+
timeout: 3e4
|
|
932
|
+
},
|
|
933
|
+
output: "status=0\n\nApproving pull request #123",
|
|
934
|
+
comment: "Multiline body: --body flag and content must be SEPARATE array elements"
|
|
925
935
|
}
|
|
926
936
|
],
|
|
927
937
|
execute: async ({ argv, cwd, timeout }) => {
|
|
@@ -929,6 +939,7 @@ var runCommand = createGadget({
|
|
|
929
939
|
if (argv.length === 0) {
|
|
930
940
|
return "status=1\n\nerror: argv array cannot be empty";
|
|
931
941
|
}
|
|
942
|
+
let timeoutId;
|
|
932
943
|
try {
|
|
933
944
|
const proc = Bun.spawn(argv, {
|
|
934
945
|
cwd: workingDir,
|
|
@@ -936,12 +947,15 @@ var runCommand = createGadget({
|
|
|
936
947
|
stderr: "pipe"
|
|
937
948
|
});
|
|
938
949
|
const timeoutPromise = new Promise((_, reject) => {
|
|
939
|
-
setTimeout(() => {
|
|
950
|
+
timeoutId = setTimeout(() => {
|
|
940
951
|
proc.kill();
|
|
941
952
|
reject(new Error(`Command timed out after ${timeout}ms`));
|
|
942
953
|
}, timeout);
|
|
943
954
|
});
|
|
944
955
|
const exitCode = await Promise.race([proc.exited, timeoutPromise]);
|
|
956
|
+
if (timeoutId) {
|
|
957
|
+
clearTimeout(timeoutId);
|
|
958
|
+
}
|
|
945
959
|
const stdout = await new Response(proc.stdout).text();
|
|
946
960
|
const stderr = await new Response(proc.stderr).text();
|
|
947
961
|
const output = [stdout, stderr].filter(Boolean).join("\n").trim();
|
|
@@ -949,6 +963,9 @@ var runCommand = createGadget({
|
|
|
949
963
|
|
|
950
964
|
${output || "(no output)"}`;
|
|
951
965
|
} catch (error) {
|
|
966
|
+
if (timeoutId) {
|
|
967
|
+
clearTimeout(timeoutId);
|
|
968
|
+
}
|
|
952
969
|
const message = error instanceof Error ? error.message : String(error);
|
|
953
970
|
return `status=1
|
|
954
971
|
|
|
@@ -1121,6 +1138,30 @@ async function writeLogFile(dir, filename, content) {
|
|
|
1121
1138
|
await mkdir(dir, { recursive: true });
|
|
1122
1139
|
await writeFile2(join(dir, filename), content, "utf-8");
|
|
1123
1140
|
}
|
|
1141
|
+
function formatSessionTimestamp(date = /* @__PURE__ */ new Date()) {
|
|
1142
|
+
const pad = (n) => n.toString().padStart(2, "0");
|
|
1143
|
+
const year = date.getFullYear();
|
|
1144
|
+
const month = pad(date.getMonth() + 1);
|
|
1145
|
+
const day = pad(date.getDate());
|
|
1146
|
+
const hours = pad(date.getHours());
|
|
1147
|
+
const minutes = pad(date.getMinutes());
|
|
1148
|
+
const seconds = pad(date.getSeconds());
|
|
1149
|
+
return `${year}-${month}-${day}_${hours}-${minutes}-${seconds}`;
|
|
1150
|
+
}
|
|
1151
|
+
async function createSessionDir(baseDir) {
|
|
1152
|
+
const timestamp = formatSessionTimestamp();
|
|
1153
|
+
const sessionDir = join(baseDir, timestamp);
|
|
1154
|
+
try {
|
|
1155
|
+
await mkdir(sessionDir, { recursive: true });
|
|
1156
|
+
return sessionDir;
|
|
1157
|
+
} catch (error) {
|
|
1158
|
+
console.warn(`[llmist] Failed to create log session directory: ${sessionDir}`, error);
|
|
1159
|
+
return void 0;
|
|
1160
|
+
}
|
|
1161
|
+
}
|
|
1162
|
+
function formatCallNumber(n) {
|
|
1163
|
+
return n.toString().padStart(4, "0");
|
|
1164
|
+
}
|
|
1124
1165
|
|
|
1125
1166
|
// src/cli/utils.ts
|
|
1126
1167
|
init_constants();
|
|
@@ -1284,7 +1325,7 @@ function formatBytes(bytes) {
|
|
|
1284
1325
|
}
|
|
1285
1326
|
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
1286
1327
|
}
|
|
1287
|
-
function
|
|
1328
|
+
function formatGadgetSummary2(result) {
|
|
1288
1329
|
const gadgetLabel = chalk3.magenta.bold(result.gadgetName);
|
|
1289
1330
|
const timeLabel = chalk3.dim(`${Math.round(result.executionTimeMs)}ms`);
|
|
1290
1331
|
const paramsStr = formatParametersInline(result.parameters);
|
|
@@ -1369,12 +1410,21 @@ function isInteractive(stream) {
|
|
|
1369
1410
|
}
|
|
1370
1411
|
var ESC_KEY = 27;
|
|
1371
1412
|
var ESC_TIMEOUT_MS = 50;
|
|
1372
|
-
|
|
1413
|
+
var CTRL_C = 3;
|
|
1414
|
+
function createEscKeyListener(stdin, onEsc, onCtrlC) {
|
|
1373
1415
|
if (!stdin.isTTY || typeof stdin.setRawMode !== "function") {
|
|
1374
1416
|
return null;
|
|
1375
1417
|
}
|
|
1376
1418
|
let escTimeout = null;
|
|
1377
1419
|
const handleData = (data) => {
|
|
1420
|
+
if (data[0] === CTRL_C && onCtrlC) {
|
|
1421
|
+
if (escTimeout) {
|
|
1422
|
+
clearTimeout(escTimeout);
|
|
1423
|
+
escTimeout = null;
|
|
1424
|
+
}
|
|
1425
|
+
onCtrlC();
|
|
1426
|
+
return;
|
|
1427
|
+
}
|
|
1378
1428
|
if (data[0] === ESC_KEY) {
|
|
1379
1429
|
if (data.length === 1) {
|
|
1380
1430
|
escTimeout = setTimeout(() => {
|
|
@@ -1822,7 +1872,7 @@ function addCompleteOptions(cmd, defaults) {
|
|
|
1822
1872
|
OPTION_DESCRIPTIONS.maxTokens,
|
|
1823
1873
|
createNumericParser({ label: "Max tokens", integer: true, min: 1 }),
|
|
1824
1874
|
defaults?.["max-tokens"]
|
|
1825
|
-
).option(OPTION_FLAGS.quiet, OPTION_DESCRIPTIONS.quiet, defaults?.quiet).option(OPTION_FLAGS.logLlmRequests, OPTION_DESCRIPTIONS.logLlmRequests, defaults?.["log-llm-requests"])
|
|
1875
|
+
).option(OPTION_FLAGS.quiet, OPTION_DESCRIPTIONS.quiet, defaults?.quiet).option(OPTION_FLAGS.logLlmRequests, OPTION_DESCRIPTIONS.logLlmRequests, defaults?.["log-llm-requests"]);
|
|
1826
1876
|
}
|
|
1827
1877
|
function addAgentOptions(cmd, defaults) {
|
|
1828
1878
|
const gadgetAccumulator = (value, previous = []) => [
|
|
@@ -1846,7 +1896,7 @@ function addAgentOptions(cmd, defaults) {
|
|
|
1846
1896
|
OPTION_FLAGS.noBuiltinInteraction,
|
|
1847
1897
|
OPTION_DESCRIPTIONS.noBuiltinInteraction,
|
|
1848
1898
|
defaults?.["builtin-interaction"] !== false
|
|
1849
|
-
).option(OPTION_FLAGS.quiet, OPTION_DESCRIPTIONS.quiet, defaults?.quiet).option(OPTION_FLAGS.logLlmRequests, OPTION_DESCRIPTIONS.logLlmRequests, defaults?.["log-llm-requests"]).option(OPTION_FLAGS.
|
|
1899
|
+
).option(OPTION_FLAGS.quiet, OPTION_DESCRIPTIONS.quiet, defaults?.quiet).option(OPTION_FLAGS.logLlmRequests, OPTION_DESCRIPTIONS.logLlmRequests, defaults?.["log-llm-requests"]).option(OPTION_FLAGS.docker, OPTION_DESCRIPTIONS.docker).option(OPTION_FLAGS.dockerRo, OPTION_DESCRIPTIONS.dockerRo).option(OPTION_FLAGS.noDocker, OPTION_DESCRIPTIONS.noDocker).option(OPTION_FLAGS.dockerDev, OPTION_DESCRIPTIONS.dockerDev);
|
|
1850
1900
|
}
|
|
1851
1901
|
function configToCompleteOptions(config) {
|
|
1852
1902
|
const result = {};
|
|
@@ -1856,7 +1906,6 @@ function configToCompleteOptions(config) {
|
|
|
1856
1906
|
if (config["max-tokens"] !== void 0) result.maxTokens = config["max-tokens"];
|
|
1857
1907
|
if (config.quiet !== void 0) result.quiet = config.quiet;
|
|
1858
1908
|
if (config["log-llm-requests"] !== void 0) result.logLlmRequests = config["log-llm-requests"];
|
|
1859
|
-
if (config["log-llm-responses"] !== void 0) result.logLlmResponses = config["log-llm-responses"];
|
|
1860
1909
|
return result;
|
|
1861
1910
|
}
|
|
1862
1911
|
function configToAgentOptions(config) {
|
|
@@ -1880,7 +1929,6 @@ function configToAgentOptions(config) {
|
|
|
1880
1929
|
result.gadgetApproval = config["gadget-approval"];
|
|
1881
1930
|
if (config.quiet !== void 0) result.quiet = config.quiet;
|
|
1882
1931
|
if (config["log-llm-requests"] !== void 0) result.logLlmRequests = config["log-llm-requests"];
|
|
1883
|
-
if (config["log-llm-responses"] !== void 0) result.logLlmResponses = config["log-llm-responses"];
|
|
1884
1932
|
if (config.docker !== void 0) result.docker = config.docker;
|
|
1885
1933
|
if (config["docker-cwd-permission"] !== void 0)
|
|
1886
1934
|
result.dockerCwdPermission = config["docker-cwd-permission"];
|
|
@@ -1898,7 +1946,8 @@ var DOCKER_CONFIG_KEYS = /* @__PURE__ */ new Set([
|
|
|
1898
1946
|
"env-vars",
|
|
1899
1947
|
"image-name",
|
|
1900
1948
|
"dev-mode",
|
|
1901
|
-
"dev-source"
|
|
1949
|
+
"dev-source",
|
|
1950
|
+
"docker-args"
|
|
1902
1951
|
]);
|
|
1903
1952
|
var DEFAULT_IMAGE_NAME = "llmist-sandbox";
|
|
1904
1953
|
var DEFAULT_CWD_PERMISSION = "rw";
|
|
@@ -2013,7 +2062,6 @@ var COMPLETE_CONFIG_KEYS = /* @__PURE__ */ new Set([
|
|
|
2013
2062
|
"log-file",
|
|
2014
2063
|
"log-reset",
|
|
2015
2064
|
"log-llm-requests",
|
|
2016
|
-
"log-llm-responses",
|
|
2017
2065
|
"type",
|
|
2018
2066
|
// Allowed for inheritance compatibility, ignored for built-in commands
|
|
2019
2067
|
"docker",
|
|
@@ -2046,7 +2094,6 @@ var AGENT_CONFIG_KEYS = /* @__PURE__ */ new Set([
|
|
|
2046
2094
|
"log-file",
|
|
2047
2095
|
"log-reset",
|
|
2048
2096
|
"log-llm-requests",
|
|
2049
|
-
"log-llm-responses",
|
|
2050
2097
|
"type",
|
|
2051
2098
|
// Allowed for inheritance compatibility, ignored for built-in commands
|
|
2052
2099
|
"docker",
|
|
@@ -2234,13 +2281,6 @@ function validateCompleteConfig(raw, section) {
|
|
|
2234
2281
|
section
|
|
2235
2282
|
);
|
|
2236
2283
|
}
|
|
2237
|
-
if ("log-llm-responses" in rawObj) {
|
|
2238
|
-
result["log-llm-responses"] = validateStringOrBoolean(
|
|
2239
|
-
rawObj["log-llm-responses"],
|
|
2240
|
-
"log-llm-responses",
|
|
2241
|
-
section
|
|
2242
|
-
);
|
|
2243
|
-
}
|
|
2244
2284
|
return result;
|
|
2245
2285
|
}
|
|
2246
2286
|
function validateAgentConfig(raw, section) {
|
|
@@ -2319,13 +2359,6 @@ function validateAgentConfig(raw, section) {
|
|
|
2319
2359
|
section
|
|
2320
2360
|
);
|
|
2321
2361
|
}
|
|
2322
|
-
if ("log-llm-responses" in rawObj) {
|
|
2323
|
-
result["log-llm-responses"] = validateStringOrBoolean(
|
|
2324
|
-
rawObj["log-llm-responses"],
|
|
2325
|
-
"log-llm-responses",
|
|
2326
|
-
section
|
|
2327
|
-
);
|
|
2328
|
-
}
|
|
2329
2362
|
return result;
|
|
2330
2363
|
}
|
|
2331
2364
|
function validateStringOrBoolean(value, field, section) {
|
|
@@ -2767,6 +2800,9 @@ function validateDockerConfig(raw, section) {
|
|
|
2767
2800
|
if ("dev-source" in rawObj) {
|
|
2768
2801
|
result["dev-source"] = validateString2(rawObj["dev-source"], "dev-source", section);
|
|
2769
2802
|
}
|
|
2803
|
+
if ("docker-args" in rawObj) {
|
|
2804
|
+
result["docker-args"] = validateStringArray2(rawObj["docker-args"], "docker-args", section);
|
|
2805
|
+
}
|
|
2770
2806
|
return result;
|
|
2771
2807
|
}
|
|
2772
2808
|
|
|
@@ -2778,6 +2814,8 @@ FROM oven/bun:1-debian
|
|
|
2778
2814
|
|
|
2779
2815
|
# Install essential tools
|
|
2780
2816
|
RUN apt-get update && apt-get install -y --no-install-recommends \\
|
|
2817
|
+
# ed for EditFile gadget (line-oriented editor)
|
|
2818
|
+
ed \\
|
|
2781
2819
|
# ripgrep for fast file searching
|
|
2782
2820
|
ripgrep \\
|
|
2783
2821
|
# git for version control operations
|
|
@@ -2810,6 +2848,7 @@ FROM oven/bun:1-debian
|
|
|
2810
2848
|
|
|
2811
2849
|
# Install essential tools (same as production)
|
|
2812
2850
|
RUN apt-get update && apt-get install -y --no-install-recommends \\
|
|
2851
|
+
ed \\
|
|
2813
2852
|
ripgrep \\
|
|
2814
2853
|
git \\
|
|
2815
2854
|
curl \\
|
|
@@ -3044,6 +3083,9 @@ function buildDockerRunArgs(ctx, imageName, devMode) {
|
|
|
3044
3083
|
}
|
|
3045
3084
|
}
|
|
3046
3085
|
}
|
|
3086
|
+
if (ctx.config["docker-args"]) {
|
|
3087
|
+
args.push(...ctx.config["docker-args"]);
|
|
3088
|
+
}
|
|
3047
3089
|
args.push(imageName);
|
|
3048
3090
|
args.push(...ctx.forwardArgs);
|
|
3049
3091
|
return args;
|
|
@@ -3210,6 +3252,8 @@ async function executeAgent(promptArg, options, env) {
|
|
|
3210
3252
|
env.stderr.write(chalk5.yellow(`
|
|
3211
3253
|
[Cancelled] ${progress.formatStats()}
|
|
3212
3254
|
`));
|
|
3255
|
+
} else {
|
|
3256
|
+
handleQuit();
|
|
3213
3257
|
}
|
|
3214
3258
|
};
|
|
3215
3259
|
const keyboard = {
|
|
@@ -3217,7 +3261,7 @@ async function executeAgent(promptArg, options, env) {
|
|
|
3217
3261
|
cleanupSigint: null,
|
|
3218
3262
|
restore: () => {
|
|
3219
3263
|
if (stdinIsInteractive && stdinStream.isTTY && !wasCancelled) {
|
|
3220
|
-
keyboard.cleanupEsc = createEscKeyListener(stdinStream, handleCancel);
|
|
3264
|
+
keyboard.cleanupEsc = createEscKeyListener(stdinStream, handleCancel, handleCancel);
|
|
3221
3265
|
}
|
|
3222
3266
|
}
|
|
3223
3267
|
};
|
|
@@ -3242,7 +3286,7 @@ async function executeAgent(promptArg, options, env) {
|
|
|
3242
3286
|
process.exit(130);
|
|
3243
3287
|
};
|
|
3244
3288
|
if (stdinIsInteractive && stdinStream.isTTY) {
|
|
3245
|
-
keyboard.cleanupEsc = createEscKeyListener(stdinStream, handleCancel);
|
|
3289
|
+
keyboard.cleanupEsc = createEscKeyListener(stdinStream, handleCancel, handleCancel);
|
|
3246
3290
|
}
|
|
3247
3291
|
keyboard.cleanupSigint = createSigintListener(
|
|
3248
3292
|
handleCancel,
|
|
@@ -3268,11 +3312,11 @@ async function executeAgent(promptArg, options, env) {
|
|
|
3268
3312
|
gadgetApprovals,
|
|
3269
3313
|
defaultMode: "allowed"
|
|
3270
3314
|
};
|
|
3271
|
-
const approvalManager = new ApprovalManager(approvalConfig, env, progress);
|
|
3315
|
+
const approvalManager = new ApprovalManager(approvalConfig, env, progress, keyboard);
|
|
3272
3316
|
let usage;
|
|
3273
3317
|
let iterations = 0;
|
|
3274
|
-
const
|
|
3275
|
-
|
|
3318
|
+
const llmLogsBaseDir = resolveLogDir(options.logLlmRequests, "requests");
|
|
3319
|
+
let llmSessionDir;
|
|
3276
3320
|
let llmCallCounter = 0;
|
|
3277
3321
|
const countMessagesTokens = async (model, messages) => {
|
|
3278
3322
|
try {
|
|
@@ -3304,10 +3348,19 @@ async function executeAgent(promptArg, options, env) {
|
|
|
3304
3348
|
);
|
|
3305
3349
|
progress.startCall(context.options.model, inputTokens);
|
|
3306
3350
|
progress.setInputTokens(inputTokens, false);
|
|
3307
|
-
|
|
3308
|
-
|
|
3309
|
-
|
|
3310
|
-
|
|
3351
|
+
},
|
|
3352
|
+
// onLLMCallReady: Log the exact request being sent to the LLM
|
|
3353
|
+
// This fires AFTER controller modifications (e.g., trailing messages)
|
|
3354
|
+
onLLMCallReady: async (context) => {
|
|
3355
|
+
if (llmLogsBaseDir) {
|
|
3356
|
+
if (!llmSessionDir) {
|
|
3357
|
+
llmSessionDir = await createSessionDir(llmLogsBaseDir);
|
|
3358
|
+
}
|
|
3359
|
+
if (llmSessionDir) {
|
|
3360
|
+
const filename = `${formatCallNumber(llmCallCounter)}.request`;
|
|
3361
|
+
const content = formatLlmRequest(context.options.messages);
|
|
3362
|
+
await writeLogFile(llmSessionDir, filename, content);
|
|
3363
|
+
}
|
|
3311
3364
|
}
|
|
3312
3365
|
},
|
|
3313
3366
|
// onStreamChunk: Real-time updates as LLM generates tokens
|
|
@@ -3372,9 +3425,9 @@ async function executeAgent(promptArg, options, env) {
|
|
|
3372
3425
|
`);
|
|
3373
3426
|
}
|
|
3374
3427
|
}
|
|
3375
|
-
if (
|
|
3376
|
-
const filename = `${
|
|
3377
|
-
await writeLogFile(
|
|
3428
|
+
if (llmSessionDir) {
|
|
3429
|
+
const filename = `${formatCallNumber(llmCallCounter)}.response`;
|
|
3430
|
+
await writeLogFile(llmSessionDir, filename, context.rawResponse);
|
|
3378
3431
|
}
|
|
3379
3432
|
}
|
|
3380
3433
|
},
|
|
@@ -3471,6 +3524,13 @@ Denied: ${result.reason ?? "by user"}`
|
|
|
3471
3524
|
parameterMapping: (text) => ({ message: text, done: false, type: "info" }),
|
|
3472
3525
|
resultMapping: (text) => `\u2139\uFE0F ${text}`
|
|
3473
3526
|
});
|
|
3527
|
+
builder.withTrailingMessage(
|
|
3528
|
+
(ctx) => [
|
|
3529
|
+
`[Iteration ${ctx.iteration + 1}/${ctx.maxIterations}]`,
|
|
3530
|
+
"Think carefully: what gadget invocations can you make in parallel right now?",
|
|
3531
|
+
"Maximize efficiency by batching independent operations in a single response."
|
|
3532
|
+
].join(" ")
|
|
3533
|
+
);
|
|
3474
3534
|
const agent = builder.ask(prompt);
|
|
3475
3535
|
let textBuffer = "";
|
|
3476
3536
|
const flushTextBuffer = () => {
|
|
@@ -3496,7 +3556,7 @@ Denied: ${result.reason ?? "by user"}`
|
|
|
3496
3556
|
}
|
|
3497
3557
|
} else {
|
|
3498
3558
|
const tokenCount = await countGadgetOutputTokens(event.result.result);
|
|
3499
|
-
env.stderr.write(`${
|
|
3559
|
+
env.stderr.write(`${formatGadgetSummary2({ ...event.result, tokenCount })}
|
|
3500
3560
|
`);
|
|
3501
3561
|
}
|
|
3502
3562
|
}
|
|
@@ -3508,7 +3568,10 @@ Denied: ${result.reason ?? "by user"}`
|
|
|
3508
3568
|
} finally {
|
|
3509
3569
|
isStreaming = false;
|
|
3510
3570
|
keyboard.cleanupEsc?.();
|
|
3511
|
-
keyboard.cleanupSigint
|
|
3571
|
+
if (keyboard.cleanupSigint) {
|
|
3572
|
+
keyboard.cleanupSigint();
|
|
3573
|
+
process.once("SIGINT", () => process.exit(130));
|
|
3574
|
+
}
|
|
3512
3575
|
}
|
|
3513
3576
|
flushTextBuffer();
|
|
3514
3577
|
progress.complete();
|
|
@@ -3556,13 +3619,15 @@ async function executeComplete(promptArg, options, env) {
|
|
|
3556
3619
|
}
|
|
3557
3620
|
builder.addUser(prompt);
|
|
3558
3621
|
const messages = builder.build();
|
|
3559
|
-
const
|
|
3560
|
-
|
|
3561
|
-
|
|
3562
|
-
|
|
3563
|
-
|
|
3564
|
-
|
|
3565
|
-
|
|
3622
|
+
const llmLogsBaseDir = resolveLogDir(options.logLlmRequests, "requests");
|
|
3623
|
+
let llmSessionDir;
|
|
3624
|
+
if (llmLogsBaseDir) {
|
|
3625
|
+
llmSessionDir = await createSessionDir(llmLogsBaseDir);
|
|
3626
|
+
if (llmSessionDir) {
|
|
3627
|
+
const filename = "0001.request";
|
|
3628
|
+
const content = formatLlmRequest(messages);
|
|
3629
|
+
await writeLogFile(llmSessionDir, filename, content);
|
|
3630
|
+
}
|
|
3566
3631
|
}
|
|
3567
3632
|
const stream = client.stream({
|
|
3568
3633
|
model,
|
|
@@ -3601,9 +3666,9 @@ async function executeComplete(promptArg, options, env) {
|
|
|
3601
3666
|
progress.endCall(usage);
|
|
3602
3667
|
progress.complete();
|
|
3603
3668
|
printer.ensureNewline();
|
|
3604
|
-
if (
|
|
3605
|
-
const filename =
|
|
3606
|
-
await writeLogFile(
|
|
3669
|
+
if (llmSessionDir) {
|
|
3670
|
+
const filename = "0001.response";
|
|
3671
|
+
await writeLogFile(llmSessionDir, filename, accumulatedResponse);
|
|
3607
3672
|
}
|
|
3608
3673
|
if (stderrTTY && !options.quiet) {
|
|
3609
3674
|
const summary = renderSummary({ finishReason, usage, cost: progress.getTotalCost() });
|