@makerbi/remodex 2.0.1 → 2.3.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/package.json +2 -2
- package/src/account-status.js +5 -4
- package/src/bridge.js +1813 -670
- package/src/codex-desktop-refresher.js +35 -7
- package/src/cursor-acp-client.js +242 -0
- package/src/cursor-models.js +134 -0
- package/src/cursor-provider.js +1197 -0
- package/src/desktop-ipc-action-follower.js +2331 -964
- package/src/desktop-ipc-conversation-adapter.js +1132 -0
- package/src/desktop-ipc-conversation-projector.js +1169 -0
- package/src/desktop-ipc-live-owner.js +1790 -0
- package/src/desktop-ipc-owner-transport.js +750 -0
- package/src/desktop-ipc-shared.js +473 -0
- package/src/desktop-ipc-state-patches.js +218 -0
- package/src/opencode-models.js +108 -0
- package/src/opencode-provider.js +1151 -0
- package/src/project-handler.js +50 -7
- package/src/project-registry.js +466 -0
- package/src/push-notification-tracker.js +4 -4
- package/src/rollout-live-mirror.js +930 -218
- package/src/rollout-turn-semantics.js +20 -0
- package/src/runtime-provider-models.js +164 -0
- package/src/runtime-provider-router.js +365 -0
- package/src/scripts/codex-refresh.applescript +26 -15
- package/src/secure-transport.js +204 -9
- package/src/session-jsonl-history.js +429 -39
- package/src/thread-context-handler.js +8 -6
- package/src/thread-runtime-settings-store.js +247 -0
- package/src/voice-audio.js +344 -0
- package/src/voice-handler.js +363 -173
package/src/secure-transport.js
CHANGED
|
@@ -23,7 +23,7 @@ const {
|
|
|
23
23
|
} = require("./secure-device-state");
|
|
24
24
|
|
|
25
25
|
const PAIRING_QR_VERSION = 2;
|
|
26
|
-
const SECURE_PROTOCOL_VERSION =
|
|
26
|
+
const SECURE_PROTOCOL_VERSION = 2;
|
|
27
27
|
const HANDSHAKE_TAG = "remodex-e2ee-v1";
|
|
28
28
|
const HANDSHAKE_MODE_QR_BOOTSTRAP = "qr_bootstrap";
|
|
29
29
|
const HANDSHAKE_MODE_TRUSTED_RECONNECT = "trusted_reconnect";
|
|
@@ -52,6 +52,9 @@ function createBridgeSecureTransport({
|
|
|
52
52
|
let lastRelayedBridgeOutboundSeq = 0;
|
|
53
53
|
let currentPairingExpiresAt = Date.now() + MAX_PAIRING_AGE_MS;
|
|
54
54
|
let nextKeyEpoch = 1;
|
|
55
|
+
// Sequence numbers restart at 1 with each bridge process. Namespace them so
|
|
56
|
+
// a phone never mistakes new-process seq 1 for old-process seq 1.
|
|
57
|
+
const bridgeReplayEpoch = randomBytes(16).toString("hex");
|
|
55
58
|
let nextBridgeOutboundSeq = 1;
|
|
56
59
|
let outboundBufferBytes = 0;
|
|
57
60
|
const outboundBuffer = [];
|
|
@@ -261,6 +264,7 @@ function createBridgeSecureTransport({
|
|
|
261
264
|
macEphemeralPublicKey: pendingHandshake.macEphemeralPublicKey,
|
|
262
265
|
serverNonce: serverNonce.toString("base64"),
|
|
263
266
|
keyEpoch,
|
|
267
|
+
bridgeReplayEpoch,
|
|
264
268
|
expiresAtForTranscript,
|
|
265
269
|
macSignature,
|
|
266
270
|
clientNonce: clientNonceBase64,
|
|
@@ -410,16 +414,49 @@ function createBridgeSecureTransport({
|
|
|
410
414
|
}
|
|
411
415
|
|
|
412
416
|
const lastAppliedBridgeOutboundSeq = Number(message.lastAppliedBridgeOutboundSeq) || 0;
|
|
413
|
-
|
|
414
|
-
|
|
417
|
+
const phoneReplayEpoch = normalizeNonEmptyString(message.bridgeReplayEpoch);
|
|
418
|
+
let effectiveReplayCursor = lastAppliedBridgeOutboundSeq;
|
|
419
|
+
activeSession.isResumed = true;
|
|
420
|
+
if (phoneReplayEpoch !== bridgeReplayEpoch || lastAppliedBridgeOutboundSeq >= nextBridgeOutboundSeq) {
|
|
421
|
+
// Sequence numbers are process-local. A trusted phone can reconnect after
|
|
422
|
+
// the bridge restarted with any cursor from the previous process; the
|
|
423
|
+
// explicit epoch catches overlap that numeric comparison cannot detect.
|
|
424
|
+
effectiveReplayCursor = Math.max(0, activeSession.firstOutboundSeq - 1);
|
|
425
|
+
sendBufferedReplayResetMarker(
|
|
426
|
+
activeSession.sendWireMessage,
|
|
427
|
+
effectiveReplayCursor,
|
|
428
|
+
bridgeReplayEpoch
|
|
429
|
+
);
|
|
430
|
+
}
|
|
431
|
+
lastRelayedBridgeOutboundSeq = effectiveReplayCursor;
|
|
432
|
+
let missingEntries = replayableOutboundEntries(effectiveReplayCursor, {
|
|
415
433
|
includeCurrentSessionEntries: true,
|
|
416
434
|
});
|
|
417
|
-
|
|
435
|
+
const replayGap = bufferedReplayGapAfter(effectiveReplayCursor);
|
|
436
|
+
if (replayGap) {
|
|
437
|
+
// A partial historical tail is worse than no replay: item-scoped maps no
|
|
438
|
+
// longer exist after relaunch, so applying it can bind deltas/artifacts to
|
|
439
|
+
// unrelated rows. Tell the phone to advance past the discarded history;
|
|
440
|
+
// canonical thread history will rebuild it deterministically.
|
|
441
|
+
sendBufferedReplayGapMarker(activeSession.sendWireMessage, replayGap);
|
|
442
|
+
sendBufferedReplayCompleteMarker(activeSession.sendWireMessage);
|
|
443
|
+
missingEntries = missingEntries.filter((entry) => (
|
|
444
|
+
entry.bridgeOutboundSeq > replayGap.lastDiscardedBridgeOutboundSeq
|
|
445
|
+
));
|
|
446
|
+
}
|
|
447
|
+
let replayedHistoricalBacklog = false;
|
|
418
448
|
for (const entry of missingEntries) {
|
|
419
|
-
|
|
420
|
-
|
|
449
|
+
const outboundEntry = replayTaggedEntryIfHistorical(entry);
|
|
450
|
+
if (!sendBufferedEntry(outboundEntry, activeSession.sendWireMessage)) {
|
|
451
|
+
return;
|
|
452
|
+
}
|
|
453
|
+
if (outboundEntry !== entry) {
|
|
454
|
+
replayedHistoricalBacklog = true;
|
|
421
455
|
}
|
|
422
456
|
}
|
|
457
|
+
if (replayedHistoricalBacklog) {
|
|
458
|
+
sendBufferedReplayCompleteMarker(activeSession.sendWireMessage);
|
|
459
|
+
}
|
|
423
460
|
}
|
|
424
461
|
|
|
425
462
|
function handleEncryptedEnvelope(message, sendControlMessage, onApplicationMessage) {
|
|
@@ -553,11 +590,169 @@ function createBridgeSecureTransport({
|
|
|
553
590
|
return;
|
|
554
591
|
}
|
|
555
592
|
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
593
|
+
let replayEntries = replayableOutboundEntries(lastRelayedBridgeOutboundSeq);
|
|
594
|
+
const replayGap = bufferedReplayGapAfter(lastRelayedBridgeOutboundSeq);
|
|
595
|
+
if (replayGap) {
|
|
596
|
+
sendBufferedReplayGapMarker(activeSession.sendWireMessage, replayGap);
|
|
597
|
+
sendBufferedReplayCompleteMarker(activeSession.sendWireMessage);
|
|
598
|
+
replayEntries = replayEntries.filter((entry) => (
|
|
599
|
+
entry.bridgeOutboundSeq > replayGap.lastDiscardedBridgeOutboundSeq
|
|
600
|
+
));
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
let replayedHistoricalBacklog = false;
|
|
604
|
+
for (const entry of replayEntries) {
|
|
605
|
+
const outboundEntry = replayTaggedEntryIfHistorical(entry);
|
|
606
|
+
if (!sendBufferedEntry(outboundEntry, activeSession.sendWireMessage)) {
|
|
607
|
+
return;
|
|
608
|
+
}
|
|
609
|
+
if (outboundEntry !== entry) {
|
|
610
|
+
replayedHistoricalBacklog = true;
|
|
559
611
|
}
|
|
560
612
|
}
|
|
613
|
+
if (replayedHistoricalBacklog) {
|
|
614
|
+
sendBufferedReplayCompleteMarker(activeSession.sendWireMessage);
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
function bufferedReplayGapAfter(lastAppliedBridgeOutboundSeq) {
|
|
619
|
+
const firstUnappliedEntry = outboundBuffer.find((entry) => (
|
|
620
|
+
entry.bridgeOutboundSeq > lastAppliedBridgeOutboundSeq
|
|
621
|
+
));
|
|
622
|
+
if (!firstUnappliedEntry
|
|
623
|
+
|| firstUnappliedEntry.bridgeOutboundSeq <= lastAppliedBridgeOutboundSeq + 1) {
|
|
624
|
+
return null;
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
const lastAvailableBridgeOutboundSeq = outboundBuffer[outboundBuffer.length - 1]?.bridgeOutboundSeq
|
|
628
|
+
|| firstUnappliedEntry.bridgeOutboundSeq;
|
|
629
|
+
const historicalBoundary = (activeSession?.firstOutboundSeq || firstUnappliedEntry.bridgeOutboundSeq) - 1;
|
|
630
|
+
const lastDiscardedBridgeOutboundSeq = firstUnappliedEntry.bridgeOutboundSeq <= historicalBoundary
|
|
631
|
+
? historicalBoundary
|
|
632
|
+
: lastAvailableBridgeOutboundSeq;
|
|
633
|
+
return {
|
|
634
|
+
expectedBridgeOutboundSeq: lastAppliedBridgeOutboundSeq + 1,
|
|
635
|
+
firstAvailableBridgeOutboundSeq: firstUnappliedEntry.bridgeOutboundSeq,
|
|
636
|
+
lastDiscardedBridgeOutboundSeq: Math.max(lastAppliedBridgeOutboundSeq, lastDiscardedBridgeOutboundSeq),
|
|
637
|
+
};
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
function sendBufferedReplayGapMarker(sendWireMessage, replayGap) {
|
|
641
|
+
if (!activeSession?.isResumed || typeof sendWireMessage !== "function" || !replayGap) {
|
|
642
|
+
return;
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
const envelope = encryptEnvelopePayload(
|
|
646
|
+
{
|
|
647
|
+
payloadText: JSON.stringify({
|
|
648
|
+
method: "remodex/bufferedReplay/gap",
|
|
649
|
+
params: {
|
|
650
|
+
remodexBufferedReplayGap: true,
|
|
651
|
+
...replayGap,
|
|
652
|
+
},
|
|
653
|
+
}),
|
|
654
|
+
},
|
|
655
|
+
activeSession.macToPhoneKey,
|
|
656
|
+
SECURE_SENDER_MAC,
|
|
657
|
+
activeSession.nextOutboundCounter,
|
|
658
|
+
sessionId,
|
|
659
|
+
activeSession.keyEpoch
|
|
660
|
+
);
|
|
661
|
+
activeSession.nextOutboundCounter += 1;
|
|
662
|
+
sendWireMessage(JSON.stringify(envelope));
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
function sendBufferedReplayResetMarker(
|
|
666
|
+
sendWireMessage,
|
|
667
|
+
resetBridgeOutboundSeqTo,
|
|
668
|
+
replayEpoch
|
|
669
|
+
) {
|
|
670
|
+
if (!activeSession?.isResumed || typeof sendWireMessage !== "function") {
|
|
671
|
+
return;
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
const envelope = encryptEnvelopePayload(
|
|
675
|
+
{
|
|
676
|
+
payloadText: JSON.stringify({
|
|
677
|
+
method: "remodex/bufferedReplay/reset",
|
|
678
|
+
params: {
|
|
679
|
+
remodexBufferedReplayReset: true,
|
|
680
|
+
resetBridgeOutboundSeqTo,
|
|
681
|
+
bridgeReplayEpoch: replayEpoch,
|
|
682
|
+
},
|
|
683
|
+
}),
|
|
684
|
+
},
|
|
685
|
+
activeSession.macToPhoneKey,
|
|
686
|
+
SECURE_SENDER_MAC,
|
|
687
|
+
activeSession.nextOutboundCounter,
|
|
688
|
+
sessionId,
|
|
689
|
+
activeSession.keyEpoch
|
|
690
|
+
);
|
|
691
|
+
activeSession.nextOutboundCounter += 1;
|
|
692
|
+
sendWireMessage(JSON.stringify(envelope));
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
// Closes a replayed-backlog burst deterministically: the phone batches tagged
|
|
696
|
+
// catch-up events and settles its timeline once on this marker instead of
|
|
697
|
+
// waiting out a debounce. Sent transiently (no bridgeOutboundSeq, never
|
|
698
|
+
// buffered) so it cannot occupy replay-buffer space or be replayed itself.
|
|
699
|
+
function sendBufferedReplayCompleteMarker(sendWireMessage) {
|
|
700
|
+
if (!activeSession?.isResumed || typeof sendWireMessage !== "function") {
|
|
701
|
+
return;
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
const envelope = encryptEnvelopePayload(
|
|
705
|
+
{
|
|
706
|
+
payloadText: JSON.stringify({
|
|
707
|
+
method: "remodex/bufferedReplay/completed",
|
|
708
|
+
params: { remodexBufferedReplayComplete: true },
|
|
709
|
+
}),
|
|
710
|
+
},
|
|
711
|
+
activeSession.macToPhoneKey,
|
|
712
|
+
SECURE_SENDER_MAC,
|
|
713
|
+
activeSession.nextOutboundCounter,
|
|
714
|
+
sessionId,
|
|
715
|
+
activeSession.keyEpoch
|
|
716
|
+
);
|
|
717
|
+
activeSession.nextOutboundCounter += 1;
|
|
718
|
+
sendWireMessage(JSON.stringify(envelope));
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
// Only prior secure-session backlog is catch-up history; same-session retries
|
|
722
|
+
// may be the phone's first delivery of a still-live turn.
|
|
723
|
+
function replayTaggedEntryIfHistorical(entry) {
|
|
724
|
+
if (
|
|
725
|
+
!activeSession
|
|
726
|
+
|| entry.bridgeOutboundSeq >= activeSession.firstOutboundSeq
|
|
727
|
+
) {
|
|
728
|
+
return entry;
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
return replayTaggedEntry(entry);
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
// Marks replayed notifications so the phone applies them as catch-up content
|
|
735
|
+
// instead of live activity; replay must never revive running/streaming UI.
|
|
736
|
+
// RPC responses (id-bearing) and non-object params pass through untouched.
|
|
737
|
+
function replayTaggedEntry(entry) {
|
|
738
|
+
const parsed = safeParseJSON(entry.payloadText);
|
|
739
|
+
if (
|
|
740
|
+
!parsed
|
|
741
|
+
|| typeof parsed.method !== "string"
|
|
742
|
+
|| parsed.id !== undefined
|
|
743
|
+
|| !parsed.params
|
|
744
|
+
|| typeof parsed.params !== "object"
|
|
745
|
+
|| Array.isArray(parsed.params)
|
|
746
|
+
) {
|
|
747
|
+
return entry;
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
parsed.params.remodexReplayedEvent = true;
|
|
751
|
+
return {
|
|
752
|
+
bridgeOutboundSeq: entry.bridgeOutboundSeq,
|
|
753
|
+
payloadText: JSON.stringify(parsed),
|
|
754
|
+
sizeBytes: entry.sizeBytes,
|
|
755
|
+
};
|
|
561
756
|
}
|
|
562
757
|
|
|
563
758
|
return {
|