clawdbot 2026.1.4-1 → 2026.1.5

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.
Files changed (117) hide show
  1. package/CHANGELOG.md +26 -6
  2. package/README.md +26 -1
  3. package/dist/agents/pi-embedded-runner.js +2 -0
  4. package/dist/agents/pi-embedded-subscribe.js +18 -3
  5. package/dist/agents/pi-tools.js +45 -6
  6. package/dist/agents/tools/browser-tool.js +38 -89
  7. package/dist/agents/tools/cron-tool.js +8 -8
  8. package/dist/agents/workspace.js +8 -1
  9. package/dist/auto-reply/command-detection.js +26 -0
  10. package/dist/auto-reply/reply/agent-runner.js +15 -8
  11. package/dist/auto-reply/reply/commands.js +36 -25
  12. package/dist/auto-reply/reply/directive-handling.js +4 -2
  13. package/dist/auto-reply/reply/directives.js +12 -0
  14. package/dist/auto-reply/reply/session-updates.js +2 -4
  15. package/dist/auto-reply/reply.js +26 -4
  16. package/dist/browser/config.js +22 -4
  17. package/dist/browser/profiles-service.js +3 -1
  18. package/dist/browser/profiles.js +14 -3
  19. package/dist/canvas-host/a2ui/.bundle.hash +2 -0
  20. package/dist/cli/gateway-cli.js +2 -2
  21. package/dist/cli/profile.js +81 -0
  22. package/dist/cli/program.js +10 -1
  23. package/dist/cli/run-main.js +33 -0
  24. package/dist/commands/configure.js +5 -0
  25. package/dist/commands/onboard-providers.js +1 -1
  26. package/dist/commands/setup.js +4 -1
  27. package/dist/config/defaults.js +56 -0
  28. package/dist/config/io.js +47 -6
  29. package/dist/config/paths.js +2 -2
  30. package/dist/config/port-defaults.js +32 -0
  31. package/dist/config/sessions.js +3 -2
  32. package/dist/config/validation.js +2 -2
  33. package/dist/config/zod-schema.js +16 -0
  34. package/dist/discord/monitor.js +75 -266
  35. package/dist/entry.js +16 -0
  36. package/dist/gateway/call.js +8 -1
  37. package/dist/gateway/server-methods/chat.js +1 -1
  38. package/dist/gateway/server.js +14 -3
  39. package/dist/index.js +2 -2
  40. package/dist/infra/control-ui-assets.js +118 -0
  41. package/dist/infra/dotenv.js +15 -0
  42. package/dist/infra/shell-env.js +79 -0
  43. package/dist/infra/system-events.js +50 -23
  44. package/dist/macos/relay.js +8 -2
  45. package/dist/telegram/bot.js +24 -1
  46. package/dist/utils.js +8 -2
  47. package/dist/web/auto-reply.js +18 -21
  48. package/dist/web/inbound.js +5 -1
  49. package/dist/web/qr-image.js +4 -4
  50. package/dist/web/session.js +2 -3
  51. package/docs/agent.md +0 -2
  52. package/docs/assets/markdown.css +4 -1
  53. package/docs/audio.md +0 -2
  54. package/docs/clawd.md +0 -2
  55. package/docs/configuration.md +62 -3
  56. package/docs/docs.json +9 -1
  57. package/docs/faq.md +32 -7
  58. package/docs/gateway.md +28 -0
  59. package/docs/images.md +0 -2
  60. package/docs/index.md +2 -4
  61. package/docs/mac/icon.md +1 -1
  62. package/docs/nix.md +57 -11
  63. package/docs/onboarding.md +0 -2
  64. package/docs/refactor/webagent-session.md +0 -2
  65. package/docs/research/memory.md +1 -1
  66. package/docs/skills.md +0 -2
  67. package/docs/templates/AGENTS.md +2 -2
  68. package/docs/tools.md +15 -0
  69. package/docs/whatsapp.md +2 -0
  70. package/package.json +8 -16
  71. package/dist/control-ui/assets/index-BFID3yAA.css +0 -1
  72. package/dist/control-ui/assets/index-CE_axlTS.js +0 -2235
  73. package/dist/control-ui/assets/index-CE_axlTS.js.map +0 -1
  74. package/dist/control-ui/index.html +0 -15
  75. package/dist/daemon/constants.js +0 -10
  76. package/dist/daemon/launchd.js +0 -276
  77. package/dist/daemon/legacy.js +0 -63
  78. package/dist/daemon/program-args.js +0 -76
  79. package/dist/daemon/schtasks.js +0 -257
  80. package/dist/daemon/service.js +0 -60
  81. package/dist/daemon/systemd.js +0 -266
  82. package/dist/imessage/client.js +0 -165
  83. package/dist/imessage/index.js +0 -3
  84. package/dist/imessage/monitor.js +0 -272
  85. package/dist/imessage/probe.js +0 -26
  86. package/dist/imessage/send.js +0 -83
  87. package/dist/imessage/targets.js +0 -176
  88. package/dist/sessions/send-policy.js +0 -68
  89. package/dist/signal/client.js +0 -134
  90. package/dist/signal/daemon.js +0 -69
  91. package/dist/signal/index.js +0 -3
  92. package/dist/signal/monitor.js +0 -336
  93. package/dist/signal/probe.js +0 -46
  94. package/dist/signal/send.js +0 -91
  95. package/dist/slack/actions.js +0 -97
  96. package/dist/slack/index.js +0 -5
  97. package/dist/slack/monitor.js +0 -1029
  98. package/dist/slack/probe.js +0 -47
  99. package/dist/slack/send.js +0 -131
  100. package/dist/slack/token.js +0 -10
  101. package/dist/tui/commands.js +0 -74
  102. package/dist/tui/components/assistant-message.js +0 -16
  103. package/dist/tui/components/chat-log.js +0 -92
  104. package/dist/tui/components/custom-editor.js +0 -53
  105. package/dist/tui/components/selectors.js +0 -8
  106. package/dist/tui/components/tool-execution.js +0 -111
  107. package/dist/tui/components/user-message.js +0 -17
  108. package/dist/tui/gateway-chat.js +0 -140
  109. package/dist/tui/layout.js +0 -41
  110. package/dist/tui/message-list.js +0 -57
  111. package/dist/tui/theme/theme.js +0 -80
  112. package/dist/tui/theme.js +0 -25
  113. package/dist/tui/tui.js +0 -708
  114. package/dist/wizard/clack-prompter.js +0 -56
  115. package/dist/wizard/onboarding.js +0 -452
  116. package/dist/wizard/prompts.js +0 -6
  117. package/dist/wizard/session.js +0 -203
