@makerbi/remodex 1.5.8 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/remodex.js +56 -5
- package/package.json +1 -1
- package/src/bridge.js +85 -2
- package/src/desktop-ipc-action-follower.js +836 -3
- package/src/git-handler.js +92 -28
- package/src/ios-app-compatibility.js +7 -7
- package/src/macos-launch-agent.js +46 -1
- package/src/qr.js +2 -5
- package/src/rollout-live-mirror.js +159 -3
- package/src/secure-device-state.js +41 -2
- package/src/secure-transport.js +7 -0
- package/src/session-jsonl-history.js +54 -1
- package/src/voice-handler.js +33 -3
|
@@ -7,10 +7,12 @@
|
|
|
7
7
|
const net = require("net");
|
|
8
8
|
const os = require("os");
|
|
9
9
|
const path = require("path");
|
|
10
|
+
const { buildApplyPatchFileChangeItem } = require("./apply-patch-changes");
|
|
10
11
|
|
|
11
12
|
const FRAME_HEADER_BYTES = 4;
|
|
12
13
|
const MAX_FRAME_BYTES = 256 * 1024 * 1024;
|
|
13
14
|
const REQUEST_TIMEOUT_MS = 10_000;
|
|
15
|
+
const TURN_COMPLETION_IDLE_MS = 3_500;
|
|
14
16
|
const DESKTOP_IPC_ACTION_SOURCE = "desktop-ipc-action-follower";
|
|
15
17
|
const DESKTOP_RESUME_METHODS = new Set(["thread/read", "thread/resume"]);
|
|
16
18
|
const ACTION_METHODS = new Set([
|
|
@@ -44,6 +46,9 @@ function createDesktopIpcActionFollower({
|
|
|
44
46
|
netModule = net,
|
|
45
47
|
now = () => Date.now(),
|
|
46
48
|
requestTimeoutMs = REQUEST_TIMEOUT_MS,
|
|
49
|
+
turnCompletionIdleMs = TURN_COMPLETION_IDLE_MS,
|
|
50
|
+
setTimeoutFn = setTimeout,
|
|
51
|
+
clearTimeoutFn = clearTimeout,
|
|
47
52
|
} = {}) {
|
|
48
53
|
const ipc = createDesktopIpcClient({
|
|
49
54
|
socketPath,
|
|
@@ -56,6 +61,9 @@ function createDesktopIpcActionFollower({
|
|
|
56
61
|
});
|
|
57
62
|
const rawStatesByThreadId = new Map();
|
|
58
63
|
const assistantMessageTextsByThreadId = new Map();
|
|
64
|
+
const mirroredActivityKeysByThreadId = new Map();
|
|
65
|
+
const mirroredUserMessageKeysByThreadId = new Map();
|
|
66
|
+
const activeDesktopTurnsByThreadId = new Map();
|
|
59
67
|
const pendingRoutesByRequestId = new Map();
|
|
60
68
|
const activeThreadIds = new Set();
|
|
61
69
|
const recoveringThreadIds = new Set();
|
|
@@ -87,6 +95,9 @@ function createDesktopIpcActionFollower({
|
|
|
87
95
|
function stopAll() {
|
|
88
96
|
rawStatesByThreadId.clear();
|
|
89
97
|
assistantMessageTextsByThreadId.clear();
|
|
98
|
+
mirroredActivityKeysByThreadId.clear();
|
|
99
|
+
mirroredUserMessageKeysByThreadId.clear();
|
|
100
|
+
clearAllDesktopTurnCompletionTimers();
|
|
90
101
|
pendingRoutesByRequestId.clear();
|
|
91
102
|
activeThreadIds.clear();
|
|
92
103
|
recoveringThreadIds.clear();
|
|
@@ -135,13 +146,16 @@ function createDesktopIpcActionFollower({
|
|
|
135
146
|
}
|
|
136
147
|
|
|
137
148
|
rawStatesByThreadId.set(threadId, nextState);
|
|
138
|
-
|
|
149
|
+
syncProjectedLiveState(threadId, previousState, nextState);
|
|
139
150
|
syncProjectedActions(threadId, projectPendingDesktopActions(threadId, nextState));
|
|
140
151
|
}
|
|
141
152
|
|
|
142
153
|
function onDisconnect() {
|
|
143
154
|
rawStatesByThreadId.clear();
|
|
144
155
|
assistantMessageTextsByThreadId.clear();
|
|
156
|
+
mirroredActivityKeysByThreadId.clear();
|
|
157
|
+
mirroredUserMessageKeysByThreadId.clear();
|
|
158
|
+
clearAllDesktopTurnCompletionTimers();
|
|
145
159
|
pendingRoutesByRequestId.clear();
|
|
146
160
|
recoveringThreadIds.clear();
|
|
147
161
|
queuedChangesByThreadId.clear();
|
|
@@ -278,14 +292,21 @@ function createDesktopIpcActionFollower({
|
|
|
278
292
|
}
|
|
279
293
|
|
|
280
294
|
rawStatesByThreadId.set(threadId, nextState);
|
|
281
|
-
|
|
295
|
+
syncProjectedLiveState(threadId, baselineState, nextState);
|
|
282
296
|
syncProjectedActions(threadId, projectPendingDesktopActions(threadId, nextState));
|
|
283
297
|
}
|
|
284
298
|
|
|
299
|
+
function syncProjectedLiveState(threadId, previousState, nextState) {
|
|
300
|
+
syncProjectedAssistantDeltas(threadId, previousState, nextState);
|
|
301
|
+
syncProjectedDesktopActivities(threadId, nextState);
|
|
302
|
+
}
|
|
303
|
+
|
|
285
304
|
function syncProjectedAssistantDeltas(threadId, previousState, nextState) {
|
|
305
|
+
refreshTrackedDesktopTurnState(threadId, nextState);
|
|
286
306
|
const previousTexts = assistantMessageTextsByThreadId.get(threadId);
|
|
287
307
|
if (!previousTexts && !previousState) {
|
|
288
308
|
assistantMessageTextsByThreadId.set(threadId, snapshotAssistantMessageTexts(nextState));
|
|
309
|
+
syncProjectedDesktopTurnCompletions(threadId, nextState);
|
|
289
310
|
return;
|
|
290
311
|
}
|
|
291
312
|
|
|
@@ -297,13 +318,179 @@ function createDesktopIpcActionFollower({
|
|
|
297
318
|
);
|
|
298
319
|
if (notifications.length === 0) {
|
|
299
320
|
assistantMessageTextsByThreadId.set(threadId, snapshotAssistantMessageTexts(nextState));
|
|
321
|
+
syncProjectedDesktopTurnCompletions(threadId, nextState);
|
|
300
322
|
return;
|
|
301
323
|
}
|
|
302
324
|
|
|
303
|
-
|
|
325
|
+
const activeDeltaTurnIds = new Set(
|
|
326
|
+
notifications
|
|
327
|
+
.map((notification) => readString(notification?.params?.turnId) || readString(notification?.params?.turn_id))
|
|
328
|
+
.filter(Boolean)
|
|
329
|
+
);
|
|
330
|
+
const userNotifications = projectDesktopUserMessageNotifications(
|
|
331
|
+
threadId,
|
|
332
|
+
nextState,
|
|
333
|
+
mirroredUserMessageKeysForThread(threadId),
|
|
334
|
+
activeDeltaTurnIds
|
|
335
|
+
);
|
|
336
|
+
|
|
337
|
+
// Desktop IPC state can report assistant text growth before rollout replay catches
|
|
338
|
+
// up with the user prelude. Emit the opening prompt first to avoid mobile row jumps.
|
|
339
|
+
for (const notification of [...userNotifications, ...notifications]) {
|
|
304
340
|
sendApplicationResponse(JSON.stringify(notification));
|
|
305
341
|
}
|
|
342
|
+
for (const turnId of activeDeltaTurnIds) {
|
|
343
|
+
noteDesktopIpcTurnActivity(threadId, turnId, nextState);
|
|
344
|
+
}
|
|
306
345
|
assistantMessageTextsByThreadId.set(threadId, snapshotAssistantMessageTexts(nextState));
|
|
346
|
+
syncProjectedDesktopTurnCompletions(threadId, nextState);
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
function syncProjectedDesktopActivities(threadId, nextState) {
|
|
350
|
+
refreshTrackedDesktopTurnState(threadId, nextState);
|
|
351
|
+
const activityKeys = mirroredActivityKeysForThread(threadId);
|
|
352
|
+
const notifications = projectDesktopActivityNotifications(threadId, nextState, activityKeys);
|
|
353
|
+
if (notifications.length === 0) {
|
|
354
|
+
syncProjectedDesktopTurnCompletions(threadId, nextState);
|
|
355
|
+
return;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
const activeActivityTurnIds = new Set(
|
|
359
|
+
notifications
|
|
360
|
+
.map((notification) => readString(notification?.params?.turnId) || readString(notification?.params?.turn_id))
|
|
361
|
+
.filter(Boolean)
|
|
362
|
+
);
|
|
363
|
+
const userNotifications = projectDesktopUserMessageNotifications(
|
|
364
|
+
threadId,
|
|
365
|
+
nextState,
|
|
366
|
+
mirroredUserMessageKeysForThread(threadId),
|
|
367
|
+
activeActivityTurnIds
|
|
368
|
+
);
|
|
369
|
+
|
|
370
|
+
// Tool-call snapshots are independent from assistant text deltas. Emit them
|
|
371
|
+
// through the same app-server event names rollout mirroring uses so iOS keeps
|
|
372
|
+
// showing active tool rows while the Mac-owned run is still executing.
|
|
373
|
+
for (const notification of [...userNotifications, ...notifications]) {
|
|
374
|
+
sendApplicationResponse(JSON.stringify(notification));
|
|
375
|
+
}
|
|
376
|
+
for (const turnId of activeActivityTurnIds) {
|
|
377
|
+
noteDesktopIpcTurnActivity(threadId, turnId, nextState);
|
|
378
|
+
}
|
|
379
|
+
syncProjectedDesktopTurnCompletions(threadId, nextState);
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
function mirroredUserMessageKeysForThread(threadId) {
|
|
383
|
+
let keys = mirroredUserMessageKeysByThreadId.get(threadId);
|
|
384
|
+
if (!keys) {
|
|
385
|
+
keys = new Set();
|
|
386
|
+
mirroredUserMessageKeysByThreadId.set(threadId, keys);
|
|
387
|
+
}
|
|
388
|
+
return keys;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
function mirroredActivityKeysForThread(threadId) {
|
|
392
|
+
let keys = mirroredActivityKeysByThreadId.get(threadId);
|
|
393
|
+
if (!keys) {
|
|
394
|
+
keys = new Set();
|
|
395
|
+
mirroredActivityKeysByThreadId.set(threadId, keys);
|
|
396
|
+
}
|
|
397
|
+
return keys;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
function noteDesktopIpcTurnActivity(threadId, turnId, latestState) {
|
|
401
|
+
if (!turnId || turnCompletionIdleMs <= 0) {
|
|
402
|
+
return;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
let turns = activeDesktopTurnsByThreadId.get(threadId);
|
|
406
|
+
if (!turns) {
|
|
407
|
+
turns = new Map();
|
|
408
|
+
activeDesktopTurnsByThreadId.set(threadId, turns);
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
const existing = turns.get(turnId);
|
|
412
|
+
if (existing?.timer) {
|
|
413
|
+
clearTimeoutFn(existing.timer);
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
const entry = { latestState, timer: null };
|
|
417
|
+
turns.set(turnId, entry);
|
|
418
|
+
scheduleDesktopIpcTurnIdleCompletion(threadId, turnId, entry);
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
function scheduleDesktopIpcTurnIdleCompletion(threadId, turnId, entry) {
|
|
422
|
+
entry.timer = setTimeoutFn(() => {
|
|
423
|
+
const currentTurns = activeDesktopTurnsByThreadId.get(threadId);
|
|
424
|
+
const currentEntry = currentTurns?.get(turnId);
|
|
425
|
+
if (!currentEntry) {
|
|
426
|
+
return;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
if (hasOpenDesktopRequestForTurn(currentEntry.latestState, turnId)
|
|
430
|
+
|| hasActiveDesktopActivityForTurn(currentEntry.latestState, turnId)) {
|
|
431
|
+
scheduleDesktopIpcTurnIdleCompletion(threadId, turnId, currentEntry);
|
|
432
|
+
return;
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
completeDesktopIpcTurn(threadId, turnId);
|
|
436
|
+
}, turnCompletionIdleMs);
|
|
437
|
+
entry.timer.unref?.();
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
function refreshTrackedDesktopTurnState(threadId, latestState) {
|
|
441
|
+
const turns = activeDesktopTurnsByThreadId.get(threadId);
|
|
442
|
+
if (!turns) {
|
|
443
|
+
return;
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
for (const entry of turns.values()) {
|
|
447
|
+
entry.latestState = latestState;
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
function syncProjectedDesktopTurnCompletions(threadId, nextState) {
|
|
452
|
+
const turns = activeDesktopTurnsByThreadId.get(threadId);
|
|
453
|
+
if (!turns || turns.size === 0) {
|
|
454
|
+
return;
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
const completions = projectDesktopTurnCompletedNotifications(
|
|
458
|
+
threadId,
|
|
459
|
+
nextState,
|
|
460
|
+
new Set(turns.keys())
|
|
461
|
+
);
|
|
462
|
+
for (const notification of completions) {
|
|
463
|
+
completeDesktopIpcTurn(threadId, notification.params.turnId, notification.params.status || "completed");
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
function completeDesktopIpcTurn(threadId, turnId, status = "completed") {
|
|
468
|
+
const turns = activeDesktopTurnsByThreadId.get(threadId);
|
|
469
|
+
const entry = turns?.get(turnId);
|
|
470
|
+
if (!entry) {
|
|
471
|
+
return;
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
if (entry.timer) {
|
|
475
|
+
clearTimeoutFn(entry.timer);
|
|
476
|
+
}
|
|
477
|
+
turns.delete(turnId);
|
|
478
|
+
if (turns.size === 0) {
|
|
479
|
+
activeDesktopTurnsByThreadId.delete(threadId);
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
sendApplicationResponse(JSON.stringify(createDesktopIpcTurnCompletedNotification(threadId, turnId, status)));
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
function clearAllDesktopTurnCompletionTimers() {
|
|
486
|
+
for (const turns of activeDesktopTurnsByThreadId.values()) {
|
|
487
|
+
for (const entry of turns.values()) {
|
|
488
|
+
if (entry.timer) {
|
|
489
|
+
clearTimeoutFn(entry.timer);
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
activeDesktopTurnsByThreadId.clear();
|
|
307
494
|
}
|
|
308
495
|
|
|
309
496
|
return {
|
|
@@ -605,6 +792,7 @@ function projectDesktopAssistantDeltaNotifications(
|
|
|
605
792
|
turnId: message.turnId,
|
|
606
793
|
itemId: message.itemId,
|
|
607
794
|
delta,
|
|
795
|
+
...(message.phase ? { phase: message.phase } : {}),
|
|
608
796
|
},
|
|
609
797
|
});
|
|
610
798
|
}
|
|
@@ -612,10 +800,263 @@ function projectDesktopAssistantDeltaNotifications(
|
|
|
612
800
|
return notifications;
|
|
613
801
|
}
|
|
614
802
|
|
|
803
|
+
function projectDesktopUserMessageNotifications(
|
|
804
|
+
threadId,
|
|
805
|
+
conversationState,
|
|
806
|
+
mirroredKeys = new Set(),
|
|
807
|
+
turnIdFilter = null
|
|
808
|
+
) {
|
|
809
|
+
const messages = collectUserMessages(conversationState);
|
|
810
|
+
const notifications = [];
|
|
811
|
+
|
|
812
|
+
for (const message of messages) {
|
|
813
|
+
if (turnIdFilter && turnIdFilter.size > 0 && !turnIdFilter.has(message.turnId)) {
|
|
814
|
+
continue;
|
|
815
|
+
}
|
|
816
|
+
if (mirroredKeys.has(message.key)) {
|
|
817
|
+
continue;
|
|
818
|
+
}
|
|
819
|
+
|
|
820
|
+
mirroredKeys.add(message.key);
|
|
821
|
+
notifications.push({
|
|
822
|
+
method: "codex/event/user_message",
|
|
823
|
+
params: {
|
|
824
|
+
threadId,
|
|
825
|
+
turnId: message.turnId,
|
|
826
|
+
message: message.text,
|
|
827
|
+
...(message.itemId ? { id: message.itemId } : {}),
|
|
828
|
+
...(message.timestamp ? { timestamp: message.timestamp } : {}),
|
|
829
|
+
remodexDesktopMirror: true,
|
|
830
|
+
remodexDesktopIpcMirror: true,
|
|
831
|
+
},
|
|
832
|
+
});
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
return notifications;
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
function projectDesktopActivityNotifications(threadId, conversationState, mirroredKeys = new Set()) {
|
|
839
|
+
const turns = Array.isArray(conversationState?.turns) ? conversationState.turns : [];
|
|
840
|
+
const notifications = [];
|
|
841
|
+
|
|
842
|
+
for (const turn of turns) {
|
|
843
|
+
const turnId = readString(turn?.id) || readString(turn?.turnId) || readString(turn?.turn_id);
|
|
844
|
+
if (!turnId || (
|
|
845
|
+
!hasActiveDesktopActivityForTurn(conversationState, turnId)
|
|
846
|
+
&& !isDesktopTurnLive(turn, conversationState)
|
|
847
|
+
&& !hasMirroredActivityOutputPending(turn, mirroredKeys)
|
|
848
|
+
)) {
|
|
849
|
+
continue;
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
const items = Array.isArray(turn?.items) ? turn.items : [];
|
|
853
|
+
const callsById = new Map();
|
|
854
|
+
for (const item of items) {
|
|
855
|
+
if (isDesktopActivityCallItem(item)) {
|
|
856
|
+
const callId = desktopActivityCallId(item);
|
|
857
|
+
if (callId) {
|
|
858
|
+
callsById.set(callId, item);
|
|
859
|
+
}
|
|
860
|
+
}
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
for (const item of items) {
|
|
864
|
+
if (isDesktopActivityCallItem(item)) {
|
|
865
|
+
notifications.push(...projectDesktopActivityBeginNotifications(threadId, turnId, item, mirroredKeys));
|
|
866
|
+
} else if (isDesktopActivityOutputItem(item)) {
|
|
867
|
+
const callId = desktopActivityCallId(item);
|
|
868
|
+
notifications.push(...projectDesktopActivityOutputNotifications(
|
|
869
|
+
threadId,
|
|
870
|
+
turnId,
|
|
871
|
+
item,
|
|
872
|
+
callsById.get(callId),
|
|
873
|
+
mirroredKeys
|
|
874
|
+
));
|
|
875
|
+
}
|
|
876
|
+
}
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
return notifications;
|
|
880
|
+
}
|
|
881
|
+
|
|
882
|
+
function projectDesktopActivityBeginNotifications(threadId, turnId, item, mirroredKeys) {
|
|
883
|
+
const callId = desktopActivityCallId(item);
|
|
884
|
+
const toolName = readString(item?.name) || readString(item?.toolName) || readString(item?.tool_name);
|
|
885
|
+
if (!callId || !toolName || !markMirroredActivityKey(mirroredKeys, turnId, callId, "begin")) {
|
|
886
|
+
return [];
|
|
887
|
+
}
|
|
888
|
+
|
|
889
|
+
if (isCommandToolName(toolName)) {
|
|
890
|
+
const argumentsObject = parseToolArguments(item?.arguments);
|
|
891
|
+
return [createDesktopIpcNotification("codex/event/exec_command_begin", {
|
|
892
|
+
threadId,
|
|
893
|
+
turnId,
|
|
894
|
+
call_id: callId,
|
|
895
|
+
command: resolveToolCommand(toolName, argumentsObject),
|
|
896
|
+
cwd: resolveToolWorkingDirectory(argumentsObject, item),
|
|
897
|
+
status: "running",
|
|
898
|
+
})];
|
|
899
|
+
}
|
|
900
|
+
|
|
901
|
+
if (toolName === "apply_patch") {
|
|
902
|
+
const isCompletedPatch = Boolean(terminalStatusFromObject(item));
|
|
903
|
+
const fileChange = buildApplyPatchFileChangeItem({
|
|
904
|
+
callId,
|
|
905
|
+
patch: readString(item?.input) || readString(item?.arguments),
|
|
906
|
+
status: readString(item?.status) || (isCompletedPatch ? "completed" : "inProgress"),
|
|
907
|
+
idFallback: buildSyntheticActivityItemId("file-change", threadId, turnId, callId),
|
|
908
|
+
cwd: readString(item?.cwd) || readString(item?.workdir),
|
|
909
|
+
});
|
|
910
|
+
if (fileChange) {
|
|
911
|
+
return [createDesktopIpcNotification(
|
|
912
|
+
isCompletedPatch ? "codex/event/patch_apply_end" : "codex/event/patch_apply_begin",
|
|
913
|
+
{
|
|
914
|
+
threadId,
|
|
915
|
+
turnId,
|
|
916
|
+
id: turnId,
|
|
917
|
+
call_id: callId,
|
|
918
|
+
itemId: fileChange.id,
|
|
919
|
+
status: fileChange.status,
|
|
920
|
+
...(isCompletedPatch ? { success: true } : {}),
|
|
921
|
+
changes: fileChange.changes,
|
|
922
|
+
}
|
|
923
|
+
)];
|
|
924
|
+
}
|
|
925
|
+
}
|
|
926
|
+
|
|
927
|
+
return [createDesktopIpcNotification("codex/event/background_event", {
|
|
928
|
+
threadId,
|
|
929
|
+
turnId,
|
|
930
|
+
call_id: callId,
|
|
931
|
+
message: genericToolActivityMessage(toolName),
|
|
932
|
+
})];
|
|
933
|
+
}
|
|
934
|
+
|
|
935
|
+
function projectDesktopActivityOutputNotifications(threadId, turnId, item, callItem, mirroredKeys) {
|
|
936
|
+
const callId = desktopActivityCallId(item);
|
|
937
|
+
const toolName = readString(callItem?.name) || readString(callItem?.toolName) || readString(callItem?.tool_name);
|
|
938
|
+
if (!callId || !toolName || !mirroredKeys.has(activityMirrorKey(turnId, callId, "begin"))) {
|
|
939
|
+
return [];
|
|
940
|
+
}
|
|
941
|
+
if (!markMirroredActivityKey(mirroredKeys, turnId, callId, "output")) {
|
|
942
|
+
return [];
|
|
943
|
+
}
|
|
944
|
+
|
|
945
|
+
if (!isCommandToolName(toolName)) {
|
|
946
|
+
return [];
|
|
947
|
+
}
|
|
948
|
+
|
|
949
|
+
const argumentsObject = parseToolArguments(callItem?.arguments);
|
|
950
|
+
const command = resolveToolCommand(toolName, argumentsObject);
|
|
951
|
+
const cwd = resolveToolWorkingDirectory(argumentsObject, callItem);
|
|
952
|
+
const output = readString(item?.output) || readString(item?.text) || readString(item?.content);
|
|
953
|
+
const notifications = [];
|
|
954
|
+
if (output) {
|
|
955
|
+
notifications.push(createDesktopIpcNotification("codex/event/exec_command_output_delta", {
|
|
956
|
+
threadId,
|
|
957
|
+
turnId,
|
|
958
|
+
call_id: callId,
|
|
959
|
+
command,
|
|
960
|
+
cwd,
|
|
961
|
+
chunk: output,
|
|
962
|
+
}));
|
|
963
|
+
}
|
|
964
|
+
notifications.push(createDesktopIpcNotification("codex/event/exec_command_end", {
|
|
965
|
+
threadId,
|
|
966
|
+
turnId,
|
|
967
|
+
call_id: callId,
|
|
968
|
+
command,
|
|
969
|
+
cwd,
|
|
970
|
+
status: "completed",
|
|
971
|
+
output: output || "",
|
|
972
|
+
}));
|
|
973
|
+
return notifications;
|
|
974
|
+
}
|
|
975
|
+
|
|
976
|
+
function projectDesktopTurnCompletedNotifications(
|
|
977
|
+
threadId,
|
|
978
|
+
conversationState,
|
|
979
|
+
trackedTurnIds = new Set()
|
|
980
|
+
) {
|
|
981
|
+
if (!trackedTurnIds || trackedTurnIds.size === 0) {
|
|
982
|
+
return [];
|
|
983
|
+
}
|
|
984
|
+
|
|
985
|
+
const turns = Array.isArray(conversationState?.turns) ? conversationState.turns : [];
|
|
986
|
+
const notifications = [];
|
|
987
|
+
for (const turn of turns) {
|
|
988
|
+
const turnId = readString(turn?.id) || readString(turn?.turnId) || readString(turn?.turn_id);
|
|
989
|
+
if (!turnId || !trackedTurnIds.has(turnId)) {
|
|
990
|
+
continue;
|
|
991
|
+
}
|
|
992
|
+
// Terminal snapshots can race active tool rows. Keep tracking the turn until
|
|
993
|
+
// the activity itself closes, otherwise the phone may stay running forever.
|
|
994
|
+
if (hasActiveDesktopActivityForTurn(conversationState, turnId)) {
|
|
995
|
+
continue;
|
|
996
|
+
}
|
|
997
|
+
if (!isDesktopTurnTerminal(turn, conversationState)) {
|
|
998
|
+
continue;
|
|
999
|
+
}
|
|
1000
|
+
|
|
1001
|
+
notifications.push(createDesktopIpcTurnCompletedNotification(
|
|
1002
|
+
threadId,
|
|
1003
|
+
turnId,
|
|
1004
|
+
desktopTerminalStatus(turn, conversationState) || "completed"
|
|
1005
|
+
));
|
|
1006
|
+
}
|
|
1007
|
+
return notifications;
|
|
1008
|
+
}
|
|
1009
|
+
|
|
1010
|
+
function createDesktopIpcTurnCompletedNotification(threadId, turnId, status = "completed") {
|
|
1011
|
+
return {
|
|
1012
|
+
method: "turn/completed",
|
|
1013
|
+
params: {
|
|
1014
|
+
threadId,
|
|
1015
|
+
turnId,
|
|
1016
|
+
id: turnId,
|
|
1017
|
+
status,
|
|
1018
|
+
remodexDesktopMirror: true,
|
|
1019
|
+
remodexDesktopIpcMirror: true,
|
|
1020
|
+
},
|
|
1021
|
+
};
|
|
1022
|
+
}
|
|
1023
|
+
|
|
615
1024
|
function snapshotAssistantMessageTexts(conversationState) {
|
|
616
1025
|
return new Map(collectAssistantMessages(conversationState).map((message) => [message.key, message.text]));
|
|
617
1026
|
}
|
|
618
1027
|
|
|
1028
|
+
function collectUserMessages(conversationState) {
|
|
1029
|
+
const turns = Array.isArray(conversationState?.turns) ? conversationState.turns : [];
|
|
1030
|
+
const messages = [];
|
|
1031
|
+
for (const turn of turns) {
|
|
1032
|
+
const turnId = readString(turn?.id) || readString(turn?.turnId) || readString(turn?.turn_id);
|
|
1033
|
+
const items = Array.isArray(turn?.items) ? turn.items : [];
|
|
1034
|
+
for (const item of items) {
|
|
1035
|
+
if (!isUserMessageItem(item)) {
|
|
1036
|
+
continue;
|
|
1037
|
+
}
|
|
1038
|
+
|
|
1039
|
+
const text = userMessageText(item);
|
|
1040
|
+
if (!turnId || !text) {
|
|
1041
|
+
continue;
|
|
1042
|
+
}
|
|
1043
|
+
|
|
1044
|
+
const itemId = readString(item?.id) || readString(item?.itemId) || readString(item?.item_id);
|
|
1045
|
+
messages.push({
|
|
1046
|
+
key: userMessageKey(turnId, itemId, text),
|
|
1047
|
+
turnId,
|
|
1048
|
+
itemId,
|
|
1049
|
+
text,
|
|
1050
|
+
timestamp: readString(item?.createdAt)
|
|
1051
|
+
|| readString(item?.created_at)
|
|
1052
|
+
|| readString(item?.timestamp)
|
|
1053
|
+
|| readString(item?.time),
|
|
1054
|
+
});
|
|
1055
|
+
}
|
|
1056
|
+
}
|
|
1057
|
+
return messages;
|
|
1058
|
+
}
|
|
1059
|
+
|
|
619
1060
|
function collectAssistantMessages(conversationState) {
|
|
620
1061
|
const turns = Array.isArray(conversationState?.turns) ? conversationState.turns : [];
|
|
621
1062
|
const messages = [];
|
|
@@ -629,6 +1070,7 @@ function collectAssistantMessages(conversationState) {
|
|
|
629
1070
|
|
|
630
1071
|
const itemId = readString(item?.id) || readString(item?.itemId) || readString(item?.item_id);
|
|
631
1072
|
const text = assistantMessageText(item);
|
|
1073
|
+
const phase = assistantMessagePhase(item);
|
|
632
1074
|
if (!turnId || !itemId) {
|
|
633
1075
|
continue;
|
|
634
1076
|
}
|
|
@@ -637,6 +1079,7 @@ function collectAssistantMessages(conversationState) {
|
|
|
637
1079
|
key: `${turnId}:${itemId}`,
|
|
638
1080
|
turnId,
|
|
639
1081
|
itemId,
|
|
1082
|
+
phase,
|
|
640
1083
|
text,
|
|
641
1084
|
});
|
|
642
1085
|
}
|
|
@@ -644,6 +1087,295 @@ function collectAssistantMessages(conversationState) {
|
|
|
644
1087
|
return messages;
|
|
645
1088
|
}
|
|
646
1089
|
|
|
1090
|
+
function userMessageKey(turnId, itemId, text) {
|
|
1091
|
+
if (itemId) {
|
|
1092
|
+
return `${turnId}:${itemId}`;
|
|
1093
|
+
}
|
|
1094
|
+
return `${turnId}:text:${crypto
|
|
1095
|
+
.createHash("sha256")
|
|
1096
|
+
.update(text)
|
|
1097
|
+
.digest("hex")
|
|
1098
|
+
.slice(0, 16)}`;
|
|
1099
|
+
}
|
|
1100
|
+
|
|
1101
|
+
function markMirroredActivityKey(mirroredKeys, turnId, callId, phase) {
|
|
1102
|
+
const key = activityMirrorKey(turnId, callId, phase);
|
|
1103
|
+
if (mirroredKeys.has(key)) {
|
|
1104
|
+
return false;
|
|
1105
|
+
}
|
|
1106
|
+
mirroredKeys.add(key);
|
|
1107
|
+
return true;
|
|
1108
|
+
}
|
|
1109
|
+
|
|
1110
|
+
function activityMirrorKey(turnId, callId, phase) {
|
|
1111
|
+
return `${turnId}:${callId}:${phase}`;
|
|
1112
|
+
}
|
|
1113
|
+
|
|
1114
|
+
function hasMirroredActivityOutputPending(turn, mirroredKeys) {
|
|
1115
|
+
const turnId = readString(turn?.id) || readString(turn?.turnId) || readString(turn?.turn_id);
|
|
1116
|
+
const items = Array.isArray(turn?.items) ? turn.items : [];
|
|
1117
|
+
return items.some((item) => {
|
|
1118
|
+
if (!isDesktopActivityOutputItem(item)) {
|
|
1119
|
+
return false;
|
|
1120
|
+
}
|
|
1121
|
+
const callId = desktopActivityCallId(item);
|
|
1122
|
+
return callId
|
|
1123
|
+
&& mirroredKeys.has(activityMirrorKey(turnId, callId, "begin"))
|
|
1124
|
+
&& !mirroredKeys.has(activityMirrorKey(turnId, callId, "output"));
|
|
1125
|
+
});
|
|
1126
|
+
}
|
|
1127
|
+
|
|
1128
|
+
function isDesktopTurnTerminal(turn, conversationState) {
|
|
1129
|
+
if (desktopTerminalStatus(turn, conversationState)) {
|
|
1130
|
+
return true;
|
|
1131
|
+
}
|
|
1132
|
+
|
|
1133
|
+
return hasExplicitFalseFlag(turn, ["running", "isRunning", "streaming", "isStreaming"])
|
|
1134
|
+
|| (
|
|
1135
|
+
hasExplicitFalseFlag(conversationState, ["running", "isRunning", "streaming", "isStreaming"])
|
|
1136
|
+
&& !hasOpenDesktopRequestForTurn(conversationState, readString(turn?.id) || readString(turn?.turnId) || readString(turn?.turn_id))
|
|
1137
|
+
);
|
|
1138
|
+
}
|
|
1139
|
+
|
|
1140
|
+
function isDesktopTurnLive(turn, conversationState) {
|
|
1141
|
+
return hasExplicitTrueFlag(turn, ["running", "isRunning", "streaming", "isStreaming"])
|
|
1142
|
+
|| hasExplicitTrueFlag(conversationState, ["running", "isRunning", "streaming", "isStreaming"])
|
|
1143
|
+
|| ACTIVE_STATUS_TOKENS.has(normalizeToken(readString(turn?.status)))
|
|
1144
|
+
|| ACTIVE_STATUS_TOKENS.has(normalizeToken(readString(turn?.state)))
|
|
1145
|
+
|| ACTIVE_STATUS_TOKENS.has(normalizeToken(readString(turn?.phase)))
|
|
1146
|
+
|| ACTIVE_STATUS_TOKENS.has(normalizeToken(readString(conversationState?.status)))
|
|
1147
|
+
|| ACTIVE_STATUS_TOKENS.has(normalizeToken(readString(conversationState?.state)))
|
|
1148
|
+
|| ACTIVE_STATUS_TOKENS.has(normalizeToken(readString(conversationState?.phase)));
|
|
1149
|
+
}
|
|
1150
|
+
|
|
1151
|
+
function desktopTerminalStatus(turn, conversationState) {
|
|
1152
|
+
const terminal = terminalStatusFromObject(turn)
|
|
1153
|
+
|| terminalStatusFromObject(turn?.turn)
|
|
1154
|
+
|| terminalStatusFromObject(conversationState);
|
|
1155
|
+
return terminal || "";
|
|
1156
|
+
}
|
|
1157
|
+
|
|
1158
|
+
function terminalStatusFromObject(value) {
|
|
1159
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
1160
|
+
return "";
|
|
1161
|
+
}
|
|
1162
|
+
|
|
1163
|
+
const booleanStatus = terminalStatusFromBooleans(value);
|
|
1164
|
+
if (booleanStatus) {
|
|
1165
|
+
return booleanStatus;
|
|
1166
|
+
}
|
|
1167
|
+
|
|
1168
|
+
const candidates = [
|
|
1169
|
+
value.status,
|
|
1170
|
+
value.state,
|
|
1171
|
+
value.phase,
|
|
1172
|
+
value.lifecycle,
|
|
1173
|
+
value.lifecycleStatus,
|
|
1174
|
+
value.lifecycle_status,
|
|
1175
|
+
value.runStatus,
|
|
1176
|
+
value.run_status,
|
|
1177
|
+
value.turnStatus,
|
|
1178
|
+
value.turn_status,
|
|
1179
|
+
];
|
|
1180
|
+
for (const candidate of candidates) {
|
|
1181
|
+
const token = normalizeToken(readString(candidate));
|
|
1182
|
+
if (TERMINAL_STATUS_TOKENS.has(token)) {
|
|
1183
|
+
return canonicalTerminalStatus(token);
|
|
1184
|
+
}
|
|
1185
|
+
}
|
|
1186
|
+
return "";
|
|
1187
|
+
}
|
|
1188
|
+
|
|
1189
|
+
function terminalStatusFromBooleans(value) {
|
|
1190
|
+
if (value.completed === true || value.complete === true || value.done === true || value.finished === true) {
|
|
1191
|
+
return "completed";
|
|
1192
|
+
}
|
|
1193
|
+
if (value.failed === true || value.error === true) {
|
|
1194
|
+
return "failed";
|
|
1195
|
+
}
|
|
1196
|
+
if (value.cancelled === true || value.canceled === true || value.interrupted === true) {
|
|
1197
|
+
return "canceled";
|
|
1198
|
+
}
|
|
1199
|
+
return "";
|
|
1200
|
+
}
|
|
1201
|
+
|
|
1202
|
+
const TERMINAL_STATUS_TOKENS = new Set([
|
|
1203
|
+
"completed",
|
|
1204
|
+
"complete",
|
|
1205
|
+
"finished",
|
|
1206
|
+
"succeeded",
|
|
1207
|
+
"success",
|
|
1208
|
+
"failed",
|
|
1209
|
+
"failure",
|
|
1210
|
+
"error",
|
|
1211
|
+
"cancelled",
|
|
1212
|
+
"canceled",
|
|
1213
|
+
"interrupted",
|
|
1214
|
+
]);
|
|
1215
|
+
|
|
1216
|
+
function canonicalTerminalStatus(token) {
|
|
1217
|
+
if (token === "failed" || token === "failure" || token === "error") {
|
|
1218
|
+
return "failed";
|
|
1219
|
+
}
|
|
1220
|
+
if (token === "cancelled" || token === "canceled" || token === "interrupted") {
|
|
1221
|
+
return "canceled";
|
|
1222
|
+
}
|
|
1223
|
+
return "completed";
|
|
1224
|
+
}
|
|
1225
|
+
|
|
1226
|
+
function hasExplicitFalseFlag(value, keys) {
|
|
1227
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
1228
|
+
return false;
|
|
1229
|
+
}
|
|
1230
|
+
|
|
1231
|
+
return keys.some((key) => Object.prototype.hasOwnProperty.call(value, key) && value[key] === false);
|
|
1232
|
+
}
|
|
1233
|
+
|
|
1234
|
+
function hasExplicitTrueFlag(value, keys) {
|
|
1235
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
1236
|
+
return false;
|
|
1237
|
+
}
|
|
1238
|
+
|
|
1239
|
+
return keys.some((key) => Object.prototype.hasOwnProperty.call(value, key) && value[key] === true);
|
|
1240
|
+
}
|
|
1241
|
+
|
|
1242
|
+
const ACTIVE_STATUS_TOKENS = new Set([
|
|
1243
|
+
"running",
|
|
1244
|
+
"streaming",
|
|
1245
|
+
"inprogress",
|
|
1246
|
+
"inflight",
|
|
1247
|
+
"started",
|
|
1248
|
+
"pending",
|
|
1249
|
+
"active",
|
|
1250
|
+
]);
|
|
1251
|
+
|
|
1252
|
+
function hasOpenDesktopRequestForTurn(conversationState, turnId) {
|
|
1253
|
+
if (!turnId) {
|
|
1254
|
+
return false;
|
|
1255
|
+
}
|
|
1256
|
+
|
|
1257
|
+
const requests = Array.isArray(conversationState?.requests) ? conversationState.requests : [];
|
|
1258
|
+
return requests.some((request) => {
|
|
1259
|
+
if (!request || request.completed === true) {
|
|
1260
|
+
return false;
|
|
1261
|
+
}
|
|
1262
|
+
|
|
1263
|
+
const params = request.params && typeof request.params === "object" && !Array.isArray(request.params)
|
|
1264
|
+
? request.params
|
|
1265
|
+
: {};
|
|
1266
|
+
const requestTurnId = readString(params.turnId)
|
|
1267
|
+
|| readString(params.turn_id)
|
|
1268
|
+
|| readString(request.turnId)
|
|
1269
|
+
|| readString(request.turn_id);
|
|
1270
|
+
return requestTurnId === turnId;
|
|
1271
|
+
});
|
|
1272
|
+
}
|
|
1273
|
+
|
|
1274
|
+
function hasActiveDesktopActivityForTurn(conversationState, turnId) {
|
|
1275
|
+
const turn = desktopTurnById(conversationState, turnId);
|
|
1276
|
+
const items = Array.isArray(turn?.items) ? turn.items : [];
|
|
1277
|
+
const completedActivityIds = completedDesktopActivityIds(items);
|
|
1278
|
+
return items.some((item) => isActiveDesktopActivityItem(item, completedActivityIds));
|
|
1279
|
+
}
|
|
1280
|
+
|
|
1281
|
+
function desktopTurnById(conversationState, turnId) {
|
|
1282
|
+
if (!turnId) {
|
|
1283
|
+
return null;
|
|
1284
|
+
}
|
|
1285
|
+
|
|
1286
|
+
const turns = Array.isArray(conversationState?.turns) ? conversationState.turns : [];
|
|
1287
|
+
return turns.find((turn) => {
|
|
1288
|
+
const candidateTurnId = readString(turn?.id) || readString(turn?.turnId) || readString(turn?.turn_id);
|
|
1289
|
+
return candidateTurnId === turnId;
|
|
1290
|
+
}) || null;
|
|
1291
|
+
}
|
|
1292
|
+
|
|
1293
|
+
function isActiveDesktopActivityItem(item, completedActivityIds = new Set()) {
|
|
1294
|
+
if (!item || typeof item !== "object" || Array.isArray(item)) {
|
|
1295
|
+
return false;
|
|
1296
|
+
}
|
|
1297
|
+
if (!isDesktopActivityItem(item) || isDesktopActivityOutputItem(item) || terminalStatusFromObject(item)) {
|
|
1298
|
+
return false;
|
|
1299
|
+
}
|
|
1300
|
+
if (hasExplicitTrueFlag(item, ["running", "isRunning", "streaming", "isStreaming"])
|
|
1301
|
+
|| ACTIVE_STATUS_TOKENS.has(normalizeToken(readString(item.status)))
|
|
1302
|
+
|| ACTIVE_STATUS_TOKENS.has(normalizeToken(readString(item.state)))
|
|
1303
|
+
|| ACTIVE_STATUS_TOKENS.has(normalizeToken(readString(item.phase)))
|
|
1304
|
+
|| ACTIVE_STATUS_TOKENS.has(normalizeToken(readString(item.lifecycle)))) {
|
|
1305
|
+
return true;
|
|
1306
|
+
}
|
|
1307
|
+
|
|
1308
|
+
// Codex Desktop often represents a live tool as a bare function_call/custom_tool_call
|
|
1309
|
+
// with only call_id, then appends a separate *_output item. Treat the call as active
|
|
1310
|
+
// until that output appears, even when no explicit "running" status is present.
|
|
1311
|
+
const activityId = desktopActivityCallId(item);
|
|
1312
|
+
return isDesktopActivityCallItem(item) && activityId && !completedActivityIds.has(activityId);
|
|
1313
|
+
}
|
|
1314
|
+
|
|
1315
|
+
function isDesktopActivityItem(item) {
|
|
1316
|
+
const type = normalizeToken(item?.type);
|
|
1317
|
+
return type.includes("tool")
|
|
1318
|
+
|| type.includes("command")
|
|
1319
|
+
|| type.includes("exec")
|
|
1320
|
+
|| type.includes("mcp")
|
|
1321
|
+
|| type.includes("function");
|
|
1322
|
+
}
|
|
1323
|
+
|
|
1324
|
+
function isDesktopActivityCallItem(item) {
|
|
1325
|
+
const type = normalizeToken(item?.type);
|
|
1326
|
+
if (isDesktopActivityOutputItem(item)) {
|
|
1327
|
+
return false;
|
|
1328
|
+
}
|
|
1329
|
+
return type.endsWith("call")
|
|
1330
|
+
|| type.includes("toolcall")
|
|
1331
|
+
|| type.includes("functioncall")
|
|
1332
|
+
|| type.includes("commandexecution")
|
|
1333
|
+
|| type.includes("localshellcall");
|
|
1334
|
+
}
|
|
1335
|
+
|
|
1336
|
+
function isDesktopActivityOutputItem(item) {
|
|
1337
|
+
const type = normalizeToken(item?.type);
|
|
1338
|
+
return type.includes("output")
|
|
1339
|
+
|| type.includes("result")
|
|
1340
|
+
|| type.endsWith("end")
|
|
1341
|
+
|| type.includes("completed");
|
|
1342
|
+
}
|
|
1343
|
+
|
|
1344
|
+
function completedDesktopActivityIds(items) {
|
|
1345
|
+
const ids = new Set();
|
|
1346
|
+
for (const item of items) {
|
|
1347
|
+
if (!item || typeof item !== "object" || Array.isArray(item)) {
|
|
1348
|
+
continue;
|
|
1349
|
+
}
|
|
1350
|
+
if (!isDesktopActivityOutputItem(item) && !terminalStatusFromObject(item)) {
|
|
1351
|
+
continue;
|
|
1352
|
+
}
|
|
1353
|
+
const id = desktopActivityCallId(item);
|
|
1354
|
+
if (id) {
|
|
1355
|
+
ids.add(id);
|
|
1356
|
+
}
|
|
1357
|
+
}
|
|
1358
|
+
return ids;
|
|
1359
|
+
}
|
|
1360
|
+
|
|
1361
|
+
function desktopActivityCallId(item) {
|
|
1362
|
+
return readString(item?.call_id)
|
|
1363
|
+
|| readString(item?.callId)
|
|
1364
|
+
|| readString(item?.tool_call_id)
|
|
1365
|
+
|| readString(item?.toolCallId)
|
|
1366
|
+
|| readString(item?.requestId)
|
|
1367
|
+
|| readString(item?.request_id)
|
|
1368
|
+
|| readString(item?.id);
|
|
1369
|
+
}
|
|
1370
|
+
|
|
1371
|
+
function isUserMessageItem(item) {
|
|
1372
|
+
const type = normalizeToken(item?.type);
|
|
1373
|
+
if (type === "usermessage") {
|
|
1374
|
+
return true;
|
|
1375
|
+
}
|
|
1376
|
+
return type === "message" && normalizeToken(item?.role) === "user";
|
|
1377
|
+
}
|
|
1378
|
+
|
|
647
1379
|
function isAssistantMessageItem(item) {
|
|
648
1380
|
const type = normalizeToken(item?.type);
|
|
649
1381
|
if (type === "agentmessage" || type === "assistantmessage") {
|
|
@@ -652,6 +1384,41 @@ function isAssistantMessageItem(item) {
|
|
|
652
1384
|
return type === "message" && normalizeToken(item?.role) === "assistant";
|
|
653
1385
|
}
|
|
654
1386
|
|
|
1387
|
+
function userMessageText(item) {
|
|
1388
|
+
const directText = readString(item?.text) || readString(item?.message);
|
|
1389
|
+
if (directText) {
|
|
1390
|
+
return directText;
|
|
1391
|
+
}
|
|
1392
|
+
|
|
1393
|
+
const content = Array.isArray(item?.content) ? item.content : [];
|
|
1394
|
+
return content
|
|
1395
|
+
.map((entry) => entry && typeof entry === "object" ? entry : null)
|
|
1396
|
+
.filter(Boolean)
|
|
1397
|
+
.map((entry) => readString(entry.text) || readString(entry?.data?.text))
|
|
1398
|
+
.filter(Boolean)
|
|
1399
|
+
.join("");
|
|
1400
|
+
}
|
|
1401
|
+
|
|
1402
|
+
function assistantMessagePhase(item) {
|
|
1403
|
+
return normalizeAssistantPhase(
|
|
1404
|
+
readString(item?.phase)
|
|
1405
|
+
|| readString(item?.assistantPhase)
|
|
1406
|
+
|| readString(item?.assistant_phase)
|
|
1407
|
+
|| readString(item?.metadata?.phase)
|
|
1408
|
+
);
|
|
1409
|
+
}
|
|
1410
|
+
|
|
1411
|
+
function normalizeAssistantPhase(value) {
|
|
1412
|
+
const normalized = normalizeToken(value);
|
|
1413
|
+
if (!normalized) {
|
|
1414
|
+
return "";
|
|
1415
|
+
}
|
|
1416
|
+
if (normalized === "finalanswer") {
|
|
1417
|
+
return "final_answer";
|
|
1418
|
+
}
|
|
1419
|
+
return normalized;
|
|
1420
|
+
}
|
|
1421
|
+
|
|
655
1422
|
function assistantMessageText(item) {
|
|
656
1423
|
const directText = readString(item?.text) || readString(item?.message);
|
|
657
1424
|
if (directText) {
|
|
@@ -817,6 +1584,68 @@ function requestIdKey(value) {
|
|
|
817
1584
|
return "";
|
|
818
1585
|
}
|
|
819
1586
|
|
|
1587
|
+
function parseToolArguments(rawArguments) {
|
|
1588
|
+
const parsed = safeParseJSON(rawArguments);
|
|
1589
|
+
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {};
|
|
1590
|
+
}
|
|
1591
|
+
|
|
1592
|
+
function resolveToolCommand(toolName, argumentsObject) {
|
|
1593
|
+
if (!isCommandToolName(toolName)) {
|
|
1594
|
+
return toolName;
|
|
1595
|
+
}
|
|
1596
|
+
|
|
1597
|
+
return readString(argumentsObject.cmd)
|
|
1598
|
+
|| readString(argumentsObject.command)
|
|
1599
|
+
|| readString(argumentsObject.raw_command)
|
|
1600
|
+
|| readString(argumentsObject.rawCommand)
|
|
1601
|
+
|| toolName;
|
|
1602
|
+
}
|
|
1603
|
+
|
|
1604
|
+
function resolveToolWorkingDirectory(argumentsObject, item = {}) {
|
|
1605
|
+
return readString(argumentsObject.workdir)
|
|
1606
|
+
|| readString(argumentsObject.cwd)
|
|
1607
|
+
|| readString(argumentsObject.working_directory)
|
|
1608
|
+
|| readString(item?.cwd)
|
|
1609
|
+
|| readString(item?.workdir)
|
|
1610
|
+
|| "";
|
|
1611
|
+
}
|
|
1612
|
+
|
|
1613
|
+
function isCommandToolName(toolName) {
|
|
1614
|
+
const normalized = readString(toolName).toLowerCase();
|
|
1615
|
+
return normalized === "exec_command"
|
|
1616
|
+
|| normalized === "shell_command"
|
|
1617
|
+
|| normalized.endsWith(".exec_command")
|
|
1618
|
+
|| normalized.endsWith(".shell_command");
|
|
1619
|
+
}
|
|
1620
|
+
|
|
1621
|
+
function genericToolActivityMessage(toolName) {
|
|
1622
|
+
switch (readString(toolName).toLowerCase()) {
|
|
1623
|
+
case "apply_patch":
|
|
1624
|
+
return "Applying patch";
|
|
1625
|
+
case "write_stdin":
|
|
1626
|
+
return "Writing to terminal";
|
|
1627
|
+
case "read_thread_terminal":
|
|
1628
|
+
return "Reading terminal output";
|
|
1629
|
+
default:
|
|
1630
|
+
return `Running ${toolName}`;
|
|
1631
|
+
}
|
|
1632
|
+
}
|
|
1633
|
+
|
|
1634
|
+
function buildSyntheticActivityItemId(kind, threadId, turnId, callId) {
|
|
1635
|
+
return `${kind}:${threadId}:${turnId}:${callId}`;
|
|
1636
|
+
}
|
|
1637
|
+
|
|
1638
|
+
function createDesktopIpcNotification(method, params = {}) {
|
|
1639
|
+
return {
|
|
1640
|
+
method,
|
|
1641
|
+
params: {
|
|
1642
|
+
remodexDesktopMirror: true,
|
|
1643
|
+
remodexDesktopIpcMirror: true,
|
|
1644
|
+
...params,
|
|
1645
|
+
},
|
|
1646
|
+
};
|
|
1647
|
+
}
|
|
1648
|
+
|
|
820
1649
|
function readString(value) {
|
|
821
1650
|
return typeof value === "string" && value.trim() ? value.trim() : "";
|
|
822
1651
|
}
|
|
@@ -843,7 +1672,11 @@ module.exports = {
|
|
|
843
1672
|
applyConversationStateChange,
|
|
844
1673
|
createDesktopIpcActionFollower,
|
|
845
1674
|
desktopFollowerPayloadForResponse,
|
|
1675
|
+
hasActiveDesktopActivityForTurn,
|
|
846
1676
|
projectDesktopAssistantDeltaNotifications,
|
|
1677
|
+
projectDesktopActivityNotifications,
|
|
1678
|
+
projectDesktopTurnCompletedNotifications,
|
|
1679
|
+
projectDesktopUserMessageNotifications,
|
|
847
1680
|
projectPendingDesktopActions,
|
|
848
1681
|
resolveDefaultIpcSocketPath,
|
|
849
1682
|
seedConversationStateFromThreadRead,
|