llmist 3.1.0 → 5.0.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-JCFPJMRQ.js → chunk-3SZIQI45.js} +148 -36
- package/dist/chunk-3SZIQI45.js.map +1 -0
- package/dist/{chunk-LFI4WQVV.js → chunk-UBPZUVIN.js} +2 -2
- package/dist/chunk-UBPZUVIN.js.map +1 -0
- package/dist/cli.cjs +545 -280
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +371 -218
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +147 -35
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +7 -7
- package/dist/index.d.ts +7 -7
- package/dist/index.js +2 -2
- package/dist/{mock-stream-CTLm00_q.d.cts → mock-stream-CAY53Q6u.d.cts} +106 -25
- package/dist/{mock-stream-CTLm00_q.d.ts → mock-stream-CAY53Q6u.d.ts} +106 -25
- package/dist/testing/index.cjs +147 -35
- 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 +3 -2
- package/dist/chunk-JCFPJMRQ.js.map +0 -1
- package/dist/chunk-LFI4WQVV.js.map +0 -1
package/dist/cli.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import "./chunk-
|
|
2
|
+
import "./chunk-UBPZUVIN.js";
|
|
3
3
|
import {
|
|
4
4
|
AbstractGadget,
|
|
5
5
|
AgentBuilder,
|
|
@@ -34,7 +34,7 @@ import {
|
|
|
34
34
|
schemaToJSONSchema,
|
|
35
35
|
text,
|
|
36
36
|
validateGadgetSchema
|
|
37
|
-
} from "./chunk-
|
|
37
|
+
} from "./chunk-3SZIQI45.js";
|
|
38
38
|
|
|
39
39
|
// src/cli/constants.ts
|
|
40
40
|
var CLI_NAME = "llmist";
|
|
@@ -68,7 +68,6 @@ var OPTION_FLAGS = {
|
|
|
68
68
|
docker: "--docker",
|
|
69
69
|
dockerRo: "--docker-ro",
|
|
70
70
|
noDocker: "--no-docker",
|
|
71
|
-
dockerDev: "--docker-dev",
|
|
72
71
|
// Multimodal input options
|
|
73
72
|
inputImage: "--image <path>",
|
|
74
73
|
inputAudio: "--audio <path>",
|
|
@@ -103,7 +102,6 @@ var OPTION_DESCRIPTIONS = {
|
|
|
103
102
|
docker: "Run agent in a Docker sandbox container for security isolation.",
|
|
104
103
|
dockerRo: "Run in Docker with current directory mounted read-only.",
|
|
105
104
|
noDocker: "Disable Docker sandboxing (override config).",
|
|
106
|
-
dockerDev: "Run in Docker dev mode (mount local source instead of npm install).",
|
|
107
105
|
// Image generation descriptions
|
|
108
106
|
imageSize: "Image size/aspect ratio, e.g. '1024x1024', '1:1', '16:9'.",
|
|
109
107
|
imageQuality: "Image quality: 'standard', 'hd', 'low', 'medium', 'high'.",
|
|
@@ -123,7 +121,7 @@ import { Command, InvalidArgumentError as InvalidArgumentError2 } from "commande
|
|
|
123
121
|
// package.json
|
|
124
122
|
var package_default = {
|
|
125
123
|
name: "llmist",
|
|
126
|
-
version: "
|
|
124
|
+
version: "5.0.0",
|
|
127
125
|
description: "TypeScript LLM client with streaming tool execution. Tools fire mid-stream. Built-in function calling works with any model\u2014no structured outputs or native tool support required.",
|
|
128
126
|
type: "module",
|
|
129
127
|
main: "dist/index.cjs",
|
|
@@ -166,7 +164,8 @@ var package_default = {
|
|
|
166
164
|
"test:all": "bun run test && bun run test:e2e",
|
|
167
165
|
clean: "rimraf dist",
|
|
168
166
|
prepare: "node scripts/install-hooks.js || true",
|
|
169
|
-
"release:dry": "bunx semantic-release --dry-run"
|
|
167
|
+
"release:dry": "bunx semantic-release --dry-run",
|
|
168
|
+
"release:publish": 'test "$(git branch --show-current)" = "main" && git pull origin main && bun run build && npm publish'
|
|
170
169
|
},
|
|
171
170
|
bin: {
|
|
172
171
|
llmist: "dist/cli.js"
|
|
@@ -1525,20 +1524,17 @@ var DOCKER_CONFIG_KEYS = /* @__PURE__ */ new Set([
|
|
|
1525
1524
|
"mounts",
|
|
1526
1525
|
"env-vars",
|
|
1527
1526
|
"image-name",
|
|
1528
|
-
"dev-mode",
|
|
1529
|
-
"dev-source",
|
|
1530
1527
|
"docker-args"
|
|
1531
1528
|
]);
|
|
1532
1529
|
var DEFAULT_IMAGE_NAME = "llmist-sandbox";
|
|
1533
1530
|
var DEFAULT_CWD_PERMISSION = "rw";
|
|
1534
1531
|
var DEFAULT_CONFIG_PERMISSION = "ro";
|
|
1532
|
+
var GADGET_CACHE_VOLUME = "llmist-gadget-cache";
|
|
1535
1533
|
var FORWARDED_API_KEYS = [
|
|
1536
1534
|
"ANTHROPIC_API_KEY",
|
|
1537
1535
|
"OPENAI_API_KEY",
|
|
1538
1536
|
"GEMINI_API_KEY"
|
|
1539
1537
|
];
|
|
1540
|
-
var DEV_IMAGE_NAME = "llmist-dev-sandbox";
|
|
1541
|
-
var DEV_SOURCE_MOUNT_TARGET = "/llmist-src";
|
|
1542
1538
|
|
|
1543
1539
|
// src/cli/docker/docker-config.ts
|
|
1544
1540
|
var MOUNT_CONFIG_KEYS = /* @__PURE__ */ new Set(["source", "target", "permission"]);
|
|
@@ -1650,12 +1646,6 @@ function validateDockerConfig(raw, section) {
|
|
|
1650
1646
|
if ("image-name" in rawObj) {
|
|
1651
1647
|
result["image-name"] = validateString2(rawObj["image-name"], "image-name", section);
|
|
1652
1648
|
}
|
|
1653
|
-
if ("dev-mode" in rawObj) {
|
|
1654
|
-
result["dev-mode"] = validateBoolean2(rawObj["dev-mode"], "dev-mode", section);
|
|
1655
|
-
}
|
|
1656
|
-
if ("dev-source" in rawObj) {
|
|
1657
|
-
result["dev-source"] = validateString2(rawObj["dev-source"], "dev-source", section);
|
|
1658
|
-
}
|
|
1659
1649
|
if ("docker-args" in rawObj) {
|
|
1660
1650
|
result["docker-args"] = validateStringArray2(rawObj["docker-args"], "docker-args", section);
|
|
1661
1651
|
}
|
|
@@ -1665,7 +1655,6 @@ function validateDockerConfig(raw, section) {
|
|
|
1665
1655
|
// src/cli/docker/docker-wrapper.ts
|
|
1666
1656
|
import { existsSync as existsSync4, readFileSync as readFileSync4 } from "node:fs";
|
|
1667
1657
|
import { homedir as homedir3 } from "node:os";
|
|
1668
|
-
import { dirname, join as join3 } from "node:path";
|
|
1669
1658
|
|
|
1670
1659
|
// src/cli/docker/dockerfile.ts
|
|
1671
1660
|
var DEFAULT_DOCKERFILE = `# llmist sandbox image
|
|
@@ -1685,6 +1674,10 @@ RUN apt-get update && apt-get install -y --no-install-recommends \\
|
|
|
1685
1674
|
curl \\
|
|
1686
1675
|
# ca-certificates for HTTPS
|
|
1687
1676
|
ca-certificates \\
|
|
1677
|
+
# python3 for native module compilation (node-gyp)
|
|
1678
|
+
python3 \\
|
|
1679
|
+
# build-essential for compiling native modules
|
|
1680
|
+
build-essential \\
|
|
1688
1681
|
&& rm -rf /var/lib/apt/lists/*
|
|
1689
1682
|
|
|
1690
1683
|
# Install ast-grep for code search/refactoring
|
|
@@ -1702,37 +1695,8 @@ WORKDIR /workspace
|
|
|
1702
1695
|
# Entry point - llmist with all arguments forwarded
|
|
1703
1696
|
ENTRYPOINT ["llmist"]
|
|
1704
1697
|
`;
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
FROM oven/bun:1-debian
|
|
1709
|
-
|
|
1710
|
-
# Install essential tools (same as production)
|
|
1711
|
-
RUN apt-get update && apt-get install -y --no-install-recommends \\
|
|
1712
|
-
ed \\
|
|
1713
|
-
ripgrep \\
|
|
1714
|
-
git \\
|
|
1715
|
-
curl \\
|
|
1716
|
-
ca-certificates \\
|
|
1717
|
-
&& rm -rf /var/lib/apt/lists/*
|
|
1718
|
-
|
|
1719
|
-
# Install ast-grep for code search/refactoring
|
|
1720
|
-
RUN curl -fsSL https://raw.githubusercontent.com/ast-grep/ast-grep/main/install.sh | bash \\
|
|
1721
|
-
&& mv /root/.local/bin/ast-grep /usr/local/bin/ 2>/dev/null || true \\
|
|
1722
|
-
&& mv /root/.local/bin/sg /usr/local/bin/ 2>/dev/null || true
|
|
1723
|
-
|
|
1724
|
-
# Working directory (host CWD will be mounted here)
|
|
1725
|
-
WORKDIR /workspace
|
|
1726
|
-
|
|
1727
|
-
# Entry point - run llmist from mounted source
|
|
1728
|
-
# Source is mounted at ${DEV_SOURCE_MOUNT_TARGET}
|
|
1729
|
-
ENTRYPOINT ["bun", "run", "${DEV_SOURCE_MOUNT_TARGET}/src/cli.ts"]
|
|
1730
|
-
`;
|
|
1731
|
-
function resolveDockerfile(config, devMode = false) {
|
|
1732
|
-
if (config.dockerfile) {
|
|
1733
|
-
return config.dockerfile;
|
|
1734
|
-
}
|
|
1735
|
-
return devMode ? DEV_DOCKERFILE : DEFAULT_DOCKERFILE;
|
|
1698
|
+
function resolveDockerfile(config) {
|
|
1699
|
+
return config.dockerfile ?? DEFAULT_DOCKERFILE;
|
|
1736
1700
|
}
|
|
1737
1701
|
function computeDockerfileHash(dockerfile) {
|
|
1738
1702
|
const encoder = new TextEncoder();
|
|
@@ -1794,10 +1758,13 @@ async function buildImage(imageName, dockerfile) {
|
|
|
1794
1758
|
ensureCacheDir();
|
|
1795
1759
|
const dockerfilePath = join2(CACHE_DIR, "Dockerfile");
|
|
1796
1760
|
writeFileSync(dockerfilePath, dockerfile);
|
|
1797
|
-
const proc = Bun.spawn(
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1761
|
+
const proc = Bun.spawn(
|
|
1762
|
+
["docker", "build", "--no-cache", "-t", imageName, "-f", dockerfilePath, CACHE_DIR],
|
|
1763
|
+
{
|
|
1764
|
+
stdout: "pipe",
|
|
1765
|
+
stderr: "pipe"
|
|
1766
|
+
}
|
|
1767
|
+
);
|
|
1801
1768
|
const exitCode = await proc.exited;
|
|
1802
1769
|
const stdout = await new Response(proc.stdout).text();
|
|
1803
1770
|
const stderr = await new Response(proc.stderr).text();
|
|
@@ -1859,46 +1826,13 @@ function isInsideContainer() {
|
|
|
1859
1826
|
}
|
|
1860
1827
|
return false;
|
|
1861
1828
|
}
|
|
1862
|
-
function autoDetectDevSource() {
|
|
1863
|
-
const scriptPath = process.argv[1];
|
|
1864
|
-
if (!scriptPath || !scriptPath.endsWith("src/cli.ts")) {
|
|
1865
|
-
return void 0;
|
|
1866
|
-
}
|
|
1867
|
-
const srcDir = dirname(scriptPath);
|
|
1868
|
-
const projectDir = dirname(srcDir);
|
|
1869
|
-
const packageJsonPath = join3(projectDir, "package.json");
|
|
1870
|
-
if (!existsSync4(packageJsonPath)) {
|
|
1871
|
-
return void 0;
|
|
1872
|
-
}
|
|
1873
|
-
try {
|
|
1874
|
-
const pkg = JSON.parse(readFileSync4(packageJsonPath, "utf-8"));
|
|
1875
|
-
if (pkg.name === "llmist") {
|
|
1876
|
-
return projectDir;
|
|
1877
|
-
}
|
|
1878
|
-
} catch {
|
|
1879
|
-
}
|
|
1880
|
-
return void 0;
|
|
1881
|
-
}
|
|
1882
|
-
function resolveDevMode(config, cliDevMode) {
|
|
1883
|
-
const enabled = cliDevMode || config?.["dev-mode"] || process.env.LLMIST_DEV_MODE === "1";
|
|
1884
|
-
if (!enabled) {
|
|
1885
|
-
return { enabled: false, sourcePath: void 0 };
|
|
1886
|
-
}
|
|
1887
|
-
const sourcePath = config?.["dev-source"] || process.env.LLMIST_DEV_SOURCE || autoDetectDevSource();
|
|
1888
|
-
if (!sourcePath) {
|
|
1889
|
-
throw new Error(
|
|
1890
|
-
"Docker dev mode enabled but llmist source path not found. Set [docker].dev-source in config, LLMIST_DEV_SOURCE env var, or run from the llmist source directory (bun src/cli.ts)."
|
|
1891
|
-
);
|
|
1892
|
-
}
|
|
1893
|
-
return { enabled: true, sourcePath };
|
|
1894
|
-
}
|
|
1895
1829
|
function expandHome(path6) {
|
|
1896
1830
|
if (path6.startsWith("~")) {
|
|
1897
1831
|
return path6.replace(/^~/, homedir3());
|
|
1898
1832
|
}
|
|
1899
1833
|
return path6;
|
|
1900
1834
|
}
|
|
1901
|
-
function buildDockerRunArgs(ctx, imageName
|
|
1835
|
+
function buildDockerRunArgs(ctx, imageName) {
|
|
1902
1836
|
const args = ["run", "--rm"];
|
|
1903
1837
|
const timestamp = Date.now();
|
|
1904
1838
|
const random = Math.random().toString(36).slice(2, 8);
|
|
@@ -1912,11 +1846,15 @@ function buildDockerRunArgs(ctx, imageName, devMode) {
|
|
|
1912
1846
|
args.push("-w", "/workspace");
|
|
1913
1847
|
const configPermission = ctx.config["config-permission"] ?? DEFAULT_CONFIG_PERMISSION;
|
|
1914
1848
|
const llmistDir = expandHome("~/.llmist");
|
|
1915
|
-
|
|
1916
|
-
if (
|
|
1917
|
-
|
|
1918
|
-
args.push("-v", `${expandedSource}:${DEV_SOURCE_MOUNT_TARGET}:ro`);
|
|
1849
|
+
const cliTomlPath = `${llmistDir}/cli.toml`;
|
|
1850
|
+
if (existsSync4(cliTomlPath)) {
|
|
1851
|
+
args.push("-v", `${cliTomlPath}:/root/.llmist/cli.toml:${configPermission}`);
|
|
1919
1852
|
}
|
|
1853
|
+
const gadgetsDir = `${llmistDir}/gadgets`;
|
|
1854
|
+
if (existsSync4(gadgetsDir)) {
|
|
1855
|
+
args.push("-v", `${gadgetsDir}:/root/.llmist/gadgets:${configPermission}`);
|
|
1856
|
+
}
|
|
1857
|
+
args.push("-v", `${GADGET_CACHE_VOLUME}:/root/.llmist/gadget-cache`);
|
|
1920
1858
|
if (ctx.config.mounts) {
|
|
1921
1859
|
for (const mount of ctx.config.mounts) {
|
|
1922
1860
|
const source = expandHome(mount.source);
|
|
@@ -1943,7 +1881,7 @@ function buildDockerRunArgs(ctx, imageName, devMode) {
|
|
|
1943
1881
|
return args;
|
|
1944
1882
|
}
|
|
1945
1883
|
function filterDockerArgs(argv) {
|
|
1946
|
-
const dockerFlags = /* @__PURE__ */ new Set(["--docker", "--docker-ro", "--no-docker"
|
|
1884
|
+
const dockerFlags = /* @__PURE__ */ new Set(["--docker", "--docker-ro", "--no-docker"]);
|
|
1947
1885
|
return argv.filter((arg) => !dockerFlags.has(arg));
|
|
1948
1886
|
}
|
|
1949
1887
|
function resolveDockerEnabled(config, options, profileDocker) {
|
|
@@ -1958,22 +1896,16 @@ function resolveDockerEnabled(config, options, profileDocker) {
|
|
|
1958
1896
|
}
|
|
1959
1897
|
return config?.enabled ?? false;
|
|
1960
1898
|
}
|
|
1961
|
-
async function executeInDocker(ctx
|
|
1899
|
+
async function executeInDocker(ctx) {
|
|
1962
1900
|
if (isInsideContainer()) {
|
|
1963
|
-
console.error(
|
|
1964
|
-
"Warning: Docker mode requested but already inside a container. Proceeding without re-containerization."
|
|
1965
|
-
);
|
|
1966
1901
|
throw new DockerSkipError();
|
|
1967
1902
|
}
|
|
1968
1903
|
const available = await checkDockerAvailable();
|
|
1969
1904
|
if (!available) {
|
|
1970
1905
|
throw new DockerUnavailableError();
|
|
1971
1906
|
}
|
|
1972
|
-
const dockerfile = resolveDockerfile(ctx.config
|
|
1973
|
-
const imageName =
|
|
1974
|
-
if (devMode.enabled) {
|
|
1975
|
-
console.error(`[dev mode] Mounting source from ${devMode.sourcePath}`);
|
|
1976
|
-
}
|
|
1907
|
+
const dockerfile = resolveDockerfile(ctx.config);
|
|
1908
|
+
const imageName = ctx.config["image-name"] ?? DEFAULT_IMAGE_NAME;
|
|
1977
1909
|
try {
|
|
1978
1910
|
await ensureImage(imageName, dockerfile);
|
|
1979
1911
|
} catch (error) {
|
|
@@ -1984,7 +1916,7 @@ async function executeInDocker(ctx, devMode) {
|
|
|
1984
1916
|
}
|
|
1985
1917
|
throw error;
|
|
1986
1918
|
}
|
|
1987
|
-
const dockerArgs = buildDockerRunArgs(ctx, imageName
|
|
1919
|
+
const dockerArgs = buildDockerRunArgs(ctx, imageName);
|
|
1988
1920
|
const proc = Bun.spawn(["docker", ...dockerArgs], {
|
|
1989
1921
|
stdin: "inherit",
|
|
1990
1922
|
stdout: "inherit",
|
|
@@ -2592,7 +2524,7 @@ async function installNpmPackage(spec, cacheDir) {
|
|
|
2592
2524
|
fs5.writeFileSync(path4.join(cacheDir, "package.json"), JSON.stringify(packageJson, null, 2));
|
|
2593
2525
|
const packageSpec = spec.version ? `${spec.package}@${spec.version}` : spec.package;
|
|
2594
2526
|
try {
|
|
2595
|
-
execSync(`
|
|
2527
|
+
execSync(`bun add "${packageSpec}"`, {
|
|
2596
2528
|
stdio: "pipe",
|
|
2597
2529
|
cwd: cacheDir
|
|
2598
2530
|
});
|
|
@@ -2623,7 +2555,7 @@ async function installGitPackage(spec, cacheDir) {
|
|
|
2623
2555
|
}
|
|
2624
2556
|
if (fs5.existsSync(path4.join(cacheDir, "package.json"))) {
|
|
2625
2557
|
try {
|
|
2626
|
-
execSync("
|
|
2558
|
+
execSync("bun install", { cwd: cacheDir, stdio: "inherit" });
|
|
2627
2559
|
} catch (error) {
|
|
2628
2560
|
const message = error instanceof Error ? error.message : String(error);
|
|
2629
2561
|
throw new Error(`Failed to install dependencies for '${spec.package}': ${message}`);
|
|
@@ -2631,7 +2563,7 @@ async function installGitPackage(spec, cacheDir) {
|
|
|
2631
2563
|
try {
|
|
2632
2564
|
const packageJson = JSON.parse(fs5.readFileSync(path4.join(cacheDir, "package.json"), "utf-8"));
|
|
2633
2565
|
if (packageJson.scripts?.build) {
|
|
2634
|
-
execSync("
|
|
2566
|
+
execSync("bun run build", { cwd: cacheDir, stdio: "inherit" });
|
|
2635
2567
|
}
|
|
2636
2568
|
} catch (error) {
|
|
2637
2569
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -2878,11 +2810,11 @@ async function loadGadgets(specifiers, cwd, importer = (specifier) => import(spe
|
|
|
2878
2810
|
init_messages();
|
|
2879
2811
|
import { mkdir, writeFile as writeFile2 } from "node:fs/promises";
|
|
2880
2812
|
import { homedir as homedir4 } from "node:os";
|
|
2881
|
-
import { join as
|
|
2882
|
-
var DEFAULT_LLM_LOG_DIR =
|
|
2813
|
+
import { join as join3 } from "node:path";
|
|
2814
|
+
var DEFAULT_LLM_LOG_DIR = join3(homedir4(), ".llmist", "logs");
|
|
2883
2815
|
function resolveLogDir(option, subdir) {
|
|
2884
2816
|
if (option === true) {
|
|
2885
|
-
return
|
|
2817
|
+
return join3(DEFAULT_LLM_LOG_DIR, subdir);
|
|
2886
2818
|
}
|
|
2887
2819
|
if (typeof option === "string") {
|
|
2888
2820
|
return option;
|
|
@@ -2900,7 +2832,7 @@ function formatLlmRequest(messages) {
|
|
|
2900
2832
|
}
|
|
2901
2833
|
async function writeLogFile(dir, filename, content) {
|
|
2902
2834
|
await mkdir(dir, { recursive: true });
|
|
2903
|
-
await writeFile2(
|
|
2835
|
+
await writeFile2(join3(dir, filename), content, "utf-8");
|
|
2904
2836
|
}
|
|
2905
2837
|
function formatSessionTimestamp(date = /* @__PURE__ */ new Date()) {
|
|
2906
2838
|
const pad = (n) => n.toString().padStart(2, "0");
|
|
@@ -2914,7 +2846,7 @@ function formatSessionTimestamp(date = /* @__PURE__ */ new Date()) {
|
|
|
2914
2846
|
}
|
|
2915
2847
|
async function createSessionDir(baseDir) {
|
|
2916
2848
|
const timestamp = formatSessionTimestamp();
|
|
2917
|
-
const sessionDir =
|
|
2849
|
+
const sessionDir = join3(baseDir, timestamp);
|
|
2918
2850
|
try {
|
|
2919
2851
|
await mkdir(sessionDir, { recursive: true });
|
|
2920
2852
|
return sessionDir;
|
|
@@ -3007,6 +2939,45 @@ function formatCost(cost) {
|
|
|
3007
2939
|
}
|
|
3008
2940
|
return cost.toFixed(2);
|
|
3009
2941
|
}
|
|
2942
|
+
function formatLLMCallLine(info) {
|
|
2943
|
+
const parts = [];
|
|
2944
|
+
parts.push(`${chalk3.cyan(`#${info.iteration}`)} ${chalk3.magenta(info.model)}`);
|
|
2945
|
+
if (info.contextPercent !== void 0 && info.contextPercent !== null) {
|
|
2946
|
+
const formatted = `${Math.round(info.contextPercent)}%`;
|
|
2947
|
+
if (info.contextPercent >= 80) {
|
|
2948
|
+
parts.push(chalk3.red(formatted));
|
|
2949
|
+
} else if (info.contextPercent >= 50) {
|
|
2950
|
+
parts.push(chalk3.yellow(formatted));
|
|
2951
|
+
} else {
|
|
2952
|
+
parts.push(chalk3.green(formatted));
|
|
2953
|
+
}
|
|
2954
|
+
}
|
|
2955
|
+
if (info.inputTokens && info.inputTokens > 0) {
|
|
2956
|
+
const prefix = info.estimated?.input ? "~" : "";
|
|
2957
|
+
parts.push(chalk3.dim("\u2191") + chalk3.yellow(` ${prefix}${formatTokens(info.inputTokens)}`));
|
|
2958
|
+
}
|
|
2959
|
+
if (info.cachedInputTokens && info.cachedInputTokens > 0) {
|
|
2960
|
+
parts.push(chalk3.dim("\u27F3") + chalk3.blue(` ${formatTokens(info.cachedInputTokens)}`));
|
|
2961
|
+
}
|
|
2962
|
+
if (info.outputTokens !== void 0 && info.outputTokens > 0 || info.isStreaming) {
|
|
2963
|
+
const prefix = info.estimated?.output ? "~" : "";
|
|
2964
|
+
parts.push(chalk3.dim("\u2193") + chalk3.green(` ${prefix}${formatTokens(info.outputTokens ?? 0)}`));
|
|
2965
|
+
}
|
|
2966
|
+
parts.push(chalk3.dim(`${info.elapsedSeconds.toFixed(1)}s`));
|
|
2967
|
+
if (info.cost !== void 0 && info.cost > 0) {
|
|
2968
|
+
parts.push(chalk3.cyan(`$${formatCost(info.cost)}`));
|
|
2969
|
+
}
|
|
2970
|
+
if (info.isStreaming && info.spinner) {
|
|
2971
|
+
parts.push(chalk3.cyan(info.spinner));
|
|
2972
|
+
} else if (info.finishReason !== void 0) {
|
|
2973
|
+
if (!info.finishReason || info.finishReason === "stop" || info.finishReason === "end_turn") {
|
|
2974
|
+
parts.push(chalk3.green("\u2713"));
|
|
2975
|
+
} else {
|
|
2976
|
+
parts.push(chalk3.yellow(info.finishReason));
|
|
2977
|
+
}
|
|
2978
|
+
}
|
|
2979
|
+
return parts.join(chalk3.dim(" | "));
|
|
2980
|
+
}
|
|
3010
2981
|
function renderSummary(metadata) {
|
|
3011
2982
|
const parts = [];
|
|
3012
2983
|
if (metadata.iterations !== void 0) {
|
|
@@ -3063,23 +3034,84 @@ function renderOverallSummary(metadata) {
|
|
|
3063
3034
|
}
|
|
3064
3035
|
return parts.join(chalk3.dim(" | "));
|
|
3065
3036
|
}
|
|
3066
|
-
function
|
|
3037
|
+
function getRawValue(value) {
|
|
3038
|
+
if (typeof value === "string") {
|
|
3039
|
+
return value;
|
|
3040
|
+
}
|
|
3041
|
+
if (typeof value === "boolean" || typeof value === "number") {
|
|
3042
|
+
return String(value);
|
|
3043
|
+
}
|
|
3044
|
+
return JSON.stringify(value);
|
|
3045
|
+
}
|
|
3046
|
+
function truncateValue(str, maxLen) {
|
|
3047
|
+
if (maxLen <= 0) return "";
|
|
3048
|
+
if (str.length <= maxLen) return str;
|
|
3049
|
+
return `${str.slice(0, maxLen)}\u2026`;
|
|
3050
|
+
}
|
|
3051
|
+
function formatParametersInline(params, maxWidth) {
|
|
3067
3052
|
if (!params || Object.keys(params).length === 0) {
|
|
3068
3053
|
return "";
|
|
3069
3054
|
}
|
|
3070
|
-
|
|
3071
|
-
|
|
3072
|
-
|
|
3073
|
-
|
|
3074
|
-
|
|
3075
|
-
|
|
3055
|
+
const entries = Object.entries(params);
|
|
3056
|
+
const defaultLimit = 30;
|
|
3057
|
+
const rawValues = entries.map(([, value]) => getRawValue(value));
|
|
3058
|
+
const overhead = entries.reduce((sum, [key], i) => {
|
|
3059
|
+
return sum + key.length + 1 + (i > 0 ? 2 : 0);
|
|
3060
|
+
}, 0);
|
|
3061
|
+
let limits;
|
|
3062
|
+
if (maxWidth && maxWidth > overhead) {
|
|
3063
|
+
const availableForValues = maxWidth - overhead;
|
|
3064
|
+
const totalRawLength = rawValues.reduce((sum, v) => sum + v.length, 0);
|
|
3065
|
+
if (totalRawLength <= availableForValues) {
|
|
3066
|
+
limits = rawValues.map(() => Infinity);
|
|
3076
3067
|
} else {
|
|
3077
|
-
const
|
|
3078
|
-
|
|
3068
|
+
const minPerValue = 10;
|
|
3069
|
+
const minTotal = entries.length * minPerValue;
|
|
3070
|
+
if (availableForValues <= minTotal) {
|
|
3071
|
+
limits = rawValues.map(() => Math.max(1, Math.floor(availableForValues / entries.length)));
|
|
3072
|
+
} else {
|
|
3073
|
+
limits = rawValues.map((v) => {
|
|
3074
|
+
const proportion = v.length / totalRawLength;
|
|
3075
|
+
return Math.max(minPerValue, Math.floor(proportion * availableForValues));
|
|
3076
|
+
});
|
|
3077
|
+
}
|
|
3079
3078
|
}
|
|
3079
|
+
} else {
|
|
3080
|
+
limits = rawValues.map(() => defaultLimit);
|
|
3081
|
+
}
|
|
3082
|
+
return entries.map(([key, _], i) => {
|
|
3083
|
+
const formatted = truncateValue(rawValues[i], limits[i]);
|
|
3080
3084
|
return `${chalk3.dim(key)}${chalk3.dim("=")}${chalk3.cyan(formatted)}`;
|
|
3081
3085
|
}).join(chalk3.dim(", "));
|
|
3082
3086
|
}
|
|
3087
|
+
function formatGadgetLine(info, maxWidth) {
|
|
3088
|
+
const terminalWidth = maxWidth ?? process.stdout.columns ?? 80;
|
|
3089
|
+
const gadgetLabel = chalk3.magenta.bold(info.name);
|
|
3090
|
+
const timeStr = `${info.elapsedSeconds.toFixed(1)}s`;
|
|
3091
|
+
const timeLabel = chalk3.dim(timeStr);
|
|
3092
|
+
const fixedLength = 2 + info.name.length + 2 + 1 + timeStr.length;
|
|
3093
|
+
const availableForParams = Math.max(40, terminalWidth - fixedLength - 2);
|
|
3094
|
+
const paramsStr = formatParametersInline(info.parameters, availableForParams);
|
|
3095
|
+
const paramsLabel = paramsStr ? `${chalk3.dim("(")}${paramsStr}${chalk3.dim(")")}` : "";
|
|
3096
|
+
if (info.error) {
|
|
3097
|
+
const errorMsg = info.error.length > 50 ? `${info.error.slice(0, 50)}\u2026` : info.error;
|
|
3098
|
+
return `${chalk3.red("\u2717")} ${gadgetLabel}${paramsLabel} ${chalk3.red("error:")} ${errorMsg} ${timeLabel}`;
|
|
3099
|
+
}
|
|
3100
|
+
if (!info.isComplete) {
|
|
3101
|
+
return `${chalk3.blue("\u23F5")} ${gadgetLabel}${paramsLabel} ${timeLabel}`;
|
|
3102
|
+
}
|
|
3103
|
+
let outputStr;
|
|
3104
|
+
if (info.tokenCount !== void 0 && info.tokenCount > 0) {
|
|
3105
|
+
outputStr = `${formatTokens(info.tokenCount)} tokens`;
|
|
3106
|
+
} else if (info.outputBytes !== void 0 && info.outputBytes > 0) {
|
|
3107
|
+
outputStr = formatBytes(info.outputBytes);
|
|
3108
|
+
} else {
|
|
3109
|
+
outputStr = "";
|
|
3110
|
+
}
|
|
3111
|
+
const icon = info.breaksLoop ? chalk3.yellow("\u23F9") : chalk3.green("\u2713");
|
|
3112
|
+
const outputLabel = outputStr ? ` ${chalk3.dim("\u2192")} ${chalk3.green(outputStr)}` : "";
|
|
3113
|
+
return `${icon} ${gadgetLabel}${paramsLabel}${outputLabel} ${timeLabel}`;
|
|
3114
|
+
}
|
|
3083
3115
|
function formatBytes(bytes) {
|
|
3084
3116
|
if (bytes < 1024) {
|
|
3085
3117
|
return `${bytes} bytes`;
|
|
@@ -3112,25 +3144,28 @@ function formatMediaLine(media) {
|
|
|
3112
3144
|
return `${chalk3.dim("[")}${icon} ${id} ${mimeType} ${size}${chalk3.dim("]")} ${chalk3.dim("\u2192")} ${path6}`;
|
|
3113
3145
|
}
|
|
3114
3146
|
function formatGadgetSummary2(result) {
|
|
3147
|
+
const terminalWidth = process.stdout.columns || 80;
|
|
3115
3148
|
const gadgetLabel = chalk3.magenta.bold(result.gadgetName);
|
|
3116
|
-
const
|
|
3117
|
-
|
|
3118
|
-
|
|
3119
|
-
const paramsStr = formatParametersInline(result.parameters);
|
|
3120
|
-
const paramsLabel = paramsStr ? `${chalk3.dim("(")}${paramsStr}${chalk3.dim(")")}` : "";
|
|
3121
|
-
if (result.error) {
|
|
3122
|
-
const errorMsg = result.error.length > 50 ? `${result.error.slice(0, 50)}\u2026` : result.error;
|
|
3123
|
-
return `${chalk3.red("\u2717")} ${gadgetLabel}${paramsLabel} ${chalk3.red("error:")} ${errorMsg} ${timeLabel}`;
|
|
3124
|
-
}
|
|
3125
|
-
let outputLabel;
|
|
3149
|
+
const timeStr = result.executionTimeMs >= 1e3 ? `${(result.executionTimeMs / 1e3).toFixed(1)}s` : `${Math.round(result.executionTimeMs)}ms`;
|
|
3150
|
+
const timeLabel = chalk3.dim(timeStr);
|
|
3151
|
+
let outputStr;
|
|
3126
3152
|
if (result.tokenCount !== void 0 && result.tokenCount > 0) {
|
|
3127
|
-
|
|
3153
|
+
outputStr = `${formatTokens(result.tokenCount)} tokens`;
|
|
3128
3154
|
} else if (result.result) {
|
|
3129
3155
|
const outputBytes = Buffer.byteLength(result.result, "utf-8");
|
|
3130
|
-
|
|
3156
|
+
outputStr = outputBytes > 0 ? formatBytes(outputBytes) : "no output";
|
|
3131
3157
|
} else {
|
|
3132
|
-
|
|
3158
|
+
outputStr = "no output";
|
|
3133
3159
|
}
|
|
3160
|
+
const fixedLength = 2 + result.gadgetName.length + 2 + 3 + outputStr.length + 1 + timeStr.length;
|
|
3161
|
+
const availableForParams = Math.max(40, terminalWidth - fixedLength - 2);
|
|
3162
|
+
const paramsStr = formatParametersInline(result.parameters, availableForParams);
|
|
3163
|
+
const paramsLabel = paramsStr ? `${chalk3.dim("(")}${paramsStr}${chalk3.dim(")")}` : "";
|
|
3164
|
+
if (result.error) {
|
|
3165
|
+
const errorMsg = result.error.length > 50 ? `${result.error.slice(0, 50)}\u2026` : result.error;
|
|
3166
|
+
return `${chalk3.red("\u2717")} ${gadgetLabel}${paramsLabel} ${chalk3.red("error:")} ${errorMsg} ${timeLabel}`;
|
|
3167
|
+
}
|
|
3168
|
+
const outputLabel = outputStr === "no output" ? chalk3.dim(outputStr) : chalk3.green(outputStr);
|
|
3134
3169
|
const icon = result.breaksLoop ? chalk3.yellow("\u23F9") : chalk3.green("\u2713");
|
|
3135
3170
|
let summaryLine = `${icon} ${gadgetLabel}${paramsLabel} ${chalk3.dim("\u2192")} ${outputLabel} ${timeLabel}`;
|
|
3136
3171
|
if (result.media && result.media.length > 0) {
|
|
@@ -3355,11 +3390,35 @@ var StreamProgress = class {
|
|
|
3355
3390
|
}
|
|
3356
3391
|
/**
|
|
3357
3392
|
* Update a nested agent with completion info (called when nested llm_call_end event received).
|
|
3393
|
+
* Records completion time to freeze the elapsed timer.
|
|
3394
|
+
* @param info - Full LLM call info including tokens, cache details, and cost
|
|
3358
3395
|
*/
|
|
3359
|
-
updateNestedAgent(id,
|
|
3396
|
+
updateNestedAgent(id, info) {
|
|
3360
3397
|
const agent = this.nestedAgents.get(id);
|
|
3361
3398
|
if (agent) {
|
|
3362
|
-
agent.
|
|
3399
|
+
agent.inputTokens = info.inputTokens;
|
|
3400
|
+
agent.outputTokens = info.outputTokens;
|
|
3401
|
+
agent.cachedInputTokens = info.cachedInputTokens;
|
|
3402
|
+
agent.cacheCreationInputTokens = info.cacheCreationInputTokens;
|
|
3403
|
+
agent.finishReason = info.finishReason;
|
|
3404
|
+
if (info.cost !== void 0) {
|
|
3405
|
+
agent.cost = info.cost;
|
|
3406
|
+
} else if (this.modelRegistry && agent.model && info.outputTokens) {
|
|
3407
|
+
try {
|
|
3408
|
+
const modelName = agent.model.includes(":") ? agent.model.split(":")[1] : agent.model;
|
|
3409
|
+
const costResult = this.modelRegistry.estimateCost(
|
|
3410
|
+
modelName,
|
|
3411
|
+
info.inputTokens ?? 0,
|
|
3412
|
+
info.outputTokens,
|
|
3413
|
+
info.cachedInputTokens,
|
|
3414
|
+
info.cacheCreationInputTokens
|
|
3415
|
+
);
|
|
3416
|
+
agent.cost = costResult?.totalCost;
|
|
3417
|
+
} catch {
|
|
3418
|
+
}
|
|
3419
|
+
}
|
|
3420
|
+
agent.completed = true;
|
|
3421
|
+
agent.completedTime = Date.now();
|
|
3363
3422
|
if (this.isRunning && this.isTTY) {
|
|
3364
3423
|
this.render();
|
|
3365
3424
|
}
|
|
@@ -3377,11 +3436,12 @@ var StreamProgress = class {
|
|
|
3377
3436
|
/**
|
|
3378
3437
|
* Add a nested gadget call (called when nested gadget_call event received).
|
|
3379
3438
|
*/
|
|
3380
|
-
addNestedGadget(id, depth, parentInvocationId, name) {
|
|
3439
|
+
addNestedGadget(id, depth, parentInvocationId, name, parameters) {
|
|
3381
3440
|
this.nestedGadgets.set(id, {
|
|
3382
3441
|
depth,
|
|
3383
3442
|
parentInvocationId,
|
|
3384
3443
|
name,
|
|
3444
|
+
parameters,
|
|
3385
3445
|
startTime: Date.now()
|
|
3386
3446
|
});
|
|
3387
3447
|
if (this.isRunning && this.isTTY) {
|
|
@@ -3397,6 +3457,20 @@ var StreamProgress = class {
|
|
|
3397
3457
|
this.render();
|
|
3398
3458
|
}
|
|
3399
3459
|
}
|
|
3460
|
+
/**
|
|
3461
|
+
* Mark a nested gadget as completed (keeps it visible with ✓ indicator).
|
|
3462
|
+
* Records completion time to freeze the elapsed timer.
|
|
3463
|
+
*/
|
|
3464
|
+
completeNestedGadget(id) {
|
|
3465
|
+
const gadget = this.nestedGadgets.get(id);
|
|
3466
|
+
if (gadget) {
|
|
3467
|
+
gadget.completed = true;
|
|
3468
|
+
gadget.completedTime = Date.now();
|
|
3469
|
+
if (this.isRunning && this.isTTY) {
|
|
3470
|
+
this.render();
|
|
3471
|
+
}
|
|
3472
|
+
}
|
|
3473
|
+
}
|
|
3400
3474
|
/**
|
|
3401
3475
|
* Starts a new LLM call. Switches to streaming mode.
|
|
3402
3476
|
* @param model - Model name being used
|
|
@@ -3533,24 +3607,73 @@ var StreamProgress = class {
|
|
|
3533
3607
|
}
|
|
3534
3608
|
if (this.isTTY) {
|
|
3535
3609
|
for (const [gadgetId, gadget] of this.inFlightGadgets) {
|
|
3536
|
-
const
|
|
3537
|
-
const gadgetLine = ` ${
|
|
3610
|
+
const elapsedSeconds = (Date.now() - gadget.startTime) / 1e3;
|
|
3611
|
+
const gadgetLine = ` ${formatGadgetLine({
|
|
3612
|
+
name: gadget.name,
|
|
3613
|
+
parameters: gadget.params,
|
|
3614
|
+
elapsedSeconds,
|
|
3615
|
+
isComplete: false
|
|
3616
|
+
})}`;
|
|
3538
3617
|
lines.push(gadgetLine);
|
|
3618
|
+
const nestedOps = [];
|
|
3539
3619
|
for (const [_agentId, nested] of this.nestedAgents) {
|
|
3540
|
-
if (nested.parentInvocationId
|
|
3541
|
-
|
|
3542
|
-
|
|
3543
|
-
|
|
3544
|
-
|
|
3545
|
-
|
|
3546
|
-
|
|
3620
|
+
if (nested.parentInvocationId === gadgetId) {
|
|
3621
|
+
nestedOps.push({
|
|
3622
|
+
type: "agent",
|
|
3623
|
+
startTime: nested.startTime,
|
|
3624
|
+
depth: nested.depth,
|
|
3625
|
+
iteration: nested.iteration,
|
|
3626
|
+
model: nested.model,
|
|
3627
|
+
inputTokens: nested.inputTokens,
|
|
3628
|
+
cachedInputTokens: nested.cachedInputTokens,
|
|
3629
|
+
outputTokens: nested.outputTokens,
|
|
3630
|
+
cost: nested.cost,
|
|
3631
|
+
finishReason: nested.finishReason,
|
|
3632
|
+
completed: nested.completed,
|
|
3633
|
+
completedTime: nested.completedTime
|
|
3634
|
+
});
|
|
3635
|
+
}
|
|
3547
3636
|
}
|
|
3548
3637
|
for (const [_nestedId, nestedGadget] of this.nestedGadgets) {
|
|
3549
3638
|
if (nestedGadget.parentInvocationId === gadgetId) {
|
|
3550
|
-
|
|
3551
|
-
|
|
3552
|
-
|
|
3553
|
-
|
|
3639
|
+
nestedOps.push({
|
|
3640
|
+
type: "gadget",
|
|
3641
|
+
startTime: nestedGadget.startTime,
|
|
3642
|
+
depth: nestedGadget.depth,
|
|
3643
|
+
name: nestedGadget.name,
|
|
3644
|
+
parameters: nestedGadget.parameters,
|
|
3645
|
+
completed: nestedGadget.completed,
|
|
3646
|
+
completedTime: nestedGadget.completedTime
|
|
3647
|
+
});
|
|
3648
|
+
}
|
|
3649
|
+
}
|
|
3650
|
+
nestedOps.sort((a, b) => a.startTime - b.startTime);
|
|
3651
|
+
for (const op of nestedOps) {
|
|
3652
|
+
const indent = " ".repeat(op.depth + 1);
|
|
3653
|
+
const endTime = op.completedTime ?? Date.now();
|
|
3654
|
+
const elapsedSeconds2 = (endTime - op.startTime) / 1e3;
|
|
3655
|
+
if (op.type === "agent") {
|
|
3656
|
+
const line = formatLLMCallLine({
|
|
3657
|
+
iteration: op.iteration ?? 0,
|
|
3658
|
+
model: op.model ?? "",
|
|
3659
|
+
inputTokens: op.inputTokens,
|
|
3660
|
+
cachedInputTokens: op.cachedInputTokens,
|
|
3661
|
+
outputTokens: op.outputTokens,
|
|
3662
|
+
elapsedSeconds: elapsedSeconds2,
|
|
3663
|
+
cost: op.cost,
|
|
3664
|
+
finishReason: op.completed ? op.finishReason ?? "stop" : void 0,
|
|
3665
|
+
isStreaming: !op.completed,
|
|
3666
|
+
spinner
|
|
3667
|
+
});
|
|
3668
|
+
lines.push(`${indent}${line}`);
|
|
3669
|
+
} else {
|
|
3670
|
+
const line = formatGadgetLine({
|
|
3671
|
+
name: op.name ?? "",
|
|
3672
|
+
parameters: op.parameters,
|
|
3673
|
+
elapsedSeconds: elapsedSeconds2,
|
|
3674
|
+
isComplete: op.completed ?? false
|
|
3675
|
+
});
|
|
3676
|
+
lines.push(`${indent}${line}`);
|
|
3554
3677
|
}
|
|
3555
3678
|
}
|
|
3556
3679
|
}
|
|
@@ -3572,42 +3695,27 @@ var StreamProgress = class {
|
|
|
3572
3695
|
}
|
|
3573
3696
|
/**
|
|
3574
3697
|
* Format the streaming mode progress line (returns string, doesn't write).
|
|
3698
|
+
* Uses the shared formatLLMCallLine() function for consistent formatting
|
|
3699
|
+
* between main agent and nested subagent displays.
|
|
3575
3700
|
*/
|
|
3576
3701
|
formatStreamingLine(spinner) {
|
|
3577
|
-
const elapsed = ((Date.now() - this.callStartTime) / 1e3).toFixed(1);
|
|
3578
3702
|
const outTokens = this.callOutputTokensEstimated ? Math.round(this.callOutputChars / FALLBACK_CHARS_PER_TOKEN) : this.callOutputTokens;
|
|
3579
|
-
|
|
3580
|
-
|
|
3581
|
-
|
|
3582
|
-
|
|
3583
|
-
|
|
3584
|
-
|
|
3585
|
-
|
|
3586
|
-
|
|
3587
|
-
|
|
3588
|
-
|
|
3589
|
-
|
|
3590
|
-
|
|
3591
|
-
|
|
3592
|
-
|
|
3593
|
-
} else {
|
|
3594
|
-
parts.push(chalk4.green(formatted));
|
|
3703
|
+
return formatLLMCallLine({
|
|
3704
|
+
iteration: this.currentIteration,
|
|
3705
|
+
model: this.model ?? "",
|
|
3706
|
+
inputTokens: this.callInputTokens,
|
|
3707
|
+
cachedInputTokens: this.callCachedInputTokens,
|
|
3708
|
+
outputTokens: outTokens,
|
|
3709
|
+
elapsedSeconds: (Date.now() - this.callStartTime) / 1e3,
|
|
3710
|
+
cost: this.calculateCurrentCallCost(outTokens),
|
|
3711
|
+
isStreaming: true,
|
|
3712
|
+
spinner,
|
|
3713
|
+
contextPercent: this.getContextUsagePercent(),
|
|
3714
|
+
estimated: {
|
|
3715
|
+
input: this.callInputTokensEstimated,
|
|
3716
|
+
output: this.callOutputTokensEstimated
|
|
3595
3717
|
}
|
|
3596
|
-
}
|
|
3597
|
-
if (this.callInputTokens > 0) {
|
|
3598
|
-
const prefix = this.callInputTokensEstimated ? "~" : "";
|
|
3599
|
-
parts.push(chalk4.dim("\u2191") + chalk4.yellow(` ${prefix}${formatTokens(this.callInputTokens)}`));
|
|
3600
|
-
}
|
|
3601
|
-
if (this.isStreaming || outTokens > 0) {
|
|
3602
|
-
const prefix = this.callOutputTokensEstimated ? "~" : "";
|
|
3603
|
-
parts.push(chalk4.dim("\u2193") + chalk4.green(` ${prefix}${formatTokens(outTokens)}`));
|
|
3604
|
-
}
|
|
3605
|
-
parts.push(chalk4.dim(`${elapsed}s`));
|
|
3606
|
-
const callCost = this.calculateCurrentCallCost(outTokens);
|
|
3607
|
-
if (callCost > 0) {
|
|
3608
|
-
parts.push(chalk4.cyan(`$${formatCost(callCost)}`));
|
|
3609
|
-
}
|
|
3610
|
-
return `${parts.join(chalk4.dim(" | "))} ${chalk4.cyan(spinner)}`;
|
|
3718
|
+
});
|
|
3611
3719
|
}
|
|
3612
3720
|
/**
|
|
3613
3721
|
* Calculates live cost estimate for the current streaming call.
|
|
@@ -3835,7 +3943,7 @@ function addAgentOptions(cmd, defaults) {
|
|
|
3835
3943
|
OPTION_FLAGS.logLlmRequests,
|
|
3836
3944
|
OPTION_DESCRIPTIONS.logLlmRequests,
|
|
3837
3945
|
defaults?.["log-llm-requests"]
|
|
3838
|
-
).option(OPTION_FLAGS.inputImage, OPTION_DESCRIPTIONS.inputImage).option(OPTION_FLAGS.inputAudio, OPTION_DESCRIPTIONS.inputAudio).option(OPTION_FLAGS.docker, OPTION_DESCRIPTIONS.docker).option(OPTION_FLAGS.dockerRo, OPTION_DESCRIPTIONS.dockerRo).option(OPTION_FLAGS.noDocker, OPTION_DESCRIPTIONS.noDocker)
|
|
3946
|
+
).option(OPTION_FLAGS.inputImage, OPTION_DESCRIPTIONS.inputImage).option(OPTION_FLAGS.inputAudio, OPTION_DESCRIPTIONS.inputAudio).option(OPTION_FLAGS.docker, OPTION_DESCRIPTIONS.docker).option(OPTION_FLAGS.dockerRo, OPTION_DESCRIPTIONS.dockerRo).option(OPTION_FLAGS.noDocker, OPTION_DESCRIPTIONS.noDocker);
|
|
3839
3947
|
}
|
|
3840
3948
|
function configToCompleteOptions(config) {
|
|
3841
3949
|
const result = {};
|
|
@@ -3912,8 +4020,7 @@ async function executeAgent(promptArg, options, env) {
|
|
|
3912
4020
|
const dockerOptions = {
|
|
3913
4021
|
docker: options.docker ?? false,
|
|
3914
4022
|
dockerRo: options.dockerRo ?? false,
|
|
3915
|
-
noDocker: options.noDocker ?? false
|
|
3916
|
-
dockerDev: options.dockerDev ?? false
|
|
4023
|
+
noDocker: options.noDocker ?? false
|
|
3917
4024
|
};
|
|
3918
4025
|
const dockerEnabled = resolveDockerEnabled(
|
|
3919
4026
|
env.dockerConfig,
|
|
@@ -3922,7 +4029,6 @@ async function executeAgent(promptArg, options, env) {
|
|
|
3922
4029
|
// Profile-level docker: true/false
|
|
3923
4030
|
);
|
|
3924
4031
|
if (dockerEnabled) {
|
|
3925
|
-
const devMode = resolveDevMode(env.dockerConfig, dockerOptions.dockerDev);
|
|
3926
4032
|
const ctx = createDockerContext(
|
|
3927
4033
|
env.dockerConfig,
|
|
3928
4034
|
dockerOptions,
|
|
@@ -3933,7 +4039,7 @@ async function executeAgent(promptArg, options, env) {
|
|
|
3933
4039
|
// Profile-level CWD permission override
|
|
3934
4040
|
);
|
|
3935
4041
|
try {
|
|
3936
|
-
await executeInDocker(ctx
|
|
4042
|
+
await executeInDocker(ctx);
|
|
3937
4043
|
} catch (error) {
|
|
3938
4044
|
if (error instanceof DockerSkipError) {
|
|
3939
4045
|
} else {
|
|
@@ -3960,9 +4066,18 @@ async function executeAgent(promptArg, options, env) {
|
|
|
3960
4066
|
registry.registerByClass(gadget);
|
|
3961
4067
|
}
|
|
3962
4068
|
}
|
|
4069
|
+
if (!options.quiet) {
|
|
4070
|
+
const allNames = registry.getAll().map((g) => g.name).join(", ");
|
|
4071
|
+
env.stderr.write(chalk5.dim(`Gadgets: ${allNames}
|
|
4072
|
+
`));
|
|
4073
|
+
}
|
|
3963
4074
|
const printer = new StreamPrinter(env.stdout);
|
|
3964
4075
|
const stderrTTY = env.stderr.isTTY === true;
|
|
3965
|
-
const progress = new StreamProgress(
|
|
4076
|
+
const progress = new StreamProgress(
|
|
4077
|
+
env.stderr,
|
|
4078
|
+
stderrTTY,
|
|
4079
|
+
client.modelRegistry
|
|
4080
|
+
);
|
|
3966
4081
|
const abortController = new AbortController();
|
|
3967
4082
|
let wasCancelled = false;
|
|
3968
4083
|
let isStreaming = false;
|
|
@@ -3972,9 +4087,11 @@ async function executeAgent(promptArg, options, env) {
|
|
|
3972
4087
|
wasCancelled = true;
|
|
3973
4088
|
abortController.abort();
|
|
3974
4089
|
progress.pause();
|
|
3975
|
-
env.stderr.write(
|
|
4090
|
+
env.stderr.write(
|
|
4091
|
+
chalk5.yellow(`
|
|
3976
4092
|
[Cancelled] ${progress.formatStats()}
|
|
3977
|
-
`)
|
|
4093
|
+
`)
|
|
4094
|
+
);
|
|
3978
4095
|
} else {
|
|
3979
4096
|
handleQuit();
|
|
3980
4097
|
}
|
|
@@ -3984,7 +4101,11 @@ async function executeAgent(promptArg, options, env) {
|
|
|
3984
4101
|
cleanupSigint: null,
|
|
3985
4102
|
restore: () => {
|
|
3986
4103
|
if (stdinIsInteractive && stdinStream.isTTY && !wasCancelled) {
|
|
3987
|
-
keyboard.cleanupEsc = createEscKeyListener(
|
|
4104
|
+
keyboard.cleanupEsc = createEscKeyListener(
|
|
4105
|
+
stdinStream,
|
|
4106
|
+
handleCancel,
|
|
4107
|
+
handleCancel
|
|
4108
|
+
);
|
|
3988
4109
|
}
|
|
3989
4110
|
}
|
|
3990
4111
|
};
|
|
@@ -4009,7 +4130,11 @@ async function executeAgent(promptArg, options, env) {
|
|
|
4009
4130
|
process.exit(130);
|
|
4010
4131
|
};
|
|
4011
4132
|
if (stdinIsInteractive && stdinStream.isTTY) {
|
|
4012
|
-
keyboard.cleanupEsc = createEscKeyListener(
|
|
4133
|
+
keyboard.cleanupEsc = createEscKeyListener(
|
|
4134
|
+
stdinStream,
|
|
4135
|
+
handleCancel,
|
|
4136
|
+
handleCancel
|
|
4137
|
+
);
|
|
4013
4138
|
}
|
|
4014
4139
|
keyboard.cleanupSigint = createSigintListener(
|
|
4015
4140
|
handleCancel,
|
|
@@ -4035,7 +4160,12 @@ async function executeAgent(promptArg, options, env) {
|
|
|
4035
4160
|
gadgetApprovals,
|
|
4036
4161
|
defaultMode: "allowed"
|
|
4037
4162
|
};
|
|
4038
|
-
const approvalManager = new ApprovalManager(
|
|
4163
|
+
const approvalManager = new ApprovalManager(
|
|
4164
|
+
approvalConfig,
|
|
4165
|
+
env,
|
|
4166
|
+
progress,
|
|
4167
|
+
keyboard
|
|
4168
|
+
);
|
|
4039
4169
|
let usage;
|
|
4040
4170
|
let iterations = 0;
|
|
4041
4171
|
const llmLogsBaseDir = resolveLogDir(options.logLlmRequests, "requests");
|
|
@@ -4045,7 +4175,10 @@ async function executeAgent(promptArg, options, env) {
|
|
|
4045
4175
|
try {
|
|
4046
4176
|
return await client.countTokens(model, messages);
|
|
4047
4177
|
} catch {
|
|
4048
|
-
const totalChars = messages.reduce(
|
|
4178
|
+
const totalChars = messages.reduce(
|
|
4179
|
+
(sum, m) => sum + (m.content?.length ?? 0),
|
|
4180
|
+
0
|
|
4181
|
+
);
|
|
4049
4182
|
return Math.round(totalChars / FALLBACK_CHARS_PER_TOKEN);
|
|
4050
4183
|
}
|
|
4051
4184
|
};
|
|
@@ -4067,7 +4200,9 @@ async function executeAgent(promptArg, options, env) {
|
|
|
4067
4200
|
observers: {
|
|
4068
4201
|
// onLLMCallStart: Start progress indicator for each LLM call
|
|
4069
4202
|
// This showcases how to react to agent lifecycle events
|
|
4203
|
+
// Skip for subagent events (tracked separately via nested display)
|
|
4070
4204
|
onLLMCallStart: async (context) => {
|
|
4205
|
+
if (context.subagentContext) return;
|
|
4071
4206
|
isStreaming = true;
|
|
4072
4207
|
llmCallCounter++;
|
|
4073
4208
|
const inputTokens = await countMessagesTokens(
|
|
@@ -4093,7 +4228,9 @@ async function executeAgent(promptArg, options, env) {
|
|
|
4093
4228
|
},
|
|
4094
4229
|
// onStreamChunk: Real-time updates as LLM generates tokens
|
|
4095
4230
|
// This enables responsive UIs that show progress during generation
|
|
4231
|
+
// Skip for subagent events (tracked separately via nested display)
|
|
4096
4232
|
onStreamChunk: async (context) => {
|
|
4233
|
+
if (context.subagentContext) return;
|
|
4097
4234
|
progress.update(context.accumulatedText.length);
|
|
4098
4235
|
if (context.usage) {
|
|
4099
4236
|
if (context.usage.inputTokens) {
|
|
@@ -4110,7 +4247,9 @@ async function executeAgent(promptArg, options, env) {
|
|
|
4110
4247
|
},
|
|
4111
4248
|
// onLLMCallComplete: Finalize metrics after each LLM call
|
|
4112
4249
|
// This is where you'd typically log metrics or update dashboards
|
|
4250
|
+
// Skip progress updates for subagent events (tracked separately via nested display)
|
|
4113
4251
|
onLLMCallComplete: async (context) => {
|
|
4252
|
+
if (context.subagentContext) return;
|
|
4114
4253
|
isStreaming = false;
|
|
4115
4254
|
usage = context.usage;
|
|
4116
4255
|
iterations = Math.max(iterations, context.iteration + 1);
|
|
@@ -4139,7 +4278,7 @@ async function executeAgent(promptArg, options, env) {
|
|
|
4139
4278
|
}
|
|
4140
4279
|
const callElapsed = progress.getCallElapsedSeconds();
|
|
4141
4280
|
progress.endCall(context.usage);
|
|
4142
|
-
if (!options.quiet) {
|
|
4281
|
+
if (!options.quiet && !context.subagentContext) {
|
|
4143
4282
|
const summary = renderSummary({
|
|
4144
4283
|
iterations: context.iteration + 1,
|
|
4145
4284
|
model: options.model,
|
|
@@ -4197,7 +4336,10 @@ ${ctx.gadgetName} is denied by configuration.`
|
|
|
4197
4336
|
}
|
|
4198
4337
|
return { action: "proceed" };
|
|
4199
4338
|
}
|
|
4200
|
-
const result = await approvalManager.requestApproval(
|
|
4339
|
+
const result = await approvalManager.requestApproval(
|
|
4340
|
+
ctx.gadgetName,
|
|
4341
|
+
ctx.parameters
|
|
4342
|
+
);
|
|
4201
4343
|
if (!result.approved) {
|
|
4202
4344
|
return {
|
|
4203
4345
|
action: "skip",
|
|
@@ -4240,11 +4382,11 @@ Denied: ${result.reason ?? "by user"}`
|
|
|
4240
4382
|
builder.withSyntheticGadgetCall(
|
|
4241
4383
|
"TellUser",
|
|
4242
4384
|
{
|
|
4243
|
-
message: "\u{1F44B} Hello! I'm ready to help.\n\
|
|
4385
|
+
message: "\u{1F44B} Hello! I'm ready to help.\n\nWhat would you like me to work on?",
|
|
4244
4386
|
done: false,
|
|
4245
4387
|
type: "info"
|
|
4246
4388
|
},
|
|
4247
|
-
"\u2139\uFE0F \u{1F44B} Hello! I'm ready to help.\n\
|
|
4389
|
+
"\u2139\uFE0F \u{1F44B} Hello! I'm ready to help.\n\nWhat would you like me to work on?"
|
|
4248
4390
|
);
|
|
4249
4391
|
builder.withTextOnlyHandler("acknowledge");
|
|
4250
4392
|
builder.withTextWithGadgetsHandler({
|
|
@@ -4260,34 +4402,41 @@ Denied: ${result.reason ?? "by user"}`
|
|
|
4260
4402
|
].join(" ")
|
|
4261
4403
|
);
|
|
4262
4404
|
if (!options.quiet) {
|
|
4263
|
-
builder.
|
|
4264
|
-
if (
|
|
4265
|
-
const info =
|
|
4266
|
-
const
|
|
4405
|
+
builder.withSubagentEventCallback((subagentEvent) => {
|
|
4406
|
+
if (subagentEvent.type === "llm_call_start") {
|
|
4407
|
+
const info = subagentEvent.event;
|
|
4408
|
+
const subagentId = `${subagentEvent.gadgetInvocationId}:${info.iteration}`;
|
|
4267
4409
|
progress.addNestedAgent(
|
|
4268
|
-
|
|
4269
|
-
|
|
4270
|
-
|
|
4410
|
+
subagentId,
|
|
4411
|
+
subagentEvent.gadgetInvocationId,
|
|
4412
|
+
subagentEvent.depth,
|
|
4271
4413
|
info.model,
|
|
4272
4414
|
info.iteration,
|
|
4273
4415
|
info.inputTokens
|
|
4274
4416
|
);
|
|
4275
|
-
} else if (
|
|
4276
|
-
const info =
|
|
4277
|
-
const
|
|
4278
|
-
progress.updateNestedAgent(
|
|
4279
|
-
|
|
4280
|
-
|
|
4281
|
-
|
|
4417
|
+
} else if (subagentEvent.type === "llm_call_end") {
|
|
4418
|
+
const info = subagentEvent.event;
|
|
4419
|
+
const subagentId = `${subagentEvent.gadgetInvocationId}:${info.iteration}`;
|
|
4420
|
+
progress.updateNestedAgent(subagentId, {
|
|
4421
|
+
inputTokens: info.usage?.inputTokens ?? info.inputTokens,
|
|
4422
|
+
outputTokens: info.usage?.outputTokens ?? info.outputTokens,
|
|
4423
|
+
cachedInputTokens: info.usage?.cachedInputTokens,
|
|
4424
|
+
cacheCreationInputTokens: info.usage?.cacheCreationInputTokens,
|
|
4425
|
+
finishReason: info.finishReason,
|
|
4426
|
+
cost: info.cost
|
|
4427
|
+
});
|
|
4428
|
+
} else if (subagentEvent.type === "gadget_call") {
|
|
4429
|
+
const gadgetEvent = subagentEvent.event;
|
|
4282
4430
|
progress.addNestedGadget(
|
|
4283
4431
|
gadgetEvent.call.invocationId,
|
|
4284
|
-
|
|
4285
|
-
|
|
4286
|
-
gadgetEvent.call.gadgetName
|
|
4432
|
+
subagentEvent.depth,
|
|
4433
|
+
subagentEvent.gadgetInvocationId,
|
|
4434
|
+
gadgetEvent.call.gadgetName,
|
|
4435
|
+
gadgetEvent.call.parameters
|
|
4287
4436
|
);
|
|
4288
|
-
} else if (
|
|
4289
|
-
const resultEvent =
|
|
4290
|
-
progress.
|
|
4437
|
+
} else if (subagentEvent.type === "gadget_result") {
|
|
4438
|
+
const resultEvent = subagentEvent.event;
|
|
4439
|
+
progress.completeNestedGadget(resultEvent.result.invocationId);
|
|
4291
4440
|
}
|
|
4292
4441
|
});
|
|
4293
4442
|
}
|
|
@@ -4348,6 +4497,7 @@ Denied: ${result.reason ?? "by user"}`
|
|
|
4348
4497
|
if (progress.hasInFlightGadgets()) {
|
|
4349
4498
|
progress.start();
|
|
4350
4499
|
}
|
|
4500
|
+
} else if (event.type === "subagent_event") {
|
|
4351
4501
|
}
|
|
4352
4502
|
}
|
|
4353
4503
|
} catch (error) {
|
|
@@ -4381,7 +4531,10 @@ Denied: ${result.reason ?? "by user"}`
|
|
|
4381
4531
|
}
|
|
4382
4532
|
}
|
|
4383
4533
|
function registerAgentCommand(program, env, config, globalSubagents) {
|
|
4384
|
-
const cmd = program.command(COMMANDS.agent).description("Run the llmist agent loop with optional gadgets.").argument(
|
|
4534
|
+
const cmd = program.command(COMMANDS.agent).description("Run the llmist agent loop with optional gadgets.").argument(
|
|
4535
|
+
"[prompt]",
|
|
4536
|
+
"Prompt for the agent loop. Falls back to stdin when available."
|
|
4537
|
+
);
|
|
4385
4538
|
addAgentOptions(cmd, config);
|
|
4386
4539
|
cmd.action(
|
|
4387
4540
|
(prompt, options) => executeAction(() => {
|
|
@@ -4491,7 +4644,7 @@ function registerCompleteCommand(program, env, config) {
|
|
|
4491
4644
|
|
|
4492
4645
|
// src/cli/init-command.ts
|
|
4493
4646
|
import { existsSync as existsSync5, mkdirSync as mkdirSync2, writeFileSync as writeFileSync2 } from "node:fs";
|
|
4494
|
-
import { dirname
|
|
4647
|
+
import { dirname } from "node:path";
|
|
4495
4648
|
var STARTER_CONFIG = `# ~/.llmist/cli.toml
|
|
4496
4649
|
# llmist CLI configuration file
|
|
4497
4650
|
#
|
|
@@ -4547,7 +4700,7 @@ var STARTER_CONFIG = `# ~/.llmist/cli.toml
|
|
|
4547
4700
|
`;
|
|
4548
4701
|
async function executeInit(_options, env) {
|
|
4549
4702
|
const configPath = getConfigPath();
|
|
4550
|
-
const configDir =
|
|
4703
|
+
const configDir = dirname(configPath);
|
|
4551
4704
|
if (existsSync5(configPath)) {
|
|
4552
4705
|
env.stderr.write(`Configuration already exists at ${configPath}
|
|
4553
4706
|
`);
|