@ai-setting/roy-agent-cli 1.7.42 → 1.7.43
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/bin/roy-agent.js +817 -211
- package/dist/index.js +817 -211
- package/dist/roy-agent-linux-x64/bin/roy-agent +2 -2
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -608,6 +608,7 @@ ${COLORS.system("[已中断]")}
|
|
|
608
608
|
this.emitChunk({
|
|
609
609
|
type: "tool.result",
|
|
610
610
|
toolName: name,
|
|
611
|
+
toolId: payload.id,
|
|
611
612
|
toolResult: payload.result?.output
|
|
612
613
|
});
|
|
613
614
|
}
|
|
@@ -1558,69 +1559,94 @@ var init_theme = __esm(() => {
|
|
|
1558
1559
|
});
|
|
1559
1560
|
|
|
1560
1561
|
// src/tui/StatusBar.tsx
|
|
1562
|
+
import React2 from "react";
|
|
1561
1563
|
import { Box as Box2, Text as Text3 } from "ink";
|
|
1562
1564
|
import { jsxDEV as jsxDEV3 } from "react/jsx-dev-runtime";
|
|
1563
|
-
|
|
1565
|
+
function detectColumns() {
|
|
1566
|
+
const c = process.stdout?.columns;
|
|
1567
|
+
if (typeof c === "number" && c > 0)
|
|
1568
|
+
return c;
|
|
1569
|
+
return 80;
|
|
1570
|
+
}
|
|
1571
|
+
function shortenWorkdir(input) {
|
|
1572
|
+
if (!input)
|
|
1573
|
+
return input;
|
|
1574
|
+
if (input.length <= WORKDIR_TAIL_MAX)
|
|
1575
|
+
return input;
|
|
1576
|
+
const tail = input.slice(input.length - WORKDIR_TAIL_MAX);
|
|
1577
|
+
return `…/${tail}`;
|
|
1578
|
+
}
|
|
1579
|
+
function clipModel(input, max = MODEL_NARROW_MAX) {
|
|
1580
|
+
if (!input)
|
|
1581
|
+
return input;
|
|
1582
|
+
if (input.length <= max)
|
|
1583
|
+
return input;
|
|
1584
|
+
return input.slice(0, max - 1) + "…";
|
|
1585
|
+
}
|
|
1586
|
+
function planSegments(props, columns) {
|
|
1587
|
+
const segs = [];
|
|
1588
|
+
const tier = columns >= 80 ? 4 : columns >= 60 ? 3 : columns >= 40 ? 2 : 1;
|
|
1589
|
+
if (props.model) {
|
|
1590
|
+
const text = tier === 1 ? clipModel(props.model) ?? "" : props.model;
|
|
1591
|
+
segs.push({ text, color: "primary", dimPrefix: "\uD83E\uDD16 " });
|
|
1592
|
+
}
|
|
1593
|
+
if (tier >= 4 || tier === 3) {
|
|
1594
|
+
if (props.workdir) {
|
|
1595
|
+
segs.push({
|
|
1596
|
+
text: shortenWorkdir(props.workdir) ?? "",
|
|
1597
|
+
color: "muted",
|
|
1598
|
+
dimPrefix: "\uD83D\uDCC1 "
|
|
1599
|
+
});
|
|
1600
|
+
}
|
|
1601
|
+
}
|
|
1602
|
+
if (tier >= 4 || tier === 3 || tier === 2) {
|
|
1603
|
+
if (typeof props.tokenCount === "number") {
|
|
1604
|
+
segs.push({ text: `n=${props.tokenCount}`, color: "accent", dimPrefix: "" });
|
|
1605
|
+
}
|
|
1606
|
+
}
|
|
1607
|
+
if (tier >= 4) {
|
|
1608
|
+
segs.push({ text: "Ctrl+C to exit", color: "muted" });
|
|
1609
|
+
}
|
|
1610
|
+
return segs;
|
|
1611
|
+
}
|
|
1612
|
+
var WORKDIR_TAIL_MAX = 30, MODEL_NARROW_MAX = 24, StatusBar = ({
|
|
1564
1613
|
model,
|
|
1565
1614
|
workdir,
|
|
1566
|
-
tokenCount
|
|
1615
|
+
tokenCount,
|
|
1616
|
+
columns
|
|
1567
1617
|
}) => {
|
|
1568
1618
|
const theme = getTheme();
|
|
1619
|
+
const cols = typeof columns === "number" ? columns : detectColumns();
|
|
1620
|
+
const segs = planSegments({ model, workdir, tokenCount }, cols);
|
|
1621
|
+
if (segs.length === 0) {
|
|
1622
|
+
return /* @__PURE__ */ jsxDEV3(Box2, {
|
|
1623
|
+
paddingX: 1
|
|
1624
|
+
}, undefined, false, undefined, this);
|
|
1625
|
+
}
|
|
1569
1626
|
return /* @__PURE__ */ jsxDEV3(Box2, {
|
|
1570
|
-
borderStyle: "single",
|
|
1571
|
-
borderColor: "gray",
|
|
1572
1627
|
paddingX: 1,
|
|
1573
|
-
|
|
1574
|
-
children:
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
/* @__PURE__ */ jsxDEV3(Text3, {
|
|
1579
|
-
dimColor: true,
|
|
1580
|
-
children: "\uD83E\uDD16 "
|
|
1581
|
-
}, undefined, false, undefined, this),
|
|
1582
|
-
/* @__PURE__ */ jsxDEV3(Text3, {
|
|
1583
|
-
color: theme.primary,
|
|
1584
|
-
children: model
|
|
1585
|
-
}, undefined, false, undefined, this)
|
|
1586
|
-
]
|
|
1587
|
-
}, undefined, true, undefined, this) : null
|
|
1588
|
-
}, undefined, false, undefined, this),
|
|
1589
|
-
/* @__PURE__ */ jsxDEV3(Box2, {
|
|
1590
|
-
children: workdir ? /* @__PURE__ */ jsxDEV3(Text3, {
|
|
1591
|
-
children: [
|
|
1592
|
-
/* @__PURE__ */ jsxDEV3(Text3, {
|
|
1593
|
-
dimColor: true,
|
|
1594
|
-
children: "\uD83D\uDCC1 "
|
|
1595
|
-
}, undefined, false, undefined, this),
|
|
1596
|
-
/* @__PURE__ */ jsxDEV3(Text3, {
|
|
1597
|
-
children: workdir
|
|
1598
|
-
}, undefined, false, undefined, this)
|
|
1599
|
-
]
|
|
1600
|
-
}, undefined, true, undefined, this) : null
|
|
1601
|
-
}, undefined, false, undefined, this),
|
|
1602
|
-
/* @__PURE__ */ jsxDEV3(Box2, {
|
|
1628
|
+
flexDirection: "row",
|
|
1629
|
+
children: segs.map((seg, i) => {
|
|
1630
|
+
const color = seg.color === "primary" ? theme.primary : seg.color === "accent" ? theme.accent : theme.muted;
|
|
1631
|
+
const isFirst = i === 0;
|
|
1632
|
+
return /* @__PURE__ */ jsxDEV3(React2.Fragment, {
|
|
1603
1633
|
children: [
|
|
1604
|
-
|
|
1605
|
-
children: [
|
|
1606
|
-
/* @__PURE__ */ jsxDEV3(Text3, {
|
|
1607
|
-
dimColor: true,
|
|
1608
|
-
children: "tokens: "
|
|
1609
|
-
}, undefined, false, undefined, this),
|
|
1610
|
-
/* @__PURE__ */ jsxDEV3(Text3, {
|
|
1611
|
-
color: theme.accent,
|
|
1612
|
-
children: tokenCount
|
|
1613
|
-
}, undefined, false, undefined, this)
|
|
1614
|
-
]
|
|
1615
|
-
}, undefined, true, undefined, this) : null,
|
|
1616
|
-
/* @__PURE__ */ jsxDEV3(Text3, {
|
|
1634
|
+
!isFirst ? /* @__PURE__ */ jsxDEV3(Text3, {
|
|
1617
1635
|
dimColor: true,
|
|
1618
|
-
children: "
|
|
1636
|
+
children: " · "
|
|
1637
|
+
}, undefined, false, undefined, this) : null,
|
|
1638
|
+
seg.dimPrefix ? /* @__PURE__ */ jsxDEV3(Text3, {
|
|
1639
|
+
dimColor: true,
|
|
1640
|
+
children: seg.dimPrefix
|
|
1641
|
+
}, undefined, false, undefined, this) : null,
|
|
1642
|
+
/* @__PURE__ */ jsxDEV3(Text3, {
|
|
1643
|
+
color,
|
|
1644
|
+
children: seg.text
|
|
1619
1645
|
}, undefined, false, undefined, this)
|
|
1620
1646
|
]
|
|
1621
|
-
},
|
|
1622
|
-
|
|
1623
|
-
}, undefined,
|
|
1647
|
+
}, i, true, undefined, this);
|
|
1648
|
+
})
|
|
1649
|
+
}, undefined, false, undefined, this);
|
|
1624
1650
|
};
|
|
1625
1651
|
var init_StatusBar = __esm(() => {
|
|
1626
1652
|
init_theme();
|
|
@@ -1675,7 +1701,7 @@ __export(exports_repl_ink, {
|
|
|
1675
1701
|
REPLInk: () => REPLInk,
|
|
1676
1702
|
MultilineInput: () => MultilineInput
|
|
1677
1703
|
});
|
|
1678
|
-
import * as
|
|
1704
|
+
import * as React3 from "react";
|
|
1679
1705
|
import { Box as Box3, Text as Text4, Static, useApp, useInput as useInput2 } from "ink";
|
|
1680
1706
|
import { createLogger as createLogger5 } from "@ai-setting/roy-agent-core";
|
|
1681
1707
|
import { jsxDEV as jsxDEV4 } from "react/jsx-dev-runtime";
|
|
@@ -1700,7 +1726,7 @@ function REPLInk(props) {
|
|
|
1700
1726
|
onEscape
|
|
1701
1727
|
} = props;
|
|
1702
1728
|
const { exit: appExit } = useApp();
|
|
1703
|
-
const [messages, setMessages] =
|
|
1729
|
+
const [messages, setMessages] = React3.useState(() => [
|
|
1704
1730
|
withMessageId({
|
|
1705
1731
|
role: "system",
|
|
1706
1732
|
kind: "welcome",
|
|
@@ -1708,47 +1734,47 @@ function REPLInk(props) {
|
|
|
1708
1734
|
}),
|
|
1709
1735
|
...initialMessages.map(withMessageId)
|
|
1710
1736
|
]);
|
|
1711
|
-
const [mode, setMode] =
|
|
1712
|
-
const [statusBarCtx, setStatusBarCtxState] =
|
|
1737
|
+
const [mode, setMode] = React3.useState(initialMode);
|
|
1738
|
+
const [statusBarCtx, setStatusBarCtxState] = React3.useState({
|
|
1713
1739
|
mode: initialMode,
|
|
1714
1740
|
cwd: initialStatusBarCtx.cwd ?? initialContext.cwd,
|
|
1715
1741
|
gitBranch: initialStatusBarCtx.gitBranch,
|
|
1716
1742
|
model: initialStatusBarCtx.model ?? initialContext.model
|
|
1717
1743
|
});
|
|
1718
|
-
const [inputValue, setInputValue] =
|
|
1719
|
-
const [popupIndex, setPopupIndex] =
|
|
1720
|
-
const filteredCmds =
|
|
1744
|
+
const [inputValue, setInputValue] = React3.useState("");
|
|
1745
|
+
const [popupIndex, setPopupIndex] = React3.useState(0);
|
|
1746
|
+
const filteredCmds = React3.useMemo(() => {
|
|
1721
1747
|
if (!/^\/\S*$/.test(inputValue))
|
|
1722
1748
|
return [];
|
|
1723
1749
|
return filterCommands(inputValue);
|
|
1724
1750
|
}, [inputValue]);
|
|
1725
1751
|
const showPopup = filteredCmds.length > 0;
|
|
1726
|
-
|
|
1752
|
+
React3.useEffect(() => {
|
|
1727
1753
|
setPopupIndex(0);
|
|
1728
1754
|
}, [filteredCmds]);
|
|
1729
1755
|
const safePopupIndex = clampSelectedIndex(popupIndex, filteredCmds.length);
|
|
1730
|
-
const handlePopupSelect =
|
|
1756
|
+
const handlePopupSelect = React3.useCallback((cmd) => {
|
|
1731
1757
|
multilineInputRef.current?.setBuffer(cmd.cmd + " ");
|
|
1732
1758
|
setInputValue(cmd.cmd + " ");
|
|
1733
1759
|
}, []);
|
|
1734
|
-
const handlePopupClose =
|
|
1760
|
+
const handlePopupClose = React3.useCallback(() => {
|
|
1735
1761
|
const current = multilineInputRef.current?.getBuffer() ?? "";
|
|
1736
1762
|
const stripped = current.replace(/^\/+/, "");
|
|
1737
1763
|
multilineInputRef.current?.setBuffer(stripped);
|
|
1738
1764
|
setInputValue(stripped);
|
|
1739
1765
|
}, []);
|
|
1740
|
-
const handlePopupIndexChange =
|
|
1766
|
+
const handlePopupIndexChange = React3.useCallback((idx) => {
|
|
1741
1767
|
setPopupIndex(idx);
|
|
1742
1768
|
}, []);
|
|
1743
|
-
const streamingIndexRef =
|
|
1744
|
-
const lastFlushTsRef =
|
|
1769
|
+
const streamingIndexRef = React3.useRef(-1);
|
|
1770
|
+
const lastFlushTsRef = React3.useRef(0);
|
|
1745
1771
|
const STALE_STREAM_THRESHOLD_MS = 1000;
|
|
1746
|
-
const multilineInputRef =
|
|
1747
|
-
const lastEscapeTsRef =
|
|
1748
|
-
const pendingDeltaRef =
|
|
1749
|
-
const throttleTimerRef =
|
|
1750
|
-
const isFirstDeltaInStreamRef =
|
|
1751
|
-
const flushStreamingDelta =
|
|
1772
|
+
const multilineInputRef = React3.useRef(null);
|
|
1773
|
+
const lastEscapeTsRef = React3.useRef(0);
|
|
1774
|
+
const pendingDeltaRef = React3.useRef("");
|
|
1775
|
+
const throttleTimerRef = React3.useRef(null);
|
|
1776
|
+
const isFirstDeltaInStreamRef = React3.useRef(true);
|
|
1777
|
+
const flushStreamingDelta = React3.useCallback(() => {
|
|
1752
1778
|
const delta = pendingDeltaRef.current;
|
|
1753
1779
|
pendingDeltaRef.current = "";
|
|
1754
1780
|
throttleTimerRef.current = null;
|
|
@@ -1777,7 +1803,7 @@ function REPLInk(props) {
|
|
|
1777
1803
|
});
|
|
1778
1804
|
lastFlushTsRef.current = Date.now();
|
|
1779
1805
|
}, []);
|
|
1780
|
-
|
|
1806
|
+
React3.useEffect(() => {
|
|
1781
1807
|
return () => {
|
|
1782
1808
|
if (throttleTimerRef.current !== null) {
|
|
1783
1809
|
clearTimeout(throttleTimerRef.current);
|
|
@@ -1785,8 +1811,8 @@ function REPLInk(props) {
|
|
|
1785
1811
|
}
|
|
1786
1812
|
};
|
|
1787
1813
|
}, []);
|
|
1788
|
-
const apiRef =
|
|
1789
|
-
|
|
1814
|
+
const apiRef = React3.useRef(null);
|
|
1815
|
+
React3.useEffect(() => {
|
|
1790
1816
|
const api = {
|
|
1791
1817
|
appendMessage: (msg) => {
|
|
1792
1818
|
setMessages((msgs) => [...msgs, withMessageId(msg)]);
|
|
@@ -1862,7 +1888,7 @@ function REPLInk(props) {
|
|
|
1862
1888
|
apiRef.current = api;
|
|
1863
1889
|
onReady?.(api);
|
|
1864
1890
|
}, [appExit, onCommand, onReady]);
|
|
1865
|
-
const handleEscapeCascade =
|
|
1891
|
+
const handleEscapeCascade = React3.useCallback(() => {
|
|
1866
1892
|
const now = Date.now();
|
|
1867
1893
|
if (now - lastEscapeTsRef.current < 100) {
|
|
1868
1894
|
return;
|
|
@@ -1906,7 +1932,7 @@ function REPLInk(props) {
|
|
|
1906
1932
|
}
|
|
1907
1933
|
}
|
|
1908
1934
|
});
|
|
1909
|
-
const handleSubmit =
|
|
1935
|
+
const handleSubmit = React3.useCallback(async (value) => {
|
|
1910
1936
|
if (!value || value.trim() === "") {
|
|
1911
1937
|
return;
|
|
1912
1938
|
}
|
|
@@ -1935,7 +1961,7 @@ function REPLInk(props) {
|
|
|
1935
1961
|
]);
|
|
1936
1962
|
}
|
|
1937
1963
|
}, [onCommand, onSubmit]);
|
|
1938
|
-
const staticItems =
|
|
1964
|
+
const staticItems = React3.useMemo(() => {
|
|
1939
1965
|
if (messages.length <= 1)
|
|
1940
1966
|
return [];
|
|
1941
1967
|
return messages.slice(0, -1);
|
|
@@ -1943,8 +1969,8 @@ function REPLInk(props) {
|
|
|
1943
1969
|
const tailMsg = messages.length > 0 ? messages[messages.length - 1] : null;
|
|
1944
1970
|
const terminalWidth = process.stdout.columns ?? 80;
|
|
1945
1971
|
const terminalHeight = process.stdout.rows ?? 24;
|
|
1946
|
-
const dynamicTailBounds =
|
|
1947
|
-
const staticTailBounds =
|
|
1972
|
+
const dynamicTailBounds = React3.useMemo(() => computeDynamicTailBounds(terminalHeight, 12, showPopup), [terminalHeight, showPopup]);
|
|
1973
|
+
const staticTailBounds = React3.useMemo(() => computeDynamicTailBounds(terminalHeight, 12, showPopup), [terminalHeight, showPopup]);
|
|
1948
1974
|
const tailKey = messages.length > 0 ? tailMsg?.id ?? `tail-${messages.length - 1}` : "tail-empty";
|
|
1949
1975
|
const statusBarModel = shortModel(statusBarCtx.model);
|
|
1950
1976
|
const statusBarWorkdir = statusBarCtx.cwd;
|
|
@@ -2011,7 +2037,7 @@ function shortModel(model) {
|
|
|
2011
2037
|
}
|
|
2012
2038
|
async function renderInkREPL(props) {
|
|
2013
2039
|
const ink = await import("ink");
|
|
2014
|
-
const { waitUntilExit } = ink.render(
|
|
2040
|
+
const { waitUntilExit } = ink.render(React3.createElement(REPLInk, props));
|
|
2015
2041
|
await waitUntilExit();
|
|
2016
2042
|
}
|
|
2017
2043
|
var MarkdownBounded, inkLogger, _msgIdCounter = 0, MultilineInput, MessageView;
|
|
@@ -2023,7 +2049,7 @@ var init_repl_ink = __esm(() => {
|
|
|
2023
2049
|
init_Markdown();
|
|
2024
2050
|
init_StatusBar();
|
|
2025
2051
|
init_dynamic_tail();
|
|
2026
|
-
MarkdownBounded =
|
|
2052
|
+
MarkdownBounded = React3.memo(({ text, bounds, width }) => {
|
|
2027
2053
|
const out = renderBoundedAssistant(text, bounds, width);
|
|
2028
2054
|
if (!out) {
|
|
2029
2055
|
return null;
|
|
@@ -2033,16 +2059,16 @@ var init_repl_ink = __esm(() => {
|
|
|
2033
2059
|
}, undefined, false, undefined, this);
|
|
2034
2060
|
});
|
|
2035
2061
|
inkLogger = createLogger5("repl-ink");
|
|
2036
|
-
MultilineInput =
|
|
2062
|
+
MultilineInput = React3.memo(React3.forwardRef(function MultilineInput2(props, ref) {
|
|
2037
2063
|
const { onSubmit, placeholder, popupActive, onChange, onEscape } = props;
|
|
2038
|
-
const [buffer, setBuffer] =
|
|
2039
|
-
const [cursor, setCursor] =
|
|
2040
|
-
|
|
2064
|
+
const [buffer, setBuffer] = React3.useState("");
|
|
2065
|
+
const [cursor, setCursor] = React3.useState(0);
|
|
2066
|
+
React3.useEffect(() => {
|
|
2041
2067
|
if (popupActive || buffer.startsWith("/")) {
|
|
2042
2068
|
onChange?.(buffer);
|
|
2043
2069
|
}
|
|
2044
2070
|
}, [buffer, onChange, popupActive]);
|
|
2045
|
-
|
|
2071
|
+
React3.useImperativeHandle(ref, () => ({
|
|
2046
2072
|
clearBuffer: () => {
|
|
2047
2073
|
setBuffer("");
|
|
2048
2074
|
setCursor(0);
|
|
@@ -2280,7 +2306,7 @@ var init_repl_ink = __esm(() => {
|
|
|
2280
2306
|
})
|
|
2281
2307
|
}, undefined, false, undefined, this);
|
|
2282
2308
|
}));
|
|
2283
|
-
MessageView =
|
|
2309
|
+
MessageView = React3.memo(function MessageView2({
|
|
2284
2310
|
msg,
|
|
2285
2311
|
terminalWidth,
|
|
2286
2312
|
tailBounds
|
|
@@ -9460,7 +9486,7 @@ class CliQuietModeService {
|
|
|
9460
9486
|
// package.json
|
|
9461
9487
|
var package_default = {
|
|
9462
9488
|
name: "@ai-setting/roy-agent-cli",
|
|
9463
|
-
version: "1.7.
|
|
9489
|
+
version: "1.7.43",
|
|
9464
9490
|
type: "module",
|
|
9465
9491
|
description: "CLI for roy-agent - Non-interactive command execution",
|
|
9466
9492
|
main: "./dist/index.js",
|
|
@@ -13590,7 +13616,7 @@ ${chalk7.bold("╰")}${chalk7.dim("─".repeat(49))}${chalk7.bold("╯")}
|
|
|
13590
13616
|
}
|
|
13591
13617
|
function createInteractiveCommand(externalEnvService) {
|
|
13592
13618
|
return {
|
|
13593
|
-
command: "
|
|
13619
|
+
command: "run",
|
|
13594
13620
|
aliases: ["i", "repl"],
|
|
13595
13621
|
describe: "交互式模式 - 多轮对话交互",
|
|
13596
13622
|
builder: (yargs) => yargs.option("session", {
|
|
@@ -13675,7 +13701,7 @@ function createInteractiveCommand(externalEnvService) {
|
|
|
13675
13701
|
configPath: args.config,
|
|
13676
13702
|
plugins: [...componentPluginNames, ...coderPluginNames],
|
|
13677
13703
|
loadPlugins: true,
|
|
13678
|
-
commandName: "
|
|
13704
|
+
commandName: "run"
|
|
13679
13705
|
});
|
|
13680
13706
|
}
|
|
13681
13707
|
const env = envService.getEnvironment();
|
|
@@ -13982,16 +14008,16 @@ function createInteractiveCommand(externalEnvService) {
|
|
|
13982
14008
|
var InteractiveCommand = createInteractiveCommand();
|
|
13983
14009
|
|
|
13984
14010
|
// src/commands/run.ts
|
|
13985
|
-
import
|
|
14011
|
+
import React7 from "react";
|
|
13986
14012
|
import { render } from "ink";
|
|
13987
14013
|
|
|
13988
14014
|
// src/RunTui.tsx
|
|
13989
|
-
import { useState as useState6, useCallback as useCallback4, useRef as
|
|
13990
|
-
import { Box as
|
|
14015
|
+
import { useState as useState6, useCallback as useCallback4, useRef as useRef3, useEffect as useEffect5 } from "react";
|
|
14016
|
+
import { Box as Box7, Text as Text9, useApp as useApp2, useStdout } from "ink";
|
|
13991
14017
|
|
|
13992
14018
|
// src/tui/App.tsx
|
|
13993
14019
|
init_Markdown();
|
|
13994
|
-
import { Box as
|
|
14020
|
+
import { Box as Box5, Static as Static2, Text as Text7 } from "ink";
|
|
13995
14021
|
|
|
13996
14022
|
// src/tui/Spinner.tsx
|
|
13997
14023
|
init_theme();
|
|
@@ -14028,8 +14054,262 @@ var Spinner = ({
|
|
|
14028
14054
|
|
|
14029
14055
|
// src/tui/App.tsx
|
|
14030
14056
|
init_StatusBar();
|
|
14057
|
+
|
|
14058
|
+
// src/tui/ToolCall.tsx
|
|
14059
|
+
import { Box as Box4, Text as Text6 } from "ink";
|
|
14031
14060
|
init_theme();
|
|
14061
|
+
|
|
14062
|
+
// src/tui/tool-call.ts
|
|
14063
|
+
var TOOL_META = {
|
|
14064
|
+
bash: { icon: "❯", label: "Bash", argKeys: ["command", "cmd"] },
|
|
14065
|
+
shell: { icon: "❯", label: "Bash", argKeys: ["command", "cmd"] },
|
|
14066
|
+
echo: { icon: "\uD83D\uDCAC", label: "Echo", argKeys: ["message", "text"] },
|
|
14067
|
+
read_file: {
|
|
14068
|
+
icon: "\uD83D\uDCD6",
|
|
14069
|
+
label: "Read",
|
|
14070
|
+
argKeys: ["file_path", "path", "filePath", "filename"]
|
|
14071
|
+
},
|
|
14072
|
+
write_file: {
|
|
14073
|
+
icon: "\uD83D\uDCDD",
|
|
14074
|
+
label: "Write",
|
|
14075
|
+
argKeys: ["file_path", "path", "filePath", "filename"]
|
|
14076
|
+
},
|
|
14077
|
+
edit_file: {
|
|
14078
|
+
icon: "✏️",
|
|
14079
|
+
label: "Edit",
|
|
14080
|
+
argKeys: ["file_path", "path", "filePath", "filename"]
|
|
14081
|
+
},
|
|
14082
|
+
glob: { icon: "\uD83D\uDD0D", label: "Glob", argKeys: ["pattern"] },
|
|
14083
|
+
grep: { icon: "\uD83D\uDD0E", label: "Grep", argKeys: ["pattern", "query"] },
|
|
14084
|
+
query_background_process: {
|
|
14085
|
+
icon: "\uD83D\uDCE1",
|
|
14086
|
+
label: "Background Process",
|
|
14087
|
+
argKeys: ["id", "process_id"]
|
|
14088
|
+
},
|
|
14089
|
+
understand_image: {
|
|
14090
|
+
icon: "\uD83D\uDDBC️",
|
|
14091
|
+
label: "Understand Image",
|
|
14092
|
+
argKeys: ["image_path", "path", "url"]
|
|
14093
|
+
},
|
|
14094
|
+
delegate_task: {
|
|
14095
|
+
icon: "\uD83E\uDD16",
|
|
14096
|
+
label: "Task",
|
|
14097
|
+
argKeys: ["description", "prompt"]
|
|
14098
|
+
},
|
|
14099
|
+
stop_task: { icon: "\uD83D\uDED1", label: "Stop Task", argKeys: ["task_id", "id"] },
|
|
14100
|
+
task_search: { icon: "\uD83D\uDD0E", label: "Task Search", argKeys: ["query"] },
|
|
14101
|
+
ask_user: { icon: "❓", label: "Ask User", argKeys: ["question"] },
|
|
14102
|
+
workflow_run: {
|
|
14103
|
+
icon: "\uD83D\uDD00",
|
|
14104
|
+
label: "Run Workflow",
|
|
14105
|
+
argKeys: ["workflow", "name", "path"]
|
|
14106
|
+
},
|
|
14107
|
+
web_search: { icon: "\uD83C\uDF10", label: "Web Search", argKeys: ["query"] },
|
|
14108
|
+
webfetch: { icon: "\uD83C\uDF10", label: "Fetch", argKeys: ["url"] },
|
|
14109
|
+
fetch: { icon: "\uD83C\uDF10", label: "Fetch", argKeys: ["url"] },
|
|
14110
|
+
skill: { icon: "\uD83C\uDFAF", label: "Skill", argKeys: ["name"] }
|
|
14111
|
+
};
|
|
14112
|
+
var TOOL_ALIASES = {
|
|
14113
|
+
read: "read_file",
|
|
14114
|
+
write: "write_file",
|
|
14115
|
+
edit: "edit_file",
|
|
14116
|
+
search: "grep",
|
|
14117
|
+
task: "delegate_task",
|
|
14118
|
+
run_terminal_cmd: "bash",
|
|
14119
|
+
terminal: "bash"
|
|
14120
|
+
};
|
|
14121
|
+
function humanizeToolName(name) {
|
|
14122
|
+
if (!name)
|
|
14123
|
+
return "Tool";
|
|
14124
|
+
const spaced = name.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/[_-]+/g, " ").trim();
|
|
14125
|
+
return spaced.split(/\s+/).map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
|
|
14126
|
+
}
|
|
14127
|
+
function firstLine(value, max = 120) {
|
|
14128
|
+
const line = value.split(/\r?\n/)[0]?.trim() ?? "";
|
|
14129
|
+
return line.length > max ? `${line.slice(0, max - 1)}…` : line;
|
|
14130
|
+
}
|
|
14131
|
+
function summarizeArgs(args) {
|
|
14132
|
+
for (const value of Object.values(args)) {
|
|
14133
|
+
if (typeof value === "string" && value.trim())
|
|
14134
|
+
return firstLine(value);
|
|
14135
|
+
if (typeof value === "number" || typeof value === "boolean") {
|
|
14136
|
+
return String(value);
|
|
14137
|
+
}
|
|
14138
|
+
if (Array.isArray(value) && value.length > 0) {
|
|
14139
|
+
return firstLine(value.map((v) => String(v)).join(" "));
|
|
14140
|
+
}
|
|
14141
|
+
}
|
|
14142
|
+
return "";
|
|
14143
|
+
}
|
|
14144
|
+
function describeToolCall(name, args) {
|
|
14145
|
+
const meta = TOOL_META[name] ?? TOOL_META[TOOL_ALIASES[name] ?? ""];
|
|
14146
|
+
const label = meta?.label ?? humanizeToolName(name);
|
|
14147
|
+
const icon = meta?.icon ?? "⚙";
|
|
14148
|
+
let detail = "";
|
|
14149
|
+
if (args && Object.keys(args).length > 0) {
|
|
14150
|
+
const keys = meta?.argKeys ?? [];
|
|
14151
|
+
for (const key of keys) {
|
|
14152
|
+
const value = args[key];
|
|
14153
|
+
if (typeof value === "string" && value.trim()) {
|
|
14154
|
+
detail = firstLine(value);
|
|
14155
|
+
break;
|
|
14156
|
+
}
|
|
14157
|
+
if (value !== undefined && value !== null && typeof value !== "object") {
|
|
14158
|
+
detail = String(value);
|
|
14159
|
+
break;
|
|
14160
|
+
}
|
|
14161
|
+
}
|
|
14162
|
+
if (!detail)
|
|
14163
|
+
detail = summarizeArgs(args);
|
|
14164
|
+
}
|
|
14165
|
+
return { icon, label, detail };
|
|
14166
|
+
}
|
|
14167
|
+
function stringifyToolOutput(output) {
|
|
14168
|
+
if (output === undefined || output === null)
|
|
14169
|
+
return "";
|
|
14170
|
+
if (typeof output === "string")
|
|
14171
|
+
return output;
|
|
14172
|
+
if (typeof output === "object") {
|
|
14173
|
+
const obj = output;
|
|
14174
|
+
for (const key of ["output", "text", "content", "message"]) {
|
|
14175
|
+
if (typeof obj[key] === "string")
|
|
14176
|
+
return obj[key];
|
|
14177
|
+
}
|
|
14178
|
+
try {
|
|
14179
|
+
return JSON.stringify(output, null, 2);
|
|
14180
|
+
} catch {
|
|
14181
|
+
return String(output);
|
|
14182
|
+
}
|
|
14183
|
+
}
|
|
14184
|
+
return String(output);
|
|
14185
|
+
}
|
|
14186
|
+
function formatToolOutput(output, options = {}) {
|
|
14187
|
+
const { maxLines = 12, maxChars = 200 } = options;
|
|
14188
|
+
const text = stringifyToolOutput(output).replace(/\r\n?/g, `
|
|
14189
|
+
`).replace(/\s+$/, "");
|
|
14190
|
+
if (!text)
|
|
14191
|
+
return "";
|
|
14192
|
+
const lines = text.split(`
|
|
14193
|
+
`);
|
|
14194
|
+
const shown = lines.slice(0, maxLines);
|
|
14195
|
+
const hidden = lines.length - shown.length;
|
|
14196
|
+
const body = shown.map((line) => firstLine(line, maxChars)).join(`
|
|
14197
|
+
`);
|
|
14198
|
+
return hidden > 0 ? `${body}
|
|
14199
|
+
… (+${hidden} more lines)` : body;
|
|
14200
|
+
}
|
|
14201
|
+
function formatDuration(ms) {
|
|
14202
|
+
if (!Number.isFinite(ms) || ms < 0)
|
|
14203
|
+
return "";
|
|
14204
|
+
if (ms < 1000)
|
|
14205
|
+
return `${Math.round(ms)}ms`;
|
|
14206
|
+
const seconds = ms / 1000;
|
|
14207
|
+
if (seconds < 10)
|
|
14208
|
+
return `${seconds.toFixed(1)}s`;
|
|
14209
|
+
if (seconds < 60)
|
|
14210
|
+
return `${Math.round(seconds)}s`;
|
|
14211
|
+
const minutes = Math.floor(seconds / 60);
|
|
14212
|
+
const rest = Math.round(seconds % 60);
|
|
14213
|
+
return `${minutes}m${rest.toString().padStart(2, "0")}s`;
|
|
14214
|
+
}
|
|
14215
|
+
|
|
14216
|
+
// src/tui/ToolCall.tsx
|
|
14032
14217
|
import { jsxDEV as jsxDEV6 } from "react/jsx-dev-runtime";
|
|
14218
|
+
var StatusBadge = ({ info }) => {
|
|
14219
|
+
const theme = getTheme();
|
|
14220
|
+
if (info.status === "running") {
|
|
14221
|
+
return /* @__PURE__ */ jsxDEV6(Spinner, {
|
|
14222
|
+
label: "running"
|
|
14223
|
+
}, undefined, false, undefined, this);
|
|
14224
|
+
}
|
|
14225
|
+
const duration = info.durationMs !== undefined ? ` ${formatDuration(info.durationMs)}` : "";
|
|
14226
|
+
if (info.status === "error") {
|
|
14227
|
+
return /* @__PURE__ */ jsxDEV6(Text6, {
|
|
14228
|
+
color: theme.error,
|
|
14229
|
+
children: [
|
|
14230
|
+
"✗",
|
|
14231
|
+
duration || " failed"
|
|
14232
|
+
]
|
|
14233
|
+
}, undefined, true, undefined, this);
|
|
14234
|
+
}
|
|
14235
|
+
return /* @__PURE__ */ jsxDEV6(Text6, {
|
|
14236
|
+
color: theme.success,
|
|
14237
|
+
children: [
|
|
14238
|
+
"✓",
|
|
14239
|
+
duration
|
|
14240
|
+
]
|
|
14241
|
+
}, undefined, true, undefined, this);
|
|
14242
|
+
};
|
|
14243
|
+
var ToolCall = ({
|
|
14244
|
+
info,
|
|
14245
|
+
showOutput = true
|
|
14246
|
+
}) => {
|
|
14247
|
+
const theme = getTheme();
|
|
14248
|
+
const { icon, label, detail } = describeToolCall(info.name, info.args);
|
|
14249
|
+
const outputLines = showOutput && info.output ? info.output.split(`
|
|
14250
|
+
`) : [];
|
|
14251
|
+
const errorLines = showOutput && info.error ? info.error.split(`
|
|
14252
|
+
`).slice(0, 12) : [];
|
|
14253
|
+
return /* @__PURE__ */ jsxDEV6(Box4, {
|
|
14254
|
+
flexDirection: "column",
|
|
14255
|
+
marginBottom: 1,
|
|
14256
|
+
children: [
|
|
14257
|
+
/* @__PURE__ */ jsxDEV6(Box4, {
|
|
14258
|
+
children: [
|
|
14259
|
+
/* @__PURE__ */ jsxDEV6(Text6, {
|
|
14260
|
+
color: theme.tool,
|
|
14261
|
+
children: [
|
|
14262
|
+
icon,
|
|
14263
|
+
" "
|
|
14264
|
+
]
|
|
14265
|
+
}, undefined, true, undefined, this),
|
|
14266
|
+
/* @__PURE__ */ jsxDEV6(Text6, {
|
|
14267
|
+
bold: true,
|
|
14268
|
+
color: theme.tool,
|
|
14269
|
+
children: label
|
|
14270
|
+
}, undefined, false, undefined, this),
|
|
14271
|
+
/* @__PURE__ */ jsxDEV6(Text6, {
|
|
14272
|
+
children: " "
|
|
14273
|
+
}, undefined, false, undefined, this),
|
|
14274
|
+
/* @__PURE__ */ jsxDEV6(StatusBadge, {
|
|
14275
|
+
info
|
|
14276
|
+
}, undefined, false, undefined, this)
|
|
14277
|
+
]
|
|
14278
|
+
}, undefined, true, undefined, this),
|
|
14279
|
+
detail ? /* @__PURE__ */ jsxDEV6(Box4, {
|
|
14280
|
+
marginLeft: 2,
|
|
14281
|
+
children: /* @__PURE__ */ jsxDEV6(Text6, {
|
|
14282
|
+
dimColor: true,
|
|
14283
|
+
children: detail
|
|
14284
|
+
}, undefined, false, undefined, this)
|
|
14285
|
+
}, undefined, false, undefined, this) : null,
|
|
14286
|
+
outputLines.map((line, i) => /* @__PURE__ */ jsxDEV6(Box4, {
|
|
14287
|
+
marginLeft: 2,
|
|
14288
|
+
children: /* @__PURE__ */ jsxDEV6(Text6, {
|
|
14289
|
+
dimColor: true,
|
|
14290
|
+
children: [
|
|
14291
|
+
i === 0 ? "└ " : " ",
|
|
14292
|
+
line
|
|
14293
|
+
]
|
|
14294
|
+
}, undefined, true, undefined, this)
|
|
14295
|
+
}, `out-${i}`, false, undefined, this)),
|
|
14296
|
+
errorLines.map((line, i) => /* @__PURE__ */ jsxDEV6(Box4, {
|
|
14297
|
+
marginLeft: 2,
|
|
14298
|
+
children: /* @__PURE__ */ jsxDEV6(Text6, {
|
|
14299
|
+
color: theme.error,
|
|
14300
|
+
children: [
|
|
14301
|
+
i === 0 ? "└ " : " ",
|
|
14302
|
+
line
|
|
14303
|
+
]
|
|
14304
|
+
}, undefined, true, undefined, this)
|
|
14305
|
+
}, `err-${i}`, false, undefined, this))
|
|
14306
|
+
]
|
|
14307
|
+
}, undefined, true, undefined, this);
|
|
14308
|
+
};
|
|
14309
|
+
|
|
14310
|
+
// src/tui/App.tsx
|
|
14311
|
+
init_theme();
|
|
14312
|
+
import { jsxDEV as jsxDEV7 } from "react/jsx-dev-runtime";
|
|
14033
14313
|
var ROLE_PREFIX = {
|
|
14034
14314
|
user: "\uD83D\uDC64 You",
|
|
14035
14315
|
assistant: "\uD83E\uDD16 Agent",
|
|
@@ -14038,7 +14318,7 @@ var ROLE_PREFIX = {
|
|
|
14038
14318
|
var RoleLabel = ({ role }) => {
|
|
14039
14319
|
const theme = getTheme();
|
|
14040
14320
|
const roleColor = role === "user" ? theme.user : role === "assistant" ? theme.assistant : theme.tool;
|
|
14041
|
-
return /* @__PURE__ */
|
|
14321
|
+
return /* @__PURE__ */ jsxDEV7(Text7, {
|
|
14042
14322
|
bold: true,
|
|
14043
14323
|
color: roleColor,
|
|
14044
14324
|
children: ROLE_PREFIX[role]
|
|
@@ -14046,16 +14326,16 @@ var RoleLabel = ({ role }) => {
|
|
|
14046
14326
|
};
|
|
14047
14327
|
var HistoryEntry = ({ item }) => {
|
|
14048
14328
|
if (item.role === "user") {
|
|
14049
|
-
return /* @__PURE__ */
|
|
14329
|
+
return /* @__PURE__ */ jsxDEV7(Box5, {
|
|
14050
14330
|
flexDirection: "column",
|
|
14051
14331
|
marginBottom: 1,
|
|
14052
14332
|
children: [
|
|
14053
|
-
/* @__PURE__ */
|
|
14333
|
+
/* @__PURE__ */ jsxDEV7(RoleLabel, {
|
|
14054
14334
|
role: "user"
|
|
14055
14335
|
}, undefined, false, undefined, this),
|
|
14056
|
-
/* @__PURE__ */
|
|
14336
|
+
/* @__PURE__ */ jsxDEV7(Box5, {
|
|
14057
14337
|
marginLeft: 2,
|
|
14058
|
-
children: /* @__PURE__ */
|
|
14338
|
+
children: /* @__PURE__ */ jsxDEV7(Text7, {
|
|
14059
14339
|
children: item.content
|
|
14060
14340
|
}, undefined, false, undefined, this)
|
|
14061
14341
|
}, undefined, false, undefined, this)
|
|
@@ -14063,16 +14343,21 @@ var HistoryEntry = ({ item }) => {
|
|
|
14063
14343
|
}, undefined, true, undefined, this);
|
|
14064
14344
|
}
|
|
14065
14345
|
if (item.role === "tool") {
|
|
14066
|
-
|
|
14346
|
+
if (item.tool) {
|
|
14347
|
+
return /* @__PURE__ */ jsxDEV7(ToolCall, {
|
|
14348
|
+
info: item.tool
|
|
14349
|
+
}, undefined, false, undefined, this);
|
|
14350
|
+
}
|
|
14351
|
+
return /* @__PURE__ */ jsxDEV7(Box5, {
|
|
14067
14352
|
flexDirection: "column",
|
|
14068
14353
|
marginBottom: 1,
|
|
14069
14354
|
children: [
|
|
14070
|
-
/* @__PURE__ */
|
|
14355
|
+
/* @__PURE__ */ jsxDEV7(RoleLabel, {
|
|
14071
14356
|
role: "tool"
|
|
14072
14357
|
}, undefined, false, undefined, this),
|
|
14073
|
-
/* @__PURE__ */
|
|
14358
|
+
/* @__PURE__ */ jsxDEV7(Box5, {
|
|
14074
14359
|
marginLeft: 2,
|
|
14075
|
-
children: /* @__PURE__ */
|
|
14360
|
+
children: /* @__PURE__ */ jsxDEV7(Text7, {
|
|
14076
14361
|
dimColor: true,
|
|
14077
14362
|
children: item.content
|
|
14078
14363
|
}, undefined, false, undefined, this)
|
|
@@ -14080,17 +14365,17 @@ var HistoryEntry = ({ item }) => {
|
|
|
14080
14365
|
]
|
|
14081
14366
|
}, undefined, true, undefined, this);
|
|
14082
14367
|
}
|
|
14083
|
-
return /* @__PURE__ */
|
|
14368
|
+
return /* @__PURE__ */ jsxDEV7(Box5, {
|
|
14084
14369
|
flexDirection: "column",
|
|
14085
14370
|
marginBottom: 1,
|
|
14086
14371
|
children: [
|
|
14087
|
-
/* @__PURE__ */
|
|
14372
|
+
item.continuation ? null : /* @__PURE__ */ jsxDEV7(RoleLabel, {
|
|
14088
14373
|
role: "assistant"
|
|
14089
14374
|
}, undefined, false, undefined, this),
|
|
14090
|
-
/* @__PURE__ */
|
|
14375
|
+
/* @__PURE__ */ jsxDEV7(Box5, {
|
|
14091
14376
|
marginLeft: 2,
|
|
14092
14377
|
flexDirection: "column",
|
|
14093
|
-
children: /* @__PURE__ */
|
|
14378
|
+
children: /* @__PURE__ */ jsxDEV7(Markdown, {
|
|
14094
14379
|
text: item.content
|
|
14095
14380
|
}, undefined, false, undefined, this)
|
|
14096
14381
|
}, undefined, false, undefined, this)
|
|
@@ -14103,66 +14388,69 @@ var App = ({
|
|
|
14103
14388
|
model,
|
|
14104
14389
|
workdir,
|
|
14105
14390
|
streamingText,
|
|
14391
|
+
liveTools,
|
|
14106
14392
|
tokenCount
|
|
14107
14393
|
}) => {
|
|
14108
14394
|
const theme = getTheme();
|
|
14109
|
-
return /* @__PURE__ */
|
|
14395
|
+
return /* @__PURE__ */ jsxDEV7(Box5, {
|
|
14110
14396
|
flexDirection: "column",
|
|
14111
|
-
minHeight: 20,
|
|
14112
14397
|
children: [
|
|
14113
|
-
/* @__PURE__ */
|
|
14398
|
+
/* @__PURE__ */ jsxDEV7(Box5, {
|
|
14114
14399
|
borderStyle: "round",
|
|
14115
14400
|
borderColor: theme.primary,
|
|
14116
14401
|
paddingX: 1,
|
|
14117
14402
|
marginBottom: 1,
|
|
14118
14403
|
children: [
|
|
14119
|
-
/* @__PURE__ */
|
|
14404
|
+
/* @__PURE__ */ jsxDEV7(Text7, {
|
|
14120
14405
|
bold: true,
|
|
14121
14406
|
color: theme.primary,
|
|
14122
14407
|
children: "\uD83D\uDE80 Roy Agent"
|
|
14123
14408
|
}, undefined, false, undefined, this),
|
|
14124
|
-
/* @__PURE__ */
|
|
14409
|
+
/* @__PURE__ */ jsxDEV7(Text7, {
|
|
14125
14410
|
dimColor: true,
|
|
14126
14411
|
children: " — interactive TUI"
|
|
14127
14412
|
}, undefined, false, undefined, this)
|
|
14128
14413
|
]
|
|
14129
14414
|
}, undefined, true, undefined, this),
|
|
14130
|
-
/* @__PURE__ */
|
|
14415
|
+
/* @__PURE__ */ jsxDEV7(Static2, {
|
|
14131
14416
|
items: history,
|
|
14132
|
-
children: (item, index) => /* @__PURE__ */
|
|
14417
|
+
children: (item, index) => /* @__PURE__ */ jsxDEV7(HistoryEntry, {
|
|
14133
14418
|
item
|
|
14134
14419
|
}, item.id ?? `${item.role}:${index}`, false, undefined, this)
|
|
14135
14420
|
}, undefined, false, undefined, this),
|
|
14136
|
-
status === "thinking" ? /* @__PURE__ */
|
|
14421
|
+
status === "thinking" ? /* @__PURE__ */ jsxDEV7(Box5, {
|
|
14137
14422
|
marginBottom: 1,
|
|
14138
|
-
children: /* @__PURE__ */
|
|
14423
|
+
children: /* @__PURE__ */ jsxDEV7(Spinner, {
|
|
14139
14424
|
label: "Agent is thinking..."
|
|
14140
14425
|
}, undefined, false, undefined, this)
|
|
14141
14426
|
}, undefined, false, undefined, this) : null,
|
|
14142
|
-
status === "streaming" && streamingText ? /* @__PURE__ */
|
|
14427
|
+
status === "streaming" && streamingText ? /* @__PURE__ */ jsxDEV7(Box5, {
|
|
14143
14428
|
flexDirection: "column",
|
|
14144
14429
|
marginBottom: 1,
|
|
14145
14430
|
children: [
|
|
14146
|
-
/* @__PURE__ */
|
|
14431
|
+
/* @__PURE__ */ jsxDEV7(RoleLabel, {
|
|
14147
14432
|
role: "assistant"
|
|
14148
14433
|
}, undefined, false, undefined, this),
|
|
14149
|
-
/* @__PURE__ */
|
|
14434
|
+
/* @__PURE__ */ jsxDEV7(Box5, {
|
|
14150
14435
|
marginLeft: 2,
|
|
14151
|
-
children: /* @__PURE__ */
|
|
14436
|
+
children: /* @__PURE__ */ jsxDEV7(Markdown, {
|
|
14152
14437
|
text: streamingText
|
|
14153
14438
|
}, undefined, false, undefined, this)
|
|
14154
14439
|
}, undefined, false, undefined, this)
|
|
14155
14440
|
]
|
|
14156
|
-
}, undefined, true, undefined, this) : status === "streaming" ? /* @__PURE__ */
|
|
14441
|
+
}, undefined, true, undefined, this) : status === "streaming" ? /* @__PURE__ */ jsxDEV7(Box5, {
|
|
14157
14442
|
marginBottom: 1,
|
|
14158
|
-
children: /* @__PURE__ */
|
|
14443
|
+
children: /* @__PURE__ */ jsxDEV7(Spinner, {
|
|
14159
14444
|
label: "Generating response..."
|
|
14160
14445
|
}, undefined, false, undefined, this)
|
|
14161
14446
|
}, undefined, false, undefined, this) : null,
|
|
14162
|
-
/* @__PURE__ */
|
|
14163
|
-
|
|
14164
|
-
|
|
14165
|
-
|
|
14447
|
+
liveTools && liveTools.length > 0 ? /* @__PURE__ */ jsxDEV7(Box5, {
|
|
14448
|
+
flexDirection: "column",
|
|
14449
|
+
children: liveTools.map((tool, i) => /* @__PURE__ */ jsxDEV7(ToolCall, {
|
|
14450
|
+
info: tool
|
|
14451
|
+
}, `live-tool-${i}`, false, undefined, this))
|
|
14452
|
+
}, undefined, false, undefined, this) : null,
|
|
14453
|
+
/* @__PURE__ */ jsxDEV7(StatusBar, {
|
|
14166
14454
|
model,
|
|
14167
14455
|
workdir,
|
|
14168
14456
|
tokenCount
|
|
@@ -14176,9 +14464,79 @@ init_Markdown();
|
|
|
14176
14464
|
init_StatusBar();
|
|
14177
14465
|
|
|
14178
14466
|
// src/tui/TextInput.tsx
|
|
14179
|
-
import { useEffect as useEffect4, useState as useState4 } from "react";
|
|
14180
|
-
import { Box as
|
|
14181
|
-
|
|
14467
|
+
import { useEffect as useEffect4, useReducer, useState as useState4 } from "react";
|
|
14468
|
+
import { Box as Box6, Text as Text8, useInput as useInput3 } from "ink";
|
|
14469
|
+
|
|
14470
|
+
// src/tui/text-input-editing.ts
|
|
14471
|
+
function clampCursor(value, cursor) {
|
|
14472
|
+
if (!Number.isFinite(cursor))
|
|
14473
|
+
return value.length;
|
|
14474
|
+
return Math.max(0, Math.min(Math.trunc(cursor), value.length));
|
|
14475
|
+
}
|
|
14476
|
+
function insertText(state, text) {
|
|
14477
|
+
const cursor = clampCursor(state.value, state.cursor);
|
|
14478
|
+
const value = state.value.slice(0, cursor) + text + state.value.slice(cursor);
|
|
14479
|
+
return { value, cursor: cursor + text.length };
|
|
14480
|
+
}
|
|
14481
|
+
function deleteBefore(state) {
|
|
14482
|
+
const cursor = clampCursor(state.value, state.cursor);
|
|
14483
|
+
if (cursor === 0)
|
|
14484
|
+
return { value: state.value, cursor };
|
|
14485
|
+
const value = state.value.slice(0, cursor - 1) + state.value.slice(cursor);
|
|
14486
|
+
return { value, cursor: cursor - 1 };
|
|
14487
|
+
}
|
|
14488
|
+
function deleteAt(state) {
|
|
14489
|
+
const { value } = state;
|
|
14490
|
+
const cursor = clampCursor(value, state.cursor);
|
|
14491
|
+
if (cursor >= value.length)
|
|
14492
|
+
return { value, cursor };
|
|
14493
|
+
return { value: value.slice(0, cursor) + value.slice(cursor + 1), cursor };
|
|
14494
|
+
}
|
|
14495
|
+
function moveLeft(state) {
|
|
14496
|
+
return {
|
|
14497
|
+
value: state.value,
|
|
14498
|
+
cursor: Math.max(0, clampCursor(state.value, state.cursor) - 1)
|
|
14499
|
+
};
|
|
14500
|
+
}
|
|
14501
|
+
function moveRight(state) {
|
|
14502
|
+
const cursor = clampCursor(state.value, state.cursor);
|
|
14503
|
+
return {
|
|
14504
|
+
value: state.value,
|
|
14505
|
+
cursor: Math.min(state.value.length, cursor + 1)
|
|
14506
|
+
};
|
|
14507
|
+
}
|
|
14508
|
+
function splitAtCursor(state) {
|
|
14509
|
+
const cursor = clampCursor(state.value, state.cursor);
|
|
14510
|
+
return {
|
|
14511
|
+
before: state.value.slice(0, cursor),
|
|
14512
|
+
after: state.value.slice(cursor),
|
|
14513
|
+
cursor
|
|
14514
|
+
};
|
|
14515
|
+
}
|
|
14516
|
+
function editReducer(state, action) {
|
|
14517
|
+
switch (action.type) {
|
|
14518
|
+
case "insert":
|
|
14519
|
+
return insertText(state, action.text);
|
|
14520
|
+
case "backspace":
|
|
14521
|
+
return deleteBefore(state);
|
|
14522
|
+
case "delete":
|
|
14523
|
+
return deleteAt(state);
|
|
14524
|
+
case "left":
|
|
14525
|
+
return moveLeft(state);
|
|
14526
|
+
case "right":
|
|
14527
|
+
return moveRight(state);
|
|
14528
|
+
case "set":
|
|
14529
|
+
return {
|
|
14530
|
+
value: action.value,
|
|
14531
|
+
cursor: action.cursor ?? action.value.length
|
|
14532
|
+
};
|
|
14533
|
+
case "clear":
|
|
14534
|
+
return { value: "", cursor: 0 };
|
|
14535
|
+
}
|
|
14536
|
+
}
|
|
14537
|
+
|
|
14538
|
+
// src/tui/TextInput.tsx
|
|
14539
|
+
import { jsxDEV as jsxDEV8, Fragment } from "react/jsx-dev-runtime";
|
|
14182
14540
|
var TextInput = ({
|
|
14183
14541
|
label,
|
|
14184
14542
|
prefix,
|
|
@@ -14187,7 +14545,10 @@ var TextInput = ({
|
|
|
14187
14545
|
placeholder,
|
|
14188
14546
|
history = []
|
|
14189
14547
|
}) => {
|
|
14190
|
-
const [value,
|
|
14548
|
+
const [{ value, cursor }, dispatch] = useReducer(editReducer, {
|
|
14549
|
+
value: "",
|
|
14550
|
+
cursor: 0
|
|
14551
|
+
});
|
|
14191
14552
|
const [historyIndex, setHistoryIndex] = useState4(-1);
|
|
14192
14553
|
const [draft, setDraft] = useState4("");
|
|
14193
14554
|
useEffect4(() => {
|
|
@@ -14200,7 +14561,7 @@ var TextInput = ({
|
|
|
14200
14561
|
}
|
|
14201
14562
|
if (key.return) {
|
|
14202
14563
|
const submitted = value;
|
|
14203
|
-
|
|
14564
|
+
dispatch({ type: "clear" });
|
|
14204
14565
|
setHistoryIndex(-1);
|
|
14205
14566
|
setDraft("");
|
|
14206
14567
|
onSubmit(submitted);
|
|
@@ -14210,7 +14571,15 @@ var TextInput = ({
|
|
|
14210
14571
|
if (historyIndex !== -1) {
|
|
14211
14572
|
setHistoryIndex(-1);
|
|
14212
14573
|
}
|
|
14213
|
-
|
|
14574
|
+
dispatch({ type: "backspace" });
|
|
14575
|
+
return;
|
|
14576
|
+
}
|
|
14577
|
+
if (key.leftArrow) {
|
|
14578
|
+
dispatch({ type: "left" });
|
|
14579
|
+
return;
|
|
14580
|
+
}
|
|
14581
|
+
if (key.rightArrow) {
|
|
14582
|
+
dispatch({ type: "right" });
|
|
14214
14583
|
return;
|
|
14215
14584
|
}
|
|
14216
14585
|
if (key.upArrow) {
|
|
@@ -14221,7 +14590,7 @@ var TextInput = ({
|
|
|
14221
14590
|
}
|
|
14222
14591
|
const nextIndex = Math.min(historyIndex + 1, history.length - 1);
|
|
14223
14592
|
setHistoryIndex(nextIndex);
|
|
14224
|
-
|
|
14593
|
+
dispatch({ type: "set", value: history[nextIndex] ?? "" });
|
|
14225
14594
|
return;
|
|
14226
14595
|
}
|
|
14227
14596
|
if (key.downArrow) {
|
|
@@ -14230,10 +14599,10 @@ var TextInput = ({
|
|
|
14230
14599
|
const nextIndex = historyIndex - 1;
|
|
14231
14600
|
if (nextIndex < 0) {
|
|
14232
14601
|
setHistoryIndex(-1);
|
|
14233
|
-
|
|
14602
|
+
dispatch({ type: "set", value: draft });
|
|
14234
14603
|
} else {
|
|
14235
14604
|
setHistoryIndex(nextIndex);
|
|
14236
|
-
|
|
14605
|
+
dispatch({ type: "set", value: history[nextIndex] ?? "" });
|
|
14237
14606
|
}
|
|
14238
14607
|
return;
|
|
14239
14608
|
}
|
|
@@ -14241,12 +14610,13 @@ var TextInput = ({
|
|
|
14241
14610
|
setHistoryIndex(-1);
|
|
14242
14611
|
}
|
|
14243
14612
|
if (input) {
|
|
14244
|
-
|
|
14613
|
+
dispatch({ type: "insert", text: input });
|
|
14245
14614
|
}
|
|
14246
14615
|
});
|
|
14247
|
-
|
|
14616
|
+
const { before, after } = splitAtCursor({ value, cursor });
|
|
14617
|
+
return /* @__PURE__ */ jsxDEV8(Box6, {
|
|
14248
14618
|
children: [
|
|
14249
|
-
/* @__PURE__ */
|
|
14619
|
+
/* @__PURE__ */ jsxDEV8(Text8, {
|
|
14250
14620
|
bold: true,
|
|
14251
14621
|
color: "green",
|
|
14252
14622
|
children: [
|
|
@@ -14254,21 +14624,42 @@ var TextInput = ({
|
|
|
14254
14624
|
" "
|
|
14255
14625
|
]
|
|
14256
14626
|
}, undefined, true, undefined, this),
|
|
14257
|
-
/* @__PURE__ */
|
|
14627
|
+
/* @__PURE__ */ jsxDEV8(Text8, {
|
|
14258
14628
|
bold: true,
|
|
14259
14629
|
children: [
|
|
14260
14630
|
label,
|
|
14261
14631
|
": "
|
|
14262
14632
|
]
|
|
14263
14633
|
}, undefined, true, undefined, this),
|
|
14264
|
-
/* @__PURE__ */
|
|
14265
|
-
children:
|
|
14634
|
+
value ? /* @__PURE__ */ jsxDEV8(Fragment, {
|
|
14635
|
+
children: [
|
|
14636
|
+
/* @__PURE__ */ jsxDEV8(Text8, {
|
|
14637
|
+
children: before
|
|
14638
|
+
}, undefined, false, undefined, this),
|
|
14639
|
+
/* @__PURE__ */ jsxDEV8(Text8, {
|
|
14640
|
+
inverse: true,
|
|
14641
|
+
children: "▌"
|
|
14642
|
+
}, undefined, false, undefined, this),
|
|
14643
|
+
/* @__PURE__ */ jsxDEV8(Text8, {
|
|
14644
|
+
children: after
|
|
14645
|
+
}, undefined, false, undefined, this)
|
|
14646
|
+
]
|
|
14647
|
+
}, undefined, true, undefined, this) : placeholder ? /* @__PURE__ */ jsxDEV8(Fragment, {
|
|
14648
|
+
children: [
|
|
14649
|
+
/* @__PURE__ */ jsxDEV8(Text8, {
|
|
14650
|
+
inverse: true,
|
|
14651
|
+
children: "▌"
|
|
14652
|
+
}, undefined, false, undefined, this),
|
|
14653
|
+
/* @__PURE__ */ jsxDEV8(Text8, {
|
|
14654
|
+
dimColor: true,
|
|
14655
|
+
children: placeholder
|
|
14656
|
+
}, undefined, false, undefined, this)
|
|
14657
|
+
]
|
|
14658
|
+
}, undefined, true, undefined, this) : /* @__PURE__ */ jsxDEV8(Text8, {
|
|
14659
|
+
inverse: true,
|
|
14660
|
+
children: "▌"
|
|
14266
14661
|
}, undefined, false, undefined, this),
|
|
14267
|
-
|
|
14268
|
-
dimColor: true,
|
|
14269
|
-
children: placeholder
|
|
14270
|
-
}, undefined, false, undefined, this) : null,
|
|
14271
|
-
historyIndex !== -1 ? /* @__PURE__ */ jsxDEV7(Text7, {
|
|
14662
|
+
historyIndex !== -1 ? /* @__PURE__ */ jsxDEV8(Text8, {
|
|
14272
14663
|
dimColor: true,
|
|
14273
14664
|
children: " (history)"
|
|
14274
14665
|
}, undefined, false, undefined, this) : null
|
|
@@ -14281,37 +14672,119 @@ init_theme();
|
|
|
14281
14672
|
init_welcome_card2();
|
|
14282
14673
|
|
|
14283
14674
|
// src/tui/use-streaming-accumulator.ts
|
|
14284
|
-
import { useCallback as useCallback3, useState as useState5 } from "react";
|
|
14285
|
-
|
|
14675
|
+
import { useCallback as useCallback3, useRef as useRef2, useState as useState5 } from "react";
|
|
14676
|
+
|
|
14677
|
+
// src/tui/scrollback.ts
|
|
14678
|
+
var FENCE_RE = /^\s*(```|~~~)/;
|
|
14679
|
+
function lineStartOffset(lines, index) {
|
|
14680
|
+
let off = 0;
|
|
14681
|
+
for (let i = 0;i < index; i++)
|
|
14682
|
+
off += (lines[i] ?? "").length + 1;
|
|
14683
|
+
return off;
|
|
14684
|
+
}
|
|
14685
|
+
function splitStable(text, opts = {}) {
|
|
14686
|
+
if (!text)
|
|
14687
|
+
return { stable: "", tail: "" };
|
|
14688
|
+
if (opts.force)
|
|
14689
|
+
return { stable: text, tail: "" };
|
|
14690
|
+
const lines = text.split(`
|
|
14691
|
+
`);
|
|
14692
|
+
let inFence = false;
|
|
14693
|
+
let boundary = -1;
|
|
14694
|
+
for (let i = 0;i < lines.length; i++) {
|
|
14695
|
+
const line = lines[i] ?? "";
|
|
14696
|
+
const isLast = i === lines.length - 1;
|
|
14697
|
+
const next = lineStartOffset(lines, i) + line.length + 1;
|
|
14698
|
+
if (FENCE_RE.test(line)) {
|
|
14699
|
+
inFence = !inFence;
|
|
14700
|
+
} else if (!inFence && !isLast && line.trim() === "") {
|
|
14701
|
+
boundary = next;
|
|
14702
|
+
}
|
|
14703
|
+
}
|
|
14704
|
+
if (boundary > 0) {
|
|
14705
|
+
return { stable: text.slice(0, boundary), tail: text.slice(boundary) };
|
|
14706
|
+
}
|
|
14707
|
+
const maxTailLines = opts.maxTailLines ?? 0;
|
|
14708
|
+
if (maxTailLines > 0 && !inFence && lines.length > maxTailLines) {
|
|
14709
|
+
const keepFrom = lines.length - 1;
|
|
14710
|
+
const off = lineStartOffset(lines, keepFrom);
|
|
14711
|
+
if (off > 0) {
|
|
14712
|
+
return { stable: text.slice(0, off), tail: text.slice(off) };
|
|
14713
|
+
}
|
|
14714
|
+
}
|
|
14715
|
+
return { stable: "", tail: text };
|
|
14716
|
+
}
|
|
14717
|
+
|
|
14718
|
+
// src/tui/use-streaming-accumulator.ts
|
|
14719
|
+
var DEFAULT_MAX_TAIL_LINES = 24;
|
|
14720
|
+
function useStreamingAccumulator(options = {}) {
|
|
14721
|
+
const maxTailLines = options.maxTailLines ?? DEFAULT_MAX_TAIL_LINES;
|
|
14286
14722
|
const [status, setStatus] = useState5("idle");
|
|
14287
14723
|
const [streamingText, setStreamingText] = useState5("");
|
|
14724
|
+
const pendingRef = useRef2("");
|
|
14725
|
+
const emittedRef = useRef2("");
|
|
14288
14726
|
const start = useCallback3(() => {
|
|
14727
|
+
pendingRef.current = "";
|
|
14728
|
+
emittedRef.current = "";
|
|
14289
14729
|
setStreamingText("");
|
|
14290
14730
|
setStatus("thinking");
|
|
14291
14731
|
}, []);
|
|
14292
14732
|
const append = useCallback3((chunk) => {
|
|
14733
|
+
pendingRef.current += chunk;
|
|
14734
|
+
setStreamingText(pendingRef.current);
|
|
14293
14735
|
setStatus("streaming");
|
|
14294
|
-
setStreamingText((prev) => prev + chunk);
|
|
14295
14736
|
}, []);
|
|
14296
|
-
const
|
|
14297
|
-
|
|
14298
|
-
|
|
14299
|
-
setStreamingText((prev) => {
|
|
14300
|
-
snapshot = prev;
|
|
14301
|
-
return "";
|
|
14737
|
+
const drainStable = useCallback3(() => {
|
|
14738
|
+
const { stable, tail } = splitStable(pendingRef.current, {
|
|
14739
|
+
maxTailLines
|
|
14302
14740
|
});
|
|
14303
|
-
|
|
14741
|
+
if (!stable)
|
|
14742
|
+
return "";
|
|
14743
|
+
pendingRef.current = tail;
|
|
14744
|
+
emittedRef.current += stable;
|
|
14745
|
+
setStreamingText(tail);
|
|
14746
|
+
return stable;
|
|
14747
|
+
}, [maxTailLines]);
|
|
14748
|
+
const flushPending = useCallback3(() => {
|
|
14749
|
+
const pending = pendingRef.current;
|
|
14750
|
+
if (!pending)
|
|
14751
|
+
return "";
|
|
14752
|
+
pendingRef.current = "";
|
|
14753
|
+
emittedRef.current += pending;
|
|
14754
|
+
setStreamingText("");
|
|
14755
|
+
return pending;
|
|
14756
|
+
}, []);
|
|
14757
|
+
const finish = useCallback3((finalText) => {
|
|
14758
|
+
const pending = pendingRef.current;
|
|
14759
|
+
pendingRef.current = "";
|
|
14760
|
+
setStreamingText("");
|
|
14761
|
+
setStatus("idle");
|
|
14762
|
+
if (finalText === undefined) {
|
|
14763
|
+
emittedRef.current += pending;
|
|
14764
|
+
return pending;
|
|
14765
|
+
}
|
|
14766
|
+
if (finalText.startsWith(emittedRef.current)) {
|
|
14767
|
+
const suffix = finalText.slice(emittedRef.current.length);
|
|
14768
|
+
emittedRef.current = finalText;
|
|
14769
|
+
return suffix;
|
|
14770
|
+
}
|
|
14771
|
+
const emitted = emittedRef.current;
|
|
14772
|
+
let i = 0;
|
|
14773
|
+
const max = Math.min(emitted.length, finalText.length);
|
|
14774
|
+
while (i < max && emitted.charCodeAt(i) === finalText.charCodeAt(i)) {
|
|
14775
|
+
i++;
|
|
14776
|
+
}
|
|
14777
|
+
emittedRef.current = finalText;
|
|
14778
|
+
return finalText.slice(i);
|
|
14304
14779
|
}, []);
|
|
14305
14780
|
const fail = useCallback3((_err) => {
|
|
14781
|
+
const rest = pendingRef.current;
|
|
14782
|
+
pendingRef.current = "";
|
|
14783
|
+
setStreamingText("");
|
|
14306
14784
|
setStatus("idle");
|
|
14307
|
-
|
|
14308
|
-
setStreamingText((prev) => {
|
|
14309
|
-
snapshot = prev;
|
|
14310
|
-
return "";
|
|
14311
|
-
});
|
|
14312
|
-
return snapshot;
|
|
14785
|
+
return rest;
|
|
14313
14786
|
}, []);
|
|
14314
|
-
return { status, streamingText, start, append, finish, fail };
|
|
14787
|
+
return { status, streamingText, start, append, drainStable, flushPending, finish, fail };
|
|
14315
14788
|
}
|
|
14316
14789
|
|
|
14317
14790
|
// src/RunTui.tsx
|
|
@@ -14390,7 +14863,7 @@ function dispatchSlashCommand(raw, ctx) {
|
|
|
14390
14863
|
}
|
|
14391
14864
|
|
|
14392
14865
|
// src/RunTui.tsx
|
|
14393
|
-
import { jsxDEV as
|
|
14866
|
+
import { jsxDEV as jsxDEV9 } from "react/jsx-dev-runtime";
|
|
14394
14867
|
var RunScreen = ({
|
|
14395
14868
|
handleQuery,
|
|
14396
14869
|
streamHandleQuery,
|
|
@@ -14398,6 +14871,8 @@ var RunScreen = ({
|
|
|
14398
14871
|
workdir,
|
|
14399
14872
|
maxLines: initialMaxLines,
|
|
14400
14873
|
markdownEnabled: markdownEnabled2,
|
|
14874
|
+
showToolCalls = true,
|
|
14875
|
+
showToolResults = true,
|
|
14401
14876
|
onExit,
|
|
14402
14877
|
onRegisterHandlers
|
|
14403
14878
|
}) => {
|
|
@@ -14405,14 +14880,22 @@ var RunScreen = ({
|
|
|
14405
14880
|
const [errorMessage, setErrorMessage] = useState6(null);
|
|
14406
14881
|
const [maxLines, setMaxLines] = useState6(initialMaxLines);
|
|
14407
14882
|
const [userHistory, setUserHistory] = useState6([]);
|
|
14408
|
-
const
|
|
14883
|
+
const [liveTools, setLiveTools] = useState6([]);
|
|
14884
|
+
const { stdout } = useStdout();
|
|
14885
|
+
const maxTailLines = Math.max(4, (stdout?.rows ?? 24) - 12);
|
|
14886
|
+
const stream = useStreamingAccumulator({ maxTailLines });
|
|
14409
14887
|
const status = stream.status;
|
|
14410
14888
|
const { exit } = useApp2();
|
|
14411
|
-
const userTurnCountRef =
|
|
14412
|
-
const assistantTurnCountRef =
|
|
14413
|
-
const isProcessingRef =
|
|
14414
|
-
const executeQueryRef =
|
|
14415
|
-
const historyIdRef =
|
|
14889
|
+
const userTurnCountRef = useRef3(0);
|
|
14890
|
+
const assistantTurnCountRef = useRef3(0);
|
|
14891
|
+
const isProcessingRef = useRef3(0);
|
|
14892
|
+
const executeQueryRef = useRef3(null);
|
|
14893
|
+
const historyIdRef = useRef3(0);
|
|
14894
|
+
const liveRef = useRef3([]);
|
|
14895
|
+
const toolSeqRef = useRef3(0);
|
|
14896
|
+
const syncLiveTools = useCallback4(() => {
|
|
14897
|
+
setLiveTools(liveRef.current.map((t) => t.info));
|
|
14898
|
+
}, []);
|
|
14416
14899
|
const width = process.stdout.columns ?? 80;
|
|
14417
14900
|
const doExit = useCallback4(() => {
|
|
14418
14901
|
onExit?.();
|
|
@@ -14421,25 +14904,108 @@ var RunScreen = ({
|
|
|
14421
14904
|
const executeQuery = useCallback4(async (message) => {
|
|
14422
14905
|
isProcessingRef.current += 1;
|
|
14423
14906
|
stream.start();
|
|
14907
|
+
let assistantBlocks = 0;
|
|
14908
|
+
const pushAssistant = (content) => {
|
|
14909
|
+
const id = `h${++historyIdRef.current}`;
|
|
14910
|
+
const continuation = assistantBlocks > 0;
|
|
14911
|
+
assistantBlocks += 1;
|
|
14912
|
+
setHistory((h) => [
|
|
14913
|
+
...h,
|
|
14914
|
+
{ role: "assistant", content, id, continuation }
|
|
14915
|
+
]);
|
|
14916
|
+
};
|
|
14917
|
+
const commitTool = (info) => {
|
|
14918
|
+
const id = `h${++historyIdRef.current}`;
|
|
14919
|
+
setHistory((h) => [
|
|
14920
|
+
...h,
|
|
14921
|
+
{ role: "tool", content: "", tool: info, id }
|
|
14922
|
+
]);
|
|
14923
|
+
};
|
|
14924
|
+
const findLiveIndex = (event) => {
|
|
14925
|
+
if (event.id) {
|
|
14926
|
+
const byId = liveRef.current.findIndex((t) => t.key === event.id);
|
|
14927
|
+
if (byId >= 0)
|
|
14928
|
+
return byId;
|
|
14929
|
+
}
|
|
14930
|
+
return liveRef.current.findIndex((t) => t.info.name === event.name);
|
|
14931
|
+
};
|
|
14932
|
+
const handleToolEvent = (event) => {
|
|
14933
|
+
if (event.kind === "call") {
|
|
14934
|
+
const pending = stream.flushPending();
|
|
14935
|
+
if (pending.trim())
|
|
14936
|
+
pushAssistant(pending);
|
|
14937
|
+
const key = event.id ?? `${event.name}#${++toolSeqRef.current}`;
|
|
14938
|
+
liveRef.current = [
|
|
14939
|
+
...liveRef.current,
|
|
14940
|
+
{
|
|
14941
|
+
key,
|
|
14942
|
+
startedAt: Date.now(),
|
|
14943
|
+
info: { name: event.name, args: event.args, status: "running" }
|
|
14944
|
+
}
|
|
14945
|
+
];
|
|
14946
|
+
syncLiveTools();
|
|
14947
|
+
return;
|
|
14948
|
+
}
|
|
14949
|
+
const idx = findLiveIndex(event);
|
|
14950
|
+
const entry = idx >= 0 ? liveRef.current[idx] : undefined;
|
|
14951
|
+
const durationMs = entry ? Date.now() - entry.startedAt : undefined;
|
|
14952
|
+
const info = event.kind === "error" ? {
|
|
14953
|
+
name: event.name,
|
|
14954
|
+
args: entry?.info.args,
|
|
14955
|
+
status: "error",
|
|
14956
|
+
error: event.error ?? "tool failed",
|
|
14957
|
+
durationMs
|
|
14958
|
+
} : {
|
|
14959
|
+
name: event.name,
|
|
14960
|
+
args: entry?.info.args,
|
|
14961
|
+
status: "completed",
|
|
14962
|
+
output: showToolResults ? formatToolOutput(event.output) : undefined,
|
|
14963
|
+
durationMs
|
|
14964
|
+
};
|
|
14965
|
+
if (idx >= 0) {
|
|
14966
|
+
liveRef.current = liveRef.current.filter((_, i) => i !== idx);
|
|
14967
|
+
syncLiveTools();
|
|
14968
|
+
}
|
|
14969
|
+
commitTool(info);
|
|
14970
|
+
};
|
|
14971
|
+
const settleLiveTools = (status2, error) => {
|
|
14972
|
+
if (liveRef.current.length === 0)
|
|
14973
|
+
return;
|
|
14974
|
+
for (const t of liveRef.current) {
|
|
14975
|
+
commitTool({
|
|
14976
|
+
...t.info,
|
|
14977
|
+
status: status2,
|
|
14978
|
+
...error ? { error } : {},
|
|
14979
|
+
durationMs: Date.now() - t.startedAt
|
|
14980
|
+
});
|
|
14981
|
+
}
|
|
14982
|
+
liveRef.current = [];
|
|
14983
|
+
syncLiveTools();
|
|
14984
|
+
};
|
|
14424
14985
|
try {
|
|
14425
14986
|
let responseText;
|
|
14426
14987
|
if (streamHandleQuery) {
|
|
14427
|
-
responseText = await streamHandleQuery(message, (chunk) =>
|
|
14988
|
+
responseText = await streamHandleQuery(message, (chunk) => {
|
|
14989
|
+
stream.append(chunk);
|
|
14990
|
+
const stable = stream.drainStable();
|
|
14991
|
+
if (stable)
|
|
14992
|
+
pushAssistant(stable);
|
|
14993
|
+
}, showToolCalls ? handleToolEvent : undefined);
|
|
14994
|
+
const tail = stream.finish(responseText);
|
|
14995
|
+
if (tail.trim())
|
|
14996
|
+
pushAssistant(tail);
|
|
14428
14997
|
} else {
|
|
14429
14998
|
responseText = await handleQuery(message);
|
|
14999
|
+
stream.finish();
|
|
15000
|
+
const rendered = markdownEnabled2 ? renderMarkdown(responseText, { width }) : responseText;
|
|
15001
|
+
pushAssistant(truncateForTerminal(rendered, maxLines));
|
|
14430
15002
|
}
|
|
14431
|
-
|
|
14432
|
-
const finalText = truncateForTerminal(rendered, maxLines);
|
|
14433
|
-
stream.finish();
|
|
14434
|
-
const assistantId = `h${++historyIdRef.current}`;
|
|
14435
|
-
setHistory((h) => [
|
|
14436
|
-
...h,
|
|
14437
|
-
{ role: "assistant", content: finalText, id: assistantId }
|
|
14438
|
-
]);
|
|
15003
|
+
settleLiveTools("completed");
|
|
14439
15004
|
assistantTurnCountRef.current += 1;
|
|
14440
15005
|
} catch (err) {
|
|
14441
15006
|
const msg = err instanceof Error ? err.message : String(err);
|
|
14442
15007
|
stream.fail(err instanceof Error ? err : new Error(msg));
|
|
15008
|
+
settleLiveTools("error", msg);
|
|
14443
15009
|
setErrorMessage(msg);
|
|
14444
15010
|
const errorId = `h${++historyIdRef.current}`;
|
|
14445
15011
|
setHistory((h) => [
|
|
@@ -14449,7 +15015,17 @@ var RunScreen = ({
|
|
|
14449
15015
|
} finally {
|
|
14450
15016
|
isProcessingRef.current -= 1;
|
|
14451
15017
|
}
|
|
14452
|
-
}, [
|
|
15018
|
+
}, [
|
|
15019
|
+
handleQuery,
|
|
15020
|
+
markdownEnabled2,
|
|
15021
|
+
maxLines,
|
|
15022
|
+
showToolCalls,
|
|
15023
|
+
showToolResults,
|
|
15024
|
+
stream,
|
|
15025
|
+
streamHandleQuery,
|
|
15026
|
+
syncLiveTools,
|
|
15027
|
+
width
|
|
15028
|
+
]);
|
|
14453
15029
|
useEffect5(() => {
|
|
14454
15030
|
executeQueryRef.current = executeQuery;
|
|
14455
15031
|
}, [executeQuery]);
|
|
@@ -14557,19 +15133,20 @@ var RunScreen = ({
|
|
|
14557
15133
|
const handleCancel = useCallback4(() => {
|
|
14558
15134
|
doExit();
|
|
14559
15135
|
}, [doExit]);
|
|
14560
|
-
return /* @__PURE__ */
|
|
15136
|
+
return /* @__PURE__ */ jsxDEV9(Box7, {
|
|
14561
15137
|
flexDirection: "column",
|
|
14562
15138
|
children: [
|
|
14563
|
-
/* @__PURE__ */
|
|
15139
|
+
/* @__PURE__ */ jsxDEV9(App, {
|
|
14564
15140
|
history,
|
|
14565
15141
|
status,
|
|
14566
15142
|
model,
|
|
14567
15143
|
workdir,
|
|
14568
|
-
streamingText: stream.streamingText
|
|
15144
|
+
streamingText: stream.streamingText,
|
|
15145
|
+
liveTools
|
|
14569
15146
|
}, undefined, false, undefined, this),
|
|
14570
|
-
status === "idle" ? /* @__PURE__ */
|
|
15147
|
+
status === "idle" ? /* @__PURE__ */ jsxDEV9(Box7, {
|
|
14571
15148
|
marginTop: 1,
|
|
14572
|
-
children: /* @__PURE__ */
|
|
15149
|
+
children: /* @__PURE__ */ jsxDEV9(TextInput, {
|
|
14573
15150
|
label: "You",
|
|
14574
15151
|
prefix: "\uD83D\uDC64",
|
|
14575
15152
|
placeholder: 'Type a message, "/help", or "exit"',
|
|
@@ -14578,9 +15155,9 @@ var RunScreen = ({
|
|
|
14578
15155
|
history: userHistory
|
|
14579
15156
|
}, undefined, false, undefined, this)
|
|
14580
15157
|
}, undefined, false, undefined, this) : null,
|
|
14581
|
-
errorMessage ? /* @__PURE__ */
|
|
15158
|
+
errorMessage ? /* @__PURE__ */ jsxDEV9(Box7, {
|
|
14582
15159
|
marginTop: 1,
|
|
14583
|
-
children: /* @__PURE__ */
|
|
15160
|
+
children: /* @__PURE__ */ jsxDEV9(Text9, {
|
|
14584
15161
|
color: "red",
|
|
14585
15162
|
children: [
|
|
14586
15163
|
"⚠ ",
|
|
@@ -14689,7 +15266,7 @@ async function loadHandleQuery(options, hooks) {
|
|
|
14689
15266
|
workDir: options.workdir,
|
|
14690
15267
|
loadPlugins: true,
|
|
14691
15268
|
plugins: options.plugins,
|
|
14692
|
-
commandName: "
|
|
15269
|
+
commandName: "interactive"
|
|
14693
15270
|
});
|
|
14694
15271
|
const env = envService.getEnvironment();
|
|
14695
15272
|
if (!env) {
|
|
@@ -14792,14 +15369,41 @@ async function loadHandleQuery(options, hooks) {
|
|
|
14792
15369
|
};
|
|
14793
15370
|
}
|
|
14794
15371
|
function createStreamHandleQuery(queryExecutor, sessionId, options, agentContext, eventSourceSystemPrompt) {
|
|
14795
|
-
return async (message, onChunk) => {
|
|
15372
|
+
return async (message, onChunk, onTool) => {
|
|
14796
15373
|
queryExecutor.setChunkCallback((event) => {
|
|
14797
|
-
|
|
14798
|
-
|
|
14799
|
-
|
|
14800
|
-
|
|
14801
|
-
|
|
14802
|
-
|
|
15374
|
+
switch (event.type) {
|
|
15375
|
+
case "llm.text":
|
|
15376
|
+
if (event.delta) {
|
|
15377
|
+
onChunk(event.delta);
|
|
15378
|
+
} else if (event.content) {
|
|
15379
|
+
onChunk(event.content);
|
|
15380
|
+
}
|
|
15381
|
+
break;
|
|
15382
|
+
case "tool.call":
|
|
15383
|
+
onTool?.({
|
|
15384
|
+
kind: "call",
|
|
15385
|
+
id: event.toolId,
|
|
15386
|
+
name: event.toolName ?? "tool",
|
|
15387
|
+
args: event.toolArgs
|
|
15388
|
+
});
|
|
15389
|
+
break;
|
|
15390
|
+
case "tool.result":
|
|
15391
|
+
onTool?.({
|
|
15392
|
+
kind: "result",
|
|
15393
|
+
id: event.toolId,
|
|
15394
|
+
name: event.toolName ?? "tool",
|
|
15395
|
+
output: event.toolResult
|
|
15396
|
+
});
|
|
15397
|
+
break;
|
|
15398
|
+
case "tool.error":
|
|
15399
|
+
onTool?.({
|
|
15400
|
+
kind: "error",
|
|
15401
|
+
name: event.toolName ?? "tool",
|
|
15402
|
+
error: event.toolError
|
|
15403
|
+
});
|
|
15404
|
+
break;
|
|
15405
|
+
default:
|
|
15406
|
+
break;
|
|
14803
15407
|
}
|
|
14804
15408
|
});
|
|
14805
15409
|
const mergedContext = {
|
|
@@ -14813,15 +15417,15 @@ function createStreamHandleQuery(queryExecutor, sessionId, options, agentContext
|
|
|
14813
15417
|
};
|
|
14814
15418
|
const result = await queryExecutor.execute(message, sessionId, {
|
|
14815
15419
|
showReasoning: options.reasoning ?? true,
|
|
14816
|
-
showToolCalls:
|
|
14817
|
-
showToolResults:
|
|
15420
|
+
showToolCalls: true,
|
|
15421
|
+
showToolResults: true
|
|
14818
15422
|
}, undefined, mergedContext);
|
|
14819
15423
|
return String(result ?? "");
|
|
14820
15424
|
};
|
|
14821
15425
|
}
|
|
14822
15426
|
function createRunCommand() {
|
|
14823
15427
|
return {
|
|
14824
|
-
command: "
|
|
15428
|
+
command: "interactive",
|
|
14825
15429
|
describe: "Start an interactive conversation with the agent (Ink TUI)",
|
|
14826
15430
|
builder: (yargs) => yargs.option("model", {
|
|
14827
15431
|
alias: "m",
|
|
@@ -14875,7 +15479,7 @@ function createRunCommand() {
|
|
|
14875
15479
|
}).option("tool-results", {
|
|
14876
15480
|
type: "boolean",
|
|
14877
15481
|
describe: "显示工具执行结果",
|
|
14878
|
-
default:
|
|
15482
|
+
default: true
|
|
14879
15483
|
}).option("event-source", {
|
|
14880
15484
|
alias: "es",
|
|
14881
15485
|
type: "array",
|
|
@@ -14934,13 +15538,15 @@ function createRunCommand() {
|
|
|
14934
15538
|
const streamHandleQuery = createStreamHandleQuery(queryExecutor, sessionId, argv, agentContext, eventSourceSystemPrompt);
|
|
14935
15539
|
const stopEventHandler = eventHandler.start();
|
|
14936
15540
|
const stopTaskEventHandler = taskEventHandler.start();
|
|
14937
|
-
const { waitUntilExit } = render(
|
|
15541
|
+
const { waitUntilExit } = render(React7.createElement(RunTui_default, {
|
|
14938
15542
|
handleQuery,
|
|
14939
15543
|
streamHandleQuery,
|
|
14940
15544
|
model: argv.model,
|
|
14941
15545
|
workdir: argv.workdir,
|
|
14942
15546
|
maxLines,
|
|
14943
15547
|
markdownEnabled: argv.markdown !== false,
|
|
15548
|
+
showToolCalls: argv.toolCalls !== false,
|
|
15549
|
+
showToolResults: argv.toolResults !== false,
|
|
14944
15550
|
onRegisterHandlers: (handlers) => {
|
|
14945
15551
|
runState.onNotification = handlers.onNotification;
|
|
14946
15552
|
runState.isIdle = handlers.isIdle;
|
|
@@ -14971,14 +15577,14 @@ function createRunCommand() {
|
|
|
14971
15577
|
var RunCommand = createRunCommand();
|
|
14972
15578
|
|
|
14973
15579
|
// src/commands/chat.ts
|
|
14974
|
-
import
|
|
15580
|
+
import React9 from "react";
|
|
14975
15581
|
import { render as render2 } from "ink";
|
|
14976
15582
|
|
|
14977
15583
|
// src/ChatTui.tsx
|
|
14978
|
-
import { useEffect as useEffect6, useRef as
|
|
14979
|
-
import { Box as
|
|
15584
|
+
import { useEffect as useEffect6, useRef as useRef4, useState as useState7 } from "react";
|
|
15585
|
+
import { Box as Box8, useApp as useApp3 } from "ink";
|
|
14980
15586
|
init_markdown();
|
|
14981
|
-
import { jsxDEV as
|
|
15587
|
+
import { jsxDEV as jsxDEV10 } from "react/jsx-dev-runtime";
|
|
14982
15588
|
var ChatScreen = ({
|
|
14983
15589
|
handleQuery,
|
|
14984
15590
|
streamHandleQuery,
|
|
@@ -14991,7 +15597,7 @@ var ChatScreen = ({
|
|
|
14991
15597
|
const [history, setHistory] = useState7([
|
|
14992
15598
|
{ role: "user", content: message, id: "h-init-1" }
|
|
14993
15599
|
]);
|
|
14994
|
-
const historyIdRef =
|
|
15600
|
+
const historyIdRef = useRef4(1);
|
|
14995
15601
|
const stream = useStreamingAccumulator();
|
|
14996
15602
|
const { exit } = useApp3();
|
|
14997
15603
|
const width = process.stdout.columns ?? 80;
|
|
@@ -15034,9 +15640,9 @@ var ChatScreen = ({
|
|
|
15034
15640
|
cancelled = true;
|
|
15035
15641
|
};
|
|
15036
15642
|
}, [handleQuery, streamHandleQuery, message, maxLines, markdownEnabled2, exit, width, stream]);
|
|
15037
|
-
return /* @__PURE__ */
|
|
15643
|
+
return /* @__PURE__ */ jsxDEV10(Box8, {
|
|
15038
15644
|
flexDirection: "column",
|
|
15039
|
-
children: /* @__PURE__ */
|
|
15645
|
+
children: /* @__PURE__ */ jsxDEV10(App, {
|
|
15040
15646
|
history,
|
|
15041
15647
|
status: stream.status,
|
|
15042
15648
|
model,
|
|
@@ -15122,7 +15728,7 @@ function createChatCommand() {
|
|
|
15122
15728
|
}
|
|
15123
15729
|
const maxLines = Number.parseInt(argv.maxLines ?? "500", 10);
|
|
15124
15730
|
const handleQuery = await loadHandleQuery2(argv);
|
|
15125
|
-
const { waitUntilExit } = render2(
|
|
15731
|
+
const { waitUntilExit } = render2(React9.createElement(ChatTui_default, {
|
|
15126
15732
|
handleQuery,
|
|
15127
15733
|
message: argv.message,
|
|
15128
15734
|
model: argv.model,
|
|
@@ -22777,7 +23383,7 @@ function formatDate(date) {
|
|
|
22777
23383
|
const d = typeof date === "string" ? new Date(date) : date;
|
|
22778
23384
|
return d.toLocaleString("zh-CN");
|
|
22779
23385
|
}
|
|
22780
|
-
function
|
|
23386
|
+
function formatDuration2(ms) {
|
|
22781
23387
|
if (ms === undefined || ms === null)
|
|
22782
23388
|
return "-";
|
|
22783
23389
|
if (ms < 1000)
|
|
@@ -22925,7 +23531,7 @@ function renderRunsList(runs, options) {
|
|
|
22925
23531
|
const rows = runs.map((r) => {
|
|
22926
23532
|
const id = truncateVisual3(r.id, ID_WIDTH).padEnd(ID_WIDTH);
|
|
22927
23533
|
const status = statusColor(r.status)(r.status.padEnd(STATUS_WIDTH));
|
|
22928
|
-
const duration =
|
|
23534
|
+
const duration = formatDuration2(r.durationMs).padEnd(DURATION_WIDTH);
|
|
22929
23535
|
const started = formatDate(r.startedAt);
|
|
22930
23536
|
return `${id}${GAP}${status}${GAP}${duration}${GAP}${started}`;
|
|
22931
23537
|
});
|
|
@@ -22953,7 +23559,7 @@ function renderRunDetail(run) {
|
|
|
22953
23559
|
if (run.completedAt) {
|
|
22954
23560
|
lines.push(` ${chalk67.cyan("Completed:")} ${formatDate(run.completedAt)}`);
|
|
22955
23561
|
}
|
|
22956
|
-
lines.push(` ${chalk67.cyan("Duration:")} ${
|
|
23562
|
+
lines.push(` ${chalk67.cyan("Duration:")} ${formatDuration2(run.durationMs)}`);
|
|
22957
23563
|
if (run.error) {
|
|
22958
23564
|
lines.push(chalk67.bold(`
|
|
22959
23565
|
❌ Error
|
|
@@ -23019,7 +23625,7 @@ function renderRunResult(result) {
|
|
|
23019
23625
|
}
|
|
23020
23626
|
lines.push(` Run ID: ${result.runId}`);
|
|
23021
23627
|
if (result.durationMs) {
|
|
23022
|
-
lines.push(` Duration: ${
|
|
23628
|
+
lines.push(` Duration: ${formatDuration2(result.durationMs)}`);
|
|
23023
23629
|
}
|
|
23024
23630
|
if (result.error) {
|
|
23025
23631
|
lines.push(chalk67.red(`
|