@makerbi/remodex 1.5.4 → 2.0.0
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 +77 -13
- 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 +837 -73
- 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/git-handler.js +92 -28
- package/src/index.js +4 -2
- package/src/ios-app-compatibility.js +7 -7
- package/src/macos-launch-agent.js +132 -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/qr.js +2 -5
- package/src/rollout-live-mirror.js +331 -20
- package/src/rollout-watch.js +5 -1
- package/src/secure-device-state.js +66 -2
- package/src/secure-transport.js +47 -12
- package/src/session-jsonl-history.js +850 -16
- package/src/voice-handler.js +162 -65
- package/src/workspace-handler.js +327 -14
|
@@ -12,10 +12,12 @@ const {
|
|
|
12
12
|
resolveSessionsRoot,
|
|
13
13
|
} = require("./rollout-watch");
|
|
14
14
|
const { resolveCodexGeneratedImagesRoot } = require("./codex-home");
|
|
15
|
+
const { buildApplyPatchFileChangeItem } = require("./apply-patch-changes");
|
|
15
16
|
|
|
16
17
|
const DEFAULT_POLL_INTERVAL_MS = 700;
|
|
17
18
|
const DEFAULT_LOOKUP_TIMEOUT_MS = 5_000;
|
|
18
19
|
const DEFAULT_IDLE_TIMEOUT_MS = 60_000;
|
|
20
|
+
const DEFAULT_ACTIVITY_HEARTBEAT_MS = 5_000;
|
|
19
21
|
const DESKTOP_RESUME_METHODS = new Set(["thread/read", "thread/resume"]);
|
|
20
22
|
|
|
21
23
|
// Observes desktop-authored rollout files and replays the currently active run as
|
|
@@ -30,6 +32,7 @@ function createRolloutLiveMirrorController({
|
|
|
30
32
|
pollIntervalMs = DEFAULT_POLL_INTERVAL_MS,
|
|
31
33
|
lookupTimeoutMs = DEFAULT_LOOKUP_TIMEOUT_MS,
|
|
32
34
|
idleTimeoutMs = DEFAULT_IDLE_TIMEOUT_MS,
|
|
35
|
+
activityHeartbeatMs = DEFAULT_ACTIVITY_HEARTBEAT_MS,
|
|
33
36
|
} = {}) {
|
|
34
37
|
const mirrorsByThreadId = new Map();
|
|
35
38
|
|
|
@@ -63,6 +66,7 @@ function createRolloutLiveMirrorController({
|
|
|
63
66
|
pollIntervalMs,
|
|
64
67
|
lookupTimeoutMs,
|
|
65
68
|
idleTimeoutMs,
|
|
69
|
+
activityHeartbeatMs,
|
|
66
70
|
onStop() {
|
|
67
71
|
if (mirrorsByThreadId.get(threadId) === mirror) {
|
|
68
72
|
mirrorsByThreadId.delete(threadId);
|
|
@@ -98,6 +102,7 @@ function createThreadRolloutLiveMirror({
|
|
|
98
102
|
pollIntervalMs,
|
|
99
103
|
lookupTimeoutMs,
|
|
100
104
|
idleTimeoutMs,
|
|
105
|
+
activityHeartbeatMs,
|
|
101
106
|
onStop = () => {},
|
|
102
107
|
}) {
|
|
103
108
|
const startedAt = now();
|
|
@@ -108,6 +113,7 @@ function createThreadRolloutLiveMirror({
|
|
|
108
113
|
let lastSize = 0;
|
|
109
114
|
let partialLine = "";
|
|
110
115
|
let lastActivityAt = startedAt;
|
|
116
|
+
let lastHeartbeatAt = 0;
|
|
111
117
|
let didBootstrap = false;
|
|
112
118
|
|
|
113
119
|
const intervalId = setIntervalFn(tick, pollIntervalMs);
|
|
@@ -148,6 +154,7 @@ function createThreadRolloutLiveMirror({
|
|
|
148
154
|
});
|
|
149
155
|
lastSize = fileSize;
|
|
150
156
|
lastActivityAt = currentTime;
|
|
157
|
+
lastHeartbeatAt = currentTime;
|
|
151
158
|
if (state.isDesktopOrigin === false) {
|
|
152
159
|
stop();
|
|
153
160
|
}
|
|
@@ -158,17 +165,37 @@ function createThreadRolloutLiveMirror({
|
|
|
158
165
|
const chunk = readFileSlice(rolloutPath, lastSize, fileSize, fsModule);
|
|
159
166
|
lastSize = fileSize;
|
|
160
167
|
lastActivityAt = currentTime;
|
|
168
|
+
lastHeartbeatAt = currentTime;
|
|
161
169
|
if (!chunk) {
|
|
162
170
|
return;
|
|
163
171
|
}
|
|
164
172
|
|
|
165
|
-
const combined = `${partialLine}${chunk}
|
|
166
|
-
|
|
167
|
-
|
|
173
|
+
const combined = partialLine ? `${partialLine}${chunk}` : chunk;
|
|
174
|
+
let searchStart = 0;
|
|
175
|
+
let nlIndex;
|
|
176
|
+
const lines = [];
|
|
177
|
+
while ((nlIndex = combined.indexOf("\n", searchStart)) !== -1) {
|
|
178
|
+
lines.push(combined.substring(searchStart, nlIndex));
|
|
179
|
+
searchStart = nlIndex + 1;
|
|
180
|
+
}
|
|
181
|
+
partialLine = searchStart < combined.length ? combined.substring(searchStart) : "";
|
|
168
182
|
processRolloutLines(lines, state, sendApplicationResponse);
|
|
169
183
|
return;
|
|
170
184
|
}
|
|
171
185
|
|
|
186
|
+
if (
|
|
187
|
+
state.isDesktopOrigin !== false
|
|
188
|
+
&& state.activeTurnId
|
|
189
|
+
&& currentTime - lastHeartbeatAt >= activityHeartbeatMs
|
|
190
|
+
) {
|
|
191
|
+
lastHeartbeatAt = currentTime;
|
|
192
|
+
sendApplicationResponse(JSON.stringify(createNotification("turn/activity", {
|
|
193
|
+
threadId: state.threadId,
|
|
194
|
+
turnId: state.activeTurnId,
|
|
195
|
+
id: state.activeTurnId,
|
|
196
|
+
})));
|
|
197
|
+
}
|
|
198
|
+
|
|
172
199
|
if (currentTime - lastActivityAt >= idleTimeoutMs) {
|
|
173
200
|
stop();
|
|
174
201
|
}
|
|
@@ -269,9 +296,6 @@ function bootstrapFromExistingRollout({
|
|
|
269
296
|
}
|
|
270
297
|
|
|
271
298
|
state.isDesktopOrigin = true;
|
|
272
|
-
if (activeTurnId) {
|
|
273
|
-
state.activeTurnId = activeTurnId;
|
|
274
|
-
}
|
|
275
299
|
processRolloutLines(activeRunLines, state, sendApplicationResponse);
|
|
276
300
|
}
|
|
277
301
|
|
|
@@ -320,21 +344,25 @@ function synthesizeNotificationsFromRolloutEntry(entry, state) {
|
|
|
320
344
|
const eventType = readString(payload.type);
|
|
321
345
|
|
|
322
346
|
if (eventType === "task_started") {
|
|
323
|
-
const
|
|
324
|
-
|
|
325
|
-
return [];
|
|
326
|
-
}
|
|
327
|
-
|
|
347
|
+
const explicitTurnId = readString(payload.turn_id) || readString(payload.turnId);
|
|
348
|
+
const turnId = explicitTurnId || buildSyntheticTurnId(state, entry);
|
|
328
349
|
state.activeTurnId = turnId;
|
|
350
|
+
state.activeTurnIdIsSynthetic = !explicitTurnId;
|
|
329
351
|
state.reasoningItemId = buildSyntheticItemId("thinking", state.threadId, turnId);
|
|
330
352
|
state.hasThinking = false;
|
|
331
353
|
state.commandCalls.clear();
|
|
354
|
+
state.applyPatchCalls.clear();
|
|
355
|
+
state.emittedPatchApplyEndCalls.clear();
|
|
332
356
|
|
|
333
|
-
|
|
357
|
+
const startedParams = {
|
|
334
358
|
threadId: state.threadId,
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
}
|
|
359
|
+
remodexDesktopMirror: true,
|
|
360
|
+
remodexRolloutLiveMirror: true,
|
|
361
|
+
};
|
|
362
|
+
startedParams.turnId = turnId;
|
|
363
|
+
startedParams.id = turnId;
|
|
364
|
+
notifications.push(createNotification("turn/started", startedParams));
|
|
365
|
+
notifications.push(...flushPendingUserMessageNotifications(state, turnId));
|
|
338
366
|
notifications.push(...ensureThinkingNotifications(state));
|
|
339
367
|
return notifications;
|
|
340
368
|
}
|
|
@@ -345,20 +373,32 @@ function synthesizeNotificationsFromRolloutEntry(entry, state) {
|
|
|
345
373
|
return [];
|
|
346
374
|
}
|
|
347
375
|
|
|
376
|
+
const turnId = resolveRolloutEventTurnId(state, payload);
|
|
377
|
+
if (!turnId) {
|
|
378
|
+
state.pendingUserMessages.push({
|
|
379
|
+
id: readString(payload.id),
|
|
380
|
+
message,
|
|
381
|
+
timestamp: readUserMessageTimestamp(entry, payload),
|
|
382
|
+
});
|
|
383
|
+
return [];
|
|
384
|
+
}
|
|
385
|
+
|
|
348
386
|
notifications.push(createNotification("codex/event/user_message", {
|
|
349
387
|
threadId: state.threadId,
|
|
350
|
-
turnId
|
|
388
|
+
turnId,
|
|
351
389
|
message,
|
|
390
|
+
...timestampParams(readUserMessageTimestamp(entry, payload)),
|
|
352
391
|
}));
|
|
353
392
|
return notifications;
|
|
354
393
|
}
|
|
355
394
|
|
|
356
395
|
if (eventType === "task_complete") {
|
|
357
|
-
const turnId =
|
|
396
|
+
const turnId = resolveRolloutEventTurnId(state, payload);
|
|
358
397
|
if (!turnId) {
|
|
359
398
|
return [];
|
|
360
399
|
}
|
|
361
400
|
|
|
401
|
+
notifications.push(...turnFileChangeSnapshotNotifications(state, turnId));
|
|
362
402
|
notifications.push(createNotification("turn/completed", {
|
|
363
403
|
threadId: state.threadId,
|
|
364
404
|
turnId,
|
|
@@ -368,6 +408,11 @@ function synthesizeNotificationsFromRolloutEntry(entry, state) {
|
|
|
368
408
|
return notifications;
|
|
369
409
|
}
|
|
370
410
|
|
|
411
|
+
if (eventType === "item_completed") {
|
|
412
|
+
notifications.push(...itemCompletedNotifications(state, payload));
|
|
413
|
+
return notifications;
|
|
414
|
+
}
|
|
415
|
+
|
|
371
416
|
if (eventType === "agent_reasoning") {
|
|
372
417
|
notifications.push(...reasoningNotifications(state, firstNonEmptyString([
|
|
373
418
|
readString(payload.message),
|
|
@@ -382,7 +427,7 @@ function synthesizeNotificationsFromRolloutEntry(entry, state) {
|
|
|
382
427
|
if (!message || !shouldMirrorAgentMessage(payload)) {
|
|
383
428
|
return [];
|
|
384
429
|
}
|
|
385
|
-
const turnId =
|
|
430
|
+
const turnId = resolveRolloutEventTurnId(state, payload);
|
|
386
431
|
|
|
387
432
|
notifications.push(createNotification("codex/event/agent_message", {
|
|
388
433
|
threadId: state.threadId,
|
|
@@ -400,6 +445,11 @@ function synthesizeNotificationsFromRolloutEntry(entry, state) {
|
|
|
400
445
|
return notifications;
|
|
401
446
|
}
|
|
402
447
|
|
|
448
|
+
if (eventType === "patch_apply_end") {
|
|
449
|
+
notifications.push(...patchApplyEndNotifications(state, payload));
|
|
450
|
+
return notifications;
|
|
451
|
+
}
|
|
452
|
+
|
|
403
453
|
return [];
|
|
404
454
|
}
|
|
405
455
|
|
|
@@ -420,6 +470,11 @@ function synthesizeNotificationsFromRolloutEntry(entry, state) {
|
|
|
420
470
|
return notifications;
|
|
421
471
|
}
|
|
422
472
|
|
|
473
|
+
if (itemType === "customtoolcall") {
|
|
474
|
+
notifications.push(...customToolStartNotifications(state, payload));
|
|
475
|
+
return notifications;
|
|
476
|
+
}
|
|
477
|
+
|
|
423
478
|
if (itemType === "functioncalloutput") {
|
|
424
479
|
notifications.push(...toolOutputNotifications(state, payload));
|
|
425
480
|
return notifications;
|
|
@@ -466,6 +521,13 @@ function toolStartNotifications(state, payload) {
|
|
|
466
521
|
}
|
|
467
522
|
|
|
468
523
|
const argumentsObject = parseToolArguments(payload.arguments);
|
|
524
|
+
if (isInternalProgressPlanToolName(toolName)) {
|
|
525
|
+
return [
|
|
526
|
+
...ensureThinkingNotifications(state),
|
|
527
|
+
...planUpdateNotifications(state, argumentsObject),
|
|
528
|
+
];
|
|
529
|
+
}
|
|
530
|
+
|
|
469
531
|
state.commandCalls.set(callId, {
|
|
470
532
|
toolName,
|
|
471
533
|
command: resolveToolCommand(toolName, argumentsObject),
|
|
@@ -503,6 +565,114 @@ function toolStartNotifications(state, payload) {
|
|
|
503
565
|
];
|
|
504
566
|
}
|
|
505
567
|
|
|
568
|
+
function customToolStartNotifications(state, payload) {
|
|
569
|
+
if (!state.activeTurnId) {
|
|
570
|
+
return [];
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
const callId = readString(payload.call_id) || readString(payload.callId);
|
|
574
|
+
const toolName = readString(payload.name);
|
|
575
|
+
if (!callId || !toolName) {
|
|
576
|
+
return [];
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
const notifications = [...ensureThinkingNotifications(state)];
|
|
580
|
+
if (toolName === "apply_patch") {
|
|
581
|
+
const item = buildApplyPatchFileChangeItem({
|
|
582
|
+
callId,
|
|
583
|
+
patch: readString(payload.input),
|
|
584
|
+
status: readString(payload.status) || "completed",
|
|
585
|
+
idFallback: buildSyntheticItemId("file-change", state.threadId, state.activeTurnId, callId),
|
|
586
|
+
});
|
|
587
|
+
if (item) {
|
|
588
|
+
state.applyPatchCalls.set(callId, item);
|
|
589
|
+
notifications.push(createNotification("codex/event/patch_apply_begin", {
|
|
590
|
+
threadId: state.threadId,
|
|
591
|
+
turnId: state.activeTurnId,
|
|
592
|
+
id: state.activeTurnId,
|
|
593
|
+
call_id: callId,
|
|
594
|
+
itemId: item.id,
|
|
595
|
+
status: "inProgress",
|
|
596
|
+
changes: item.changes,
|
|
597
|
+
}));
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
const activityMessage = genericToolActivityMessage(toolName);
|
|
602
|
+
if (!activityMessage) {
|
|
603
|
+
return notifications;
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
return [
|
|
607
|
+
...notifications,
|
|
608
|
+
createNotification("codex/event/background_event", {
|
|
609
|
+
threadId: state.threadId,
|
|
610
|
+
turnId: state.activeTurnId,
|
|
611
|
+
call_id: callId,
|
|
612
|
+
message: activityMessage,
|
|
613
|
+
}),
|
|
614
|
+
];
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
function patchApplyEndNotifications(state, payload) {
|
|
618
|
+
const turnId = resolveRolloutEventTurnId(state, payload);
|
|
619
|
+
const callId = readString(payload.call_id) || readString(payload.callId);
|
|
620
|
+
if (!turnId || !callId || state.emittedPatchApplyEndCalls.has(callId)) {
|
|
621
|
+
return [];
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
const fileChangeItem = state.applyPatchCalls.get(callId);
|
|
625
|
+
const changes = Array.isArray(payload.changes)
|
|
626
|
+
? payload.changes
|
|
627
|
+
: fileChangeItem?.changes || [];
|
|
628
|
+
if (changes.length === 0) {
|
|
629
|
+
return [];
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
state.emittedPatchApplyEndCalls.add(callId);
|
|
633
|
+
return [
|
|
634
|
+
...ensureThinkingNotifications(state),
|
|
635
|
+
createNotification("codex/event/patch_apply_end", {
|
|
636
|
+
threadId: state.threadId,
|
|
637
|
+
turnId,
|
|
638
|
+
id: turnId,
|
|
639
|
+
call_id: callId,
|
|
640
|
+
itemId: fileChangeItem?.id || callId,
|
|
641
|
+
status: readString(payload.status) || fileChangeItem?.status || "completed",
|
|
642
|
+
success: payload.success !== false,
|
|
643
|
+
changes,
|
|
644
|
+
}),
|
|
645
|
+
];
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
function turnFileChangeSnapshotNotifications(state, turnId) {
|
|
649
|
+
const patchEntries = Array.from(state.applyPatchCalls.entries());
|
|
650
|
+
if (!turnId || patchEntries.length === 0) {
|
|
651
|
+
return [];
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
const changes = patchEntries.flatMap(([, item]) => Array.isArray(item?.changes) ? item.changes : []);
|
|
655
|
+
if (changes.length === 0) {
|
|
656
|
+
return [];
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
const [lastCallId, lastItem] = patchEntries[patchEntries.length - 1];
|
|
660
|
+
const itemId = readString(lastItem?.id) || readString(lastCallId) || buildSyntheticItemId("file-change", state.threadId, turnId);
|
|
661
|
+
return [
|
|
662
|
+
createNotification("codex/event/patch_apply_end", {
|
|
663
|
+
threadId: state.threadId,
|
|
664
|
+
turnId,
|
|
665
|
+
id: turnId,
|
|
666
|
+
call_id: itemId,
|
|
667
|
+
itemId,
|
|
668
|
+
status: "completed",
|
|
669
|
+
success: true,
|
|
670
|
+
changes,
|
|
671
|
+
remodexTurnFileChangeSnapshot: true,
|
|
672
|
+
}),
|
|
673
|
+
];
|
|
674
|
+
}
|
|
675
|
+
|
|
506
676
|
function toolOutputNotifications(state, payload) {
|
|
507
677
|
if (!state.activeTurnId) {
|
|
508
678
|
return [];
|
|
@@ -597,6 +767,28 @@ function imageGenerationNotifications(state, payload, { preferCallId = false } =
|
|
|
597
767
|
];
|
|
598
768
|
}
|
|
599
769
|
|
|
770
|
+
function itemCompletedNotifications(state, payload) {
|
|
771
|
+
const item = payload && typeof payload.item === "object" && !Array.isArray(payload.item)
|
|
772
|
+
? payload.item
|
|
773
|
+
: null;
|
|
774
|
+
if (!item || normalizeRolloutItemType(item.type) !== "plan") {
|
|
775
|
+
return [];
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
const turnId = resolveRolloutEventTurnId(state, payload);
|
|
779
|
+
if (!turnId) {
|
|
780
|
+
return [];
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
return [
|
|
784
|
+
createNotification("item/completed", {
|
|
785
|
+
threadId: state.threadId,
|
|
786
|
+
turnId,
|
|
787
|
+
item,
|
|
788
|
+
}),
|
|
789
|
+
];
|
|
790
|
+
}
|
|
791
|
+
|
|
600
792
|
function ensureThinkingNotifications(state) {
|
|
601
793
|
if (!state.activeTurnId || state.hasThinking) {
|
|
602
794
|
return [];
|
|
@@ -626,6 +818,10 @@ function createMirrorState(threadId) {
|
|
|
626
818
|
reasoningItemId: null,
|
|
627
819
|
hasThinking: false,
|
|
628
820
|
commandCalls: new Map(),
|
|
821
|
+
applyPatchCalls: new Map(),
|
|
822
|
+
emittedPatchApplyEndCalls: new Set(),
|
|
823
|
+
pendingUserMessages: [],
|
|
824
|
+
activeTurnIdIsSynthetic: false,
|
|
629
825
|
};
|
|
630
826
|
}
|
|
631
827
|
|
|
@@ -677,6 +873,58 @@ function parseToolArguments(rawArguments) {
|
|
|
677
873
|
return parsed && typeof parsed === "object" ? parsed : {};
|
|
678
874
|
}
|
|
679
875
|
|
|
876
|
+
function planUpdateNotifications(state, argumentsObject) {
|
|
877
|
+
const plan = normalizeProgressPlanSteps(argumentsObject.plan);
|
|
878
|
+
if (plan.length === 0) {
|
|
879
|
+
return [];
|
|
880
|
+
}
|
|
881
|
+
|
|
882
|
+
const params = {
|
|
883
|
+
threadId: state.threadId,
|
|
884
|
+
turnId: state.activeTurnId,
|
|
885
|
+
plan,
|
|
886
|
+
};
|
|
887
|
+
const explanation = readString(argumentsObject.explanation);
|
|
888
|
+
if (explanation) {
|
|
889
|
+
params.explanation = explanation;
|
|
890
|
+
}
|
|
891
|
+
|
|
892
|
+
return [createNotification("turn/plan/updated", params)];
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
function normalizeProgressPlanSteps(rawPlan) {
|
|
896
|
+
if (!Array.isArray(rawPlan)) {
|
|
897
|
+
return [];
|
|
898
|
+
}
|
|
899
|
+
|
|
900
|
+
return rawPlan.flatMap((rawStep) => {
|
|
901
|
+
if (!rawStep || typeof rawStep !== "object") {
|
|
902
|
+
return [];
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
const step = readString(rawStep.step);
|
|
906
|
+
const status = normalizeProgressPlanStatus(rawStep.status);
|
|
907
|
+
if (!step || !status) {
|
|
908
|
+
return [];
|
|
909
|
+
}
|
|
910
|
+
|
|
911
|
+
return [{ step, status }];
|
|
912
|
+
});
|
|
913
|
+
}
|
|
914
|
+
|
|
915
|
+
function normalizeProgressPlanStatus(rawStatus) {
|
|
916
|
+
const normalized = readString(rawStatus);
|
|
917
|
+
switch (normalized) {
|
|
918
|
+
case "pending":
|
|
919
|
+
case "in_progress":
|
|
920
|
+
case "inProgress":
|
|
921
|
+
case "completed":
|
|
922
|
+
return normalized;
|
|
923
|
+
default:
|
|
924
|
+
return "";
|
|
925
|
+
}
|
|
926
|
+
}
|
|
927
|
+
|
|
680
928
|
function resolveToolCommand(toolName, argumentsObject) {
|
|
681
929
|
if (isCommandToolName(toolName)) {
|
|
682
930
|
return firstNonEmptyString([
|
|
@@ -704,6 +952,10 @@ function isCommandToolName(toolName) {
|
|
|
704
952
|
return normalized === "exec_command" || normalized === "shell_command";
|
|
705
953
|
}
|
|
706
954
|
|
|
955
|
+
function isInternalProgressPlanToolName(toolName) {
|
|
956
|
+
return readString(toolName).toLowerCase() === "update_plan";
|
|
957
|
+
}
|
|
958
|
+
|
|
707
959
|
function genericToolActivityMessage(toolName) {
|
|
708
960
|
switch (readString(toolName).toLowerCase()) {
|
|
709
961
|
case "apply_patch":
|
|
@@ -722,8 +974,51 @@ function shouldMirrorAgentMessage(payload) {
|
|
|
722
974
|
return phase !== "commentary";
|
|
723
975
|
}
|
|
724
976
|
|
|
725
|
-
function createNotification(method, params) {
|
|
726
|
-
|
|
977
|
+
function createNotification(method, params = {}) {
|
|
978
|
+
if (!params || typeof params !== "object" || Array.isArray(params)) {
|
|
979
|
+
return { method, params };
|
|
980
|
+
}
|
|
981
|
+
|
|
982
|
+
return {
|
|
983
|
+
method,
|
|
984
|
+
params: {
|
|
985
|
+
remodexDesktopMirror: true,
|
|
986
|
+
remodexRolloutLiveMirror: true,
|
|
987
|
+
...params,
|
|
988
|
+
},
|
|
989
|
+
};
|
|
990
|
+
}
|
|
991
|
+
|
|
992
|
+
function flushPendingUserMessageNotifications(state, turnId) {
|
|
993
|
+
const messages = state.pendingUserMessages.splice(0);
|
|
994
|
+
if (messages.length === 0) {
|
|
995
|
+
return [];
|
|
996
|
+
}
|
|
997
|
+
|
|
998
|
+
return messages.map((pending) => createNotification("codex/event/user_message", {
|
|
999
|
+
threadId: state.threadId,
|
|
1000
|
+
turnId: turnId || state.activeTurnId || "",
|
|
1001
|
+
message: pending.message,
|
|
1002
|
+
...(pending.id ? { id: pending.id } : {}),
|
|
1003
|
+
...timestampParams(pending.timestamp),
|
|
1004
|
+
}));
|
|
1005
|
+
}
|
|
1006
|
+
|
|
1007
|
+
function readUserMessageTimestamp(entry, payload = {}) {
|
|
1008
|
+
return firstNonEmptyString([
|
|
1009
|
+
readString(payload.createdAt),
|
|
1010
|
+
readString(payload.created_at),
|
|
1011
|
+
readString(payload.timestamp),
|
|
1012
|
+
readString(payload.time),
|
|
1013
|
+
readString(entry?.timestamp),
|
|
1014
|
+
]);
|
|
1015
|
+
}
|
|
1016
|
+
|
|
1017
|
+
function timestampParams(timestamp) {
|
|
1018
|
+
const normalizedTimestamp = readString(timestamp);
|
|
1019
|
+
return normalizedTimestamp
|
|
1020
|
+
? { createdAt: normalizedTimestamp, timestamp: normalizedTimestamp }
|
|
1021
|
+
: {};
|
|
727
1022
|
}
|
|
728
1023
|
|
|
729
1024
|
function buildSyntheticItemId(kind, threadId, turnId, suffix = "") {
|
|
@@ -731,6 +1026,18 @@ function buildSyntheticItemId(kind, threadId, turnId, suffix = "") {
|
|
|
731
1026
|
return `rollout-${kind}:${threadId}:${turnId}${suffixPart}`;
|
|
732
1027
|
}
|
|
733
1028
|
|
|
1029
|
+
function buildSyntheticTurnId(state, entry) {
|
|
1030
|
+
const timestamp = readString(entry?.timestamp) || "unknown";
|
|
1031
|
+
return `rollout-turn:${state.threadId}:${timestamp}`;
|
|
1032
|
+
}
|
|
1033
|
+
|
|
1034
|
+
function resolveRolloutEventTurnId(state, payload = {}) {
|
|
1035
|
+
if (state.activeTurnIdIsSynthetic && state.activeTurnId) {
|
|
1036
|
+
return state.activeTurnId;
|
|
1037
|
+
}
|
|
1038
|
+
return readString(payload.turn_id) || readString(payload.turnId) || state.activeTurnId || "";
|
|
1039
|
+
}
|
|
1040
|
+
|
|
734
1041
|
function buildAgentMessageItemId(threadId, turnId, entry, message) {
|
|
735
1042
|
const timestamp = readString(entry?.timestamp) || "untimed";
|
|
736
1043
|
const messageHash = crypto
|
|
@@ -765,6 +1072,10 @@ function resetRunState(state) {
|
|
|
765
1072
|
state.reasoningItemId = null;
|
|
766
1073
|
state.hasThinking = false;
|
|
767
1074
|
state.commandCalls.clear();
|
|
1075
|
+
state.applyPatchCalls.clear();
|
|
1076
|
+
state.emittedPatchApplyEndCalls.clear();
|
|
1077
|
+
state.pendingUserMessages.length = 0;
|
|
1078
|
+
state.activeTurnIdIsSynthetic = false;
|
|
768
1079
|
}
|
|
769
1080
|
|
|
770
1081
|
function readThreadId(params) {
|
package/src/rollout-watch.js
CHANGED
|
@@ -498,7 +498,11 @@ function collectRecentRolloutFiles(
|
|
|
498
498
|
}
|
|
499
499
|
}
|
|
500
500
|
|
|
501
|
-
candidates.sort(
|
|
501
|
+
candidates.sort((lhs, rhs) =>
|
|
502
|
+
(rhs.mtimeMs - lhs.mtimeMs)
|
|
503
|
+
|| path.basename(rhs.filePath).localeCompare(path.basename(lhs.filePath))
|
|
504
|
+
|| rhs.filePath.localeCompare(lhs.filePath)
|
|
505
|
+
);
|
|
502
506
|
return candidates.slice(0, candidateLimit);
|
|
503
507
|
}
|
|
504
508
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// FILE: secure-device-state.js
|
|
2
|
-
// Purpose: Persists canonical bridge identity, trusted
|
|
2
|
+
// Purpose: Persists canonical bridge identity, trusted mobile state, and last seen companion metadata for local QR pairing.
|
|
3
3
|
// Layer: CLI helper
|
|
4
|
-
// Exports: loadOrCreateBridgeDeviceState, readBridgeDeviceState, resetBridgeDeviceState, rememberTrustedPhone, rememberLastSeenPhoneAppVersion, getTrustedPhonePublicKey, resolveBridgeRelaySession
|
|
4
|
+
// Exports: loadOrCreateBridgeDeviceState, readBridgeDeviceState, resetBridgeDeviceState, resetBridgeTrustState, rememberTrustedPhone, rememberLastSeenPhoneAppVersion, rememberLastSeenClientDeviceKind, getTrustedPhonePublicKey, resolveBridgeRelaySession
|
|
5
5
|
// Depends on: fs, os, path, crypto, child_process
|
|
6
6
|
|
|
7
7
|
const fs = require("fs");
|
|
@@ -83,6 +83,30 @@ function resetBridgeDeviceState() {
|
|
|
83
83
|
};
|
|
84
84
|
}
|
|
85
85
|
|
|
86
|
+
// Clears trusted phones while preserving the Mac's stable identity across re-pairing.
|
|
87
|
+
function resetBridgeTrustState() {
|
|
88
|
+
const existingState = readBridgeDeviceState();
|
|
89
|
+
if (!existingState) {
|
|
90
|
+
return {
|
|
91
|
+
hadState: false,
|
|
92
|
+
preservedMacIdentity: false,
|
|
93
|
+
clearedTrustedPhones: false,
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const nextState = normalizeBridgeDeviceState({
|
|
98
|
+
...existingState,
|
|
99
|
+
trustedPhones: {},
|
|
100
|
+
});
|
|
101
|
+
const clearedTrustedPhones = Object.keys(existingState.trustedPhones || {}).length > 0;
|
|
102
|
+
writeBridgeDeviceState(nextState);
|
|
103
|
+
return {
|
|
104
|
+
hadState: true,
|
|
105
|
+
preservedMacIdentity: true,
|
|
106
|
+
clearedTrustedPhones,
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
|
|
86
110
|
// Generates a fresh relay session for every bridge launch so QR pairing stays explicit per-run.
|
|
87
111
|
function resolveBridgeRelaySession(state, { persist = true } = {}) {
|
|
88
112
|
return {
|
|
@@ -129,6 +153,22 @@ function rememberLastSeenPhoneAppVersion(state, phoneAppVersion, { persist = tru
|
|
|
129
153
|
return nextState;
|
|
130
154
|
}
|
|
131
155
|
|
|
156
|
+
function rememberLastSeenClientDeviceKind(state, deviceKind, { persist = true } = {}) {
|
|
157
|
+
const normalizedDeviceKind = normalizeDeviceKind(deviceKind);
|
|
158
|
+
if (!normalizedDeviceKind) {
|
|
159
|
+
return state;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const nextState = normalizeBridgeDeviceState({
|
|
163
|
+
...state,
|
|
164
|
+
lastSeenDeviceKind: normalizedDeviceKind,
|
|
165
|
+
});
|
|
166
|
+
if (persist) {
|
|
167
|
+
writeBridgeDeviceState(nextState);
|
|
168
|
+
}
|
|
169
|
+
return nextState;
|
|
170
|
+
}
|
|
171
|
+
|
|
132
172
|
function getTrustedPhonePublicKey(state, phoneDeviceId) {
|
|
133
173
|
const normalizedDeviceId = normalizeNonEmptyString(phoneDeviceId);
|
|
134
174
|
if (!normalizedDeviceId) {
|
|
@@ -152,6 +192,7 @@ function createBridgeDeviceState() {
|
|
|
152
192
|
macIdentityPublicKey: base64UrlToBase64(publicJwk.x),
|
|
153
193
|
macIdentityPrivateKey: base64UrlToBase64(privateJwk.d),
|
|
154
194
|
trustedPhones: {},
|
|
195
|
+
lastSeenDeviceKind: null,
|
|
155
196
|
lastSeenPhoneAppVersion: null,
|
|
156
197
|
};
|
|
157
198
|
}
|
|
@@ -361,6 +402,8 @@ function normalizeBridgeDeviceState(rawState) {
|
|
|
361
402
|
const macIdentityPublicKey = normalizeNonEmptyString(rawState?.macIdentityPublicKey);
|
|
362
403
|
const macIdentityPrivateKey = normalizeNonEmptyString(rawState?.macIdentityPrivateKey);
|
|
363
404
|
const lastSeenPhoneAppVersion = normalizeNonEmptyString(rawState?.lastSeenPhoneAppVersion) || null;
|
|
405
|
+
const lastSeenDeviceKind = normalizeDeviceKind(rawState?.lastSeenDeviceKind)
|
|
406
|
+
|| inferLegacyDeviceKind({ lastSeenPhoneAppVersion });
|
|
364
407
|
|
|
365
408
|
if (!macDeviceId || !macIdentityPublicKey || !macIdentityPrivateKey) {
|
|
366
409
|
throw new Error("Bridge device state is incomplete");
|
|
@@ -384,6 +427,7 @@ function normalizeBridgeDeviceState(rawState) {
|
|
|
384
427
|
macIdentityPublicKey,
|
|
385
428
|
macIdentityPrivateKey,
|
|
386
429
|
trustedPhones,
|
|
430
|
+
lastSeenDeviceKind,
|
|
387
431
|
lastSeenPhoneAppVersion,
|
|
388
432
|
};
|
|
389
433
|
}
|
|
@@ -406,6 +450,24 @@ function recoverBridgeDeviceIdentity(state, { fallbackState = null } = {}) {
|
|
|
406
450
|
return nextState;
|
|
407
451
|
}
|
|
408
452
|
|
|
453
|
+
function normalizeDeviceKind(value) {
|
|
454
|
+
const normalized = normalizeNonEmptyString(value).toLowerCase();
|
|
455
|
+
if (normalized === "ios" || normalized === "iphone") {
|
|
456
|
+
return "iphone";
|
|
457
|
+
}
|
|
458
|
+
if (normalized === "android") {
|
|
459
|
+
return "android";
|
|
460
|
+
}
|
|
461
|
+
if (normalized === "mac" || normalized === "macos" || normalized === "darwin") {
|
|
462
|
+
return "mac";
|
|
463
|
+
}
|
|
464
|
+
return normalized || null;
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
function inferLegacyDeviceKind({ lastSeenPhoneAppVersion } = {}) {
|
|
468
|
+
return lastSeenPhoneAppVersion ? "iphone" : null;
|
|
469
|
+
}
|
|
470
|
+
|
|
409
471
|
function bridgeStatesEqual(left, right) {
|
|
410
472
|
return JSON.stringify(left) === JSON.stringify(right);
|
|
411
473
|
}
|
|
@@ -451,8 +513,10 @@ module.exports = {
|
|
|
451
513
|
getTrustedPhonePublicKey,
|
|
452
514
|
loadOrCreateBridgeDeviceState,
|
|
453
515
|
readBridgeDeviceState,
|
|
516
|
+
rememberLastSeenClientDeviceKind,
|
|
454
517
|
rememberLastSeenPhoneAppVersion,
|
|
455
518
|
rememberTrustedPhone,
|
|
456
519
|
resetBridgeDeviceState,
|
|
520
|
+
resetBridgeTrustState,
|
|
457
521
|
resolveBridgeRelaySession,
|
|
458
522
|
};
|