llmist 4.0.0 → 5.1.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-Q6NQRMYD.js → chunk-F5QK5YVI.js} +2 -2
- package/dist/{chunk-RHR2M6T6.js → chunk-YJKUWFIC.js} +28 -8
- package/dist/chunk-YJKUWFIC.js.map +1 -0
- package/dist/cli.cjs +533 -250
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +478 -215
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +27 -7
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/{mock-stream-BvNYtrlG.d.cts → mock-stream-CAY53Q6u.d.cts} +23 -2
- package/dist/{mock-stream-BvNYtrlG.d.ts → mock-stream-CAY53Q6u.d.ts} +23 -2
- package/dist/testing/index.cjs +27 -7
- 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-RHR2M6T6.js.map +0 -1
- /package/dist/{chunk-Q6NQRMYD.js.map → chunk-F5QK5YVI.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-F5QK5YVI.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-YJKUWFIC.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.1.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) {
|
|
@@ -3075,7 +3046,7 @@ function getRawValue(value) {
|
|
|
3075
3046
|
function truncateValue(str, maxLen) {
|
|
3076
3047
|
if (maxLen <= 0) return "";
|
|
3077
3048
|
if (str.length <= maxLen) return str;
|
|
3078
|
-
return `${str.slice(0, maxLen)}\u2026`;
|
|
3049
|
+
return `${str.slice(0, maxLen - 1)}\u2026`;
|
|
3079
3050
|
}
|
|
3080
3051
|
function formatParametersInline(params, maxWidth) {
|
|
3081
3052
|
if (!params || Object.keys(params).length === 0) {
|
|
@@ -3103,6 +3074,11 @@ function formatParametersInline(params, maxWidth) {
|
|
|
3103
3074
|
const proportion = v.length / totalRawLength;
|
|
3104
3075
|
return Math.max(minPerValue, Math.floor(proportion * availableForValues));
|
|
3105
3076
|
});
|
|
3077
|
+
const totalLimits = limits.reduce((sum, l) => sum + l, 0);
|
|
3078
|
+
if (totalLimits > availableForValues) {
|
|
3079
|
+
const scale = availableForValues / totalLimits;
|
|
3080
|
+
limits = limits.map((l) => Math.max(1, Math.floor(l * scale)));
|
|
3081
|
+
}
|
|
3106
3082
|
}
|
|
3107
3083
|
}
|
|
3108
3084
|
} else {
|
|
@@ -3113,6 +3089,38 @@ function formatParametersInline(params, maxWidth) {
|
|
|
3113
3089
|
return `${chalk3.dim(key)}${chalk3.dim("=")}${chalk3.cyan(formatted)}`;
|
|
3114
3090
|
}).join(chalk3.dim(", "));
|
|
3115
3091
|
}
|
|
3092
|
+
function formatGadgetLine(info, maxWidth) {
|
|
3093
|
+
const terminalWidth = maxWidth ?? process.stdout.columns ?? 80;
|
|
3094
|
+
const gadgetLabel = chalk3.magenta.bold(info.name);
|
|
3095
|
+
const timeStr = `${info.elapsedSeconds.toFixed(1)}s`;
|
|
3096
|
+
const timeLabel = chalk3.dim(timeStr);
|
|
3097
|
+
const fixedLength = 3 + info.name.length + 2 + 1 + timeStr.length;
|
|
3098
|
+
const availableForParams = Math.max(40, terminalWidth - fixedLength - 3);
|
|
3099
|
+
const paramsStr = formatParametersInline(info.parameters, availableForParams);
|
|
3100
|
+
const paramsLabel = paramsStr ? `${chalk3.dim("(")}${paramsStr}${chalk3.dim(")")}` : "";
|
|
3101
|
+
if (info.error) {
|
|
3102
|
+
const errorMsg = info.error.length > 50 ? `${info.error.slice(0, 50)}\u2026` : info.error;
|
|
3103
|
+
return `${chalk3.red("\u2717")} ${gadgetLabel}${paramsLabel} ${chalk3.red("error:")} ${errorMsg} ${timeLabel}`;
|
|
3104
|
+
}
|
|
3105
|
+
if (!info.isComplete) {
|
|
3106
|
+
return `${chalk3.blue("\u23F5")} ${gadgetLabel}${paramsLabel} ${timeLabel}`;
|
|
3107
|
+
}
|
|
3108
|
+
let outputLabel;
|
|
3109
|
+
if (info.tokenCount !== void 0 && info.tokenCount > 0) {
|
|
3110
|
+
outputLabel = chalk3.dim("\u2193") + chalk3.green(` ${formatTokens(info.tokenCount)} `);
|
|
3111
|
+
} else if (info.outputBytes !== void 0 && info.outputBytes > 0) {
|
|
3112
|
+
outputLabel = chalk3.green(formatBytes(info.outputBytes)) + " ";
|
|
3113
|
+
} else {
|
|
3114
|
+
outputLabel = "";
|
|
3115
|
+
}
|
|
3116
|
+
const icon = info.breaksLoop ? chalk3.yellow("\u23F9") : chalk3.green("\u2713");
|
|
3117
|
+
const nameRef = chalk3.magenta(info.name);
|
|
3118
|
+
const line1 = `${icon} ${gadgetLabel}${paramsLabel}`;
|
|
3119
|
+
const line2Prefix = ` ${chalk3.dim("\u2192")} ${nameRef} ${outputLabel}`;
|
|
3120
|
+
const line2 = `${line2Prefix}${timeLabel}`;
|
|
3121
|
+
return `${line1}
|
|
3122
|
+
${line2}`;
|
|
3123
|
+
}
|
|
3116
3124
|
function formatBytes(bytes) {
|
|
3117
3125
|
if (bytes < 1024) {
|
|
3118
3126
|
return `${bytes} bytes`;
|
|
@@ -3122,6 +3130,11 @@ function formatBytes(bytes) {
|
|
|
3122
3130
|
}
|
|
3123
3131
|
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
3124
3132
|
}
|
|
3133
|
+
function truncateOutputPreview(output, maxWidth) {
|
|
3134
|
+
const normalized = output.replace(/\s+/g, " ").trim();
|
|
3135
|
+
if (normalized.length <= maxWidth) return normalized;
|
|
3136
|
+
return normalized.slice(0, maxWidth - 1) + "\u2026";
|
|
3137
|
+
}
|
|
3125
3138
|
function getMediaIcon(kind) {
|
|
3126
3139
|
switch (kind) {
|
|
3127
3140
|
case "image":
|
|
@@ -3149,37 +3162,99 @@ function formatGadgetSummary2(result) {
|
|
|
3149
3162
|
const gadgetLabel = chalk3.magenta.bold(result.gadgetName);
|
|
3150
3163
|
const timeStr = result.executionTimeMs >= 1e3 ? `${(result.executionTimeMs / 1e3).toFixed(1)}s` : `${Math.round(result.executionTimeMs)}ms`;
|
|
3151
3164
|
const timeLabel = chalk3.dim(timeStr);
|
|
3152
|
-
|
|
3153
|
-
|
|
3154
|
-
|
|
3155
|
-
|
|
3165
|
+
const fixedLength = 3 + result.gadgetName.length + 2;
|
|
3166
|
+
const availableForParams = Math.max(40, terminalWidth - fixedLength - 3);
|
|
3167
|
+
const paramsStr = formatParametersInline(result.parameters, availableForParams);
|
|
3168
|
+
const paramsLabel = paramsStr ? `${chalk3.dim("(")}${paramsStr}${chalk3.dim(")")}` : "";
|
|
3169
|
+
const icon = result.breaksLoop ? chalk3.yellow("\u23F9") : result.error ? chalk3.red("\u2717") : chalk3.green("\u2713");
|
|
3170
|
+
const line1 = `${icon} ${gadgetLabel}${paramsLabel}`;
|
|
3171
|
+
const nameRef = chalk3.magenta(result.gadgetName);
|
|
3172
|
+
const hasSubagentMetrics = result.subagentMetrics && result.subagentMetrics.callCount > 0;
|
|
3173
|
+
let outputLabel;
|
|
3174
|
+
let outputStrRaw;
|
|
3175
|
+
if (!hasSubagentMetrics && result.tokenCount !== void 0 && result.tokenCount > 0) {
|
|
3176
|
+
const tokenStr = formatTokens(result.tokenCount);
|
|
3177
|
+
outputLabel = chalk3.dim("\u2193") + chalk3.green(` ${tokenStr} `);
|
|
3178
|
+
outputStrRaw = `\u2193 ${tokenStr} `;
|
|
3179
|
+
} else if (!hasSubagentMetrics && result.result) {
|
|
3156
3180
|
const outputBytes = Buffer.byteLength(result.result, "utf-8");
|
|
3157
|
-
|
|
3181
|
+
if (outputBytes > 0) {
|
|
3182
|
+
const bytesStr = formatBytes(outputBytes);
|
|
3183
|
+
outputLabel = chalk3.green(bytesStr) + " ";
|
|
3184
|
+
outputStrRaw = bytesStr + " ";
|
|
3185
|
+
} else {
|
|
3186
|
+
outputLabel = "";
|
|
3187
|
+
outputStrRaw = "";
|
|
3188
|
+
}
|
|
3158
3189
|
} else {
|
|
3159
|
-
|
|
3190
|
+
outputLabel = "";
|
|
3191
|
+
outputStrRaw = "";
|
|
3160
3192
|
}
|
|
3161
|
-
const fixedLength = 2 + result.gadgetName.length + 2 + 3 + outputStr.length + 1 + timeStr.length;
|
|
3162
|
-
const availableForParams = Math.max(40, terminalWidth - fixedLength - 2);
|
|
3163
|
-
const paramsStr = formatParametersInline(result.parameters, availableForParams);
|
|
3164
|
-
const paramsLabel = paramsStr ? `${chalk3.dim("(")}${paramsStr}${chalk3.dim(")")}` : "";
|
|
3165
3193
|
if (result.error) {
|
|
3166
3194
|
const errorMsg = result.error.length > 50 ? `${result.error.slice(0, 50)}\u2026` : result.error;
|
|
3167
|
-
|
|
3195
|
+
const line22 = ` ${chalk3.dim("\u2192")} ${nameRef} ${chalk3.red("error:")} ${errorMsg} ${timeLabel}`;
|
|
3196
|
+
return `${line1}
|
|
3197
|
+
${line22}`;
|
|
3198
|
+
}
|
|
3199
|
+
const previewWidth = Math.floor(terminalWidth * 0.6);
|
|
3200
|
+
const prefixLength = 4 + result.gadgetName.length + 1 + outputStrRaw.length + 1 + timeStr.length + 2;
|
|
3201
|
+
const availablePreview = Math.max(20, previewWidth - prefixLength);
|
|
3202
|
+
let customPreview;
|
|
3203
|
+
if (result.gadgetName === "TodoUpsert" && result.parameters?.content) {
|
|
3204
|
+
const statusEmoji = result.parameters.status === "done" ? "\u2705" : result.parameters.status === "in_progress" ? "\u{1F504}" : "\u2B1C";
|
|
3205
|
+
const content = String(result.parameters.content);
|
|
3206
|
+
customPreview = `${statusEmoji} ${truncateOutputPreview(content, availablePreview - 3)}`;
|
|
3207
|
+
}
|
|
3208
|
+
if (result.gadgetName === "GoogleSearch" && result.parameters?.query) {
|
|
3209
|
+
const query = String(result.parameters.query);
|
|
3210
|
+
const countMatch = result.result?.match(/\((\d+)\s+of\s+[\d,]+\s+results?\)/i) || // "(10 of 36400000 results)"
|
|
3211
|
+
result.result?.match(/(\d+)\s+results?\s+found/i) || // "10 results found"
|
|
3212
|
+
result.result?.match(/found\s+(\d+)\s+results?/i);
|
|
3213
|
+
const count = countMatch?.[1] ?? (result.parameters.maxResults ? String(result.parameters.maxResults) : null);
|
|
3214
|
+
const countStr = count ? ` \u2192 ${count} results` : "";
|
|
3215
|
+
const queryPreview = truncateOutputPreview(query, availablePreview - 5 - countStr.length);
|
|
3216
|
+
customPreview = `\u{1F50D} "${queryPreview}"${countStr}`;
|
|
3217
|
+
}
|
|
3218
|
+
let subagentMetricsStr = "";
|
|
3219
|
+
if (result.subagentMetrics && result.subagentMetrics.callCount > 0) {
|
|
3220
|
+
const parts = [];
|
|
3221
|
+
const m = result.subagentMetrics;
|
|
3222
|
+
if (m.inputTokens > 0) {
|
|
3223
|
+
parts.push(chalk3.dim("\u2191") + chalk3.yellow(` ${formatTokens(m.inputTokens)}`));
|
|
3224
|
+
}
|
|
3225
|
+
if (m.cachedInputTokens > 0) {
|
|
3226
|
+
parts.push(chalk3.dim("\u27F3") + chalk3.blue(` ${formatTokens(m.cachedInputTokens)}`));
|
|
3227
|
+
}
|
|
3228
|
+
if (m.outputTokens > 0) {
|
|
3229
|
+
parts.push(chalk3.dim("\u2193") + chalk3.green(` ${formatTokens(m.outputTokens)}`));
|
|
3230
|
+
}
|
|
3231
|
+
if (m.cost > 0) {
|
|
3232
|
+
parts.push(chalk3.cyan(`$${formatCost(m.cost)}`));
|
|
3233
|
+
}
|
|
3234
|
+
if (parts.length > 0) {
|
|
3235
|
+
subagentMetricsStr = parts.join(chalk3.dim(" | ")) + chalk3.dim(" | ");
|
|
3236
|
+
}
|
|
3237
|
+
}
|
|
3238
|
+
let line2;
|
|
3239
|
+
const previewContent = customPreview ?? (result.result?.trim() ? truncateOutputPreview(result.result, availablePreview) : null);
|
|
3240
|
+
if (previewContent) {
|
|
3241
|
+
line2 = ` ${chalk3.dim("\u2192")} ${nameRef} ${outputLabel}${subagentMetricsStr}${timeLabel}${chalk3.dim(":")} ${chalk3.dim(previewContent)}`;
|
|
3242
|
+
} else {
|
|
3243
|
+
line2 = ` ${chalk3.dim("\u2192")} ${nameRef} ${outputLabel}${subagentMetricsStr}${timeLabel}`;
|
|
3168
3244
|
}
|
|
3169
|
-
|
|
3170
|
-
|
|
3171
|
-
let summaryLine = `${icon} ${gadgetLabel}${paramsLabel} ${chalk3.dim("\u2192")} ${outputLabel} ${timeLabel}`;
|
|
3245
|
+
let output = `${line1}
|
|
3246
|
+
${line2}`;
|
|
3172
3247
|
if (result.media && result.media.length > 0) {
|
|
3173
3248
|
const mediaLines = result.media.map(formatMediaLine);
|
|
3174
|
-
|
|
3249
|
+
output += "\n" + mediaLines.join("\n");
|
|
3175
3250
|
}
|
|
3176
3251
|
if (result.gadgetName === "TellUser" && result.parameters?.message) {
|
|
3177
3252
|
const message = String(result.parameters.message);
|
|
3178
3253
|
const rendered = renderMarkdownWithSeparators(message);
|
|
3179
|
-
return `${
|
|
3254
|
+
return `${output}
|
|
3180
3255
|
${rendered}`;
|
|
3181
3256
|
}
|
|
3182
|
-
return
|
|
3257
|
+
return output;
|
|
3183
3258
|
}
|
|
3184
3259
|
|
|
3185
3260
|
// src/cli/utils.ts
|
|
@@ -3376,14 +3451,15 @@ var StreamProgress = class {
|
|
|
3376
3451
|
* Add a nested agent LLM call (called when nested llm_call_start event received).
|
|
3377
3452
|
* Used to display hierarchical progress for subagent gadgets.
|
|
3378
3453
|
*/
|
|
3379
|
-
addNestedAgent(id, parentInvocationId, depth, model, iteration,
|
|
3454
|
+
addNestedAgent(id, parentInvocationId, depth, model, iteration, info) {
|
|
3380
3455
|
this.nestedAgents.set(id, {
|
|
3381
3456
|
parentInvocationId,
|
|
3382
3457
|
depth,
|
|
3383
3458
|
model,
|
|
3384
3459
|
iteration,
|
|
3385
3460
|
startTime: Date.now(),
|
|
3386
|
-
inputTokens
|
|
3461
|
+
inputTokens: info?.inputTokens,
|
|
3462
|
+
cachedInputTokens: info?.cachedInputTokens
|
|
3387
3463
|
});
|
|
3388
3464
|
if (this.isRunning && this.isTTY) {
|
|
3389
3465
|
this.render();
|
|
@@ -3391,11 +3467,36 @@ var StreamProgress = class {
|
|
|
3391
3467
|
}
|
|
3392
3468
|
/**
|
|
3393
3469
|
* Update a nested agent with completion info (called when nested llm_call_end event received).
|
|
3470
|
+
* Records completion time to freeze the elapsed timer.
|
|
3471
|
+
* @param info - Full LLM call info including tokens, cache details, and cost
|
|
3394
3472
|
*/
|
|
3395
|
-
updateNestedAgent(id,
|
|
3473
|
+
updateNestedAgent(id, info) {
|
|
3396
3474
|
const agent = this.nestedAgents.get(id);
|
|
3397
3475
|
if (agent) {
|
|
3398
|
-
agent.
|
|
3476
|
+
if (info.inputTokens !== void 0) agent.inputTokens = info.inputTokens;
|
|
3477
|
+
if (info.outputTokens !== void 0) agent.outputTokens = info.outputTokens;
|
|
3478
|
+
if (info.cachedInputTokens !== void 0) agent.cachedInputTokens = info.cachedInputTokens;
|
|
3479
|
+
if (info.cacheCreationInputTokens !== void 0)
|
|
3480
|
+
agent.cacheCreationInputTokens = info.cacheCreationInputTokens;
|
|
3481
|
+
if (info.finishReason !== void 0) agent.finishReason = info.finishReason;
|
|
3482
|
+
if (info.cost !== void 0) {
|
|
3483
|
+
agent.cost = info.cost;
|
|
3484
|
+
} else if (this.modelRegistry && agent.model && agent.outputTokens) {
|
|
3485
|
+
try {
|
|
3486
|
+
const modelName = agent.model.includes(":") ? agent.model.split(":")[1] : agent.model;
|
|
3487
|
+
const costResult = this.modelRegistry.estimateCost(
|
|
3488
|
+
modelName,
|
|
3489
|
+
agent.inputTokens ?? 0,
|
|
3490
|
+
agent.outputTokens,
|
|
3491
|
+
agent.cachedInputTokens,
|
|
3492
|
+
agent.cacheCreationInputTokens
|
|
3493
|
+
);
|
|
3494
|
+
agent.cost = costResult?.totalCost;
|
|
3495
|
+
} catch {
|
|
3496
|
+
}
|
|
3497
|
+
}
|
|
3498
|
+
agent.completed = true;
|
|
3499
|
+
agent.completedTime = Date.now();
|
|
3399
3500
|
if (this.isRunning && this.isTTY) {
|
|
3400
3501
|
this.render();
|
|
3401
3502
|
}
|
|
@@ -3410,14 +3511,36 @@ var StreamProgress = class {
|
|
|
3410
3511
|
this.render();
|
|
3411
3512
|
}
|
|
3412
3513
|
}
|
|
3514
|
+
/**
|
|
3515
|
+
* Get aggregated metrics from all nested agents for a parent gadget.
|
|
3516
|
+
* Used to show total token counts and cost for subagent gadgets like BrowseWeb.
|
|
3517
|
+
*/
|
|
3518
|
+
getAggregatedSubagentMetrics(parentInvocationId) {
|
|
3519
|
+
let inputTokens = 0;
|
|
3520
|
+
let outputTokens = 0;
|
|
3521
|
+
let cachedInputTokens = 0;
|
|
3522
|
+
let cost = 0;
|
|
3523
|
+
let callCount = 0;
|
|
3524
|
+
for (const [, nested] of this.nestedAgents) {
|
|
3525
|
+
if (nested.parentInvocationId === parentInvocationId) {
|
|
3526
|
+
inputTokens += nested.inputTokens ?? 0;
|
|
3527
|
+
outputTokens += nested.outputTokens ?? 0;
|
|
3528
|
+
cachedInputTokens += nested.cachedInputTokens ?? 0;
|
|
3529
|
+
cost += nested.cost ?? 0;
|
|
3530
|
+
callCount++;
|
|
3531
|
+
}
|
|
3532
|
+
}
|
|
3533
|
+
return { inputTokens, outputTokens, cachedInputTokens, cost, callCount };
|
|
3534
|
+
}
|
|
3413
3535
|
/**
|
|
3414
3536
|
* Add a nested gadget call (called when nested gadget_call event received).
|
|
3415
3537
|
*/
|
|
3416
|
-
addNestedGadget(id, depth, parentInvocationId, name) {
|
|
3538
|
+
addNestedGadget(id, depth, parentInvocationId, name, parameters) {
|
|
3417
3539
|
this.nestedGadgets.set(id, {
|
|
3418
3540
|
depth,
|
|
3419
3541
|
parentInvocationId,
|
|
3420
3542
|
name,
|
|
3543
|
+
parameters,
|
|
3421
3544
|
startTime: Date.now()
|
|
3422
3545
|
});
|
|
3423
3546
|
if (this.isRunning && this.isTTY) {
|
|
@@ -3435,11 +3558,13 @@ var StreamProgress = class {
|
|
|
3435
3558
|
}
|
|
3436
3559
|
/**
|
|
3437
3560
|
* Mark a nested gadget as completed (keeps it visible with ✓ indicator).
|
|
3561
|
+
* Records completion time to freeze the elapsed timer.
|
|
3438
3562
|
*/
|
|
3439
3563
|
completeNestedGadget(id) {
|
|
3440
3564
|
const gadget = this.nestedGadgets.get(id);
|
|
3441
3565
|
if (gadget) {
|
|
3442
3566
|
gadget.completed = true;
|
|
3567
|
+
gadget.completedTime = Date.now();
|
|
3443
3568
|
if (this.isRunning && this.isTTY) {
|
|
3444
3569
|
this.render();
|
|
3445
3570
|
}
|
|
@@ -3574,38 +3699,130 @@ var StreamProgress = class {
|
|
|
3574
3699
|
this.clearRenderedLines();
|
|
3575
3700
|
const spinner = SPINNER_FRAMES[this.frameIndex++ % SPINNER_FRAMES.length];
|
|
3576
3701
|
const lines = [];
|
|
3577
|
-
|
|
3578
|
-
lines.push(this.formatStreamingLine(spinner));
|
|
3579
|
-
} else {
|
|
3580
|
-
lines.push(this.formatCumulativeLine(spinner));
|
|
3581
|
-
}
|
|
3702
|
+
const activeNestedStreams = [];
|
|
3582
3703
|
if (this.isTTY) {
|
|
3583
3704
|
for (const [gadgetId, gadget] of this.inFlightGadgets) {
|
|
3584
|
-
const
|
|
3585
|
-
const
|
|
3705
|
+
const elapsedSeconds = (Date.now() - gadget.startTime) / 1e3;
|
|
3706
|
+
const termWidth = process.stdout.columns ?? 80;
|
|
3707
|
+
const gadgetIndent = " ";
|
|
3708
|
+
const line = formatGadgetLine(
|
|
3709
|
+
{
|
|
3710
|
+
name: gadget.name,
|
|
3711
|
+
parameters: gadget.params,
|
|
3712
|
+
elapsedSeconds,
|
|
3713
|
+
isComplete: false
|
|
3714
|
+
},
|
|
3715
|
+
termWidth - gadgetIndent.length
|
|
3716
|
+
);
|
|
3717
|
+
const gadgetLine = line.split("\n").map((l) => gadgetIndent + l).join("\n");
|
|
3586
3718
|
lines.push(gadgetLine);
|
|
3719
|
+
const nestedOps = [];
|
|
3587
3720
|
for (const [_agentId, nested] of this.nestedAgents) {
|
|
3588
|
-
if (nested.parentInvocationId
|
|
3589
|
-
|
|
3590
|
-
|
|
3591
|
-
|
|
3592
|
-
|
|
3593
|
-
|
|
3594
|
-
|
|
3721
|
+
if (nested.parentInvocationId === gadgetId) {
|
|
3722
|
+
nestedOps.push({
|
|
3723
|
+
type: "agent",
|
|
3724
|
+
startTime: nested.startTime,
|
|
3725
|
+
depth: nested.depth,
|
|
3726
|
+
iteration: nested.iteration,
|
|
3727
|
+
model: nested.model,
|
|
3728
|
+
inputTokens: nested.inputTokens,
|
|
3729
|
+
cachedInputTokens: nested.cachedInputTokens,
|
|
3730
|
+
outputTokens: nested.outputTokens,
|
|
3731
|
+
cost: nested.cost,
|
|
3732
|
+
finishReason: nested.finishReason,
|
|
3733
|
+
completed: nested.completed,
|
|
3734
|
+
completedTime: nested.completedTime
|
|
3735
|
+
});
|
|
3736
|
+
if (!nested.completed) {
|
|
3737
|
+
activeNestedStreams.push({
|
|
3738
|
+
depth: nested.depth,
|
|
3739
|
+
iteration: nested.iteration,
|
|
3740
|
+
model: nested.model,
|
|
3741
|
+
inputTokens: nested.inputTokens,
|
|
3742
|
+
cachedInputTokens: nested.cachedInputTokens,
|
|
3743
|
+
outputTokens: nested.outputTokens,
|
|
3744
|
+
cost: nested.cost,
|
|
3745
|
+
startTime: nested.startTime
|
|
3746
|
+
});
|
|
3747
|
+
}
|
|
3748
|
+
}
|
|
3595
3749
|
}
|
|
3596
|
-
for (const [
|
|
3750
|
+
for (const [_nestedId, nestedGadget] of this.nestedGadgets) {
|
|
3597
3751
|
if (nestedGadget.parentInvocationId === gadgetId) {
|
|
3598
|
-
|
|
3599
|
-
|
|
3600
|
-
|
|
3601
|
-
|
|
3602
|
-
|
|
3752
|
+
nestedOps.push({
|
|
3753
|
+
type: "gadget",
|
|
3754
|
+
startTime: nestedGadget.startTime,
|
|
3755
|
+
depth: nestedGadget.depth,
|
|
3756
|
+
name: nestedGadget.name,
|
|
3757
|
+
parameters: nestedGadget.parameters,
|
|
3758
|
+
completed: nestedGadget.completed,
|
|
3759
|
+
completedTime: nestedGadget.completedTime
|
|
3760
|
+
});
|
|
3761
|
+
}
|
|
3762
|
+
}
|
|
3763
|
+
nestedOps.sort((a, b) => a.startTime - b.startTime);
|
|
3764
|
+
for (const op of nestedOps) {
|
|
3765
|
+
if (op.type === "agent" && !op.completed) {
|
|
3766
|
+
continue;
|
|
3767
|
+
}
|
|
3768
|
+
const indent = " ".repeat(op.depth + 2);
|
|
3769
|
+
const endTime = op.completedTime ?? Date.now();
|
|
3770
|
+
const elapsedSeconds2 = (endTime - op.startTime) / 1e3;
|
|
3771
|
+
if (op.type === "agent") {
|
|
3772
|
+
const line2 = formatLLMCallLine({
|
|
3773
|
+
iteration: op.iteration ?? 0,
|
|
3774
|
+
model: op.model ?? "",
|
|
3775
|
+
inputTokens: op.inputTokens,
|
|
3776
|
+
cachedInputTokens: op.cachedInputTokens,
|
|
3777
|
+
outputTokens: op.outputTokens,
|
|
3778
|
+
elapsedSeconds: elapsedSeconds2,
|
|
3779
|
+
cost: op.cost,
|
|
3780
|
+
finishReason: op.completed ? op.finishReason ?? "stop" : void 0,
|
|
3781
|
+
isStreaming: !op.completed,
|
|
3782
|
+
spinner
|
|
3783
|
+
});
|
|
3784
|
+
lines.push(`${indent}${line2}`);
|
|
3785
|
+
} else {
|
|
3786
|
+
const termWidth2 = process.stdout.columns ?? 80;
|
|
3787
|
+
const line2 = formatGadgetLine(
|
|
3788
|
+
{
|
|
3789
|
+
name: op.name ?? "",
|
|
3790
|
+
parameters: op.parameters,
|
|
3791
|
+
elapsedSeconds: elapsedSeconds2,
|
|
3792
|
+
isComplete: op.completed ?? false
|
|
3793
|
+
},
|
|
3794
|
+
termWidth2 - indent.length
|
|
3795
|
+
);
|
|
3796
|
+
const indentedLine = line2.split("\n").map((l) => indent + l).join("\n");
|
|
3797
|
+
lines.push(indentedLine);
|
|
3603
3798
|
}
|
|
3604
3799
|
}
|
|
3605
3800
|
}
|
|
3606
3801
|
}
|
|
3607
|
-
|
|
3608
|
-
|
|
3802
|
+
for (const stream of activeNestedStreams) {
|
|
3803
|
+
const indent = " ".repeat(stream.depth + 2);
|
|
3804
|
+
const elapsedSeconds = (Date.now() - stream.startTime) / 1e3;
|
|
3805
|
+
const line = formatLLMCallLine({
|
|
3806
|
+
iteration: stream.iteration,
|
|
3807
|
+
model: stream.model,
|
|
3808
|
+
inputTokens: stream.inputTokens,
|
|
3809
|
+
cachedInputTokens: stream.cachedInputTokens,
|
|
3810
|
+
outputTokens: stream.outputTokens,
|
|
3811
|
+
elapsedSeconds,
|
|
3812
|
+
cost: stream.cost,
|
|
3813
|
+
isStreaming: true,
|
|
3814
|
+
spinner
|
|
3815
|
+
});
|
|
3816
|
+
lines.push(`${indent}${line}`);
|
|
3817
|
+
}
|
|
3818
|
+
if (this.mode === "streaming") {
|
|
3819
|
+
lines.push(this.formatStreamingLine(spinner));
|
|
3820
|
+
} else {
|
|
3821
|
+
lines.push(this.formatCumulativeLine(spinner));
|
|
3822
|
+
}
|
|
3823
|
+
const output = lines.join("\n");
|
|
3824
|
+
this.lastRenderLineCount = (output.match(/\n/g) || []).length + 1;
|
|
3825
|
+
this.target.write("\r" + output);
|
|
3609
3826
|
this.hasRendered = true;
|
|
3610
3827
|
}
|
|
3611
3828
|
/**
|
|
@@ -3621,42 +3838,27 @@ var StreamProgress = class {
|
|
|
3621
3838
|
}
|
|
3622
3839
|
/**
|
|
3623
3840
|
* Format the streaming mode progress line (returns string, doesn't write).
|
|
3841
|
+
* Uses the shared formatLLMCallLine() function for consistent formatting
|
|
3842
|
+
* between main agent and nested subagent displays.
|
|
3624
3843
|
*/
|
|
3625
3844
|
formatStreamingLine(spinner) {
|
|
3626
|
-
const elapsed = ((Date.now() - this.callStartTime) / 1e3).toFixed(1);
|
|
3627
3845
|
const outTokens = this.callOutputTokensEstimated ? Math.round(this.callOutputChars / FALLBACK_CHARS_PER_TOKEN) : this.callOutputTokens;
|
|
3628
|
-
|
|
3629
|
-
|
|
3630
|
-
|
|
3631
|
-
|
|
3632
|
-
|
|
3633
|
-
|
|
3634
|
-
|
|
3635
|
-
|
|
3636
|
-
|
|
3637
|
-
|
|
3638
|
-
|
|
3639
|
-
|
|
3640
|
-
|
|
3641
|
-
|
|
3642
|
-
} else {
|
|
3643
|
-
parts.push(chalk4.green(formatted));
|
|
3846
|
+
return formatLLMCallLine({
|
|
3847
|
+
iteration: this.currentIteration,
|
|
3848
|
+
model: this.model ?? "",
|
|
3849
|
+
inputTokens: this.callInputTokens,
|
|
3850
|
+
cachedInputTokens: this.callCachedInputTokens,
|
|
3851
|
+
outputTokens: outTokens,
|
|
3852
|
+
elapsedSeconds: (Date.now() - this.callStartTime) / 1e3,
|
|
3853
|
+
cost: this.calculateCurrentCallCost(outTokens),
|
|
3854
|
+
isStreaming: true,
|
|
3855
|
+
spinner,
|
|
3856
|
+
contextPercent: this.getContextUsagePercent(),
|
|
3857
|
+
estimated: {
|
|
3858
|
+
input: this.callInputTokensEstimated,
|
|
3859
|
+
output: this.callOutputTokensEstimated
|
|
3644
3860
|
}
|
|
3645
|
-
}
|
|
3646
|
-
if (this.callInputTokens > 0) {
|
|
3647
|
-
const prefix = this.callInputTokensEstimated ? "~" : "";
|
|
3648
|
-
parts.push(chalk4.dim("\u2191") + chalk4.yellow(` ${prefix}${formatTokens(this.callInputTokens)}`));
|
|
3649
|
-
}
|
|
3650
|
-
if (this.isStreaming || outTokens > 0) {
|
|
3651
|
-
const prefix = this.callOutputTokensEstimated ? "~" : "";
|
|
3652
|
-
parts.push(chalk4.dim("\u2193") + chalk4.green(` ${prefix}${formatTokens(outTokens)}`));
|
|
3653
|
-
}
|
|
3654
|
-
parts.push(chalk4.dim(`${elapsed}s`));
|
|
3655
|
-
const callCost = this.calculateCurrentCallCost(outTokens);
|
|
3656
|
-
if (callCost > 0) {
|
|
3657
|
-
parts.push(chalk4.cyan(`$${formatCost(callCost)}`));
|
|
3658
|
-
}
|
|
3659
|
-
return `${parts.join(chalk4.dim(" | "))} ${chalk4.cyan(spinner)}`;
|
|
3861
|
+
});
|
|
3660
3862
|
}
|
|
3661
3863
|
/**
|
|
3662
3864
|
* Calculates live cost estimate for the current streaming call.
|
|
@@ -3884,7 +4086,7 @@ function addAgentOptions(cmd, defaults) {
|
|
|
3884
4086
|
OPTION_FLAGS.logLlmRequests,
|
|
3885
4087
|
OPTION_DESCRIPTIONS.logLlmRequests,
|
|
3886
4088
|
defaults?.["log-llm-requests"]
|
|
3887
|
-
).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)
|
|
4089
|
+
).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);
|
|
3888
4090
|
}
|
|
3889
4091
|
function configToCompleteOptions(config) {
|
|
3890
4092
|
const result = {};
|
|
@@ -3961,8 +4163,7 @@ async function executeAgent(promptArg, options, env) {
|
|
|
3961
4163
|
const dockerOptions = {
|
|
3962
4164
|
docker: options.docker ?? false,
|
|
3963
4165
|
dockerRo: options.dockerRo ?? false,
|
|
3964
|
-
noDocker: options.noDocker ?? false
|
|
3965
|
-
dockerDev: options.dockerDev ?? false
|
|
4166
|
+
noDocker: options.noDocker ?? false
|
|
3966
4167
|
};
|
|
3967
4168
|
const dockerEnabled = resolveDockerEnabled(
|
|
3968
4169
|
env.dockerConfig,
|
|
@@ -3971,7 +4172,6 @@ async function executeAgent(promptArg, options, env) {
|
|
|
3971
4172
|
// Profile-level docker: true/false
|
|
3972
4173
|
);
|
|
3973
4174
|
if (dockerEnabled) {
|
|
3974
|
-
const devMode = resolveDevMode(env.dockerConfig, dockerOptions.dockerDev);
|
|
3975
4175
|
const ctx = createDockerContext(
|
|
3976
4176
|
env.dockerConfig,
|
|
3977
4177
|
dockerOptions,
|
|
@@ -3982,7 +4182,7 @@ async function executeAgent(promptArg, options, env) {
|
|
|
3982
4182
|
// Profile-level CWD permission override
|
|
3983
4183
|
);
|
|
3984
4184
|
try {
|
|
3985
|
-
await executeInDocker(ctx
|
|
4185
|
+
await executeInDocker(ctx);
|
|
3986
4186
|
} catch (error) {
|
|
3987
4187
|
if (error instanceof DockerSkipError) {
|
|
3988
4188
|
} else {
|
|
@@ -4009,9 +4209,18 @@ async function executeAgent(promptArg, options, env) {
|
|
|
4009
4209
|
registry.registerByClass(gadget);
|
|
4010
4210
|
}
|
|
4011
4211
|
}
|
|
4212
|
+
if (!options.quiet) {
|
|
4213
|
+
const allNames = registry.getAll().map((g) => g.name).join(", ");
|
|
4214
|
+
env.stderr.write(chalk5.dim(`Gadgets: ${allNames}
|
|
4215
|
+
`));
|
|
4216
|
+
}
|
|
4012
4217
|
const printer = new StreamPrinter(env.stdout);
|
|
4013
4218
|
const stderrTTY = env.stderr.isTTY === true;
|
|
4014
|
-
const progress = new StreamProgress(
|
|
4219
|
+
const progress = new StreamProgress(
|
|
4220
|
+
env.stderr,
|
|
4221
|
+
stderrTTY,
|
|
4222
|
+
client.modelRegistry
|
|
4223
|
+
);
|
|
4015
4224
|
const abortController = new AbortController();
|
|
4016
4225
|
let wasCancelled = false;
|
|
4017
4226
|
let isStreaming = false;
|
|
@@ -4021,9 +4230,11 @@ async function executeAgent(promptArg, options, env) {
|
|
|
4021
4230
|
wasCancelled = true;
|
|
4022
4231
|
abortController.abort();
|
|
4023
4232
|
progress.pause();
|
|
4024
|
-
env.stderr.write(
|
|
4233
|
+
env.stderr.write(
|
|
4234
|
+
chalk5.yellow(`
|
|
4025
4235
|
[Cancelled] ${progress.formatStats()}
|
|
4026
|
-
`)
|
|
4236
|
+
`)
|
|
4237
|
+
);
|
|
4027
4238
|
} else {
|
|
4028
4239
|
handleQuit();
|
|
4029
4240
|
}
|
|
@@ -4033,7 +4244,11 @@ async function executeAgent(promptArg, options, env) {
|
|
|
4033
4244
|
cleanupSigint: null,
|
|
4034
4245
|
restore: () => {
|
|
4035
4246
|
if (stdinIsInteractive && stdinStream.isTTY && !wasCancelled) {
|
|
4036
|
-
keyboard.cleanupEsc = createEscKeyListener(
|
|
4247
|
+
keyboard.cleanupEsc = createEscKeyListener(
|
|
4248
|
+
stdinStream,
|
|
4249
|
+
handleCancel,
|
|
4250
|
+
handleCancel
|
|
4251
|
+
);
|
|
4037
4252
|
}
|
|
4038
4253
|
}
|
|
4039
4254
|
};
|
|
@@ -4058,7 +4273,11 @@ async function executeAgent(promptArg, options, env) {
|
|
|
4058
4273
|
process.exit(130);
|
|
4059
4274
|
};
|
|
4060
4275
|
if (stdinIsInteractive && stdinStream.isTTY) {
|
|
4061
|
-
keyboard.cleanupEsc = createEscKeyListener(
|
|
4276
|
+
keyboard.cleanupEsc = createEscKeyListener(
|
|
4277
|
+
stdinStream,
|
|
4278
|
+
handleCancel,
|
|
4279
|
+
handleCancel
|
|
4280
|
+
);
|
|
4062
4281
|
}
|
|
4063
4282
|
keyboard.cleanupSigint = createSigintListener(
|
|
4064
4283
|
handleCancel,
|
|
@@ -4084,7 +4303,12 @@ async function executeAgent(promptArg, options, env) {
|
|
|
4084
4303
|
gadgetApprovals,
|
|
4085
4304
|
defaultMode: "allowed"
|
|
4086
4305
|
};
|
|
4087
|
-
const approvalManager = new ApprovalManager(
|
|
4306
|
+
const approvalManager = new ApprovalManager(
|
|
4307
|
+
approvalConfig,
|
|
4308
|
+
env,
|
|
4309
|
+
progress,
|
|
4310
|
+
keyboard
|
|
4311
|
+
);
|
|
4088
4312
|
let usage;
|
|
4089
4313
|
let iterations = 0;
|
|
4090
4314
|
const llmLogsBaseDir = resolveLogDir(options.logLlmRequests, "requests");
|
|
@@ -4094,7 +4318,10 @@ async function executeAgent(promptArg, options, env) {
|
|
|
4094
4318
|
try {
|
|
4095
4319
|
return await client.countTokens(model, messages);
|
|
4096
4320
|
} catch {
|
|
4097
|
-
const totalChars = messages.reduce(
|
|
4321
|
+
const totalChars = messages.reduce(
|
|
4322
|
+
(sum, m) => sum + (m.content?.length ?? 0),
|
|
4323
|
+
0
|
|
4324
|
+
);
|
|
4098
4325
|
return Math.round(totalChars / FALLBACK_CHARS_PER_TOKEN);
|
|
4099
4326
|
}
|
|
4100
4327
|
};
|
|
@@ -4116,7 +4343,9 @@ async function executeAgent(promptArg, options, env) {
|
|
|
4116
4343
|
observers: {
|
|
4117
4344
|
// onLLMCallStart: Start progress indicator for each LLM call
|
|
4118
4345
|
// This showcases how to react to agent lifecycle events
|
|
4346
|
+
// Skip for subagent events (tracked separately via nested display)
|
|
4119
4347
|
onLLMCallStart: async (context) => {
|
|
4348
|
+
if (context.subagentContext) return;
|
|
4120
4349
|
isStreaming = true;
|
|
4121
4350
|
llmCallCounter++;
|
|
4122
4351
|
const inputTokens = await countMessagesTokens(
|
|
@@ -4142,7 +4371,9 @@ async function executeAgent(promptArg, options, env) {
|
|
|
4142
4371
|
},
|
|
4143
4372
|
// onStreamChunk: Real-time updates as LLM generates tokens
|
|
4144
4373
|
// This enables responsive UIs that show progress during generation
|
|
4374
|
+
// Skip for subagent events (tracked separately via nested display)
|
|
4145
4375
|
onStreamChunk: async (context) => {
|
|
4376
|
+
if (context.subagentContext) return;
|
|
4146
4377
|
progress.update(context.accumulatedText.length);
|
|
4147
4378
|
if (context.usage) {
|
|
4148
4379
|
if (context.usage.inputTokens) {
|
|
@@ -4159,7 +4390,9 @@ async function executeAgent(promptArg, options, env) {
|
|
|
4159
4390
|
},
|
|
4160
4391
|
// onLLMCallComplete: Finalize metrics after each LLM call
|
|
4161
4392
|
// This is where you'd typically log metrics or update dashboards
|
|
4393
|
+
// Skip progress updates for subagent events (tracked separately via nested display)
|
|
4162
4394
|
onLLMCallComplete: async (context) => {
|
|
4395
|
+
if (context.subagentContext) return;
|
|
4163
4396
|
isStreaming = false;
|
|
4164
4397
|
usage = context.usage;
|
|
4165
4398
|
iterations = Math.max(iterations, context.iteration + 1);
|
|
@@ -4188,7 +4421,7 @@ async function executeAgent(promptArg, options, env) {
|
|
|
4188
4421
|
}
|
|
4189
4422
|
const callElapsed = progress.getCallElapsedSeconds();
|
|
4190
4423
|
progress.endCall(context.usage);
|
|
4191
|
-
if (!options.quiet) {
|
|
4424
|
+
if (!options.quiet && !context.subagentContext) {
|
|
4192
4425
|
const summary = renderSummary({
|
|
4193
4426
|
iterations: context.iteration + 1,
|
|
4194
4427
|
model: options.model,
|
|
@@ -4201,6 +4434,7 @@ async function executeAgent(promptArg, options, env) {
|
|
|
4201
4434
|
env.stderr.write(`${summary}
|
|
4202
4435
|
`);
|
|
4203
4436
|
}
|
|
4437
|
+
env.stderr.write("\n");
|
|
4204
4438
|
}
|
|
4205
4439
|
if (llmSessionDir) {
|
|
4206
4440
|
const filename = `${formatCallNumber(llmCallCounter)}.response`;
|
|
@@ -4246,7 +4480,10 @@ ${ctx.gadgetName} is denied by configuration.`
|
|
|
4246
4480
|
}
|
|
4247
4481
|
return { action: "proceed" };
|
|
4248
4482
|
}
|
|
4249
|
-
const result = await approvalManager.requestApproval(
|
|
4483
|
+
const result = await approvalManager.requestApproval(
|
|
4484
|
+
ctx.gadgetName,
|
|
4485
|
+
ctx.parameters
|
|
4486
|
+
);
|
|
4250
4487
|
if (!result.approved) {
|
|
4251
4488
|
return {
|
|
4252
4489
|
action: "skip",
|
|
@@ -4289,11 +4526,11 @@ Denied: ${result.reason ?? "by user"}`
|
|
|
4289
4526
|
builder.withSyntheticGadgetCall(
|
|
4290
4527
|
"TellUser",
|
|
4291
4528
|
{
|
|
4292
|
-
message: "\u{1F44B} Hello! I'm ready to help.\n\
|
|
4529
|
+
message: "\u{1F44B} Hello! I'm ready to help.\n\nWhat would you like me to work on?",
|
|
4293
4530
|
done: false,
|
|
4294
4531
|
type: "info"
|
|
4295
4532
|
},
|
|
4296
|
-
"\u2139\uFE0F \u{1F44B} Hello! I'm ready to help.\n\
|
|
4533
|
+
"\u2139\uFE0F \u{1F44B} Hello! I'm ready to help.\n\nWhat would you like me to work on?"
|
|
4297
4534
|
);
|
|
4298
4535
|
builder.withTextOnlyHandler("acknowledge");
|
|
4299
4536
|
builder.withTextWithGadgetsHandler({
|
|
@@ -4304,8 +4541,7 @@ Denied: ${result.reason ?? "by user"}`
|
|
|
4304
4541
|
builder.withTrailingMessage(
|
|
4305
4542
|
(ctx) => [
|
|
4306
4543
|
`[Iteration ${ctx.iteration + 1}/${ctx.maxIterations}]`,
|
|
4307
|
-
"Think carefully: what gadget invocations can
|
|
4308
|
-
"Maximize efficiency by batching independent operations in a single response."
|
|
4544
|
+
"Think carefully in two steps: 1. what gadget invocations we should be making next? 2. how do they depend on one another so we can run all of them in the right order? Then respond with all the gadget invocations you are able to do now."
|
|
4309
4545
|
].join(" ")
|
|
4310
4546
|
);
|
|
4311
4547
|
if (!options.quiet) {
|
|
@@ -4318,21 +4554,32 @@ Denied: ${result.reason ?? "by user"}`
|
|
|
4318
4554
|
subagentEvent.gadgetInvocationId,
|
|
4319
4555
|
subagentEvent.depth,
|
|
4320
4556
|
info.model,
|
|
4321
|
-
info.iteration,
|
|
4322
|
-
|
|
4557
|
+
info.iteration + 1,
|
|
4558
|
+
// Make 1-indexed like main agent
|
|
4559
|
+
{
|
|
4560
|
+
inputTokens: info.usage?.inputTokens ?? info.inputTokens,
|
|
4561
|
+
cachedInputTokens: info.usage?.cachedInputTokens
|
|
4562
|
+
}
|
|
4323
4563
|
);
|
|
4324
4564
|
} else if (subagentEvent.type === "llm_call_end") {
|
|
4325
4565
|
const info = subagentEvent.event;
|
|
4326
4566
|
const subagentId = `${subagentEvent.gadgetInvocationId}:${info.iteration}`;
|
|
4327
|
-
progress.updateNestedAgent(subagentId,
|
|
4328
|
-
|
|
4567
|
+
progress.updateNestedAgent(subagentId, {
|
|
4568
|
+
inputTokens: info.usage?.inputTokens ?? info.inputTokens,
|
|
4569
|
+
outputTokens: info.usage?.outputTokens ?? info.outputTokens,
|
|
4570
|
+
cachedInputTokens: info.usage?.cachedInputTokens,
|
|
4571
|
+
cacheCreationInputTokens: info.usage?.cacheCreationInputTokens,
|
|
4572
|
+
finishReason: info.finishReason,
|
|
4573
|
+
cost: info.cost
|
|
4574
|
+
});
|
|
4329
4575
|
} else if (subagentEvent.type === "gadget_call") {
|
|
4330
4576
|
const gadgetEvent = subagentEvent.event;
|
|
4331
4577
|
progress.addNestedGadget(
|
|
4332
4578
|
gadgetEvent.call.invocationId,
|
|
4333
4579
|
subagentEvent.depth,
|
|
4334
4580
|
subagentEvent.gadgetInvocationId,
|
|
4335
|
-
gadgetEvent.call.gadgetName
|
|
4581
|
+
gadgetEvent.call.gadgetName,
|
|
4582
|
+
gadgetEvent.call.parameters
|
|
4336
4583
|
);
|
|
4337
4584
|
} else if (subagentEvent.type === "gadget_result") {
|
|
4338
4585
|
const resultEvent = subagentEvent.event;
|
|
@@ -4389,10 +4636,23 @@ Denied: ${result.reason ?? "by user"}`
|
|
|
4389
4636
|
}
|
|
4390
4637
|
} else {
|
|
4391
4638
|
const tokenCount = await countGadgetOutputTokens(event.result.result);
|
|
4392
|
-
|
|
4393
|
-
|
|
4394
|
-
`
|
|
4639
|
+
const subagentMetrics = progress.getAggregatedSubagentMetrics(
|
|
4640
|
+
event.result.invocationId
|
|
4395
4641
|
);
|
|
4642
|
+
const summary = formatGadgetSummary2({
|
|
4643
|
+
...event.result,
|
|
4644
|
+
tokenCount,
|
|
4645
|
+
media: event.result.storedMedia,
|
|
4646
|
+
subagentMetrics: subagentMetrics.callCount > 0 ? subagentMetrics : void 0
|
|
4647
|
+
});
|
|
4648
|
+
if (event.result.gadgetName === "TellUser") {
|
|
4649
|
+
env.stderr.write(`${summary}
|
|
4650
|
+
`);
|
|
4651
|
+
} else {
|
|
4652
|
+
const indentedSummary = summary.split("\n").map((line) => " " + line).join("\n");
|
|
4653
|
+
env.stderr.write(`${indentedSummary}
|
|
4654
|
+
`);
|
|
4655
|
+
}
|
|
4396
4656
|
}
|
|
4397
4657
|
if (progress.hasInFlightGadgets()) {
|
|
4398
4658
|
progress.start();
|
|
@@ -4431,7 +4691,10 @@ Denied: ${result.reason ?? "by user"}`
|
|
|
4431
4691
|
}
|
|
4432
4692
|
}
|
|
4433
4693
|
function registerAgentCommand(program, env, config, globalSubagents) {
|
|
4434
|
-
const cmd = program.command(COMMANDS.agent).description("Run the llmist agent loop with optional gadgets.").argument(
|
|
4694
|
+
const cmd = program.command(COMMANDS.agent).description("Run the llmist agent loop with optional gadgets.").argument(
|
|
4695
|
+
"[prompt]",
|
|
4696
|
+
"Prompt for the agent loop. Falls back to stdin when available."
|
|
4697
|
+
);
|
|
4435
4698
|
addAgentOptions(cmd, config);
|
|
4436
4699
|
cmd.action(
|
|
4437
4700
|
(prompt, options) => executeAction(() => {
|
|
@@ -4541,7 +4804,7 @@ function registerCompleteCommand(program, env, config) {
|
|
|
4541
4804
|
|
|
4542
4805
|
// src/cli/init-command.ts
|
|
4543
4806
|
import { existsSync as existsSync5, mkdirSync as mkdirSync2, writeFileSync as writeFileSync2 } from "node:fs";
|
|
4544
|
-
import { dirname
|
|
4807
|
+
import { dirname } from "node:path";
|
|
4545
4808
|
var STARTER_CONFIG = `# ~/.llmist/cli.toml
|
|
4546
4809
|
# llmist CLI configuration file
|
|
4547
4810
|
#
|
|
@@ -4597,7 +4860,7 @@ var STARTER_CONFIG = `# ~/.llmist/cli.toml
|
|
|
4597
4860
|
`;
|
|
4598
4861
|
async function executeInit(_options, env) {
|
|
4599
4862
|
const configPath = getConfigPath();
|
|
4600
|
-
const configDir =
|
|
4863
|
+
const configDir = dirname(configPath);
|
|
4601
4864
|
if (existsSync5(configPath)) {
|
|
4602
4865
|
env.stderr.write(`Configuration already exists at ${configPath}
|
|
4603
4866
|
`);
|