@makerbi/remodex 1.5.4 → 1.5.8
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/bin/remodex.js +22 -9
- package/package.json +1 -1
- package/src/account-status.js +7 -1
- package/src/apply-patch-changes.js +185 -0
- package/src/bootstrap-codex-cli.js +1 -1
- package/src/bridge-status.js +3 -2
- package/src/bridge.js +752 -71
- package/src/codex-transport.js +10 -10
- package/src/desktop-handler.js +14 -1
- package/src/desktop-ipc-action-follower.js +129 -0
- package/src/index.js +4 -2
- package/src/macos-launch-agent.js +87 -2
- package/src/project-handler.js +162 -1
- package/src/push-notification-service-client.js +85 -37
- package/src/push-notification-tracker.js +15 -0
- package/src/rollout-live-mirror.js +331 -20
- package/src/rollout-watch.js +5 -1
- package/src/secure-device-state.js +26 -1
- package/src/secure-transport.js +40 -12
- package/src/session-jsonl-history.js +796 -15
- package/src/voice-handler.js +129 -62
- package/src/workspace-handler.js +327 -14
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
// Purpose: Reconstructs a small thread/turns/list page from local Codex session JSONL files.
|
|
3
3
|
|
|
4
4
|
const fs = require("fs");
|
|
5
|
+
const { buildApplyPatchFileChangeItem } = require("./apply-patch-changes");
|
|
5
6
|
|
|
6
7
|
function readThreadTurnsListPageFromSessionJsonl(filePath, {
|
|
7
8
|
threadId = "",
|
|
@@ -31,16 +32,72 @@ function readThreadTurnsListPageFromSessionJsonl(filePath, {
|
|
|
31
32
|
};
|
|
32
33
|
}
|
|
33
34
|
|
|
35
|
+
// Extracts thread-level context that app-server history can omit for desktop-origin runs.
|
|
36
|
+
function parseSessionJsonlMetadata(content) {
|
|
37
|
+
let threadId = "";
|
|
38
|
+
let cwd = "";
|
|
39
|
+
|
|
40
|
+
const raw = String(content || "");
|
|
41
|
+
let lineStart = 0;
|
|
42
|
+
while (lineStart < raw.length) {
|
|
43
|
+
let lineEnd = raw.indexOf("\n", lineStart);
|
|
44
|
+
if (lineEnd === -1) {
|
|
45
|
+
lineEnd = raw.length;
|
|
46
|
+
}
|
|
47
|
+
const line = raw.substring(lineStart, lineEnd).trim();
|
|
48
|
+
lineStart = lineEnd + 1;
|
|
49
|
+
if (!line) {
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
let entry;
|
|
54
|
+
try {
|
|
55
|
+
entry = JSON.parse(line);
|
|
56
|
+
} catch {
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (entry?.type !== "session_meta") {
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const payload = objectValue(entry.payload);
|
|
65
|
+
threadId ||= normalizeString(payload?.id)
|
|
66
|
+
|| normalizeString(payload?.thread_id)
|
|
67
|
+
|| normalizeString(payload?.threadId);
|
|
68
|
+
cwd ||= normalizeString(payload?.cwd)
|
|
69
|
+
|| normalizeString(payload?.current_working_directory)
|
|
70
|
+
|| normalizeString(payload?.working_directory);
|
|
71
|
+
|
|
72
|
+
if (threadId && cwd) {
|
|
73
|
+
break;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return { threadId, cwd };
|
|
78
|
+
}
|
|
79
|
+
|
|
34
80
|
function parseSessionJsonlTurns(content, { threadId = "" } = {}) {
|
|
35
81
|
const turns = [];
|
|
36
82
|
const turnsById = new Map();
|
|
37
83
|
let activeTurnId = "";
|
|
38
84
|
let sessionThreadId = normalizeString(threadId);
|
|
85
|
+
let sessionCwd = "";
|
|
39
86
|
const skippedCallIds = new Set();
|
|
87
|
+
const toolCallsByCallId = new Map();
|
|
88
|
+
const pendingUserMessages = [];
|
|
40
89
|
|
|
41
|
-
const
|
|
42
|
-
|
|
43
|
-
|
|
90
|
+
const raw = String(content || "");
|
|
91
|
+
let index = -1;
|
|
92
|
+
let lineStart = 0;
|
|
93
|
+
while (lineStart < raw.length) {
|
|
94
|
+
index += 1;
|
|
95
|
+
let lineEnd = raw.indexOf("\n", lineStart);
|
|
96
|
+
if (lineEnd === -1) {
|
|
97
|
+
lineEnd = raw.length;
|
|
98
|
+
}
|
|
99
|
+
const line = raw.substring(lineStart, lineEnd).trim();
|
|
100
|
+
lineStart = lineEnd + 1;
|
|
44
101
|
if (!line) {
|
|
45
102
|
continue;
|
|
46
103
|
}
|
|
@@ -57,6 +114,7 @@ function parseSessionJsonlTurns(content, { threadId = "" } = {}) {
|
|
|
57
114
|
sessionThreadId ||= normalizeString(payload?.id)
|
|
58
115
|
|| normalizeString(payload?.thread_id)
|
|
59
116
|
|| normalizeString(payload?.threadId);
|
|
117
|
+
sessionCwd ||= normalizeString(payload?.cwd);
|
|
60
118
|
continue;
|
|
61
119
|
}
|
|
62
120
|
|
|
@@ -68,7 +126,8 @@ function parseSessionJsonlTurns(content, { threadId = "" } = {}) {
|
|
|
68
126
|
|| normalizeString(payload?.turnId)
|
|
69
127
|
|| activeTurnId
|
|
70
128
|
|| `turn-line-${index + 1}`;
|
|
71
|
-
ensureTurn(turns, turnsById, activeTurnId, sessionThreadId, entry.timestamp);
|
|
129
|
+
const turn = ensureTurn(turns, turnsById, activeTurnId, sessionThreadId, entry.timestamp);
|
|
130
|
+
flushPendingUserMessagesToTurn(turn, pendingUserMessages);
|
|
72
131
|
continue;
|
|
73
132
|
}
|
|
74
133
|
|
|
@@ -81,10 +140,16 @@ function parseSessionJsonlTurns(content, { threadId = "" } = {}) {
|
|
|
81
140
|
entry.timestamp
|
|
82
141
|
);
|
|
83
142
|
turn.status = "completed";
|
|
143
|
+
activeTurnId = "";
|
|
84
144
|
continue;
|
|
85
145
|
}
|
|
86
146
|
|
|
87
|
-
if (eventType === "
|
|
147
|
+
if (eventType === "item_completed") {
|
|
148
|
+
const completedItem = objectValue(payload?.item);
|
|
149
|
+
if (!completedItem) {
|
|
150
|
+
continue;
|
|
151
|
+
}
|
|
152
|
+
|
|
88
153
|
const turn = ensureTurn(
|
|
89
154
|
turns,
|
|
90
155
|
turnsById,
|
|
@@ -92,12 +157,32 @@ function parseSessionJsonlTurns(content, { threadId = "" } = {}) {
|
|
|
92
157
|
sessionThreadId,
|
|
93
158
|
entry.timestamp
|
|
94
159
|
);
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
role: "user",
|
|
99
|
-
text: normalizeString(payload?.message) || normalizeString(payload?.text),
|
|
160
|
+
const item = normalizeResponseItemForHistory(completedItem, index + 1, {
|
|
161
|
+
cwd: sessionCwd,
|
|
162
|
+
toolCallsByCallId,
|
|
100
163
|
});
|
|
164
|
+
if (item) {
|
|
165
|
+
turn.items.push(item);
|
|
166
|
+
}
|
|
167
|
+
continue;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
if (eventType === "user_message") {
|
|
171
|
+
const explicitTurnId = normalizeString(payload?.turn_id) || normalizeString(payload?.turnId);
|
|
172
|
+
const item = createUserMessageHistoryItem(payload, index + 1, entry.timestamp);
|
|
173
|
+
if (!explicitTurnId && !activeTurnId) {
|
|
174
|
+
pushPendingUserMessage(pendingUserMessages, item);
|
|
175
|
+
continue;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const turn = ensureTurn(
|
|
179
|
+
turns,
|
|
180
|
+
turnsById,
|
|
181
|
+
explicitTurnId || activeTurnId || `turn-line-${index + 1}`,
|
|
182
|
+
sessionThreadId,
|
|
183
|
+
entry.timestamp
|
|
184
|
+
);
|
|
185
|
+
addHistoryItemToTurn(turn, item);
|
|
101
186
|
continue;
|
|
102
187
|
}
|
|
103
188
|
|
|
@@ -111,6 +196,7 @@ function parseSessionJsonlTurns(content, { threadId = "" } = {}) {
|
|
|
111
196
|
if (!payload) {
|
|
112
197
|
continue;
|
|
113
198
|
}
|
|
199
|
+
rememberToolCallForHistory(payload, toolCallsByCallId);
|
|
114
200
|
if (shouldSkipResponseItemForHistory(payload, skippedCallIds)) {
|
|
115
201
|
continue;
|
|
116
202
|
}
|
|
@@ -121,9 +207,22 @@ function parseSessionJsonlTurns(content, { threadId = "" } = {}) {
|
|
|
121
207
|
sessionThreadId,
|
|
122
208
|
entry.timestamp
|
|
123
209
|
);
|
|
124
|
-
const item = normalizeResponseItemForHistory(payload, index + 1
|
|
210
|
+
const item = normalizeResponseItemForHistory(payload, index + 1, {
|
|
211
|
+
cwd: sessionCwd,
|
|
212
|
+
toolCallsByCallId,
|
|
213
|
+
});
|
|
125
214
|
if (item) {
|
|
126
|
-
turn
|
|
215
|
+
if (shouldSkipDuplicateProposedPlanMessage(turn, item)) {
|
|
216
|
+
continue;
|
|
217
|
+
}
|
|
218
|
+
const itemTimestamp = historyItemTimestamp(item, entry.timestamp);
|
|
219
|
+
if (itemTimestamp && !item.createdAt) {
|
|
220
|
+
item.createdAt = itemTimestamp;
|
|
221
|
+
}
|
|
222
|
+
if (itemTimestamp && !item.timestamp) {
|
|
223
|
+
item.timestamp = itemTimestamp;
|
|
224
|
+
}
|
|
225
|
+
addHistoryItemToTurn(turn, item);
|
|
127
226
|
}
|
|
128
227
|
}
|
|
129
228
|
}
|
|
@@ -131,6 +230,159 @@ function parseSessionJsonlTurns(content, { threadId = "" } = {}) {
|
|
|
131
230
|
return turns.filter((turn) => turn.items.length > 0);
|
|
132
231
|
}
|
|
133
232
|
|
|
233
|
+
function createUserMessageHistoryItem(payload, lineNumber, timestamp) {
|
|
234
|
+
const createdAt = historyItemTimestamp(payload, timestamp);
|
|
235
|
+
return {
|
|
236
|
+
id: normalizeString(payload?.id) || `user-message-line-${lineNumber}`,
|
|
237
|
+
type: "user_message",
|
|
238
|
+
role: "user",
|
|
239
|
+
text: normalizeString(payload?.message) || normalizeString(payload?.text),
|
|
240
|
+
createdAt: createdAt || undefined,
|
|
241
|
+
timestamp: createdAt || undefined,
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
function pushPendingUserMessage(pendingUserMessages, item) {
|
|
246
|
+
if (!item || !historyUserItemText(item)) {
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
if (pendingUserMessages.some((candidate) => areDuplicateUserHistoryItems(candidate, item))) {
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
pendingUserMessages.push(item);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
function addHistoryItemToTurn(turn, item) {
|
|
256
|
+
if (!turn || !item) {
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
if (isUserHistoryItem(item)) {
|
|
261
|
+
const duplicateIndex = turn.items.findIndex((candidate) => areDuplicateUserHistoryItems(candidate, item));
|
|
262
|
+
if (duplicateIndex !== -1) {
|
|
263
|
+
turn.items[duplicateIndex] = mergeDuplicateUserHistoryItems(turn.items[duplicateIndex], item);
|
|
264
|
+
return;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
turn.items.push(item);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
function mergeDuplicateUserHistoryItems(existing, incoming) {
|
|
272
|
+
const existingHasStructuredContent = hasStructuredUserHistoryContent(existing);
|
|
273
|
+
const incomingHasStructuredContent = hasStructuredUserHistoryContent(incoming);
|
|
274
|
+
const preferStructured = existingHasStructuredContent !== incomingHasStructuredContent;
|
|
275
|
+
const preferIncoming = preferStructured
|
|
276
|
+
? incomingHasStructuredContent
|
|
277
|
+
: normalizeHistoryToken(incoming?.type) === "usermessage"
|
|
278
|
+
&& normalizeHistoryToken(existing?.type) !== "usermessage";
|
|
279
|
+
const base = preferIncoming ? incoming : existing;
|
|
280
|
+
const fallback = preferIncoming ? existing : incoming;
|
|
281
|
+
return {
|
|
282
|
+
...base,
|
|
283
|
+
content: Array.isArray(base?.content) ? base.content : fallback?.content,
|
|
284
|
+
attachments: Array.isArray(base?.attachments) ? base.attachments : fallback?.attachments,
|
|
285
|
+
createdAt: historyItemTimestamp(base, historyItemTimestamp(fallback)) || undefined,
|
|
286
|
+
timestamp: historyItemTimestamp(base, historyItemTimestamp(fallback)) || undefined,
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
function hasStructuredUserHistoryContent(item) {
|
|
291
|
+
const content = Array.isArray(item?.content) ? item.content : [];
|
|
292
|
+
return content
|
|
293
|
+
.map((entry) => objectValue(entry))
|
|
294
|
+
.filter(Boolean)
|
|
295
|
+
.some((entry) => {
|
|
296
|
+
const type = normalizeHistoryToken(entry.type);
|
|
297
|
+
return type === "skill"
|
|
298
|
+
|| type === "mention"
|
|
299
|
+
|| type === "image"
|
|
300
|
+
|| type === "inputimage";
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
function areDuplicateUserHistoryItems(first, second) {
|
|
305
|
+
if (!isUserHistoryItem(first) || !isUserHistoryItem(second)) {
|
|
306
|
+
return false;
|
|
307
|
+
}
|
|
308
|
+
const firstText = historyUserItemText(first);
|
|
309
|
+
const secondText = historyUserItemText(second);
|
|
310
|
+
if (!firstText || !secondText) {
|
|
311
|
+
return false;
|
|
312
|
+
}
|
|
313
|
+
if (firstText === secondText) {
|
|
314
|
+
return true;
|
|
315
|
+
}
|
|
316
|
+
const firstKey = canonicalUserHistoryTextKey(firstText);
|
|
317
|
+
const secondKey = canonicalUserHistoryTextKey(secondText);
|
|
318
|
+
if (firstKey.hasMentions && firstKey.key === secondKey.key) {
|
|
319
|
+
return true;
|
|
320
|
+
}
|
|
321
|
+
return Boolean(
|
|
322
|
+
firstKey.text
|
|
323
|
+
&& firstKey.text === secondKey.text
|
|
324
|
+
&& (firstKey.hasMentions || secondKey.hasMentions)
|
|
325
|
+
&& sameUserHistoryTimestamp(first, second)
|
|
326
|
+
);
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
function sameUserHistoryTimestamp(first, second) {
|
|
330
|
+
const firstTimestamp = historyItemTimestamp(first);
|
|
331
|
+
const secondTimestamp = historyItemTimestamp(second);
|
|
332
|
+
return Boolean(firstTimestamp && secondTimestamp && firstTimestamp === secondTimestamp);
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
function historyItemTimestamp(item, fallbackTimestamp = "") {
|
|
336
|
+
return firstNonEmptyString([
|
|
337
|
+
normalizeString(item?.createdAt),
|
|
338
|
+
normalizeString(item?.created_at),
|
|
339
|
+
normalizeString(item?.timestamp),
|
|
340
|
+
normalizeString(item?.time),
|
|
341
|
+
normalizeString(fallbackTimestamp),
|
|
342
|
+
]);
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
function isUserHistoryItem(item) {
|
|
346
|
+
return normalizeHistoryToken(item?.type) === "usermessage"
|
|
347
|
+
|| normalizeString(item?.role).toLowerCase() === "user";
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
function historyUserItemText(item) {
|
|
351
|
+
return normalizeString(item?.text)
|
|
352
|
+
|| normalizeString(item?.message)
|
|
353
|
+
|| responseItemMessageText(item);
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
function shouldSkipDuplicateProposedPlanMessage(turn, item) {
|
|
357
|
+
if (!turn || !item || normalizeHistoryToken(item.type) !== "message") {
|
|
358
|
+
return false;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
const role = normalizeString(item.role).toLowerCase();
|
|
362
|
+
if (role && role !== "assistant") {
|
|
363
|
+
return false;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
if (!responseItemMessageText(item).includes("<proposed_plan>")) {
|
|
367
|
+
return false;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
return turn.items.some((candidate) => (
|
|
371
|
+
normalizeHistoryToken(candidate?.type) === "plan"
|
|
372
|
+
&& candidate?.remodexJsonlProgressPlan !== true
|
|
373
|
+
));
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
function flushPendingUserMessagesToTurn(turn, pendingUserMessages) {
|
|
377
|
+
if (!turn || pendingUserMessages.length === 0) {
|
|
378
|
+
return;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
for (const item of pendingUserMessages.splice(0)) {
|
|
382
|
+
addHistoryItemToTurn(turn, item);
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
|
|
134
386
|
function ensureTurn(turns, turnsById, turnId, threadId, timestamp) {
|
|
135
387
|
const normalizedTurnId = normalizeString(turnId) || `turn-${turns.length + 1}`;
|
|
136
388
|
let turn = turnsById.get(normalizedTurnId);
|
|
@@ -151,12 +403,34 @@ function ensureTurn(turns, turnsById, turnId, threadId, timestamp) {
|
|
|
151
403
|
return turn;
|
|
152
404
|
}
|
|
153
405
|
|
|
154
|
-
function normalizeResponseItemForHistory(payload, lineNumber) {
|
|
406
|
+
function normalizeResponseItemForHistory(payload, lineNumber, { cwd = "", toolCallsByCallId = new Map() } = {}) {
|
|
155
407
|
const type = normalizeHistoryItemType(payload.type);
|
|
156
408
|
if (!type) {
|
|
157
409
|
return null;
|
|
158
410
|
}
|
|
159
411
|
|
|
412
|
+
const progressPlanItem = normalizeProgressPlanItemForHistory(payload);
|
|
413
|
+
if (progressPlanItem) {
|
|
414
|
+
return progressPlanItem;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
const applyPatchItem = normalizeApplyPatchItemForHistory(payload, lineNumber, { cwd });
|
|
418
|
+
if (applyPatchItem) {
|
|
419
|
+
return applyPatchItem;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
const toolOutputImageViewItem = normalizeToolOutputImageViewItemForHistory(payload, lineNumber, {
|
|
423
|
+
toolCallsByCallId,
|
|
424
|
+
});
|
|
425
|
+
if (toolOutputImageViewItem) {
|
|
426
|
+
return toolOutputImageViewItem;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
const readableToolItem = normalizeReadableToolItemForHistory(payload, lineNumber, { cwd });
|
|
430
|
+
if (readableToolItem) {
|
|
431
|
+
return readableToolItem;
|
|
432
|
+
}
|
|
433
|
+
|
|
160
434
|
const item = {
|
|
161
435
|
...payload,
|
|
162
436
|
id: normalizeString(payload.id)
|
|
@@ -173,6 +447,423 @@ function normalizeResponseItemForHistory(payload, lineNumber) {
|
|
|
173
447
|
return item;
|
|
174
448
|
}
|
|
175
449
|
|
|
450
|
+
// Converts `view_image` tool output blobs into a lightweight local image reference.
|
|
451
|
+
function normalizeToolOutputImageViewItemForHistory(payload, lineNumber, { toolCallsByCallId = new Map() } = {}) {
|
|
452
|
+
const type = normalizeHistoryItemType(payload.type);
|
|
453
|
+
if (normalizeHistoryToken(type) !== "toolcalloutput") {
|
|
454
|
+
return null;
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
const callId = normalizeString(payload.call_id)
|
|
458
|
+
|| normalizeString(payload.callId)
|
|
459
|
+
|| normalizeString(payload.id);
|
|
460
|
+
const toolCall = callId ? toolCallsByCallId.get(callId) : null;
|
|
461
|
+
if (!toolCall || normalizeString(toolCall.toolName).toLowerCase() !== "view_image") {
|
|
462
|
+
return null;
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
const imagePath = normalizeString(toolCall.imagePath);
|
|
466
|
+
if (!imagePath || !toolCallOutputContainsInlineImage(payload.output)) {
|
|
467
|
+
return null;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
return {
|
|
471
|
+
id: `${callId || `tool-output-line-${lineNumber}`}-image-view`,
|
|
472
|
+
type: "imageView",
|
|
473
|
+
status: normalizeString(payload.status) || "completed",
|
|
474
|
+
path: imagePath,
|
|
475
|
+
call_id: callId || undefined,
|
|
476
|
+
tool_name: toolCall.toolName,
|
|
477
|
+
remodexJsonlToolOutputImage: true,
|
|
478
|
+
};
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
// Enriches raw tool-call JSONL records so mobile history can render useful rows.
|
|
482
|
+
function normalizeReadableToolItemForHistory(payload, lineNumber, { cwd = "" } = {}) {
|
|
483
|
+
const type = normalizeHistoryItemType(payload.type);
|
|
484
|
+
const typeToken = normalizeHistoryToken(type);
|
|
485
|
+
if (typeToken !== "toolcall" && typeToken !== "customtoolcall") {
|
|
486
|
+
return null;
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
const toolName = normalizeString(payload.name)
|
|
490
|
+
|| normalizeString(payload.tool_name)
|
|
491
|
+
|| normalizeString(payload.toolName);
|
|
492
|
+
if (!toolName) {
|
|
493
|
+
return null;
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
const callId = normalizeString(payload.call_id)
|
|
497
|
+
|| normalizeString(payload.callId)
|
|
498
|
+
|| normalizeString(payload.id);
|
|
499
|
+
const argumentsObject = parseToolArguments(
|
|
500
|
+
payload.arguments !== undefined ? payload.arguments : payload.input
|
|
501
|
+
);
|
|
502
|
+
const id = callId || normalizeString(payload.id) || `tool-call-line-${lineNumber}`;
|
|
503
|
+
const status = normalizeString(payload.status) || "completed";
|
|
504
|
+
|
|
505
|
+
if (isCommandToolName(toolName)) {
|
|
506
|
+
return {
|
|
507
|
+
...payload,
|
|
508
|
+
id,
|
|
509
|
+
type: "commandExecution",
|
|
510
|
+
status,
|
|
511
|
+
command: resolveToolCommand(toolName, argumentsObject),
|
|
512
|
+
cwd: resolveToolWorkingDirectory(argumentsObject, { cwd }),
|
|
513
|
+
call_id: callId || undefined,
|
|
514
|
+
tool_name: toolName,
|
|
515
|
+
arguments: payload.arguments,
|
|
516
|
+
};
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
const message = readableToolActivityMessage(toolName, argumentsObject, payload);
|
|
520
|
+
if (!message) {
|
|
521
|
+
return null;
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
return {
|
|
525
|
+
...payload,
|
|
526
|
+
id,
|
|
527
|
+
type: "tool_call",
|
|
528
|
+
status,
|
|
529
|
+
message,
|
|
530
|
+
call_id: callId || undefined,
|
|
531
|
+
tool_name: toolName,
|
|
532
|
+
};
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
function rememberToolCallForHistory(payload, toolCallsByCallId) {
|
|
536
|
+
const typeToken = normalizeHistoryToken(normalizeHistoryItemType(payload?.type));
|
|
537
|
+
if (typeToken !== "toolcall" && typeToken !== "customtoolcall") {
|
|
538
|
+
return;
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
const callId = normalizeString(payload.call_id)
|
|
542
|
+
|| normalizeString(payload.callId)
|
|
543
|
+
|| normalizeString(payload.id);
|
|
544
|
+
const toolName = normalizeString(payload.name)
|
|
545
|
+
|| normalizeString(payload.tool_name)
|
|
546
|
+
|| normalizeString(payload.toolName);
|
|
547
|
+
if (!callId || !toolName) {
|
|
548
|
+
return;
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
const argumentsObject = parseToolArguments(
|
|
552
|
+
payload.arguments !== undefined ? payload.arguments : payload.input
|
|
553
|
+
);
|
|
554
|
+
toolCallsByCallId.set(callId, {
|
|
555
|
+
toolName,
|
|
556
|
+
imagePath: resolveToolImagePath(toolName, argumentsObject, payload),
|
|
557
|
+
});
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
function resolveToolImagePath(toolName, argumentsObject, payload) {
|
|
561
|
+
if (normalizeString(toolName).toLowerCase() !== "view_image") {
|
|
562
|
+
return "";
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
return firstNonEmptyString([
|
|
566
|
+
normalizeString(argumentsObject.path),
|
|
567
|
+
normalizeString(argumentsObject.filePath),
|
|
568
|
+
normalizeString(argumentsObject.file_path),
|
|
569
|
+
normalizeString(argumentsObject.localPath),
|
|
570
|
+
normalizeString(argumentsObject.local_path),
|
|
571
|
+
normalizeString(payload.path),
|
|
572
|
+
normalizeString(payload.filePath),
|
|
573
|
+
normalizeString(payload.file_path),
|
|
574
|
+
normalizeString(payload.localPath),
|
|
575
|
+
normalizeString(payload.local_path),
|
|
576
|
+
]);
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
function toolCallOutputContainsInlineImage(rawOutput) {
|
|
580
|
+
const parsedOutput = typeof rawOutput === "string"
|
|
581
|
+
? safeParseJSON(rawOutput) || rawOutput
|
|
582
|
+
: rawOutput;
|
|
583
|
+
return containsInlineImageDataURL(parsedOutput);
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
function containsInlineImageDataURL(value) {
|
|
587
|
+
if (typeof value === "string") {
|
|
588
|
+
return value.toLowerCase().startsWith("data:image");
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
if (Array.isArray(value)) {
|
|
592
|
+
return value.some(containsInlineImageDataURL);
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
if (value && typeof value === "object") {
|
|
596
|
+
return Object.values(value).some(containsInlineImageDataURL);
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
return false;
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
function normalizeApplyPatchItemForHistory(payload, lineNumber, { cwd = "" } = {}) {
|
|
603
|
+
const type = normalizeHistoryItemType(payload.type);
|
|
604
|
+
if (normalizeString(payload.name) !== "apply_patch" || normalizeHistoryToken(type) !== "customtoolcall") {
|
|
605
|
+
return null;
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
const callId = normalizeString(payload.call_id)
|
|
609
|
+
|| normalizeString(payload.callId)
|
|
610
|
+
|| normalizeString(payload.id);
|
|
611
|
+
const item = buildApplyPatchFileChangeItem({
|
|
612
|
+
callId,
|
|
613
|
+
patch: normalizeString(payload.input),
|
|
614
|
+
status: normalizeString(payload.status) || "completed",
|
|
615
|
+
idFallback: callId || `apply-patch-line-${lineNumber}`,
|
|
616
|
+
cwd,
|
|
617
|
+
});
|
|
618
|
+
return item ? { ...payload, ...item } : null;
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
function normalizeProgressPlanItemForHistory(payload) {
|
|
622
|
+
const type = normalizeHistoryItemType(payload.type);
|
|
623
|
+
if (!isInternalProgressPlanCall(payload) || normalizeHistoryToken(type) !== "toolcall") {
|
|
624
|
+
return null;
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
const argumentsObject = parseToolArguments(payload.arguments);
|
|
628
|
+
const explanation = normalizeString(argumentsObject.explanation);
|
|
629
|
+
const plan = normalizeHistoryPlanSteps(argumentsObject.plan);
|
|
630
|
+
if (!explanation && plan.length === 0) {
|
|
631
|
+
return null;
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
return {
|
|
635
|
+
id: normalizeString(payload.call_id)
|
|
636
|
+
|| normalizeString(payload.callId)
|
|
637
|
+
|| normalizeString(payload.id)
|
|
638
|
+
|| undefined,
|
|
639
|
+
type: "plan",
|
|
640
|
+
text: explanation || "Planning...",
|
|
641
|
+
explanation: explanation || undefined,
|
|
642
|
+
plan,
|
|
643
|
+
remodexJsonlProgressPlan: true,
|
|
644
|
+
};
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
function normalizeHistoryPlanSteps(rawPlan) {
|
|
648
|
+
if (!Array.isArray(rawPlan)) {
|
|
649
|
+
return [];
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
return rawPlan.flatMap((rawStep) => {
|
|
653
|
+
const stepObject = objectValue(rawStep);
|
|
654
|
+
const step = normalizeString(stepObject?.step);
|
|
655
|
+
const status = normalizeHistoryPlanStatus(stepObject?.status);
|
|
656
|
+
return step && status ? [{ step, status }] : [];
|
|
657
|
+
});
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
function normalizeHistoryPlanStatus(rawStatus) {
|
|
661
|
+
const normalized = normalizeString(rawStatus);
|
|
662
|
+
switch (normalized) {
|
|
663
|
+
case "pending":
|
|
664
|
+
case "in_progress":
|
|
665
|
+
case "inProgress":
|
|
666
|
+
case "completed":
|
|
667
|
+
return normalized;
|
|
668
|
+
default:
|
|
669
|
+
return "";
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
function parseToolArguments(rawArguments) {
|
|
674
|
+
const parsed = typeof rawArguments === "string"
|
|
675
|
+
? safeParseJSON(normalizeString(rawArguments))
|
|
676
|
+
: rawArguments;
|
|
677
|
+
return objectValue(parsed) || {};
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
function resolveToolCommand(toolName, argumentsObject) {
|
|
681
|
+
if (!isCommandToolName(toolName)) {
|
|
682
|
+
return toolName;
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
return firstNonEmptyString([
|
|
686
|
+
normalizeString(argumentsObject.cmd),
|
|
687
|
+
normalizeString(argumentsObject.command),
|
|
688
|
+
normalizeString(argumentsObject.raw_command),
|
|
689
|
+
normalizeString(argumentsObject.rawCommand),
|
|
690
|
+
normalizeString(argumentsObject.input),
|
|
691
|
+
]) || toolName;
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
function resolveToolWorkingDirectory(argumentsObject, { cwd = "" } = {}) {
|
|
695
|
+
return firstNonEmptyString([
|
|
696
|
+
normalizeString(argumentsObject.workdir),
|
|
697
|
+
normalizeString(argumentsObject.cwd),
|
|
698
|
+
normalizeString(argumentsObject.working_directory),
|
|
699
|
+
normalizeString(argumentsObject.workingDirectory),
|
|
700
|
+
normalizeString(cwd),
|
|
701
|
+
]) || "";
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
function isCommandToolName(toolName) {
|
|
705
|
+
const normalized = normalizeString(toolName).toLowerCase();
|
|
706
|
+
return normalized === "exec_command" || normalized === "shell_command";
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
function readableToolActivityMessage(toolName, argumentsObject, payload) {
|
|
710
|
+
const normalized = normalizeString(toolName).toLowerCase();
|
|
711
|
+
switch (normalized) {
|
|
712
|
+
case "write_stdin":
|
|
713
|
+
return "Write to terminal";
|
|
714
|
+
case "read_thread_terminal":
|
|
715
|
+
return "Read terminal output";
|
|
716
|
+
case "view_image": {
|
|
717
|
+
const imagePath = firstNonEmptyString([
|
|
718
|
+
normalizeString(argumentsObject.path),
|
|
719
|
+
normalizeString(payload.path),
|
|
720
|
+
]);
|
|
721
|
+
return imagePath ? `Open image ${compactHistoryPath(imagePath)}` : "Open image";
|
|
722
|
+
}
|
|
723
|
+
case "open":
|
|
724
|
+
case "browser.open":
|
|
725
|
+
return readableTargetMessage("Open", argumentsObject, payload);
|
|
726
|
+
case "click":
|
|
727
|
+
case "browser.click":
|
|
728
|
+
return readableTargetMessage("Click", argumentsObject, payload);
|
|
729
|
+
case "find":
|
|
730
|
+
case "browser.find":
|
|
731
|
+
return readableTargetMessage("Find", argumentsObject, payload);
|
|
732
|
+
case "screenshot":
|
|
733
|
+
case "browser.screenshot":
|
|
734
|
+
return "Capture screenshot";
|
|
735
|
+
case "web.run":
|
|
736
|
+
case "search_query":
|
|
737
|
+
case "image_query":
|
|
738
|
+
return readableSearchMessage(argumentsObject, payload);
|
|
739
|
+
case "weather":
|
|
740
|
+
return readableLocationMessage("Check weather", argumentsObject, payload);
|
|
741
|
+
case "finance":
|
|
742
|
+
return readableSymbolMessage("Check market data", argumentsObject, payload);
|
|
743
|
+
case "sports":
|
|
744
|
+
return readableLocationMessage("Check sports", argumentsObject, payload);
|
|
745
|
+
case "automation_update":
|
|
746
|
+
return "Update automation";
|
|
747
|
+
case "update_goal":
|
|
748
|
+
return "Update goal";
|
|
749
|
+
case "create_goal":
|
|
750
|
+
return "Create goal";
|
|
751
|
+
case "get_goal":
|
|
752
|
+
return "Read goal";
|
|
753
|
+
case "request_user_input":
|
|
754
|
+
return "Request input";
|
|
755
|
+
default:
|
|
756
|
+
return `Run ${humanizeToolName(toolName)}`;
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
function readableTargetMessage(verb, argumentsObject, payload) {
|
|
761
|
+
const target = firstNonEmptyString([
|
|
762
|
+
normalizeString(argumentsObject.ref_id),
|
|
763
|
+
normalizeString(argumentsObject.refId),
|
|
764
|
+
normalizeString(argumentsObject.url),
|
|
765
|
+
normalizeString(argumentsObject.pattern),
|
|
766
|
+
normalizeString(argumentsObject.query),
|
|
767
|
+
normalizeString(payload.ref_id),
|
|
768
|
+
normalizeString(payload.url),
|
|
769
|
+
]);
|
|
770
|
+
return target ? `${verb} ${compactHistoryPath(target)}` : verb;
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
function readableSearchMessage(argumentsObject, payload) {
|
|
774
|
+
const query = firstSearchQuery(argumentsObject) || firstSearchQuery(payload);
|
|
775
|
+
return query ? `Search ${query}` : "Search web";
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
function firstSearchQuery(object) {
|
|
779
|
+
const direct = normalizeString(object.q)
|
|
780
|
+
|| normalizeString(object.query)
|
|
781
|
+
|| normalizeString(object.search_query);
|
|
782
|
+
if (direct) {
|
|
783
|
+
return direct;
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
let searchArray = null;
|
|
787
|
+
if (Array.isArray(object.search_query)) {
|
|
788
|
+
searchArray = object.search_query;
|
|
789
|
+
} else if (Array.isArray(object.image_query)) {
|
|
790
|
+
searchArray = object.image_query;
|
|
791
|
+
}
|
|
792
|
+
if (!searchArray) {
|
|
793
|
+
return "";
|
|
794
|
+
}
|
|
795
|
+
for (const item of searchArray) {
|
|
796
|
+
const query = normalizeString(objectValue(item)?.q)
|
|
797
|
+
|| normalizeString(objectValue(item)?.query);
|
|
798
|
+
if (query) {
|
|
799
|
+
return query;
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
return "";
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
function readableLocationMessage(verb, argumentsObject, payload) {
|
|
806
|
+
const target = firstNonEmptyString([
|
|
807
|
+
normalizeString(argumentsObject.location),
|
|
808
|
+
normalizeString(argumentsObject.team),
|
|
809
|
+
normalizeString(argumentsObject.league),
|
|
810
|
+
normalizeString(payload.location),
|
|
811
|
+
]);
|
|
812
|
+
return target ? `${verb} ${target}` : verb;
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
function readableSymbolMessage(verb, argumentsObject, payload) {
|
|
816
|
+
const target = firstNonEmptyString([
|
|
817
|
+
normalizeString(argumentsObject.ticker),
|
|
818
|
+
normalizeString(argumentsObject.symbol),
|
|
819
|
+
normalizeString(payload.ticker),
|
|
820
|
+
]);
|
|
821
|
+
return target ? `${verb} ${target}` : verb;
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
function humanizeToolName(toolName) {
|
|
825
|
+
return normalizeString(toolName)
|
|
826
|
+
.replace(/^[^.]+\./, "")
|
|
827
|
+
.replace(/[_-]+/g, " ")
|
|
828
|
+
.replace(/\s+/g, " ")
|
|
829
|
+
.trim() || "tool";
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
function compactHistoryPath(path) {
|
|
833
|
+
const text = normalizeString(path);
|
|
834
|
+
if (!text) {
|
|
835
|
+
return "";
|
|
836
|
+
}
|
|
837
|
+
const normalized = text.replace(/\\/g, "/");
|
|
838
|
+
const parts = normalized.split("/").filter(Boolean);
|
|
839
|
+
if (parts.length <= 2) {
|
|
840
|
+
return text;
|
|
841
|
+
}
|
|
842
|
+
const prefix = normalized.startsWith("/") ? "…/" : "";
|
|
843
|
+
return `${prefix}${parts.slice(-2).join("/")}`;
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
function firstNonEmptyString(values) {
|
|
847
|
+
for (const value of values) {
|
|
848
|
+
const normalized = normalizeString(value);
|
|
849
|
+
if (normalized) {
|
|
850
|
+
return normalized;
|
|
851
|
+
}
|
|
852
|
+
}
|
|
853
|
+
return "";
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
function safeParseJSON(rawValue) {
|
|
857
|
+
if (!rawValue) {
|
|
858
|
+
return null;
|
|
859
|
+
}
|
|
860
|
+
try {
|
|
861
|
+
return JSON.parse(rawValue);
|
|
862
|
+
} catch {
|
|
863
|
+
return null;
|
|
864
|
+
}
|
|
865
|
+
}
|
|
866
|
+
|
|
176
867
|
// Filters desktop transcript internals that are stored as response items but are not chat history.
|
|
177
868
|
function shouldSkipResponseItemForHistory(payload, skippedCallIds) {
|
|
178
869
|
const type = normalizeHistoryItemType(payload.type);
|
|
@@ -189,6 +880,13 @@ function shouldSkipResponseItemForHistory(payload, skippedCallIds) {
|
|
|
189
880
|
return true;
|
|
190
881
|
}
|
|
191
882
|
|
|
883
|
+
if (type === "tool_call" && isInternalProgressPlanCall(payload)) {
|
|
884
|
+
if (callId) {
|
|
885
|
+
skippedCallIds.add(callId);
|
|
886
|
+
}
|
|
887
|
+
return false;
|
|
888
|
+
}
|
|
889
|
+
|
|
192
890
|
if (type !== "message") {
|
|
193
891
|
return false;
|
|
194
892
|
}
|
|
@@ -214,6 +912,10 @@ function isSubagentOrchestrationCall(payload) {
|
|
|
214
912
|
|| name === "close_agent";
|
|
215
913
|
}
|
|
216
914
|
|
|
915
|
+
function isInternalProgressPlanCall(payload) {
|
|
916
|
+
return normalizeString(payload.name).toLowerCase() === "update_plan";
|
|
917
|
+
}
|
|
918
|
+
|
|
217
919
|
function isSubagentNotificationMessage(payload) {
|
|
218
920
|
const text = responseItemMessageText(payload).trimStart();
|
|
219
921
|
return text.startsWith("<subagent_notification>");
|
|
@@ -229,13 +931,84 @@ function responseItemMessageText(payload) {
|
|
|
229
931
|
return content
|
|
230
932
|
.map((item) => objectValue(item))
|
|
231
933
|
.filter(Boolean)
|
|
232
|
-
.map((item) =>
|
|
934
|
+
.map((item) => responseItemContentText(item))
|
|
233
935
|
.filter(Boolean)
|
|
234
936
|
.join("\n");
|
|
235
937
|
}
|
|
236
938
|
|
|
939
|
+
function responseItemContentText(item) {
|
|
940
|
+
const type = normalizeHistoryToken(item?.type);
|
|
941
|
+
if (type === "skill") {
|
|
942
|
+
const skillName = normalizeString(item.id) || normalizeString(item.name);
|
|
943
|
+
return skillName ? `$${skillName}` : "";
|
|
944
|
+
}
|
|
945
|
+
if (type === "mention") {
|
|
946
|
+
const mentionName = normalizeString(item.name) || normalizeString(item.id);
|
|
947
|
+
return mentionName ? `@${mentionName}` : "";
|
|
948
|
+
}
|
|
949
|
+
return normalizeString(item.text) || normalizeString(objectValue(item.data)?.text);
|
|
950
|
+
}
|
|
951
|
+
|
|
952
|
+
function canonicalUserHistoryTextKey(text) {
|
|
953
|
+
const mentions = { skills: new Set(), plugins: new Set() };
|
|
954
|
+
let body = normalizeString(text).replace(
|
|
955
|
+
/(^|\s)([$/@])([A-Za-z0-9][A-Za-z0-9._-]*)(?=[\s,.;:!?)\]}>]|$)/g,
|
|
956
|
+
(match, prefix, trigger, rawName) => {
|
|
957
|
+
const name = normalizeString(rawName).toLowerCase();
|
|
958
|
+
if (!name) {
|
|
959
|
+
return match;
|
|
960
|
+
}
|
|
961
|
+
if (trigger === "$" || trigger === "/") {
|
|
962
|
+
mentions.skills.add(name);
|
|
963
|
+
} else if (trigger === "@") {
|
|
964
|
+
mentions.plugins.add(name);
|
|
965
|
+
}
|
|
966
|
+
return prefix || "";
|
|
967
|
+
}
|
|
968
|
+
);
|
|
969
|
+
|
|
970
|
+
for (const skill of mentions.skills) {
|
|
971
|
+
body = removeBoundedUserMentionPhrase(body, `$${skill}`);
|
|
972
|
+
body = removeBoundedUserMentionPhrase(body, `/${skill}`);
|
|
973
|
+
body = removeBoundedUserMentionPhrase(body, displayNameForUserMention(skill));
|
|
974
|
+
}
|
|
975
|
+
for (const plugin of mentions.plugins) {
|
|
976
|
+
body = removeBoundedUserMentionPhrase(body, `@${plugin}`);
|
|
977
|
+
}
|
|
978
|
+
|
|
979
|
+
const normalizedBody = body.trim().replace(/\s+/g, " ").toLowerCase();
|
|
980
|
+
const skills = [...mentions.skills].sort();
|
|
981
|
+
const plugins = [...mentions.plugins].sort();
|
|
982
|
+
return {
|
|
983
|
+
hasMentions: skills.length > 0 || plugins.length > 0,
|
|
984
|
+
text: normalizedBody,
|
|
985
|
+
key: `${normalizedBody}|skills:${skills.join(",")}|plugins:${plugins.join(",")}`,
|
|
986
|
+
};
|
|
987
|
+
}
|
|
988
|
+
|
|
989
|
+
function removeBoundedUserMentionPhrase(text, phrase) {
|
|
990
|
+
const normalizedPhrase = normalizeString(phrase);
|
|
991
|
+
if (!normalizedPhrase) {
|
|
992
|
+
return text;
|
|
993
|
+
}
|
|
994
|
+
const pattern = new RegExp(`(^|\\s)${escapeRegExp(normalizedPhrase)}(?=[\\s,.;:!?)\\]}>]|$)`, "gi");
|
|
995
|
+
return text.replace(pattern, (match, prefix) => prefix || "");
|
|
996
|
+
}
|
|
997
|
+
|
|
998
|
+
function displayNameForUserMention(name) {
|
|
999
|
+
return normalizeString(name)
|
|
1000
|
+
.split(/[-_]+/)
|
|
1001
|
+
.filter(Boolean)
|
|
1002
|
+
.map((part) => part.charAt(0).toUpperCase() + part.slice(1).toLowerCase())
|
|
1003
|
+
.join(" ");
|
|
1004
|
+
}
|
|
1005
|
+
|
|
1006
|
+
function escapeRegExp(value) {
|
|
1007
|
+
return String(value).replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1008
|
+
}
|
|
1009
|
+
|
|
237
1010
|
function normalizeHistoryItemType(rawType) {
|
|
238
|
-
const normalized =
|
|
1011
|
+
const normalized = normalizeHistoryToken(rawType);
|
|
239
1012
|
if (!normalized) {
|
|
240
1013
|
return "";
|
|
241
1014
|
}
|
|
@@ -245,9 +1018,16 @@ function normalizeHistoryItemType(rawType) {
|
|
|
245
1018
|
if (normalized === "functioncalloutput") {
|
|
246
1019
|
return "tool_call_output";
|
|
247
1020
|
}
|
|
1021
|
+
if (normalized === "plan") {
|
|
1022
|
+
return "plan";
|
|
1023
|
+
}
|
|
248
1024
|
return rawType;
|
|
249
1025
|
}
|
|
250
1026
|
|
|
1027
|
+
function normalizeHistoryToken(rawType) {
|
|
1028
|
+
return normalizeString(rawType).toLowerCase().replace(/[\s_-]+/g, "");
|
|
1029
|
+
}
|
|
1030
|
+
|
|
251
1031
|
function objectValue(value) {
|
|
252
1032
|
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
253
1033
|
}
|
|
@@ -257,6 +1037,7 @@ function normalizeString(value) {
|
|
|
257
1037
|
}
|
|
258
1038
|
|
|
259
1039
|
module.exports = {
|
|
1040
|
+
parseSessionJsonlMetadata,
|
|
260
1041
|
parseSessionJsonlTurns,
|
|
261
1042
|
readThreadTurnsListPageFromSessionJsonl,
|
|
262
1043
|
};
|