@sideboard-ai/core 0.1.263 → 0.1.266
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/{abletime-6AOJI2YZ.js → abletime-4ZSDI4YP.js} +9 -6
- package/dist/{abletime-U75ZGDIX.js → abletime-JNQAHLHH.js} +7 -4
- package/dist/{abletime-oauth-TA2YDCYX.js → abletime-oauth-4RXZH2KU.js} +3 -3
- package/dist/{abletime-oauth-LOAHGG4X.js → abletime-oauth-DCEIINYT.js} +5 -5
- package/dist/{abletime-rest-CTA5WV26.js → abletime-rest-KVBJFVFD.js} +3 -3
- package/dist/{abletime-rest-YZPFM6IY.js → abletime-rest-VN6PZ6P2.js} +5 -5
- package/dist/agents/cursor-runner.cjs +6 -2
- package/dist/agents/cursor-runner.js +13 -7
- package/dist/{agents-3TUMFSMH.js → agents-G3JTULM2.js} +7 -5
- package/dist/{agents-6FWDRVA2.js → agents-XRXJCAWJ.js} +11 -10
- package/dist/{app-settings-RKFFUIFQ.js → app-settings-L5HGYXWJ.js} +1 -3
- package/dist/{app-settings-MVZPM6WW.js → app-settings-RAC4PSSP.js} +3 -5
- package/dist/{caffeinate-hold-UAGLSGMM.js → caffeinate-hold-7JM5H5P3.js} +2 -2
- package/dist/{chunk-F5MFNUAA.js → chunk-27Z4MEQ5.js} +1 -1
- package/dist/{chunk-EK3KVT7I.js → chunk-2XIRV7FS.js} +53 -1254
- package/dist/{chunk-3H7O27X2.js → chunk-3VBS6MES.js} +39 -1017
- package/dist/chunk-4ECDIKAS.js +2273 -0
- package/dist/{chunk-H547WSSZ.js → chunk-4RZRHTSZ.js} +1 -1
- package/dist/{chunk-QN5DL2QO.js → chunk-5DZUWGKV.js} +1 -1
- package/dist/chunk-5N4Q46SG.js +392 -0
- package/dist/{chunk-YKYEHDMX.js → chunk-AGXZAMOF.js} +5 -5
- package/dist/{chunk-RKPBIB2U.js → chunk-DB7MVDYS.js} +33 -7
- package/dist/{chunk-BOMSJUXW.js → chunk-DBFAFI5A.js} +5 -5
- package/dist/chunk-FAGK33E2.js +994 -0
- package/dist/{chunk-A5SAYFJ4.js → chunk-FIBZEFKA.js} +4 -390
- package/dist/{chunk-5PK53SAP.js → chunk-GONPMH36.js} +5 -5
- package/dist/{chunk-L5WGAIL2.js → chunk-GZXKUUJO.js} +35 -9
- package/dist/{chunk-Z2LGMV6C.js → chunk-H5XL4KUS.js} +21 -0
- package/dist/{chunk-T3AYRQJZ.js → chunk-HSRN2CSL.js} +473 -2487
- package/dist/{chunk-TJ673EZT.js → chunk-JANCPF7Z.js} +2 -2
- package/dist/chunk-KZ2CDZBZ.js +828 -0
- package/dist/{chunk-6YY7XPPL.js → chunk-L6CYOON4.js} +5 -5
- package/dist/{chunk-6YOXQVJ7.js → chunk-LAIBYKZS.js} +3 -3
- package/dist/{chunk-4ALDX5IO.js → chunk-NWE3NOWQ.js} +3 -3
- package/dist/{chunk-36FOBJBF.js → chunk-OIENQEGG.js} +63 -38
- package/dist/{chunk-2QUQH2SZ.js → chunk-PAH7KDL2.js} +68 -24
- package/dist/{chunk-XAISOBBG.js → chunk-TQRSVE7D.js} +1 -1
- package/dist/chunk-UKSJO36E.js +1224 -0
- package/dist/{chunk-CEMNIGQH.js → chunk-UN5Q7POQ.js} +1 -1
- package/dist/{chunk-5ZPT5YAV.js → chunk-USBZ7PH4.js} +1 -1
- package/dist/chunk-UXI52MLJ.js +826 -0
- package/dist/chunk-X7RP3TQB.js +2385 -0
- package/dist/{chunk-QX6QUZVD.js → chunk-ZSGSVFWD.js} +424 -2611
- package/dist/conductor-MLM5NGTP.js +104 -0
- package/dist/conductor-NRKTEN5X.js +105 -0
- package/dist/{connected-teams-7CENZDWU.js → connected-teams-JIUOSZ6C.js} +2 -2
- package/dist/{coordinator-prompt-J7DKHOCH.js → coordinator-prompt-4GHKCVRH.js} +7 -7
- package/dist/{coordinator-prompt-HZCQZ6C5.js → coordinator-prompt-BKRQDZFT.js} +3 -3
- package/dist/{cursor-recover-FALSNEZN.js → cursor-recover-MB3T2K5T.js} +2 -2
- package/dist/{global-workspace-TI26K25J.js → global-workspace-EEWJOMED.js} +9 -3
- package/dist/{global-workspace-6LDUZ2NF.js → global-workspace-EWFL4HPW.js} +13 -7
- package/dist/index.cjs +3918 -2592
- package/dist/index.d.cts +350 -68
- package/dist/index.d.ts +350 -68
- package/dist/index.js +4181 -3462
- package/dist/{managed-accounts-4BLTJKAN.js → managed-accounts-46A4KRUE.js} +2 -2
- package/dist/mcp/run-stdio.cjs +1894 -2012
- package/dist/mcp/run-stdio.js +719 -1445
- package/dist/{orchestrator-5YU46YNS.js → orchestrator-6ZQ2CYS4.js} +9 -6
- package/dist/{orchestrator-4JSG7BWC.js → orchestrator-MA3MHYIM.js} +15 -13
- package/dist/{paths-IUR3ABNG.js → paths-HYT5WOOX.js} +1 -1
- package/dist/{thread-store-FJ5CDX2M.js → thread-store-B3ZM4QEZ.js} +3 -3
- package/dist/{workspaces-QJ3XIPUJ.js → workspaces-F33Z4CEY.js} +3 -3
- package/dist/{workspaces-WOQHJFRQ.js → workspaces-SLP24Z3S.js} +7 -7
- package/dist/{worktree-6U2FA5LO.js → worktree-ACW23KJ5.js} +6 -6
- package/dist/{worktree-KQA4ON2G.js → worktree-C7RPVHZ7.js} +2 -2
- package/package.json +1 -1
- package/dist/chunk-CEOTK5GZ.js +0 -479
- package/dist/chunk-K7B25HO4.js +0 -477
|
@@ -0,0 +1,994 @@
|
|
|
1
|
+
import {
|
|
2
|
+
clipToolResultForStore
|
|
3
|
+
} from "./chunk-5N4Q46SG.js";
|
|
4
|
+
|
|
5
|
+
// src/agents/message-parts.ts
|
|
6
|
+
function asRecord(input) {
|
|
7
|
+
if (input && typeof input === "object" && !Array.isArray(input)) {
|
|
8
|
+
return input;
|
|
9
|
+
}
|
|
10
|
+
return void 0;
|
|
11
|
+
}
|
|
12
|
+
function str(v) {
|
|
13
|
+
return typeof v === "string" && v.trim() ? v : void 0;
|
|
14
|
+
}
|
|
15
|
+
var CHAT_DETAIL_MAX = 72;
|
|
16
|
+
function clipChatLine(value, max = CHAT_DETAIL_MAX) {
|
|
17
|
+
const line = value.trim().split(/\r?\n/, 1)[0] ?? "";
|
|
18
|
+
if (line.length <= max) return line;
|
|
19
|
+
return `${line.slice(0, Math.max(1, max - 1))}\u2026`;
|
|
20
|
+
}
|
|
21
|
+
var SHELL_BIN_DIR = /^(?:\/usr\/local\/bin|\/usr\/bin|\/bin|\/opt\/homebrew\/bin|\/sbin|\/usr\/sbin)\//;
|
|
22
|
+
function rootedPathWithSpaces(trimmed) {
|
|
23
|
+
const [head = "", ...rest] = trimmed.split(/\s+/);
|
|
24
|
+
if (SHELL_BIN_DIR.test(head)) return false;
|
|
25
|
+
return rest.every((tok) => !/^[-/~$]/.test(tok) && !tok.includes("="));
|
|
26
|
+
}
|
|
27
|
+
function looksLikeFilePath(value) {
|
|
28
|
+
const trimmed = value.trim();
|
|
29
|
+
if (!trimmed || /[\r\n;|&<>]/.test(trimmed)) return false;
|
|
30
|
+
const rooted = trimmed.startsWith("/") || trimmed.startsWith("~/") || /^[A-Za-z]:[\\/]/.test(trimmed);
|
|
31
|
+
if (/\s/.test(trimmed)) return rooted && rootedPathWithSpaces(trimmed);
|
|
32
|
+
if (rooted) return true;
|
|
33
|
+
return trimmed.includes("/") || trimmed.includes("\\");
|
|
34
|
+
}
|
|
35
|
+
function stripWorktreePrefix(path, worktreePath) {
|
|
36
|
+
if (!worktreePath) return path;
|
|
37
|
+
const prefix = worktreePath.replace(/[/\\]+$/, "");
|
|
38
|
+
if (path === prefix || path === `${prefix}/`) return "";
|
|
39
|
+
if (path.startsWith(`${prefix}/`)) return path.slice(prefix.length + 1);
|
|
40
|
+
return path;
|
|
41
|
+
}
|
|
42
|
+
function visibleToolRowDetail(detail, description, worktreePath) {
|
|
43
|
+
if (!detail?.trim()) return void 0;
|
|
44
|
+
const raw = detail.trim();
|
|
45
|
+
const desc = (description ?? "").trim();
|
|
46
|
+
if (!looksLikeFilePath(raw)) {
|
|
47
|
+
const clipped = clipChatLine(raw);
|
|
48
|
+
if (!clipped || desc === clipped || desc === raw) return void 0;
|
|
49
|
+
return clipped;
|
|
50
|
+
}
|
|
51
|
+
const rel = stripWorktreePrefix(raw, worktreePath);
|
|
52
|
+
if (!rel) return void 0;
|
|
53
|
+
const base = fileBasename(rel);
|
|
54
|
+
if (desc && (desc === base || desc.endsWith(` ${base}`))) return void 0;
|
|
55
|
+
if (rel.length <= 42) return rel;
|
|
56
|
+
const parts = rel.split(/[/\\]/).filter(Boolean);
|
|
57
|
+
if (parts.length >= 2) return `\u2026/${parts.slice(-2).join("/")}`;
|
|
58
|
+
return base;
|
|
59
|
+
}
|
|
60
|
+
function isSkillToolName(name) {
|
|
61
|
+
const n = (name ?? "").trim();
|
|
62
|
+
if (/^(Skill|SlashCommand)$/i.test(n)) return true;
|
|
63
|
+
return /(?:^|__)Skill$/i.test(n);
|
|
64
|
+
}
|
|
65
|
+
function skillCommandFromInput(input) {
|
|
66
|
+
const raw = str(input?.skill) ?? str(input?.command) ?? str(input?.name) ?? str(input?.skill_name) ?? str(input?.skillName);
|
|
67
|
+
if (raw) return raw.replace(/^\//, "").trim() || void 0;
|
|
68
|
+
const path = str(input?.file_path) ?? str(input?.path);
|
|
69
|
+
if (path && /SKILL\.md$/i.test(path)) {
|
|
70
|
+
const parts = path.replace(/\\/g, "/").split("/");
|
|
71
|
+
const dir = parts[parts.length - 2];
|
|
72
|
+
if (dir && dir !== "skills") return dir;
|
|
73
|
+
}
|
|
74
|
+
return void 0;
|
|
75
|
+
}
|
|
76
|
+
function looksLikeComposerSkillExpansion(text) {
|
|
77
|
+
const t = text.trim();
|
|
78
|
+
return /^## Skill: \//m.test(t) && /Follow this skill for the request above:/i.test(t);
|
|
79
|
+
}
|
|
80
|
+
var SKILL_FRONTMATTER_NAME = /^\s*name\s*:\s*['"]?([A-Za-z0-9][A-Za-z0-9._-]*)['"]?\s*$/m;
|
|
81
|
+
function skillNameFromFrontmatter(text) {
|
|
82
|
+
const t = text.trim();
|
|
83
|
+
if (!t) return void 0;
|
|
84
|
+
let yaml = t;
|
|
85
|
+
if (!yaml.startsWith("---")) {
|
|
86
|
+
const at = t.search(/(?:^|\n)---(?:\r?\n)/);
|
|
87
|
+
if (at < 0 || at > 800) return void 0;
|
|
88
|
+
yaml = t.slice(at).replace(/^\n/, "");
|
|
89
|
+
}
|
|
90
|
+
if (!yaml.startsWith("---")) return void 0;
|
|
91
|
+
const close = yaml.indexOf("\n---", 3);
|
|
92
|
+
if (close < 0) return void 0;
|
|
93
|
+
return yaml.slice(3, close).match(SKILL_FRONTMATTER_NAME)?.[1];
|
|
94
|
+
}
|
|
95
|
+
function skillCommandFromBody(text) {
|
|
96
|
+
return skillNameFromFrontmatter(text) ?? text.match(/^## Skill: \/([A-Za-z0-9][A-Za-z0-9._-]*)/m)?.[1] ?? text.match(/^Launching skill:\s*\/?([A-Za-z0-9][A-Za-z0-9._-]*)/im)?.[1];
|
|
97
|
+
}
|
|
98
|
+
function looksLikeSkillBody(text) {
|
|
99
|
+
const t = text.trim();
|
|
100
|
+
if (!t) return false;
|
|
101
|
+
if (looksLikeComposerSkillExpansion(t)) return true;
|
|
102
|
+
if (skillNameFromFrontmatter(t) != null) return true;
|
|
103
|
+
return /^Launching skill:/im.test(t) && /Base directory for this skill:/i.test(t);
|
|
104
|
+
}
|
|
105
|
+
function looksLikeIncompleteSkillDump(text) {
|
|
106
|
+
const t = text.trim();
|
|
107
|
+
if (!t || looksLikeSkillBody(t)) return false;
|
|
108
|
+
if (/^## Skill: \//m.test(t)) return true;
|
|
109
|
+
if (/^Launching skill:/im.test(t)) return true;
|
|
110
|
+
if (/Base directory for this skill:/i.test(t)) return true;
|
|
111
|
+
return t.startsWith("---") && SKILL_FRONTMATTER_NAME.test(t);
|
|
112
|
+
}
|
|
113
|
+
function isCompactToolName(name) {
|
|
114
|
+
const short = (name ?? "").replace(/^mcp__[^_]+__/, "").trim();
|
|
115
|
+
return /^(Compact|ConversationCompact|summarize_context|SummarizeContext|SummarizeConversation)$/i.test(
|
|
116
|
+
short
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
function isContextCompactDone(text) {
|
|
120
|
+
return /\bContext compressed\b/i.test(text.trim());
|
|
121
|
+
}
|
|
122
|
+
function isContextCompactInProgress(text) {
|
|
123
|
+
const t = text.trim();
|
|
124
|
+
if (isContextCompactDone(t)) return false;
|
|
125
|
+
return /^Compressing context/i.test(t);
|
|
126
|
+
}
|
|
127
|
+
function isSystemStatusThinking(event) {
|
|
128
|
+
if (event.replace) return true;
|
|
129
|
+
const t = (event.data ?? "").trim();
|
|
130
|
+
if (!t) return true;
|
|
131
|
+
if (/^API retry\b/i.test(t)) return true;
|
|
132
|
+
if (/^MCP:/i.test(t)) return true;
|
|
133
|
+
if (isContextCompactInProgress(t) || isContextCompactDone(t)) return true;
|
|
134
|
+
return false;
|
|
135
|
+
}
|
|
136
|
+
function looksLikeConversationSummary(text) {
|
|
137
|
+
const t = text.trim();
|
|
138
|
+
if (t.length < 80) return false;
|
|
139
|
+
if (/<summarized_conversation\b/i.test(t) || /<conversation_summary\b/i.test(t)) {
|
|
140
|
+
return true;
|
|
141
|
+
}
|
|
142
|
+
if (/this session is being continued from a previous conversation/i.test(t)) {
|
|
143
|
+
return true;
|
|
144
|
+
}
|
|
145
|
+
if (/conversation compacted|replaced with a summary/i.test(t)) return true;
|
|
146
|
+
if (/^## Prior summary\b/m.test(t)) return true;
|
|
147
|
+
if (/_Automatic extractive summary/i.test(t)) return true;
|
|
148
|
+
if (/"context_summary"\s*:/.test(t)) return true;
|
|
149
|
+
if (/^(?:#+\s*)?(?:conversation summary|summary of (?:the )?conversation|compacted (?:conversation|context))\b/im.test(
|
|
150
|
+
t
|
|
151
|
+
)) {
|
|
152
|
+
return true;
|
|
153
|
+
}
|
|
154
|
+
return false;
|
|
155
|
+
}
|
|
156
|
+
function toolDetail(name, input) {
|
|
157
|
+
if (isSkillToolName(name) || isCompactToolName(name)) return void 0;
|
|
158
|
+
if (!input) return void 0;
|
|
159
|
+
const command = str(input.command) ?? str(input.cmd);
|
|
160
|
+
if (command) return clipChatLine(command);
|
|
161
|
+
const pattern = str(input.pattern) ?? str(input.glob) ?? str(input.glob_pattern);
|
|
162
|
+
const isSearch = /grep|glob|search|ripgrep|findfiles|semsearch/i.test(name);
|
|
163
|
+
if (isSearch && pattern) {
|
|
164
|
+
return pattern.length > 80 ? `${pattern.slice(0, 77)}\u2026` : pattern;
|
|
165
|
+
}
|
|
166
|
+
const path = str(input.file_path) ?? str(input.path) ?? str(input.filePath) ?? str(input.filename);
|
|
167
|
+
if (path) return path;
|
|
168
|
+
if (pattern) return pattern.length > 80 ? `${pattern.slice(0, 77)}\u2026` : pattern;
|
|
169
|
+
const query = str(input.query) ?? str(input.prompt);
|
|
170
|
+
if (query) return query.length > 80 ? `${query.slice(0, 77)}\u2026` : query;
|
|
171
|
+
try {
|
|
172
|
+
const raw = JSON.stringify(input);
|
|
173
|
+
return raw.length > 80 ? `${raw.slice(0, 77)}\u2026` : raw;
|
|
174
|
+
} catch {
|
|
175
|
+
return name;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
function toolDescription(name, input) {
|
|
179
|
+
const n = name.replace(/^mcp__/, "").replace(/__/g, " \xB7 ");
|
|
180
|
+
if (/^get_record_types$|recordTypes/i.test(name)) return "List record types";
|
|
181
|
+
if (/connectedAgentRequest/i.test(name)) {
|
|
182
|
+
return str(input?.agent_id) ? `Ask connected agent` : "Ask connected agent";
|
|
183
|
+
}
|
|
184
|
+
if (/present_artifact$/i.test(name)) {
|
|
185
|
+
return str(input?.title) ? `Present ${str(input?.title)}` : "Present artifact";
|
|
186
|
+
}
|
|
187
|
+
if (/present_plan$/i.test(name)) {
|
|
188
|
+
return str(input?.title) ? `Plan ${str(input?.title)}` : "Present plan";
|
|
189
|
+
}
|
|
190
|
+
if (/present_schema$/i.test(name)) {
|
|
191
|
+
return str(input?.title) ? `Schema ${str(input?.title)}` : "Present schema";
|
|
192
|
+
}
|
|
193
|
+
if (/present_files$/i.test(name)) {
|
|
194
|
+
return str(input?.title) ? `Files ${str(input?.title)}` : "Present files";
|
|
195
|
+
}
|
|
196
|
+
if (/wait_for_job$/i.test(name)) {
|
|
197
|
+
return str(input?.id) ? `Wait for ${str(input?.id)}` : "Wait for job";
|
|
198
|
+
}
|
|
199
|
+
if (/stop_job$/i.test(name)) {
|
|
200
|
+
return str(input?.id) ? `Stop ${str(input?.id)}` : "Stop job";
|
|
201
|
+
}
|
|
202
|
+
if (/list_run_scripts$/i.test(name)) return "List run scripts";
|
|
203
|
+
if (/run_dev_script$/i.test(name)) {
|
|
204
|
+
return str(input?.name) ? `Start ${str(input?.name)}` : "Start Dev script";
|
|
205
|
+
}
|
|
206
|
+
if (/stop_dev_script$/i.test(name)) {
|
|
207
|
+
return str(input?.name) ? `Stop ${str(input?.name)}` : "Stop Dev script";
|
|
208
|
+
}
|
|
209
|
+
if (/get_run_log$/i.test(name)) {
|
|
210
|
+
return str(input?.name) ? `Read ${str(input?.name)} log` : "Read Dev log";
|
|
211
|
+
}
|
|
212
|
+
if (/notify_orchestrator$/i.test(name)) return "Notify orchestrator";
|
|
213
|
+
if (/list_schedules$/i.test(name)) return "List schedules";
|
|
214
|
+
if (/create_schedule$/i.test(name)) {
|
|
215
|
+
return str(input?.name) ? `Schedule ${str(input?.name)}` : "Create schedule";
|
|
216
|
+
}
|
|
217
|
+
if (/update_schedule$/i.test(name)) return "Update schedule";
|
|
218
|
+
if (/delete_schedule$/i.test(name)) return "Delete schedule";
|
|
219
|
+
if (/run_schedule$/i.test(name)) return "Run schedule";
|
|
220
|
+
if (isSkillToolName(name)) {
|
|
221
|
+
const cmd = skillCommandFromInput(input);
|
|
222
|
+
return cmd ? `Using /${cmd}` : "Using skill";
|
|
223
|
+
}
|
|
224
|
+
if (isCompactToolName(name)) {
|
|
225
|
+
const trigger = str(input?.trigger);
|
|
226
|
+
if (trigger && !/^auto$/i.test(trigger) && trigger !== "injected") {
|
|
227
|
+
return `Summarized conversation (${trigger})`;
|
|
228
|
+
}
|
|
229
|
+
return "Summarized conversation";
|
|
230
|
+
}
|
|
231
|
+
if (isSubagentToolName(name)) {
|
|
232
|
+
const desc = str(input?.description);
|
|
233
|
+
const sub = asRecord(input?.subagentType);
|
|
234
|
+
const kind = str(sub?.name) ?? str(sub?.kind) ?? str(input?.subagent_type) ?? str(input?.subagentType);
|
|
235
|
+
if (/^spawn_agent$/i.test(name)) {
|
|
236
|
+
return str(input?.prompt) ?? desc ?? "Subagent";
|
|
237
|
+
}
|
|
238
|
+
if (/connectedAgentRequest/i.test(name)) {
|
|
239
|
+
return str(input?.agent_id) ? `Ask connected agent` : "Ask connected agent";
|
|
240
|
+
}
|
|
241
|
+
if (desc && kind) return `${kind}: ${desc}${subagentLiveSuffix(input)}`;
|
|
242
|
+
if (desc) return `${desc}${subagentLiveSuffix(input)}`;
|
|
243
|
+
return `Subagent${subagentLiveSuffix(input)}`;
|
|
244
|
+
}
|
|
245
|
+
if (/^(create|update)_artifact$/i.test(name.replace(/^mcp__[^_]+__/, ""))) {
|
|
246
|
+
return str(input?.title) ? `Artifact ${str(input?.title)}` : "Artifact";
|
|
247
|
+
}
|
|
248
|
+
if (!input) return n;
|
|
249
|
+
if (/bash|shell|terminal/i.test(name) && str(input.command)) {
|
|
250
|
+
const cmd = str(input.command);
|
|
251
|
+
if (/git\s+fetch/i.test(cmd)) return "Fetch latest from origin and check status";
|
|
252
|
+
if (/git\s+status/i.test(cmd)) return "Check git status";
|
|
253
|
+
if (/git\s+log/i.test(cmd)) return "Inspect recent commits";
|
|
254
|
+
if (/git\s+branch/i.test(cmd)) return "List branches";
|
|
255
|
+
if (/grep|rg\b/i.test(cmd)) return "Search repository";
|
|
256
|
+
return "Run shell command";
|
|
257
|
+
}
|
|
258
|
+
if (/edit|write|apply/i.test(name)) {
|
|
259
|
+
const path = str(input.file_path) ?? str(input.path);
|
|
260
|
+
return path ? `Edit ${path.split("/").pop()}` : "Edit file";
|
|
261
|
+
}
|
|
262
|
+
if (/read/i.test(name)) {
|
|
263
|
+
const path = str(input.file_path) ?? str(input.path);
|
|
264
|
+
return path ? `Read ${path.split("/").pop()}` : "Read file";
|
|
265
|
+
}
|
|
266
|
+
if (/grep/i.test(name)) return "Search files";
|
|
267
|
+
if (/glob/i.test(name)) return "Find files";
|
|
268
|
+
const compact = n.replace(/[_-]/g, "");
|
|
269
|
+
if (/^taskoutput$/i.test(compact)) {
|
|
270
|
+
return str(input.task_id) ? `Wait for ${str(input.task_id)}` : "Wait for background task";
|
|
271
|
+
}
|
|
272
|
+
if (/^taskstop$/i.test(compact)) {
|
|
273
|
+
return str(input.task_id) ? `Stop ${str(input.task_id)}` : "Stop background task";
|
|
274
|
+
}
|
|
275
|
+
if (/^monitor$/i.test(compact)) {
|
|
276
|
+
const command = str(input.command);
|
|
277
|
+
return command ? `Watch ${command.length > 48 ? `${command.slice(0, 45)}\u2026` : command}` : "Watch command";
|
|
278
|
+
}
|
|
279
|
+
return n;
|
|
280
|
+
}
|
|
281
|
+
function subagentLiveSuffix(input) {
|
|
282
|
+
if (!input) return "";
|
|
283
|
+
const bits = [];
|
|
284
|
+
const status = str(input.live_status);
|
|
285
|
+
if (status && !/^runn(ing)?$|^working$/i.test(status)) bits.push(status);
|
|
286
|
+
if (typeof input.live_tool_uses === "number") bits.push(`${input.live_tool_uses} tools`);
|
|
287
|
+
if (typeof input.live_duration_ms === "number") {
|
|
288
|
+
bits.push(`${Math.max(0, Math.round(Number(input.live_duration_ms) / 1e3))}s`);
|
|
289
|
+
}
|
|
290
|
+
const last = str(input.live_last_tool);
|
|
291
|
+
if (last) bits.push(last);
|
|
292
|
+
return bits.length ? ` \xB7 ${bits.join(" \xB7 ")}` : "";
|
|
293
|
+
}
|
|
294
|
+
function isSubagentToolName(name) {
|
|
295
|
+
const n = (name ?? "").trim();
|
|
296
|
+
if (/^(task|agent|spawn_agent)$/i.test(n)) return true;
|
|
297
|
+
return /connectedAgentRequest/i.test(n);
|
|
298
|
+
}
|
|
299
|
+
function isInternalAgentStatusText(text) {
|
|
300
|
+
const t = text.trim();
|
|
301
|
+
if (!t) return false;
|
|
302
|
+
if (/waiting for gate to pass/i.test(t)) return true;
|
|
303
|
+
if (/^agent is running\b/i.test(t) && t.length < 160) return true;
|
|
304
|
+
return false;
|
|
305
|
+
}
|
|
306
|
+
function isPollWrapperToolName(name) {
|
|
307
|
+
const n = (name ?? "").replace(/[_-]/g, "");
|
|
308
|
+
return /^(taskoutput|taskstop|sleep)$/i.test(n);
|
|
309
|
+
}
|
|
310
|
+
function lastTextPart(parts, type) {
|
|
311
|
+
for (let i = parts.length - 1; i >= 0; i--) {
|
|
312
|
+
const p = parts[i];
|
|
313
|
+
if (p.type !== type) continue;
|
|
314
|
+
const text = p.text.trim();
|
|
315
|
+
if (!text || isInternalAgentStatusText(text)) continue;
|
|
316
|
+
return text;
|
|
317
|
+
}
|
|
318
|
+
return "";
|
|
319
|
+
}
|
|
320
|
+
function toolLabel(tool) {
|
|
321
|
+
return tool.description || toolDescription(tool.name, tool.input) || tool.name;
|
|
322
|
+
}
|
|
323
|
+
function fileBasename(path) {
|
|
324
|
+
const parts = path.replace(/\/$/, "").split(/[/\\]/);
|
|
325
|
+
return parts[parts.length - 1] || path;
|
|
326
|
+
}
|
|
327
|
+
function classifyTool(name) {
|
|
328
|
+
const compact = name.replace(/^mcp__[^_]+__/, "").replace(/[_-]/g, "");
|
|
329
|
+
if (/bash|shell|terminal|^zsh$|^sh$/i.test(compact)) return "shell";
|
|
330
|
+
if (/grep|glob|search|ripgrep|findfiles|semsearch/i.test(compact)) return "search";
|
|
331
|
+
if (/^(read|cat)$/i.test(compact) || /^read/i.test(compact)) return "read";
|
|
332
|
+
if (/edit|write|apply|strreplace|multiedit|updatefile|createfile/i.test(compact)) {
|
|
333
|
+
return "edit";
|
|
334
|
+
}
|
|
335
|
+
return "other";
|
|
336
|
+
}
|
|
337
|
+
function isShellToolName(name) {
|
|
338
|
+
return classifyTool(name ?? "") === "shell";
|
|
339
|
+
}
|
|
340
|
+
function toolActivityLine(parts) {
|
|
341
|
+
const tools = parts.filter((p) => {
|
|
342
|
+
if (p.type !== "tool" || p.parentId) return false;
|
|
343
|
+
if (isPollWrapperToolName(p.name)) return false;
|
|
344
|
+
if (/present_plan$/i.test(p.name ?? "")) return false;
|
|
345
|
+
if (/ask_user|AskUserQuestion/i.test(p.name ?? "")) return false;
|
|
346
|
+
return true;
|
|
347
|
+
});
|
|
348
|
+
if (tools.length === 0) return null;
|
|
349
|
+
const edited = [];
|
|
350
|
+
const skillNames = [];
|
|
351
|
+
let compacts = 0;
|
|
352
|
+
let reads = 0;
|
|
353
|
+
let searches = 0;
|
|
354
|
+
let shells = 0;
|
|
355
|
+
let others = 0;
|
|
356
|
+
let additions = 0;
|
|
357
|
+
let deletions = 0;
|
|
358
|
+
for (const tool of tools) {
|
|
359
|
+
const kind = classifyTool(tool.name);
|
|
360
|
+
if (isSkillToolName(tool.name)) {
|
|
361
|
+
const cmd = skillCommandFromInput(tool.input);
|
|
362
|
+
skillNames.push(cmd ? `/${cmd}` : "skill");
|
|
363
|
+
} else if (isCompactToolName(tool.name)) {
|
|
364
|
+
compacts += 1;
|
|
365
|
+
} else if (kind === "edit") {
|
|
366
|
+
const path = tool.filePath ?? toolFilePath(tool.input);
|
|
367
|
+
edited.push({
|
|
368
|
+
name: path ? fileBasename(path) : tool.name,
|
|
369
|
+
running: tool.status === "running"
|
|
370
|
+
});
|
|
371
|
+
} else if (kind === "read") reads += 1;
|
|
372
|
+
else if (kind === "search") searches += 1;
|
|
373
|
+
else if (kind === "shell") shells += 1;
|
|
374
|
+
else others += 1;
|
|
375
|
+
if (typeof tool.additions === "number") additions += tool.additions;
|
|
376
|
+
if (typeof tool.deletions === "number") deletions += tool.deletions;
|
|
377
|
+
}
|
|
378
|
+
const bits = [];
|
|
379
|
+
const editing = edited.filter((e) => e.running);
|
|
380
|
+
const editedDone = edited.filter((e) => !e.running);
|
|
381
|
+
if (editing.length === 1) bits.push(`Editing ${editing[0].name}`);
|
|
382
|
+
else if (editing.length > 1) bits.push(`Editing ${editing.length} files`);
|
|
383
|
+
if (editedDone.length === 1) bits.push(`${editing.length ? "edited" : "Edited"} ${editedDone[0].name}`);
|
|
384
|
+
else if (editedDone.length > 1) {
|
|
385
|
+
bits.push(`${editing.length ? "edited" : "Edited"} ${editedDone.length} files`);
|
|
386
|
+
}
|
|
387
|
+
if (reads === 1) bits.push("explored 1 file");
|
|
388
|
+
else if (reads > 1) bits.push(`explored ${reads} files`);
|
|
389
|
+
if (searches === 1) bits.push("1 search");
|
|
390
|
+
else if (searches > 1) bits.push(`${searches} searches`);
|
|
391
|
+
if (shells === 1) bits.push("ran 1 command");
|
|
392
|
+
else if (shells > 1) bits.push(`ran ${shells} commands`);
|
|
393
|
+
if (skillNames.length === 1) bits.push(`used ${skillNames[0]}`);
|
|
394
|
+
else if (skillNames.length > 1) bits.push(`used ${skillNames.length} skills`);
|
|
395
|
+
if (compacts === 1) bits.push("summarized conversation");
|
|
396
|
+
else if (compacts > 1) bits.push(`summarized conversation \xD7${compacts}`);
|
|
397
|
+
if (others === 1) bits.push("1 tool");
|
|
398
|
+
else if (others > 1) bits.push(`${others} tools`);
|
|
399
|
+
if (bits.length === 0) return null;
|
|
400
|
+
return { text: bits.join(", "), additions, deletions };
|
|
401
|
+
}
|
|
402
|
+
function liveActivitySummary(parts, opts) {
|
|
403
|
+
if (opts?.queued && parts.length === 0) {
|
|
404
|
+
return "Queued \u2014 waiting for a slot";
|
|
405
|
+
}
|
|
406
|
+
const tools = parts.filter((p) => p.type === "tool");
|
|
407
|
+
const runningSubs = tools.filter(
|
|
408
|
+
(t) => t.status === "running" && !t.parentId && isSubagentToolName(t.name)
|
|
409
|
+
);
|
|
410
|
+
const runningNested = [...tools].reverse().find((t) => t.status === "running" && t.parentId && !isPollWrapperToolName(t.name));
|
|
411
|
+
const runningTop = [...tools].reverse().find(
|
|
412
|
+
(t) => t.status === "running" && !t.parentId && !isPollWrapperToolName(t.name) && !isSubagentToolName(t.name)
|
|
413
|
+
);
|
|
414
|
+
const runningPoll = [...tools].reverse().find((t) => t.status === "running" && isPollWrapperToolName(t.name));
|
|
415
|
+
const thinking = lastTextPart(parts, "thinking");
|
|
416
|
+
const text = lastTextPart(parts, "text");
|
|
417
|
+
if (runningSubs.length > 0) {
|
|
418
|
+
const heads = runningSubs.map(toolLabel);
|
|
419
|
+
const head = runningSubs.length === 1 ? heads[0] : `${runningSubs.length} subagents \xB7 ${heads.slice(0, 2).join(" \xB7 ")}`;
|
|
420
|
+
if (runningNested) return `${head} \xB7 ${toolLabel(runningNested)}`;
|
|
421
|
+
return head;
|
|
422
|
+
}
|
|
423
|
+
if (runningTop) return toolLabel(runningTop);
|
|
424
|
+
if (runningPoll) return toolLabel(runningPoll);
|
|
425
|
+
if (thinking) return thinking.length > 96 ? `\u2026${thinking.slice(-96)}` : thinking;
|
|
426
|
+
if (text) return "Writing reply\u2026";
|
|
427
|
+
const last = [...tools].reverse().find((t) => !isPollWrapperToolName(t.name)) ?? tools.at(-1);
|
|
428
|
+
if (last) {
|
|
429
|
+
const label = toolLabel(last);
|
|
430
|
+
return last.status === "running" ? label : `Finished ${label}`;
|
|
431
|
+
}
|
|
432
|
+
return "Working\u2026";
|
|
433
|
+
}
|
|
434
|
+
function messagePartParentId(part) {
|
|
435
|
+
if ("parentId" in part && typeof part.parentId === "string" && part.parentId.trim()) {
|
|
436
|
+
return part.parentId;
|
|
437
|
+
}
|
|
438
|
+
return void 0;
|
|
439
|
+
}
|
|
440
|
+
function sameParentId(a, b) {
|
|
441
|
+
return (a ?? "") === (b ?? "");
|
|
442
|
+
}
|
|
443
|
+
function withEventParentId(event, parentId) {
|
|
444
|
+
if (!parentId) return event;
|
|
445
|
+
if (event.type === "stdout" || event.type === "thinking" || event.type === "tool_use" || event.type === "tool_result") {
|
|
446
|
+
return { ...event, parentId: event.parentId ?? parentId };
|
|
447
|
+
}
|
|
448
|
+
return event;
|
|
449
|
+
}
|
|
450
|
+
function withEventsParentId(events, parentId) {
|
|
451
|
+
if (!parentId) return events;
|
|
452
|
+
return events.map((event) => withEventParentId(event, parentId));
|
|
453
|
+
}
|
|
454
|
+
function toolFilePath(input) {
|
|
455
|
+
if (!input) return void 0;
|
|
456
|
+
return str(input.file_path) ?? str(input.path) ?? str(input.filePath) ?? str(input.filename);
|
|
457
|
+
}
|
|
458
|
+
function countLines(text) {
|
|
459
|
+
if (!text) return 0;
|
|
460
|
+
return text.split("\n").length;
|
|
461
|
+
}
|
|
462
|
+
function diffFromInput(input) {
|
|
463
|
+
if (!input) return {};
|
|
464
|
+
const oldS = str(input.old_string) ?? str(input.oldString);
|
|
465
|
+
const newS = str(input.new_string) ?? str(input.newString) ?? str(input.content);
|
|
466
|
+
if (oldS != null || newS != null) {
|
|
467
|
+
return {
|
|
468
|
+
additions: countLines(newS),
|
|
469
|
+
deletions: countLines(oldS)
|
|
470
|
+
};
|
|
471
|
+
}
|
|
472
|
+
return {};
|
|
473
|
+
}
|
|
474
|
+
function parseDiffStat(result) {
|
|
475
|
+
if (!result) return {};
|
|
476
|
+
const paired = result.match(/\+(\d+)\s+-(\d+)/);
|
|
477
|
+
if (paired) {
|
|
478
|
+
return {
|
|
479
|
+
additions: Number(paired[1]),
|
|
480
|
+
deletions: Number(paired[2])
|
|
481
|
+
};
|
|
482
|
+
}
|
|
483
|
+
const verbose = result.match(
|
|
484
|
+
/(\d+)\s+insertions?(?:,\s*(\d+)\s+deletions?)?/i
|
|
485
|
+
);
|
|
486
|
+
if (verbose) {
|
|
487
|
+
return {
|
|
488
|
+
additions: Number(verbose[1]),
|
|
489
|
+
deletions: verbose[2] != null ? Number(verbose[2]) : void 0
|
|
490
|
+
};
|
|
491
|
+
}
|
|
492
|
+
return {};
|
|
493
|
+
}
|
|
494
|
+
var THINKING_PART_MAX_CHARS = 64e3;
|
|
495
|
+
function clipThinkingForStore(text, max = THINKING_PART_MAX_CHARS) {
|
|
496
|
+
if (text.length <= max) return text;
|
|
497
|
+
const marker = "\u2026(earlier thinking trimmed)\n";
|
|
498
|
+
return marker + text.slice(text.length - Math.max(1, max - marker.length));
|
|
499
|
+
}
|
|
500
|
+
var STDOUT_EVENT_MAX_CHARS = 16e3;
|
|
501
|
+
var STDERR_EVENT_MAX_CHARS = 4e3;
|
|
502
|
+
function clipEventTail(text, max) {
|
|
503
|
+
if (text.length <= max) return text;
|
|
504
|
+
const marker = "\u2026(truncated)\n";
|
|
505
|
+
return marker + text.slice(text.length - Math.max(1, max - marker.length));
|
|
506
|
+
}
|
|
507
|
+
function clipAgentEventForPaint(event) {
|
|
508
|
+
if (event.type === "tool_result" && event.content) {
|
|
509
|
+
const content = clipToolResultForStore(event.content);
|
|
510
|
+
if (content === event.content) return event;
|
|
511
|
+
return { ...event, content };
|
|
512
|
+
}
|
|
513
|
+
if (event.type === "thinking" && event.data.length > THINKING_PART_MAX_CHARS) {
|
|
514
|
+
return { ...event, data: clipThinkingForStore(event.data) };
|
|
515
|
+
}
|
|
516
|
+
if (event.type === "stdout" && event.data.length > STDOUT_EVENT_MAX_CHARS) {
|
|
517
|
+
return { ...event, data: clipEventTail(event.data, STDOUT_EVENT_MAX_CHARS) };
|
|
518
|
+
}
|
|
519
|
+
if (event.type === "stderr" && event.data.length > STDERR_EVENT_MAX_CHARS) {
|
|
520
|
+
return { ...event, data: clipEventTail(event.data, STDERR_EVENT_MAX_CHARS) };
|
|
521
|
+
}
|
|
522
|
+
return event;
|
|
523
|
+
}
|
|
524
|
+
function withPartTimes(next, prev) {
|
|
525
|
+
const now = Date.now();
|
|
526
|
+
const startedAt = prev && typeof prev.startedAt === "number" ? prev.startedAt : now;
|
|
527
|
+
return { ...next, startedAt, updatedAt: now };
|
|
528
|
+
}
|
|
529
|
+
function skillToolFromTextPart(part, id, status = "done") {
|
|
530
|
+
const command = skillCommandFromBody(part.text);
|
|
531
|
+
const input = command ? { skill: command } : void 0;
|
|
532
|
+
return {
|
|
533
|
+
type: "tool",
|
|
534
|
+
id: command ? `skill-${command}` : id,
|
|
535
|
+
name: "Skill",
|
|
536
|
+
input,
|
|
537
|
+
description: toolDescription("Skill", input),
|
|
538
|
+
status,
|
|
539
|
+
result: clipToolResultForStore(part.text),
|
|
540
|
+
...part.parentId ? { parentId: part.parentId } : {},
|
|
541
|
+
startedAt: part.startedAt,
|
|
542
|
+
updatedAt: Date.now()
|
|
543
|
+
};
|
|
544
|
+
}
|
|
545
|
+
function lastSkillToolIndex(parts, parentId) {
|
|
546
|
+
for (let i = parts.length - 1; i >= 0; i--) {
|
|
547
|
+
const p = parts[i];
|
|
548
|
+
if (p.type === "tool" && isSkillToolName(p.name) && sameParentId(p.parentId, parentId)) {
|
|
549
|
+
return i;
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
return -1;
|
|
553
|
+
}
|
|
554
|
+
function lastRunningSkillIndex(parts, parentId) {
|
|
555
|
+
for (let i = parts.length - 1; i >= 0; i--) {
|
|
556
|
+
const p = parts[i];
|
|
557
|
+
if (p.type === "tool" && isSkillToolName(p.name) && p.status === "running" && sameParentId(p.parentId, parentId)) {
|
|
558
|
+
return i;
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
return -1;
|
|
562
|
+
}
|
|
563
|
+
function markRunningSkillsDone(parts, parentId) {
|
|
564
|
+
return parts.map((p) => {
|
|
565
|
+
if (p.type === "tool" && isSkillToolName(p.name) && p.status === "running" && sameParentId(p.parentId, parentId)) {
|
|
566
|
+
return withPartTimes({ ...p, status: "done" }, p);
|
|
567
|
+
}
|
|
568
|
+
return p;
|
|
569
|
+
});
|
|
570
|
+
}
|
|
571
|
+
function promoteOrFoldSkillText(parts, textIdx) {
|
|
572
|
+
const acc = parts[textIdx];
|
|
573
|
+
if (acc?.type !== "text") return parts;
|
|
574
|
+
const complete = looksLikeSkillBody(acc.text);
|
|
575
|
+
const incomplete = looksLikeIncompleteSkillDump(acc.text);
|
|
576
|
+
if (!complete && !incomplete) return parts;
|
|
577
|
+
const existing = lastSkillToolIndex(parts, acc.parentId);
|
|
578
|
+
if (existing >= 0 && existing !== textIdx) {
|
|
579
|
+
const prev = parts[existing];
|
|
580
|
+
const next2 = parts.filter((_, i) => i !== textIdx);
|
|
581
|
+
const existingIdx = lastSkillToolIndex(next2, acc.parentId);
|
|
582
|
+
if (existingIdx < 0) return parts;
|
|
583
|
+
next2[existingIdx] = withPartTimes(
|
|
584
|
+
{
|
|
585
|
+
...prev,
|
|
586
|
+
status: complete ? "done" : prev.status,
|
|
587
|
+
result: clipToolResultForStore(acc.text) ?? prev.result
|
|
588
|
+
},
|
|
589
|
+
prev
|
|
590
|
+
);
|
|
591
|
+
return next2;
|
|
592
|
+
}
|
|
593
|
+
const next = [...parts];
|
|
594
|
+
next[textIdx] = skillToolFromTextPart(
|
|
595
|
+
acc,
|
|
596
|
+
`skill-${next.length}`,
|
|
597
|
+
complete ? "done" : "running"
|
|
598
|
+
);
|
|
599
|
+
return next;
|
|
600
|
+
}
|
|
601
|
+
function compactSummaryPart(text, id = "compact-summary") {
|
|
602
|
+
return compactToolPart(text, id, { status: "done" });
|
|
603
|
+
}
|
|
604
|
+
function compactPartsForSummaryMessage(message) {
|
|
605
|
+
const existing = (message.parts ?? []).filter(
|
|
606
|
+
(p) => p.type === "tool" && isCompactToolName(p.name)
|
|
607
|
+
);
|
|
608
|
+
if (existing.length) return existing;
|
|
609
|
+
return [compactSummaryPart(message.text ?? "")];
|
|
610
|
+
}
|
|
611
|
+
function compactToolPart(text, id, opts) {
|
|
612
|
+
const input = { trigger: opts?.trigger ?? "auto" };
|
|
613
|
+
return withPartTimes(
|
|
614
|
+
{
|
|
615
|
+
type: "tool",
|
|
616
|
+
id,
|
|
617
|
+
name: "Compact",
|
|
618
|
+
input,
|
|
619
|
+
description: toolDescription("Compact", input),
|
|
620
|
+
status: opts?.status ?? "done",
|
|
621
|
+
result: clipToolResultForStore(text) ?? "",
|
|
622
|
+
...opts?.parentId ? { parentId: opts.parentId } : {}
|
|
623
|
+
},
|
|
624
|
+
opts?.prev
|
|
625
|
+
);
|
|
626
|
+
}
|
|
627
|
+
function contextCompactInProgress(parts, parentId) {
|
|
628
|
+
let compressing = false;
|
|
629
|
+
for (const p of parts) {
|
|
630
|
+
if (!sameParentId(p.parentId, parentId)) continue;
|
|
631
|
+
if (p.type === "thinking" && isContextCompactInProgress(p.text)) compressing = true;
|
|
632
|
+
if (p.type === "thinking" && isContextCompactDone(p.text)) compressing = false;
|
|
633
|
+
}
|
|
634
|
+
return compressing;
|
|
635
|
+
}
|
|
636
|
+
function lastRunningCompactIndex(parts, parentId) {
|
|
637
|
+
for (let i = parts.length - 1; i >= 0; i--) {
|
|
638
|
+
const p = parts[i];
|
|
639
|
+
if (p.type === "tool" && isCompactToolName(p.name) && p.status === "running" && sameParentId(p.parentId, parentId)) {
|
|
640
|
+
return i;
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
return -1;
|
|
644
|
+
}
|
|
645
|
+
function markRunningCompactDone(parts, parentId) {
|
|
646
|
+
return parts.map((p) => {
|
|
647
|
+
if (p.type === "tool" && isCompactToolName(p.name) && p.status === "running" && sameParentId(p.parentId, parentId)) {
|
|
648
|
+
return withPartTimes({ ...p, status: "done" }, p);
|
|
649
|
+
}
|
|
650
|
+
return p;
|
|
651
|
+
});
|
|
652
|
+
}
|
|
653
|
+
function ensureCompactTool(parts, parentId) {
|
|
654
|
+
const hasCompact = parts.some(
|
|
655
|
+
(p) => p.type === "tool" && isCompactToolName(p.name) && sameParentId(p.parentId, parentId)
|
|
656
|
+
);
|
|
657
|
+
if (hasCompact) return parts;
|
|
658
|
+
return [
|
|
659
|
+
...parts,
|
|
660
|
+
compactToolPart("Context compressed", `compact-${parts.length}`, {
|
|
661
|
+
parentId,
|
|
662
|
+
status: "done"
|
|
663
|
+
})
|
|
664
|
+
];
|
|
665
|
+
}
|
|
666
|
+
function withCompactFinish(parts, thinking, parentId) {
|
|
667
|
+
if (!isContextCompactDone(thinking)) return parts;
|
|
668
|
+
return ensureCompactTool(markRunningCompactDone(parts, parentId), parentId);
|
|
669
|
+
}
|
|
670
|
+
function applyAgentEvent(parts, event) {
|
|
671
|
+
if (event.type === "stdout") {
|
|
672
|
+
const data = event.data;
|
|
673
|
+
if (!data) return parts;
|
|
674
|
+
const runningIdx = lastRunningCompactIndex(parts, event.parentId);
|
|
675
|
+
if (runningIdx >= 0) {
|
|
676
|
+
const prev = parts[runningIdx];
|
|
677
|
+
const next2 = [...parts];
|
|
678
|
+
next2[runningIdx] = withPartTimes(
|
|
679
|
+
{
|
|
680
|
+
...prev,
|
|
681
|
+
result: clipToolResultForStore(`${prev.result ?? ""}${data}`)
|
|
682
|
+
},
|
|
683
|
+
prev
|
|
684
|
+
);
|
|
685
|
+
return next2;
|
|
686
|
+
}
|
|
687
|
+
const runningSkillIdx = lastRunningSkillIndex(parts, event.parentId);
|
|
688
|
+
if (runningSkillIdx >= 0) {
|
|
689
|
+
const prev = parts[runningSkillIdx];
|
|
690
|
+
const result = `${prev.result ?? ""}${data}`;
|
|
691
|
+
const next2 = [...parts];
|
|
692
|
+
next2[runningSkillIdx] = withPartTimes(
|
|
693
|
+
{
|
|
694
|
+
...prev,
|
|
695
|
+
result: clipToolResultForStore(result),
|
|
696
|
+
// Stay running after the YAML `name:` fence so later content_block_delta
|
|
697
|
+
// tokens of SKILL.md stay on this row. thinking / tool_use / finalizeParts
|
|
698
|
+
// mark the row done so the real reply can be chat.
|
|
699
|
+
status: "running"
|
|
700
|
+
},
|
|
701
|
+
prev
|
|
702
|
+
);
|
|
703
|
+
return next2;
|
|
704
|
+
}
|
|
705
|
+
if (contextCompactInProgress(parts, event.parentId)) {
|
|
706
|
+
return [
|
|
707
|
+
...parts,
|
|
708
|
+
compactToolPart(data, `compact-${parts.length}`, {
|
|
709
|
+
parentId: event.parentId,
|
|
710
|
+
status: "running"
|
|
711
|
+
})
|
|
712
|
+
];
|
|
713
|
+
}
|
|
714
|
+
const next = [...parts];
|
|
715
|
+
const last = next[next.length - 1];
|
|
716
|
+
if (last?.type === "text" && sameParentId(last.parentId, event.parentId)) {
|
|
717
|
+
next[next.length - 1] = withPartTimes(
|
|
718
|
+
{
|
|
719
|
+
type: "text",
|
|
720
|
+
text: last.text + data,
|
|
721
|
+
...event.parentId ? { parentId: event.parentId } : {}
|
|
722
|
+
},
|
|
723
|
+
last
|
|
724
|
+
);
|
|
725
|
+
} else {
|
|
726
|
+
next.push(
|
|
727
|
+
withPartTimes({
|
|
728
|
+
type: "text",
|
|
729
|
+
text: data,
|
|
730
|
+
...event.parentId ? { parentId: event.parentId } : {}
|
|
731
|
+
})
|
|
732
|
+
);
|
|
733
|
+
}
|
|
734
|
+
const acc = next[next.length - 1];
|
|
735
|
+
if (acc?.type === "text" && (looksLikeSkillBody(acc.text) || looksLikeIncompleteSkillDump(acc.text))) {
|
|
736
|
+
return promoteOrFoldSkillText(next, next.length - 1);
|
|
737
|
+
}
|
|
738
|
+
if (acc?.type === "text" && looksLikeConversationSummary(acc.text)) {
|
|
739
|
+
next[next.length - 1] = compactToolPart(acc.text, `compact-${next.length}`, {
|
|
740
|
+
parentId: acc.parentId,
|
|
741
|
+
status: "done",
|
|
742
|
+
prev: acc
|
|
743
|
+
});
|
|
744
|
+
}
|
|
745
|
+
return next;
|
|
746
|
+
}
|
|
747
|
+
if (event.type === "thinking") {
|
|
748
|
+
const data = event.data;
|
|
749
|
+
if (!data) return parts;
|
|
750
|
+
if (!isSystemStatusThinking(event)) {
|
|
751
|
+
parts = markRunningSkillsDone(parts, event.parentId);
|
|
752
|
+
}
|
|
753
|
+
const next = [...parts];
|
|
754
|
+
if (event.replace) {
|
|
755
|
+
for (let i = next.length - 1; i >= 0; i--) {
|
|
756
|
+
const prev = next[i];
|
|
757
|
+
if (prev?.type === "thinking" && sameParentId(prev.parentId, event.parentId)) {
|
|
758
|
+
next[i] = withPartTimes(
|
|
759
|
+
{
|
|
760
|
+
type: "thinking",
|
|
761
|
+
text: clipThinkingForStore(data),
|
|
762
|
+
...event.parentId ? { parentId: event.parentId } : {}
|
|
763
|
+
},
|
|
764
|
+
prev
|
|
765
|
+
);
|
|
766
|
+
return withCompactFinish(next, data, event.parentId);
|
|
767
|
+
}
|
|
768
|
+
}
|
|
769
|
+
next.push(
|
|
770
|
+
withPartTimes({
|
|
771
|
+
type: "thinking",
|
|
772
|
+
text: clipThinkingForStore(data),
|
|
773
|
+
...event.parentId ? { parentId: event.parentId } : {}
|
|
774
|
+
})
|
|
775
|
+
);
|
|
776
|
+
return withCompactFinish(next, data, event.parentId);
|
|
777
|
+
}
|
|
778
|
+
const last = next[next.length - 1];
|
|
779
|
+
if (last?.type === "thinking" && sameParentId(last.parentId, event.parentId)) {
|
|
780
|
+
next[next.length - 1] = withPartTimes(
|
|
781
|
+
{
|
|
782
|
+
type: "thinking",
|
|
783
|
+
text: clipThinkingForStore(last.text + data),
|
|
784
|
+
...event.parentId ? { parentId: event.parentId } : {}
|
|
785
|
+
},
|
|
786
|
+
last
|
|
787
|
+
);
|
|
788
|
+
} else {
|
|
789
|
+
next.push(
|
|
790
|
+
withPartTimes({
|
|
791
|
+
type: "thinking",
|
|
792
|
+
text: clipThinkingForStore(data),
|
|
793
|
+
...event.parentId ? { parentId: event.parentId } : {}
|
|
794
|
+
})
|
|
795
|
+
);
|
|
796
|
+
}
|
|
797
|
+
return withCompactFinish(next, data, event.parentId);
|
|
798
|
+
}
|
|
799
|
+
if (event.type === "tool_use") {
|
|
800
|
+
parts = markRunningSkillsDone(parts, event.parentId);
|
|
801
|
+
const input = asRecord(event.input);
|
|
802
|
+
const diff = diffFromInput(input);
|
|
803
|
+
const existing = parts.findIndex((p) => p.type === "tool" && p.id === event.id);
|
|
804
|
+
if (existing >= 0) {
|
|
805
|
+
const prev = parts[existing];
|
|
806
|
+
const next = [...parts];
|
|
807
|
+
const mergedInput = {
|
|
808
|
+
...prev.input ?? {},
|
|
809
|
+
...input ?? {}
|
|
810
|
+
};
|
|
811
|
+
const mergedRecord = Object.keys(mergedInput).length > 0 ? mergedInput : void 0;
|
|
812
|
+
next[existing] = withPartTimes(
|
|
813
|
+
{
|
|
814
|
+
...prev,
|
|
815
|
+
name: event.name || prev.name,
|
|
816
|
+
input: mergedRecord,
|
|
817
|
+
description: toolDescription(event.name || prev.name, mergedRecord),
|
|
818
|
+
detail: toolDetail(event.name || prev.name, mergedRecord) ?? prev.detail,
|
|
819
|
+
filePath: toolFilePath(mergedRecord) ?? prev.filePath,
|
|
820
|
+
additions: diff.additions ?? prev.additions,
|
|
821
|
+
deletions: diff.deletions ?? prev.deletions,
|
|
822
|
+
parentId: event.parentId ?? prev.parentId
|
|
823
|
+
},
|
|
824
|
+
prev
|
|
825
|
+
);
|
|
826
|
+
return next;
|
|
827
|
+
}
|
|
828
|
+
return [
|
|
829
|
+
...parts,
|
|
830
|
+
withPartTimes({
|
|
831
|
+
type: "tool",
|
|
832
|
+
id: event.id,
|
|
833
|
+
name: event.name,
|
|
834
|
+
input,
|
|
835
|
+
description: toolDescription(event.name, input),
|
|
836
|
+
detail: toolDetail(event.name, input),
|
|
837
|
+
status: "running",
|
|
838
|
+
filePath: toolFilePath(input),
|
|
839
|
+
...event.parentId ? { parentId: event.parentId } : {},
|
|
840
|
+
...diff
|
|
841
|
+
})
|
|
842
|
+
];
|
|
843
|
+
}
|
|
844
|
+
if (event.type === "tool_result") {
|
|
845
|
+
const existing = parts.findIndex((p) => p.type === "tool" && p.id === event.id);
|
|
846
|
+
const fromResult = parseDiffStat(event.content);
|
|
847
|
+
if (event.partial) {
|
|
848
|
+
if (existing < 0) {
|
|
849
|
+
return [
|
|
850
|
+
...parts,
|
|
851
|
+
withPartTimes({
|
|
852
|
+
type: "tool",
|
|
853
|
+
id: event.id,
|
|
854
|
+
name: "tool",
|
|
855
|
+
description: "tool",
|
|
856
|
+
status: event.isError ? "error" : "running",
|
|
857
|
+
result: clipToolResultForStore(event.content),
|
|
858
|
+
...event.parentId ? { parentId: event.parentId } : {}
|
|
859
|
+
})
|
|
860
|
+
];
|
|
861
|
+
}
|
|
862
|
+
return parts.map((p, i) => {
|
|
863
|
+
if (i !== existing || p.type !== "tool") return p;
|
|
864
|
+
if (p.status === "done" || p.status === "error") return p;
|
|
865
|
+
return withPartTimes(
|
|
866
|
+
{
|
|
867
|
+
...p,
|
|
868
|
+
result: clipToolResultForStore(event.content) ?? p.result
|
|
869
|
+
},
|
|
870
|
+
p
|
|
871
|
+
);
|
|
872
|
+
});
|
|
873
|
+
}
|
|
874
|
+
if (existing < 0) {
|
|
875
|
+
return [
|
|
876
|
+
...parts,
|
|
877
|
+
withPartTimes({
|
|
878
|
+
type: "tool",
|
|
879
|
+
id: event.id,
|
|
880
|
+
name: "tool",
|
|
881
|
+
description: "tool",
|
|
882
|
+
status: event.isError ? "error" : "done",
|
|
883
|
+
result: clipToolResultForStore(event.content),
|
|
884
|
+
...event.parentId ? { parentId: event.parentId } : {},
|
|
885
|
+
...fromResult.additions != null ? { additions: fromResult.additions } : {},
|
|
886
|
+
...fromResult.deletions != null ? { deletions: fromResult.deletions } : {}
|
|
887
|
+
})
|
|
888
|
+
];
|
|
889
|
+
}
|
|
890
|
+
return parts.map((p, i) => {
|
|
891
|
+
if (i !== existing || p.type !== "tool") return p;
|
|
892
|
+
return withPartTimes(
|
|
893
|
+
{
|
|
894
|
+
...p,
|
|
895
|
+
status: event.isError ? "error" : "done",
|
|
896
|
+
result: clipToolResultForStore(event.content),
|
|
897
|
+
...fromResult.additions != null ? { additions: fromResult.additions } : {},
|
|
898
|
+
...fromResult.deletions != null ? { deletions: fromResult.deletions } : {}
|
|
899
|
+
},
|
|
900
|
+
p
|
|
901
|
+
);
|
|
902
|
+
});
|
|
903
|
+
}
|
|
904
|
+
return parts;
|
|
905
|
+
}
|
|
906
|
+
function partsToAssistantText(parts) {
|
|
907
|
+
return parts.filter(
|
|
908
|
+
(p) => p.type === "text" && !messagePartParentId(p)
|
|
909
|
+
).map((p) => p.text).join("").trim();
|
|
910
|
+
}
|
|
911
|
+
function visibleAssistantText(text, parts) {
|
|
912
|
+
const fromParts = partsToAssistantText(parts ?? []);
|
|
913
|
+
if (fromParts) return fromParts;
|
|
914
|
+
if (parts?.some((p) => p.type === "tool" || p.type === "thinking")) return "";
|
|
915
|
+
return (text ?? "").trim();
|
|
916
|
+
}
|
|
917
|
+
function lastAssistantMessageText(parts) {
|
|
918
|
+
if (!parts?.length) return "";
|
|
919
|
+
for (let i = parts.length - 1; i >= 0; i--) {
|
|
920
|
+
const p = parts[i];
|
|
921
|
+
if (!p || p.type !== "text" || messagePartParentId(p)) continue;
|
|
922
|
+
const text = p.text.trim();
|
|
923
|
+
if (!text || isInternalAgentStatusText(text)) continue;
|
|
924
|
+
return text;
|
|
925
|
+
}
|
|
926
|
+
return "";
|
|
927
|
+
}
|
|
928
|
+
function stripBrightsyNdjsonNoise(text) {
|
|
929
|
+
if (!text || !text.includes('"type"')) return text;
|
|
930
|
+
let out = text;
|
|
931
|
+
out = out.replace(
|
|
932
|
+
/\{"type":"(?:tool_use|tool_result|tool|thinking|usage|done|error)"[\s\S]*?\}\s*(?=\{"type":"|$|(?=[A-Za-z*#]))/g,
|
|
933
|
+
""
|
|
934
|
+
);
|
|
935
|
+
out = out.replace(/\{"type":"(?:tool_use|tool_result|tool)"[\s\S]*$/g, "");
|
|
936
|
+
return out.trim();
|
|
937
|
+
}
|
|
938
|
+
function isBrightsyNdjsonLine(line) {
|
|
939
|
+
const t = line.trim();
|
|
940
|
+
if (!t.startsWith("{")) return false;
|
|
941
|
+
return /"type"\s*:\s*"(tool_use|tool_result|tool|thinking|usage|done|error|text)"/.test(t);
|
|
942
|
+
}
|
|
943
|
+
function finalizeParts(parts) {
|
|
944
|
+
return parts.map(
|
|
945
|
+
(p) => p.type === "tool" && p.status === "running" ? { ...p, status: "done" } : p
|
|
946
|
+
);
|
|
947
|
+
}
|
|
948
|
+
function normalizeParseResult(parsed) {
|
|
949
|
+
if (!parsed) return [];
|
|
950
|
+
return Array.isArray(parsed) ? parsed : [parsed];
|
|
951
|
+
}
|
|
952
|
+
|
|
953
|
+
export {
|
|
954
|
+
visibleToolRowDetail,
|
|
955
|
+
isSkillToolName,
|
|
956
|
+
skillCommandFromInput,
|
|
957
|
+
looksLikeComposerSkillExpansion,
|
|
958
|
+
skillNameFromFrontmatter,
|
|
959
|
+
skillCommandFromBody,
|
|
960
|
+
looksLikeSkillBody,
|
|
961
|
+
looksLikeIncompleteSkillDump,
|
|
962
|
+
isCompactToolName,
|
|
963
|
+
isContextCompactDone,
|
|
964
|
+
isContextCompactInProgress,
|
|
965
|
+
isSystemStatusThinking,
|
|
966
|
+
looksLikeConversationSummary,
|
|
967
|
+
toolDetail,
|
|
968
|
+
toolDescription,
|
|
969
|
+
isSubagentToolName,
|
|
970
|
+
isInternalAgentStatusText,
|
|
971
|
+
isPollWrapperToolName,
|
|
972
|
+
isShellToolName,
|
|
973
|
+
toolActivityLine,
|
|
974
|
+
liveActivitySummary,
|
|
975
|
+
messagePartParentId,
|
|
976
|
+
withEventParentId,
|
|
977
|
+
withEventsParentId,
|
|
978
|
+
toolFilePath,
|
|
979
|
+
THINKING_PART_MAX_CHARS,
|
|
980
|
+
clipThinkingForStore,
|
|
981
|
+
STDOUT_EVENT_MAX_CHARS,
|
|
982
|
+
STDERR_EVENT_MAX_CHARS,
|
|
983
|
+
clipAgentEventForPaint,
|
|
984
|
+
compactSummaryPart,
|
|
985
|
+
compactPartsForSummaryMessage,
|
|
986
|
+
applyAgentEvent,
|
|
987
|
+
partsToAssistantText,
|
|
988
|
+
visibleAssistantText,
|
|
989
|
+
lastAssistantMessageText,
|
|
990
|
+
stripBrightsyNdjsonNoise,
|
|
991
|
+
isBrightsyNdjsonLine,
|
|
992
|
+
finalizeParts,
|
|
993
|
+
normalizeParseResult
|
|
994
|
+
};
|