@@ -1,47 +0,0 @@
1
- import { WebClient } from "@slack/web-api";
2
- function withTimeout(promise, timeoutMs) {
3
- if (!timeoutMs || timeoutMs <= 0)
4
- return promise;
5
- let timer = null;
6
- const timeout = new Promise((_, reject) => {
7
- timer = setTimeout(() => reject(new Error("timeout")), timeoutMs);
8
- });
9
- return Promise.race([promise, timeout]).finally(() => {
10
- if (timer)
11
- clearTimeout(timer);
12
- });
13
- }
14
- export async function probeSlack(token, timeoutMs = 2500) {
15
- const client = new WebClient(token);
16
- const start = Date.now();
17
- try {
18
- const result = await withTimeout(client.auth.test(), timeoutMs);
19
- if (!result.ok) {
20
- return {
21
- ok: false,
22
- status: 200,
23
- error: result.error ?? "unknown",
24
- elapsedMs: Date.now() - start,
25
- };
26
- }
27
- return {
28
- ok: true,
29
- status: 200,
30
- elapsedMs: Date.now() - start,
31
- bot: { id: result.user_id ?? undefined, name: result.user ?? undefined },
32
- team: { id: result.team_id ?? undefined, name: result.team ?? undefined },
33
- };
34
- }
35
- catch (err) {
36
- const message = err instanceof Error ? err.message : String(err);
37
- const status = typeof err.status === "number"
38
- ? err.status
39
- : null;
40
- return {
41
- ok: false,
42
- status,
43
- error: message,
44
- elapsedMs: Date.now() - start,
45
- };
46
- }
47
- }
@@ -1,131 +0,0 @@
1
- import { WebClient } from "@slack/web-api";
2
- import { chunkText, resolveTextChunkLimit } from "../auto-reply/chunk.js";
3
- import { loadConfig } from "../config/config.js";
4
- import { loadWebMedia } from "../web/media.js";
5
- import { resolveSlackBotToken } from "./token.js";
6
- const SLACK_TEXT_LIMIT = 4000;
7
- function resolveToken(explicit) {
8
- const cfgToken = loadConfig().slack?.botToken;
9
- const token = resolveSlackBotToken(explicit ?? process.env.SLACK_BOT_TOKEN ?? cfgToken ?? undefined);
10
- if (!token) {
11
- throw new Error("SLACK_BOT_TOKEN or slack.botToken is required for Slack sends");
12
- }
13
- return token;
14
- }
15
- function parseRecipient(raw) {
16
- const trimmed = raw.trim();
17
- if (!trimmed) {
18
- throw new Error("Recipient is required for Slack sends");
19
- }
20
- const mentionMatch = trimmed.match(/^<@([A-Z0-9]+)>$/i);
21
- if (mentionMatch) {
22
- return { kind: "user", id: mentionMatch[1] };
23
- }
24
- if (trimmed.startsWith("user:")) {
25
- return { kind: "user", id: trimmed.slice("user:".length) };
26
- }
27
- if (trimmed.startsWith("channel:")) {
28
- return { kind: "channel", id: trimmed.slice("channel:".length) };
29
- }
30
- if (trimmed.startsWith("slack:")) {
31
- return { kind: "user", id: trimmed.slice("slack:".length) };
32
- }
33
- if (trimmed.startsWith("@")) {
34
- const candidate = trimmed.slice(1);
35
- if (!/^[A-Z0-9]+$/i.test(candidate)) {
36
- throw new Error("Slack DMs require a user id (use user:<id> or <@id>)");
37
- }
38
- return { kind: "user", id: candidate };
39
- }
40
- if (trimmed.startsWith("#")) {
41
- const candidate = trimmed.slice(1);
42
- if (!/^[A-Z0-9]+$/i.test(candidate)) {
43
- throw new Error("Slack channels require a channel id (use channel:<id>)");
44
- }
45
- return { kind: "channel", id: candidate };
46
- }
47
- return { kind: "channel", id: trimmed };
48
- }
49
- async function resolveChannelId(client, recipient) {
50
- if (recipient.kind === "channel") {
51
- return { channelId: recipient.id };
52
- }
53
- const response = await client.conversations.open({ users: recipient.id });
54
- const channelId = response.channel?.id;
55
- if (!channelId) {
56
- throw new Error("Failed to open Slack DM channel");
57
- }
58
- return { channelId, isDm: true };
59
- }
60
- async function uploadSlackFile(params) {
61
- const { buffer, contentType, fileName } = await loadWebMedia(params.mediaUrl, params.maxBytes);
62
- const basePayload = {
63
- channel_id: params.channelId,
64
- file: buffer,
65
- filename: fileName,
66
- ...(params.caption ? { initial_comment: params.caption } : {}),
67
- ...(contentType ? { filetype: contentType } : {}),
68
- };
69
- const payload = params.threadTs
70
- ? { ...basePayload, thread_ts: params.threadTs }
71
- : basePayload;
72
- const response = await params.client.files.uploadV2(payload);
73
- const parsed = response;
74
- const fileId = parsed.files?.[0]?.id ??
75
- parsed.file?.id ??
76
- parsed.files?.[0]?.name ??
77
- parsed.file?.name ??
78
- "unknown";
79
- return fileId;
80
- }
81
- export async function sendMessageSlack(to, message, opts = {}) {
82
- const trimmedMessage = message?.trim() ?? "";
83
- if (!trimmedMessage && !opts.mediaUrl) {
84
- throw new Error("Slack send requires text or media");
85
- }
86
- const token = resolveToken(opts.token);
87
- const client = opts.client ?? new WebClient(token);
88
- const recipient = parseRecipient(to);
89
- const { channelId } = await resolveChannelId(client, recipient);
90
- const cfg = loadConfig();
91
- const textLimit = resolveTextChunkLimit(cfg, "slack");
92
- const chunkLimit = Math.min(textLimit, SLACK_TEXT_LIMIT);
93
- const chunks = chunkText(trimmedMessage, chunkLimit);
94
- const mediaMaxBytes = typeof cfg.slack?.mediaMaxMb === "number"
95
- ? cfg.slack.mediaMaxMb * 1024 * 1024
96
- : undefined;
97
- let lastMessageId = "";
98
- if (opts.mediaUrl) {
99
- const [firstChunk, ...rest] = chunks;
100
- lastMessageId = await uploadSlackFile({
101
- client,
102
- channelId,
103
- mediaUrl: opts.mediaUrl,
104
- caption: firstChunk,
105
- threadTs: opts.threadTs,
106
- maxBytes: mediaMaxBytes,
107
- });
108
- for (const chunk of rest) {
109
- const response = await client.chat.postMessage({
110
- channel: channelId,
111
- text: chunk,
112
- thread_ts: opts.threadTs,
113
- });
114
- lastMessageId = response.ts ?? lastMessageId;
115
- }
116
- }
117
- else {
118
- for (const chunk of chunks.length ? chunks : [""]) {
119
- const response = await client.chat.postMessage({
120
- channel: channelId,
121
- text: chunk,
122
- thread_ts: opts.threadTs,
123
- });
124
- lastMessageId = response.ts ?? lastMessageId;
125
- }
126
- }
127
- return {
128
- messageId: lastMessageId || "unknown",
129
- channelId,
130
- };
131
- }
@@ -1,10 +0,0 @@
1
- export function normalizeSlackToken(raw) {
2
- const trimmed = raw?.trim();
3
- return trimmed ? trimmed : undefined;
4
- }
5
- export function resolveSlackBotToken(raw) {
6
- return normalizeSlackToken(raw);
7
- }
8
- export function resolveSlackAppToken(raw) {
9
- return normalizeSlackToken(raw);
10
- }
@@ -1,74 +0,0 @@
1
- const THINK_LEVELS = ["off", "minimal", "low", "medium", "high"];
2
- const VERBOSE_LEVELS = ["on", "off"];
3
- const ELEVATED_LEVELS = ["on", "off"];
4
- const ACTIVATION_LEVELS = ["mention", "always"];
5
- const TOGGLE = ["on", "off"];
6
- export function parseCommand(input) {
7
- const trimmed = input.replace(/^\//, "").trim();
8
- if (!trimmed)
9
- return { name: "", args: "" };
10
- const [name, ...rest] = trimmed.split(/\s+/);
11
- return { name: name.toLowerCase(), args: rest.join(" ").trim() };
12
- }
13
- export function getSlashCommands() {
14
- return [
15
- { name: "help", description: "Show slash command help" },
16
- { name: "status", description: "Show gateway status summary" },
17
- { name: "session", description: "Switch session (or open picker)" },
18
- { name: "sessions", description: "Open session picker" },
19
- {
20
- name: "model",
21
- description: "Set model (or open picker)",
22
- },
23
- { name: "models", description: "Open model picker" },
24
- {
25
- name: "think",
26
- description: "Set thinking level",
27
- getArgumentCompletions: (prefix) => THINK_LEVELS.filter((v) => v.startsWith(prefix.toLowerCase())).map((value) => ({ value, label: value })),
28
- },
29
- {
30
- name: "verbose",
31
- description: "Set verbose on/off",
32
- getArgumentCompletions: (prefix) => VERBOSE_LEVELS.filter((v) => v.startsWith(prefix.toLowerCase())).map((value) => ({ value, label: value })),
33
- },
34
- {
35
- name: "elevated",
36
- description: "Set elevated on/off",
37
- getArgumentCompletions: (prefix) => ELEVATED_LEVELS.filter((v) => v.startsWith(prefix.toLowerCase())).map((value) => ({ value, label: value })),
38
- },
39
- {
40
- name: "activation",
41
- description: "Set group activation",
42
- getArgumentCompletions: (prefix) => ACTIVATION_LEVELS.filter((v) => v.startsWith(prefix.toLowerCase())).map((value) => ({ value, label: value })),
43
- },
44
- {
45
- name: "deliver",
46
- description: "Toggle delivery of assistant replies",
47
- getArgumentCompletions: (prefix) => TOGGLE.filter((v) => v.startsWith(prefix.toLowerCase())).map((value) => ({ value, label: value })),
48
- },
49
- { name: "abort", description: "Abort active run" },
50
- { name: "new", description: "Reset the session" },
51
- { name: "reset", description: "Reset the session" },
52
- { name: "settings", description: "Open settings" },
53
- { name: "exit", description: "Exit the TUI" },
54
- { name: "quit", description: "Exit the TUI" },
55
- ];
56
- }
57
- export function helpText() {
58
- return [
59
- "Slash commands:",
60
- "/help",
61
- "/status",
62
- "/session <key> (or /sessions)",
63
- "/model <provider/model> (or /models)",
64
- "/think <off|minimal|low|medium|high>",
65
- "/verbose <on|off>",
66
- "/elevated <on|off>",
67
- "/activation <mention|always>",
68
- "/deliver <on|off>",
69
- "/new or /reset",
70
- "/abort",
71
- "/settings",
72
- "/exit",
73
- ].join("\n");
74
- }
@@ -1,16 +0,0 @@
1
- import { Container, Markdown, Spacer } from "@mariozechner/pi-tui";
2
- import { markdownTheme, theme } from "../theme/theme.js";
3
- export class AssistantMessageComponent extends Container {
4
- body;
5
- constructor(text) {
6
- super();
7
- this.body = new Markdown(text, 1, 0, markdownTheme, {
8
- color: (line) => theme.fg(line),
9
- });
10
- this.addChild(new Spacer(1));
11
- this.addChild(this.body);
12
- }
13
- setText(text) {
14
- this.body.setText(text);
15
- }
16
- }
@@ -1,92 +0,0 @@
1
- import { Container, Spacer, Text } from "@mariozechner/pi-tui";
2
- import { theme } from "../theme/theme.js";
3
- import { AssistantMessageComponent } from "./assistant-message.js";
4
- import { ToolExecutionComponent } from "./tool-execution.js";
5
- import { UserMessageComponent } from "./user-message.js";
6
- export class ChatLog extends Container {
7
- toolById = new Map();
8
- streamingAssistant = null;
9
- streamingRunId = null;
10
- streamingText = null;
11
- toolsExpanded = false;
12
- clearAll() {
13
- this.clear();
14
- this.toolById.clear();
15
- this.streamingAssistant = null;
16
- this.streamingRunId = null;
17
- this.streamingText = null;
18
- }
19
- addSystem(text) {
20
- this.addChild(new Spacer(1));
21
- this.addChild(new Text(theme.system(text), 1, 0));
22
- }
23
- addUser(text) {
24
- this.addChild(new UserMessageComponent(text));
25
- }
26
- startAssistant(text, runId) {
27
- const component = new AssistantMessageComponent(text);
28
- this.streamingAssistant = component;
29
- this.streamingRunId = runId ?? null;
30
- this.streamingText = text;
31
- this.addChild(component);
32
- return component;
33
- }
34
- updateAssistant(text, runId) {
35
- if (!this.streamingAssistant ||
36
- (runId && this.streamingRunId && runId !== this.streamingRunId)) {
37
- this.startAssistant(text, runId);
38
- return;
39
- }
40
- this.streamingText = text;
41
- this.streamingAssistant.setText(text);
42
- }
43
- finalizeAssistant(text, runId) {
44
- if (this.streamingAssistant &&
45
- (!runId || runId === this.streamingRunId || text === this.streamingText)) {
46
- this.streamingText = text;
47
- this.streamingAssistant.setText(text);
48
- }
49
- else {
50
- this.startAssistant(text, runId);
51
- }
52
- this.streamingAssistant = null;
53
- this.streamingRunId = null;
54
- this.streamingText = null;
55
- }
56
- startTool(toolCallId, toolName, args) {
57
- const existing = this.toolById.get(toolCallId);
58
- if (existing) {
59
- existing.setArgs(args);
60
- return existing;
61
- }
62
- const component = new ToolExecutionComponent(toolName, args);
63
- component.setExpanded(this.toolsExpanded);
64
- this.toolById.set(toolCallId, component);
65
- this.addChild(component);
66
- return component;
67
- }
68
- updateToolArgs(toolCallId, args) {
69
- const existing = this.toolById.get(toolCallId);
70
- if (!existing)
71
- return;
72
- existing.setArgs(args);
73
- }
74
- updateToolResult(toolCallId, result, opts) {
75
- const existing = this.toolById.get(toolCallId);
76
- if (!existing)
77
- return;
78
- if (opts?.partial) {
79
- existing.setPartialResult(result);
80
- return;
81
- }
82
- existing.setResult(result, {
83
- isError: opts?.isError,
84
- });
85
- }
86
- setToolsExpanded(expanded) {
87
- this.toolsExpanded = expanded;
88
- for (const tool of this.toolById.values()) {
89
- tool.setExpanded(expanded);
90
- }
91
- }
92
- }
@@ -1,53 +0,0 @@
1
- import { Editor, isAltEnter, isCtrlC, isCtrlD, isCtrlL, isCtrlO, isCtrlP, isCtrlT, isEscape, isShiftTab, } from "@mariozechner/pi-tui";
2
- export class CustomEditor extends Editor {
3
- onEscape;
4
- onCtrlC;
5
- onCtrlD;
6
- onCtrlL;
7
- onCtrlO;
8
- onCtrlP;
9
- onCtrlT;
10
- onShiftTab;
11
- onAltEnter;
12
- handleInput(data) {
13
- if (isAltEnter(data) && this.onAltEnter) {
14
- this.onAltEnter();
15
- return;
16
- }
17
- if (isCtrlL(data) && this.onCtrlL) {
18
- this.onCtrlL();
19
- return;
20
- }
21
- if (isCtrlO(data) && this.onCtrlO) {
22
- this.onCtrlO();
23
- return;
24
- }
25
- if (isCtrlP(data) && this.onCtrlP) {
26
- this.onCtrlP();
27
- return;
28
- }
29
- if (isCtrlT(data) && this.onCtrlT) {
30
- this.onCtrlT();
31
- return;
32
- }
33
- if (isShiftTab(data) && this.onShiftTab) {
34
- this.onShiftTab();
35
- return;
36
- }
37
- if (isEscape(data) && this.onEscape && !this.isShowingAutocomplete()) {
38
- this.onEscape();
39
- return;
40
- }
41
- if (isCtrlC(data) && this.onCtrlC) {
42
- this.onCtrlC();
43
- return;
44
- }
45
- if (isCtrlD(data)) {
46
- if (this.getText().length === 0 && this.onCtrlD) {
47
- this.onCtrlD();
48
- }
49
- return;
50
- }
51
- super.handleInput(data);
52
- }
53
- }
@@ -1,8 +0,0 @@
1
- import { SelectList, SettingsList, } from "@mariozechner/pi-tui";
2
- import { selectListTheme, settingsListTheme } from "../theme/theme.js";
3
- export function createSelectList(items, maxVisible = 7) {
4
- return new SelectList(items, maxVisible, selectListTheme);
5
- }
6
- export function createSettingsList(items, onChange, onCancel, maxVisible = 7) {
7
- return new SettingsList(items, maxVisible, settingsListTheme, onChange, onCancel);
8
- }
@@ -1,111 +0,0 @@
1
- import { Box, Container, Markdown, Spacer, Text } from "@mariozechner/pi-tui";
2
- import { formatToolDetail, resolveToolDisplay, } from "../../agents/tool-display.js";
3
- import { markdownTheme, theme } from "../theme/theme.js";
4
- const PREVIEW_LINES = 12;
5
- function formatArgs(toolName, args) {
6
- const display = resolveToolDisplay({ name: toolName, args });
7
- const detail = formatToolDetail(display);
8
- if (detail)
9
- return detail;
10
- if (!args || typeof args !== "object")
11
- return "";
12
- try {
13
- return JSON.stringify(args);
14
- }
15
- catch {
16
- return "";
17
- }
18
- }
19
- function extractText(result) {
20
- if (!result?.content)
21
- return "";
22
- const lines = [];
23
- for (const entry of result.content) {
24
- if (entry.type === "text" && entry.text) {
25
- lines.push(entry.text);
26
- }
27
- else if (entry.type === "image") {
28
- const mime = entry.mimeType ?? "image";
29
- const size = entry.bytes ? ` ${Math.round(entry.bytes / 1024)}kb` : "";
30
- const omitted = entry.omitted ? " (omitted)" : "";
31
- lines.push(`[${mime}${size}${omitted}]`);
32
- }
33
- }
34
- return lines.join("\n").trim();
35
- }
36
- export class ToolExecutionComponent extends Container {
37
- box;
38
- header;
39
- argsLine;
40
- output;
41
- toolName;
42
- args;
43
- result;
44
- expanded = false;
45
- isError = false;
46
- isPartial = true;
47
- constructor(toolName, args) {
48
- super();
49
- this.toolName = toolName;
50
- this.args = args;
51
- this.box = new Box(1, 1, (line) => theme.toolPendingBg(line));
52
- this.header = new Text("", 0, 0);
53
- this.argsLine = new Text("", 0, 0);
54
- this.output = new Markdown("", 0, 0, markdownTheme, {
55
- color: (line) => theme.toolOutput(line),
56
- });
57
- this.addChild(new Spacer(1));
58
- this.addChild(this.box);
59
- this.box.addChild(this.header);
60
- this.box.addChild(this.argsLine);
61
- this.box.addChild(this.output);
62
- this.refresh();
63
- }
64
- setArgs(args) {
65
- this.args = args;
66
- this.refresh();
67
- }
68
- setExpanded(expanded) {
69
- this.expanded = expanded;
70
- this.refresh();
71
- }
72
- setResult(result, opts) {
73
- this.result = result;
74
- this.isPartial = false;
75
- this.isError = Boolean(opts?.isError);
76
- this.refresh();
77
- }
78
- setPartialResult(result) {
79
- this.result = result;
80
- this.isPartial = true;
81
- this.refresh();
82
- }
83
- refresh() {
84
- const bg = this.isPartial
85
- ? theme.toolPendingBg
86
- : this.isError
87
- ? theme.toolErrorBg
88
- : theme.toolSuccessBg;
89
- this.box.setBgFn((line) => bg(line));
90
- const display = resolveToolDisplay({
91
- name: this.toolName,
92
- args: this.args,
93
- });
94
- const title = `${display.emoji} ${display.label}${this.isPartial ? " (running)" : ""}`;
95
- this.header.setText(theme.toolTitle(theme.bold(title)));
96
- const argLine = formatArgs(this.toolName, this.args);
97
- this.argsLine.setText(argLine ? theme.dim(argLine) : theme.dim(" "));
98
- const raw = extractText(this.result);
99
- const text = raw || (this.isPartial ? "…" : "");
100
- if (!this.expanded && text) {
101
- const lines = text.split("\n");
102
- const preview = lines.length > PREVIEW_LINES
103
- ? `${lines.slice(0, PREVIEW_LINES).join("\n")}\n…`
104
- : text;
105
- this.output.setText(preview);
106
- }
107
- else {
108
- this.output.setText(text);
109
- }
110
- }
111
- }
@@ -1,17 +0,0 @@
1
- import { Container, Markdown, Spacer } from "@mariozechner/pi-tui";
2
- import { markdownTheme, theme } from "../theme/theme.js";
3
- export class UserMessageComponent extends Container {
4
- body;
5
- constructor(text) {
6
- super();
7
- this.body = new Markdown(text, 1, 1, markdownTheme, {
8
- bgColor: (line) => theme.userBg(line),
9
- color: (line) => theme.userText(line),
10
- });
11
- this.addChild(new Spacer(1));
12
- this.addChild(this.body);
13
- }
14
- setText(text) {
15
- this.body.setText(text);
16
- }
17
- }