clawdbot 2026.1.4-1 → 2026.1.5-1
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/CHANGELOG.md +32 -6
- package/README.md +26 -1
- package/dist/agents/pi-embedded-runner.js +2 -0
- package/dist/agents/pi-embedded-subscribe.js +18 -3
- package/dist/agents/pi-tools.js +45 -6
- package/dist/agents/tools/browser-tool.js +38 -89
- package/dist/agents/tools/cron-tool.js +8 -8
- package/dist/agents/workspace.js +8 -1
- package/dist/auto-reply/command-detection.js +26 -0
- package/dist/auto-reply/reply/agent-runner.js +15 -8
- package/dist/auto-reply/reply/commands.js +36 -25
- package/dist/auto-reply/reply/directive-handling.js +4 -2
- package/dist/auto-reply/reply/directives.js +12 -0
- package/dist/auto-reply/reply/session-updates.js +2 -4
- package/dist/auto-reply/reply.js +26 -4
- package/dist/browser/config.js +22 -4
- package/dist/browser/profiles-service.js +3 -1
- package/dist/browser/profiles.js +14 -3
- package/dist/canvas-host/a2ui/.bundle.hash +2 -0
- package/dist/cli/gateway-cli.js +2 -2
- package/dist/cli/profile.js +81 -0
- package/dist/cli/program.js +10 -1
- package/dist/cli/run-main.js +33 -0
- package/dist/commands/configure.js +5 -0
- package/dist/commands/onboard-providers.js +1 -1
- package/dist/commands/setup.js +4 -1
- package/dist/config/defaults.js +56 -0
- package/dist/config/io.js +47 -6
- package/dist/config/paths.js +2 -2
- package/dist/config/port-defaults.js +32 -0
- package/dist/config/sessions.js +3 -2
- package/dist/config/validation.js +2 -2
- package/dist/config/zod-schema.js +16 -0
- package/dist/discord/monitor.js +75 -266
- package/dist/entry.js +16 -0
- package/dist/gateway/call.js +8 -1
- package/dist/gateway/server-methods/chat.js +1 -1
- package/dist/gateway/server.js +14 -3
- package/dist/index.js +2 -2
- package/dist/infra/control-ui-assets.js +118 -0
- package/dist/infra/dotenv.js +15 -0
- package/dist/infra/shell-env.js +79 -0
- package/dist/infra/system-events.js +50 -23
- package/dist/macos/relay.js +8 -2
- package/dist/telegram/bot.js +24 -1
- package/dist/utils.js +8 -2
- package/dist/web/auto-reply.js +18 -21
- package/dist/web/inbound.js +5 -1
- package/dist/web/qr-image.js +4 -4
- package/dist/web/session.js +2 -3
- package/docs/agent.md +0 -2
- package/docs/assets/markdown.css +4 -1
- package/docs/audio.md +0 -2
- package/docs/clawd.md +0 -2
- package/docs/configuration.md +62 -3
- package/docs/docs.json +9 -1
- package/docs/faq.md +32 -7
- package/docs/gateway.md +28 -0
- package/docs/images.md +0 -2
- package/docs/index.md +2 -4
- package/docs/mac/icon.md +1 -1
- package/docs/nix.md +57 -11
- package/docs/onboarding.md +0 -2
- package/docs/refactor/webagent-session.md +0 -2
- package/docs/research/memory.md +1 -1
- package/docs/skills.md +0 -2
- package/docs/templates/AGENTS.md +2 -2
- package/docs/tools.md +15 -0
- package/docs/whatsapp.md +2 -0
- package/package.json +9 -16
- package/dist/control-ui/assets/index-BFID3yAA.css +0 -1
- package/dist/control-ui/assets/index-CE_axlTS.js +0 -2235
- package/dist/control-ui/assets/index-CE_axlTS.js.map +0 -1
- package/dist/control-ui/index.html +0 -15
- package/dist/daemon/constants.js +0 -10
- package/dist/daemon/launchd.js +0 -276
- package/dist/daemon/legacy.js +0 -63
- package/dist/daemon/program-args.js +0 -76
- package/dist/daemon/schtasks.js +0 -257
- package/dist/daemon/service.js +0 -60
- package/dist/daemon/systemd.js +0 -266
- package/dist/imessage/client.js +0 -165
- package/dist/imessage/index.js +0 -3
- package/dist/imessage/monitor.js +0 -272
- package/dist/imessage/probe.js +0 -26
- package/dist/imessage/send.js +0 -83
- package/dist/imessage/targets.js +0 -176
- package/dist/signal/client.js +0 -134
- package/dist/signal/daemon.js +0 -69
- package/dist/signal/index.js +0 -3
- package/dist/signal/monitor.js +0 -336
- package/dist/signal/probe.js +0 -46
- package/dist/signal/send.js +0 -91
- package/dist/slack/actions.js +0 -97
- package/dist/slack/index.js +0 -5
- package/dist/slack/monitor.js +0 -1029
- package/dist/slack/probe.js +0 -47
- package/dist/slack/send.js +0 -131
- package/dist/slack/token.js +0 -10
- package/dist/tui/commands.js +0 -74
- package/dist/tui/components/assistant-message.js +0 -16
- package/dist/tui/components/chat-log.js +0 -92
- package/dist/tui/components/custom-editor.js +0 -53
- package/dist/tui/components/selectors.js +0 -8
- package/dist/tui/components/tool-execution.js +0 -111
- package/dist/tui/components/user-message.js +0 -17
- package/dist/tui/gateway-chat.js +0 -140
- package/dist/tui/layout.js +0 -41
- package/dist/tui/message-list.js +0 -57
- package/dist/tui/theme/theme.js +0 -80
- package/dist/tui/theme.js +0 -25
- package/dist/tui/tui.js +0 -708
- package/dist/wizard/clack-prompter.js +0 -56
- package/dist/wizard/onboarding.js +0 -452
- package/dist/wizard/prompts.js +0 -6
- package/dist/wizard/session.js +0 -203
package/dist/tui/tui.js
DELETED
|
@@ -1,708 +0,0 @@
|
|
|
1
|
-
import { CombinedAutocompleteProvider, Container, ProcessTerminal, Text, TUI, } from "@mariozechner/pi-tui";
|
|
2
|
-
import { loadConfig } from "../config/config.js";
|
|
3
|
-
import { getSlashCommands, helpText, parseCommand } from "./commands.js";
|
|
4
|
-
import { ChatLog } from "./components/chat-log.js";
|
|
5
|
-
import { CustomEditor } from "./components/custom-editor.js";
|
|
6
|
-
import { createSelectList, createSettingsList, } from "./components/selectors.js";
|
|
7
|
-
import { GatewayChatClient } from "./gateway-chat.js";
|
|
8
|
-
import { editorTheme, theme } from "./theme/theme.js";
|
|
9
|
-
function extractTextBlocks(content, opts) {
|
|
10
|
-
if (typeof content === "string")
|
|
11
|
-
return content.trim();
|
|
12
|
-
if (!Array.isArray(content))
|
|
13
|
-
return "";
|
|
14
|
-
const parts = [];
|
|
15
|
-
for (const block of content) {
|
|
16
|
-
if (!block || typeof block !== "object")
|
|
17
|
-
continue;
|
|
18
|
-
const record = block;
|
|
19
|
-
if (record.type === "text" && typeof record.text === "string") {
|
|
20
|
-
parts.push(record.text);
|
|
21
|
-
}
|
|
22
|
-
if (opts?.includeThinking &&
|
|
23
|
-
record.type === "thinking" &&
|
|
24
|
-
typeof record.thinking === "string") {
|
|
25
|
-
parts.push(`[thinking]\n${record.thinking}`);
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
return parts.join("\n").trim();
|
|
29
|
-
}
|
|
30
|
-
function extractTextFromMessage(message, opts) {
|
|
31
|
-
if (!message || typeof message !== "object")
|
|
32
|
-
return "";
|
|
33
|
-
const record = message;
|
|
34
|
-
return extractTextBlocks(record.content, opts);
|
|
35
|
-
}
|
|
36
|
-
function formatTokens(total, context) {
|
|
37
|
-
if (!total && !context)
|
|
38
|
-
return "tokens ?";
|
|
39
|
-
if (!context)
|
|
40
|
-
return `tokens ${total ?? 0}`;
|
|
41
|
-
const pct = typeof total === "number" && context > 0
|
|
42
|
-
? Math.min(999, Math.round((total / context) * 100))
|
|
43
|
-
: null;
|
|
44
|
-
return `tokens ${total ?? 0}/${context}${pct !== null ? ` (${pct}%)` : ""}`;
|
|
45
|
-
}
|
|
46
|
-
function asString(value, fallback = "") {
|
|
47
|
-
if (typeof value === "string")
|
|
48
|
-
return value;
|
|
49
|
-
if (typeof value === "number" || typeof value === "boolean") {
|
|
50
|
-
return String(value);
|
|
51
|
-
}
|
|
52
|
-
return fallback;
|
|
53
|
-
}
|
|
54
|
-
export async function runTui(opts) {
|
|
55
|
-
const config = loadConfig();
|
|
56
|
-
const defaultSession = (opts.session ?? config.session?.mainKey ?? "main").trim() || "main";
|
|
57
|
-
let currentSessionKey = defaultSession;
|
|
58
|
-
let currentSessionId = null;
|
|
59
|
-
let activeChatRunId = null;
|
|
60
|
-
const finalizedRuns = new Map();
|
|
61
|
-
let historyLoaded = false;
|
|
62
|
-
let isConnected = false;
|
|
63
|
-
let toolsExpanded = false;
|
|
64
|
-
let showThinking = false;
|
|
65
|
-
let deliverDefault = Boolean(opts.deliver);
|
|
66
|
-
let sessionInfo = {};
|
|
67
|
-
let lastCtrlCAt = 0;
|
|
68
|
-
const client = new GatewayChatClient({
|
|
69
|
-
url: opts.url,
|
|
70
|
-
token: opts.token,
|
|
71
|
-
password: opts.password,
|
|
72
|
-
});
|
|
73
|
-
const header = new Text("", 1, 0);
|
|
74
|
-
const status = new Text("", 1, 0);
|
|
75
|
-
const footer = new Text("", 1, 0);
|
|
76
|
-
const chatLog = new ChatLog();
|
|
77
|
-
const editor = new CustomEditor(editorTheme);
|
|
78
|
-
const overlay = new Container();
|
|
79
|
-
const root = new Container();
|
|
80
|
-
root.addChild(header);
|
|
81
|
-
root.addChild(overlay);
|
|
82
|
-
root.addChild(chatLog);
|
|
83
|
-
root.addChild(status);
|
|
84
|
-
root.addChild(footer);
|
|
85
|
-
root.addChild(editor);
|
|
86
|
-
const tui = new TUI(new ProcessTerminal());
|
|
87
|
-
tui.addChild(root);
|
|
88
|
-
tui.setFocus(editor);
|
|
89
|
-
const updateHeader = () => {
|
|
90
|
-
header.setText(theme.header(`clawdbot tui - ${client.connection.url} - session ${currentSessionKey}`));
|
|
91
|
-
};
|
|
92
|
-
const setStatus = (text) => {
|
|
93
|
-
status.setText(theme.dim(text));
|
|
94
|
-
};
|
|
95
|
-
const updateFooter = () => {
|
|
96
|
-
const connection = isConnected ? "connected" : "disconnected";
|
|
97
|
-
const sessionLabel = sessionInfo.displayName
|
|
98
|
-
? `${currentSessionKey} (${sessionInfo.displayName})`
|
|
99
|
-
: currentSessionKey;
|
|
100
|
-
const modelLabel = sessionInfo.model ?? "unknown";
|
|
101
|
-
const tokens = formatTokens(sessionInfo.totalTokens ?? null, sessionInfo.contextTokens ?? null);
|
|
102
|
-
const think = sessionInfo.thinkingLevel ?? "off";
|
|
103
|
-
const verbose = sessionInfo.verboseLevel ?? "off";
|
|
104
|
-
const deliver = deliverDefault ? "on" : "off";
|
|
105
|
-
footer.setText(theme.dim(`${connection} | session ${sessionLabel} | model ${modelLabel} | think ${think} | verbose ${verbose} | ${tokens} | deliver ${deliver}`));
|
|
106
|
-
};
|
|
107
|
-
const closeOverlay = () => {
|
|
108
|
-
overlay.clear();
|
|
109
|
-
tui.setFocus(editor);
|
|
110
|
-
};
|
|
111
|
-
const openOverlay = (component) => {
|
|
112
|
-
overlay.clear();
|
|
113
|
-
overlay.addChild(component);
|
|
114
|
-
tui.setFocus(component);
|
|
115
|
-
};
|
|
116
|
-
const refreshSessionInfo = async () => {
|
|
117
|
-
try {
|
|
118
|
-
const result = await client.listSessions({
|
|
119
|
-
includeGlobal: false,
|
|
120
|
-
includeUnknown: false,
|
|
121
|
-
});
|
|
122
|
-
const entry = result.sessions.find((row) => row.key === currentSessionKey);
|
|
123
|
-
sessionInfo = {
|
|
124
|
-
thinkingLevel: entry?.thinkingLevel,
|
|
125
|
-
verboseLevel: entry?.verboseLevel,
|
|
126
|
-
model: entry?.model ?? result.defaults?.model ?? undefined,
|
|
127
|
-
contextTokens: entry?.contextTokens ?? result.defaults?.contextTokens,
|
|
128
|
-
totalTokens: entry?.totalTokens ?? null,
|
|
129
|
-
updatedAt: entry?.updatedAt ?? null,
|
|
130
|
-
displayName: entry?.displayName,
|
|
131
|
-
};
|
|
132
|
-
}
|
|
133
|
-
catch (err) {
|
|
134
|
-
chatLog.addSystem(`sessions list failed: ${String(err)}`);
|
|
135
|
-
}
|
|
136
|
-
updateFooter();
|
|
137
|
-
tui.requestRender();
|
|
138
|
-
};
|
|
139
|
-
const loadHistory = async () => {
|
|
140
|
-
try {
|
|
141
|
-
const history = await client.loadHistory({
|
|
142
|
-
sessionKey: currentSessionKey,
|
|
143
|
-
limit: opts.historyLimit ?? 200,
|
|
144
|
-
});
|
|
145
|
-
const record = history;
|
|
146
|
-
currentSessionId =
|
|
147
|
-
typeof record.sessionId === "string" ? record.sessionId : null;
|
|
148
|
-
sessionInfo.thinkingLevel =
|
|
149
|
-
record.thinkingLevel ?? sessionInfo.thinkingLevel;
|
|
150
|
-
chatLog.clearAll();
|
|
151
|
-
chatLog.addSystem(`session ${currentSessionKey}`);
|
|
152
|
-
for (const entry of record.messages ?? []) {
|
|
153
|
-
if (!entry || typeof entry !== "object")
|
|
154
|
-
continue;
|
|
155
|
-
const message = entry;
|
|
156
|
-
if (message.role === "user") {
|
|
157
|
-
const text = extractTextFromMessage(message);
|
|
158
|
-
if (text)
|
|
159
|
-
chatLog.addUser(text);
|
|
160
|
-
continue;
|
|
161
|
-
}
|
|
162
|
-
if (message.role === "assistant") {
|
|
163
|
-
const text = extractTextFromMessage(message, {
|
|
164
|
-
includeThinking: showThinking,
|
|
165
|
-
});
|
|
166
|
-
if (text)
|
|
167
|
-
chatLog.finalizeAssistant(text);
|
|
168
|
-
continue;
|
|
169
|
-
}
|
|
170
|
-
if (message.role === "toolResult") {
|
|
171
|
-
const toolCallId = asString(message.toolCallId, "");
|
|
172
|
-
const toolName = asString(message.toolName, "tool");
|
|
173
|
-
const component = chatLog.startTool(toolCallId, toolName, {});
|
|
174
|
-
component.setResult({
|
|
175
|
-
content: Array.isArray(message.content)
|
|
176
|
-
? message.content
|
|
177
|
-
: [],
|
|
178
|
-
details: typeof message.details === "object" && message.details
|
|
179
|
-
? message.details
|
|
180
|
-
: undefined,
|
|
181
|
-
}, { isError: Boolean(message.isError) });
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
historyLoaded = true;
|
|
185
|
-
}
|
|
186
|
-
catch (err) {
|
|
187
|
-
chatLog.addSystem(`history failed: ${String(err)}`);
|
|
188
|
-
}
|
|
189
|
-
await refreshSessionInfo();
|
|
190
|
-
tui.requestRender();
|
|
191
|
-
};
|
|
192
|
-
const setSession = async (key) => {
|
|
193
|
-
currentSessionKey = key;
|
|
194
|
-
activeChatRunId = null;
|
|
195
|
-
currentSessionId = null;
|
|
196
|
-
historyLoaded = false;
|
|
197
|
-
updateHeader();
|
|
198
|
-
await loadHistory();
|
|
199
|
-
};
|
|
200
|
-
const abortActive = async () => {
|
|
201
|
-
if (!activeChatRunId) {
|
|
202
|
-
chatLog.addSystem("no active run");
|
|
203
|
-
tui.requestRender();
|
|
204
|
-
return;
|
|
205
|
-
}
|
|
206
|
-
try {
|
|
207
|
-
await client.abortChat({
|
|
208
|
-
sessionKey: currentSessionKey,
|
|
209
|
-
runId: activeChatRunId,
|
|
210
|
-
});
|
|
211
|
-
setStatus("aborted");
|
|
212
|
-
}
|
|
213
|
-
catch (err) {
|
|
214
|
-
chatLog.addSystem(`abort failed: ${String(err)}`);
|
|
215
|
-
setStatus("abort failed");
|
|
216
|
-
}
|
|
217
|
-
tui.requestRender();
|
|
218
|
-
};
|
|
219
|
-
const noteFinalizedRun = (runId) => {
|
|
220
|
-
finalizedRuns.set(runId, Date.now());
|
|
221
|
-
if (finalizedRuns.size <= 200)
|
|
222
|
-
return;
|
|
223
|
-
const keepUntil = Date.now() - 10 * 60 * 1000;
|
|
224
|
-
for (const [key, ts] of finalizedRuns) {
|
|
225
|
-
if (finalizedRuns.size <= 150)
|
|
226
|
-
break;
|
|
227
|
-
if (ts < keepUntil)
|
|
228
|
-
finalizedRuns.delete(key);
|
|
229
|
-
}
|
|
230
|
-
if (finalizedRuns.size > 200) {
|
|
231
|
-
for (const key of finalizedRuns.keys()) {
|
|
232
|
-
finalizedRuns.delete(key);
|
|
233
|
-
if (finalizedRuns.size <= 150)
|
|
234
|
-
break;
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
};
|
|
238
|
-
const handleChatEvent = (payload) => {
|
|
239
|
-
if (!payload || typeof payload !== "object")
|
|
240
|
-
return;
|
|
241
|
-
const evt = payload;
|
|
242
|
-
if (evt.sessionKey !== currentSessionKey)
|
|
243
|
-
return;
|
|
244
|
-
if (finalizedRuns.has(evt.runId)) {
|
|
245
|
-
if (evt.state === "delta")
|
|
246
|
-
return;
|
|
247
|
-
if (evt.state === "final")
|
|
248
|
-
return;
|
|
249
|
-
}
|
|
250
|
-
if (evt.state === "delta") {
|
|
251
|
-
const text = extractTextFromMessage(evt.message, {
|
|
252
|
-
includeThinking: showThinking,
|
|
253
|
-
});
|
|
254
|
-
if (!text)
|
|
255
|
-
return;
|
|
256
|
-
chatLog.updateAssistant(text, evt.runId);
|
|
257
|
-
setStatus("streaming");
|
|
258
|
-
}
|
|
259
|
-
if (evt.state === "final") {
|
|
260
|
-
const text = extractTextFromMessage(evt.message, {
|
|
261
|
-
includeThinking: showThinking,
|
|
262
|
-
});
|
|
263
|
-
chatLog.finalizeAssistant(text || "(no output)", evt.runId);
|
|
264
|
-
noteFinalizedRun(evt.runId);
|
|
265
|
-
activeChatRunId = null;
|
|
266
|
-
setStatus("idle");
|
|
267
|
-
}
|
|
268
|
-
if (evt.state === "aborted") {
|
|
269
|
-
chatLog.addSystem("run aborted");
|
|
270
|
-
activeChatRunId = null;
|
|
271
|
-
setStatus("aborted");
|
|
272
|
-
}
|
|
273
|
-
if (evt.state === "error") {
|
|
274
|
-
chatLog.addSystem(`run error: ${evt.errorMessage ?? "unknown"}`);
|
|
275
|
-
activeChatRunId = null;
|
|
276
|
-
setStatus("error");
|
|
277
|
-
}
|
|
278
|
-
tui.requestRender();
|
|
279
|
-
};
|
|
280
|
-
const handleAgentEvent = (payload) => {
|
|
281
|
-
if (!payload || typeof payload !== "object")
|
|
282
|
-
return;
|
|
283
|
-
const evt = payload;
|
|
284
|
-
if (!currentSessionId || evt.runId !== currentSessionId)
|
|
285
|
-
return;
|
|
286
|
-
if (evt.stream === "tool") {
|
|
287
|
-
const data = evt.data ?? {};
|
|
288
|
-
const phase = asString(data.phase, "");
|
|
289
|
-
const toolCallId = asString(data.toolCallId, "");
|
|
290
|
-
const toolName = asString(data.name, "tool");
|
|
291
|
-
if (!toolCallId)
|
|
292
|
-
return;
|
|
293
|
-
if (phase === "start") {
|
|
294
|
-
chatLog.startTool(toolCallId, toolName, data.args);
|
|
295
|
-
}
|
|
296
|
-
else if (phase === "update") {
|
|
297
|
-
chatLog.updateToolResult(toolCallId, data.partialResult, {
|
|
298
|
-
partial: true,
|
|
299
|
-
});
|
|
300
|
-
}
|
|
301
|
-
else if (phase === "result") {
|
|
302
|
-
chatLog.updateToolResult(toolCallId, data.result, {
|
|
303
|
-
isError: Boolean(data.isError),
|
|
304
|
-
});
|
|
305
|
-
}
|
|
306
|
-
tui.requestRender();
|
|
307
|
-
return;
|
|
308
|
-
}
|
|
309
|
-
if (evt.stream === "job") {
|
|
310
|
-
const state = typeof evt.data?.state === "string" ? evt.data.state : "";
|
|
311
|
-
if (state === "started")
|
|
312
|
-
setStatus("running");
|
|
313
|
-
if (state === "done")
|
|
314
|
-
setStatus("idle");
|
|
315
|
-
if (state === "error")
|
|
316
|
-
setStatus("error");
|
|
317
|
-
tui.requestRender();
|
|
318
|
-
}
|
|
319
|
-
};
|
|
320
|
-
const openModelSelector = async () => {
|
|
321
|
-
try {
|
|
322
|
-
const models = await client.listModels();
|
|
323
|
-
if (models.length === 0) {
|
|
324
|
-
chatLog.addSystem("no models available");
|
|
325
|
-
tui.requestRender();
|
|
326
|
-
return;
|
|
327
|
-
}
|
|
328
|
-
const items = models.map((model) => ({
|
|
329
|
-
value: `${model.provider}/${model.id}`,
|
|
330
|
-
label: `${model.provider}/${model.id}`,
|
|
331
|
-
description: model.name && model.name !== model.id ? model.name : "",
|
|
332
|
-
}));
|
|
333
|
-
const selector = createSelectList(items, 9);
|
|
334
|
-
selector.onSelect = (item) => {
|
|
335
|
-
void (async () => {
|
|
336
|
-
try {
|
|
337
|
-
await client.patchSession({
|
|
338
|
-
key: currentSessionKey,
|
|
339
|
-
model: item.value,
|
|
340
|
-
});
|
|
341
|
-
chatLog.addSystem(`model set to ${item.value}`);
|
|
342
|
-
await refreshSessionInfo();
|
|
343
|
-
}
|
|
344
|
-
catch (err) {
|
|
345
|
-
chatLog.addSystem(`model set failed: ${String(err)}`);
|
|
346
|
-
}
|
|
347
|
-
closeOverlay();
|
|
348
|
-
tui.requestRender();
|
|
349
|
-
})();
|
|
350
|
-
};
|
|
351
|
-
selector.onCancel = () => {
|
|
352
|
-
closeOverlay();
|
|
353
|
-
tui.requestRender();
|
|
354
|
-
};
|
|
355
|
-
openOverlay(selector);
|
|
356
|
-
tui.requestRender();
|
|
357
|
-
}
|
|
358
|
-
catch (err) {
|
|
359
|
-
chatLog.addSystem(`model list failed: ${String(err)}`);
|
|
360
|
-
tui.requestRender();
|
|
361
|
-
}
|
|
362
|
-
};
|
|
363
|
-
const openSessionSelector = async () => {
|
|
364
|
-
try {
|
|
365
|
-
const result = await client.listSessions({
|
|
366
|
-
includeGlobal: false,
|
|
367
|
-
includeUnknown: false,
|
|
368
|
-
});
|
|
369
|
-
const items = result.sessions.map((session) => ({
|
|
370
|
-
value: session.key,
|
|
371
|
-
label: session.displayName ?? session.key,
|
|
372
|
-
description: session.updatedAt
|
|
373
|
-
? new Date(session.updatedAt).toLocaleString()
|
|
374
|
-
: "",
|
|
375
|
-
}));
|
|
376
|
-
const selector = createSelectList(items, 9);
|
|
377
|
-
selector.onSelect = (item) => {
|
|
378
|
-
void (async () => {
|
|
379
|
-
closeOverlay();
|
|
380
|
-
await setSession(item.value);
|
|
381
|
-
tui.requestRender();
|
|
382
|
-
})();
|
|
383
|
-
};
|
|
384
|
-
selector.onCancel = () => {
|
|
385
|
-
closeOverlay();
|
|
386
|
-
tui.requestRender();
|
|
387
|
-
};
|
|
388
|
-
openOverlay(selector);
|
|
389
|
-
tui.requestRender();
|
|
390
|
-
}
|
|
391
|
-
catch (err) {
|
|
392
|
-
chatLog.addSystem(`sessions list failed: ${String(err)}`);
|
|
393
|
-
tui.requestRender();
|
|
394
|
-
}
|
|
395
|
-
};
|
|
396
|
-
const openSettings = () => {
|
|
397
|
-
const items = [
|
|
398
|
-
{
|
|
399
|
-
id: "deliver",
|
|
400
|
-
label: "Deliver replies",
|
|
401
|
-
currentValue: deliverDefault ? "on" : "off",
|
|
402
|
-
values: ["off", "on"],
|
|
403
|
-
},
|
|
404
|
-
{
|
|
405
|
-
id: "tools",
|
|
406
|
-
label: "Tool output",
|
|
407
|
-
currentValue: toolsExpanded ? "expanded" : "collapsed",
|
|
408
|
-
values: ["collapsed", "expanded"],
|
|
409
|
-
},
|
|
410
|
-
{
|
|
411
|
-
id: "thinking",
|
|
412
|
-
label: "Show thinking",
|
|
413
|
-
currentValue: showThinking ? "on" : "off",
|
|
414
|
-
values: ["off", "on"],
|
|
415
|
-
},
|
|
416
|
-
];
|
|
417
|
-
const settings = createSettingsList(items, (id, value) => {
|
|
418
|
-
if (id === "deliver") {
|
|
419
|
-
deliverDefault = value === "on";
|
|
420
|
-
updateFooter();
|
|
421
|
-
}
|
|
422
|
-
if (id === "tools") {
|
|
423
|
-
toolsExpanded = value === "expanded";
|
|
424
|
-
chatLog.setToolsExpanded(toolsExpanded);
|
|
425
|
-
}
|
|
426
|
-
if (id === "thinking") {
|
|
427
|
-
showThinking = value === "on";
|
|
428
|
-
void loadHistory();
|
|
429
|
-
}
|
|
430
|
-
tui.requestRender();
|
|
431
|
-
}, () => {
|
|
432
|
-
closeOverlay();
|
|
433
|
-
tui.requestRender();
|
|
434
|
-
});
|
|
435
|
-
openOverlay(settings);
|
|
436
|
-
tui.requestRender();
|
|
437
|
-
};
|
|
438
|
-
const handleCommand = async (raw) => {
|
|
439
|
-
const { name, args } = parseCommand(raw);
|
|
440
|
-
if (!name)
|
|
441
|
-
return;
|
|
442
|
-
switch (name) {
|
|
443
|
-
case "help":
|
|
444
|
-
chatLog.addSystem(helpText());
|
|
445
|
-
break;
|
|
446
|
-
case "status":
|
|
447
|
-
try {
|
|
448
|
-
const status = await client.getStatus();
|
|
449
|
-
chatLog.addSystem(typeof status === "string"
|
|
450
|
-
? status
|
|
451
|
-
: JSON.stringify(status, null, 2));
|
|
452
|
-
}
|
|
453
|
-
catch (err) {
|
|
454
|
-
chatLog.addSystem(`status failed: ${String(err)}`);
|
|
455
|
-
}
|
|
456
|
-
break;
|
|
457
|
-
case "session":
|
|
458
|
-
if (!args) {
|
|
459
|
-
await openSessionSelector();
|
|
460
|
-
}
|
|
461
|
-
else {
|
|
462
|
-
await setSession(args);
|
|
463
|
-
}
|
|
464
|
-
break;
|
|
465
|
-
case "sessions":
|
|
466
|
-
await openSessionSelector();
|
|
467
|
-
break;
|
|
468
|
-
case "model":
|
|
469
|
-
if (!args) {
|
|
470
|
-
await openModelSelector();
|
|
471
|
-
}
|
|
472
|
-
else {
|
|
473
|
-
try {
|
|
474
|
-
await client.patchSession({
|
|
475
|
-
key: currentSessionKey,
|
|
476
|
-
model: args,
|
|
477
|
-
});
|
|
478
|
-
chatLog.addSystem(`model set to ${args}`);
|
|
479
|
-
await refreshSessionInfo();
|
|
480
|
-
}
|
|
481
|
-
catch (err) {
|
|
482
|
-
chatLog.addSystem(`model set failed: ${String(err)}`);
|
|
483
|
-
}
|
|
484
|
-
}
|
|
485
|
-
break;
|
|
486
|
-
case "models":
|
|
487
|
-
await openModelSelector();
|
|
488
|
-
break;
|
|
489
|
-
case "think":
|
|
490
|
-
if (!args) {
|
|
491
|
-
chatLog.addSystem("usage: /think <off|minimal|low|medium|high>");
|
|
492
|
-
break;
|
|
493
|
-
}
|
|
494
|
-
try {
|
|
495
|
-
await client.patchSession({
|
|
496
|
-
key: currentSessionKey,
|
|
497
|
-
thinkingLevel: args,
|
|
498
|
-
});
|
|
499
|
-
chatLog.addSystem(`thinking set to ${args}`);
|
|
500
|
-
await refreshSessionInfo();
|
|
501
|
-
}
|
|
502
|
-
catch (err) {
|
|
503
|
-
chatLog.addSystem(`think failed: ${String(err)}`);
|
|
504
|
-
}
|
|
505
|
-
break;
|
|
506
|
-
case "verbose":
|
|
507
|
-
if (!args) {
|
|
508
|
-
chatLog.addSystem("usage: /verbose <on|off>");
|
|
509
|
-
break;
|
|
510
|
-
}
|
|
511
|
-
try {
|
|
512
|
-
await client.patchSession({
|
|
513
|
-
key: currentSessionKey,
|
|
514
|
-
verboseLevel: args,
|
|
515
|
-
});
|
|
516
|
-
chatLog.addSystem(`verbose set to ${args}`);
|
|
517
|
-
await refreshSessionInfo();
|
|
518
|
-
}
|
|
519
|
-
catch (err) {
|
|
520
|
-
chatLog.addSystem(`verbose failed: ${String(err)}`);
|
|
521
|
-
}
|
|
522
|
-
break;
|
|
523
|
-
case "elevated":
|
|
524
|
-
if (!args) {
|
|
525
|
-
chatLog.addSystem("usage: /elevated <on|off>");
|
|
526
|
-
break;
|
|
527
|
-
}
|
|
528
|
-
try {
|
|
529
|
-
await client.patchSession({
|
|
530
|
-
key: currentSessionKey,
|
|
531
|
-
elevatedLevel: args,
|
|
532
|
-
});
|
|
533
|
-
chatLog.addSystem(`elevated set to ${args}`);
|
|
534
|
-
await refreshSessionInfo();
|
|
535
|
-
}
|
|
536
|
-
catch (err) {
|
|
537
|
-
chatLog.addSystem(`elevated failed: ${String(err)}`);
|
|
538
|
-
}
|
|
539
|
-
break;
|
|
540
|
-
case "activation":
|
|
541
|
-
if (!args) {
|
|
542
|
-
chatLog.addSystem("usage: /activation <mention|always>");
|
|
543
|
-
break;
|
|
544
|
-
}
|
|
545
|
-
try {
|
|
546
|
-
await client.patchSession({
|
|
547
|
-
key: currentSessionKey,
|
|
548
|
-
groupActivation: args === "always" ? "always" : "mention",
|
|
549
|
-
});
|
|
550
|
-
chatLog.addSystem(`activation set to ${args}`);
|
|
551
|
-
await refreshSessionInfo();
|
|
552
|
-
}
|
|
553
|
-
catch (err) {
|
|
554
|
-
chatLog.addSystem(`activation failed: ${String(err)}`);
|
|
555
|
-
}
|
|
556
|
-
break;
|
|
557
|
-
case "deliver":
|
|
558
|
-
if (!args) {
|
|
559
|
-
chatLog.addSystem("usage: /deliver <on|off>");
|
|
560
|
-
break;
|
|
561
|
-
}
|
|
562
|
-
deliverDefault = args === "on";
|
|
563
|
-
updateFooter();
|
|
564
|
-
chatLog.addSystem(`deliver ${deliverDefault ? "on" : "off"}`);
|
|
565
|
-
break;
|
|
566
|
-
case "new":
|
|
567
|
-
case "reset":
|
|
568
|
-
try {
|
|
569
|
-
await client.resetSession(currentSessionKey);
|
|
570
|
-
chatLog.addSystem(`session ${currentSessionKey} reset`);
|
|
571
|
-
await loadHistory();
|
|
572
|
-
}
|
|
573
|
-
catch (err) {
|
|
574
|
-
chatLog.addSystem(`reset failed: ${String(err)}`);
|
|
575
|
-
}
|
|
576
|
-
break;
|
|
577
|
-
case "abort":
|
|
578
|
-
await abortActive();
|
|
579
|
-
break;
|
|
580
|
-
case "settings":
|
|
581
|
-
openSettings();
|
|
582
|
-
break;
|
|
583
|
-
case "exit":
|
|
584
|
-
case "quit":
|
|
585
|
-
client.stop();
|
|
586
|
-
tui.stop();
|
|
587
|
-
process.exit(0);
|
|
588
|
-
break;
|
|
589
|
-
default:
|
|
590
|
-
chatLog.addSystem(`unknown command: /${name}`);
|
|
591
|
-
break;
|
|
592
|
-
}
|
|
593
|
-
tui.requestRender();
|
|
594
|
-
};
|
|
595
|
-
const sendMessage = async (text) => {
|
|
596
|
-
try {
|
|
597
|
-
chatLog.addUser(text);
|
|
598
|
-
tui.requestRender();
|
|
599
|
-
setStatus("sending");
|
|
600
|
-
const { runId } = await client.sendChat({
|
|
601
|
-
sessionKey: currentSessionKey,
|
|
602
|
-
message: text,
|
|
603
|
-
thinking: opts.thinking,
|
|
604
|
-
deliver: deliverDefault,
|
|
605
|
-
timeoutMs: opts.timeoutMs,
|
|
606
|
-
});
|
|
607
|
-
activeChatRunId = runId;
|
|
608
|
-
setStatus("waiting");
|
|
609
|
-
}
|
|
610
|
-
catch (err) {
|
|
611
|
-
chatLog.addSystem(`send failed: ${String(err)}`);
|
|
612
|
-
setStatus("error");
|
|
613
|
-
}
|
|
614
|
-
tui.requestRender();
|
|
615
|
-
};
|
|
616
|
-
editor.setAutocompleteProvider(new CombinedAutocompleteProvider(getSlashCommands(), process.cwd()));
|
|
617
|
-
editor.onSubmit = (text) => {
|
|
618
|
-
const value = text.trim();
|
|
619
|
-
editor.setText("");
|
|
620
|
-
if (!value)
|
|
621
|
-
return;
|
|
622
|
-
if (value.startsWith("/")) {
|
|
623
|
-
void handleCommand(value);
|
|
624
|
-
return;
|
|
625
|
-
}
|
|
626
|
-
void sendMessage(value);
|
|
627
|
-
};
|
|
628
|
-
editor.onEscape = () => {
|
|
629
|
-
void abortActive();
|
|
630
|
-
};
|
|
631
|
-
editor.onCtrlC = () => {
|
|
632
|
-
const now = Date.now();
|
|
633
|
-
if (editor.getText().trim().length > 0) {
|
|
634
|
-
editor.setText("");
|
|
635
|
-
setStatus("cleared input");
|
|
636
|
-
tui.requestRender();
|
|
637
|
-
return;
|
|
638
|
-
}
|
|
639
|
-
if (now - lastCtrlCAt < 1000) {
|
|
640
|
-
client.stop();
|
|
641
|
-
tui.stop();
|
|
642
|
-
process.exit(0);
|
|
643
|
-
}
|
|
644
|
-
lastCtrlCAt = now;
|
|
645
|
-
setStatus("press ctrl+c again to exit");
|
|
646
|
-
tui.requestRender();
|
|
647
|
-
};
|
|
648
|
-
editor.onCtrlD = () => {
|
|
649
|
-
client.stop();
|
|
650
|
-
tui.stop();
|
|
651
|
-
process.exit(0);
|
|
652
|
-
};
|
|
653
|
-
editor.onCtrlO = () => {
|
|
654
|
-
toolsExpanded = !toolsExpanded;
|
|
655
|
-
chatLog.setToolsExpanded(toolsExpanded);
|
|
656
|
-
setStatus(toolsExpanded ? "tools expanded" : "tools collapsed");
|
|
657
|
-
tui.requestRender();
|
|
658
|
-
};
|
|
659
|
-
editor.onCtrlL = () => {
|
|
660
|
-
void openModelSelector();
|
|
661
|
-
};
|
|
662
|
-
editor.onCtrlP = () => {
|
|
663
|
-
void openSessionSelector();
|
|
664
|
-
};
|
|
665
|
-
editor.onCtrlT = () => {
|
|
666
|
-
showThinking = !showThinking;
|
|
667
|
-
void loadHistory();
|
|
668
|
-
};
|
|
669
|
-
client.onEvent = (evt) => {
|
|
670
|
-
if (evt.event === "chat")
|
|
671
|
-
handleChatEvent(evt.payload);
|
|
672
|
-
if (evt.event === "agent")
|
|
673
|
-
handleAgentEvent(evt.payload);
|
|
674
|
-
};
|
|
675
|
-
client.onConnected = () => {
|
|
676
|
-
isConnected = true;
|
|
677
|
-
setStatus("connected");
|
|
678
|
-
updateHeader();
|
|
679
|
-
if (!historyLoaded) {
|
|
680
|
-
void loadHistory().then(() => {
|
|
681
|
-
chatLog.addSystem("gateway connected");
|
|
682
|
-
tui.requestRender();
|
|
683
|
-
});
|
|
684
|
-
}
|
|
685
|
-
else {
|
|
686
|
-
chatLog.addSystem("gateway reconnected");
|
|
687
|
-
}
|
|
688
|
-
updateFooter();
|
|
689
|
-
tui.requestRender();
|
|
690
|
-
};
|
|
691
|
-
client.onDisconnected = (reason) => {
|
|
692
|
-
isConnected = false;
|
|
693
|
-
chatLog.addSystem(`gateway disconnected: ${reason || "closed"}`);
|
|
694
|
-
setStatus("disconnected");
|
|
695
|
-
updateFooter();
|
|
696
|
-
tui.requestRender();
|
|
697
|
-
};
|
|
698
|
-
client.onGap = (info) => {
|
|
699
|
-
chatLog.addSystem(`event gap: expected ${info.expected}, got ${info.received}`);
|
|
700
|
-
tui.requestRender();
|
|
701
|
-
};
|
|
702
|
-
updateHeader();
|
|
703
|
-
setStatus("connecting");
|
|
704
|
-
updateFooter();
|
|
705
|
-
chatLog.addSystem("connecting...");
|
|
706
|
-
tui.start();
|
|
707
|
-
client.start();
|
|
708
|
-
}
|