muuuuse 6.0.0 → 7.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/package.json +2 -2
- package/src/agents.js +4 -2
- package/src/cli.js +40 -2
- package/src/runtime.js +177 -55
- package/src/util.js +1 -1
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "muuuuse",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "🔌Muuuuse
|
|
3
|
+
"version": "7.0.1",
|
|
4
|
+
"description": "🔌Muuuuse relay protocol for long-horizon zero-drift agentic code loops. Any seat relays to any other with per-target signed trust and flow control.",
|
|
5
5
|
"type": "commonjs",
|
|
6
6
|
"bin": {
|
|
7
7
|
"muuuuse": "bin/muuse.js"
|
package/src/agents.js
CHANGED
|
@@ -468,7 +468,9 @@ function parseCodexAssistantLine(line, options = {}) {
|
|
|
468
468
|
}
|
|
469
469
|
|
|
470
470
|
const phase = String(entry.payload?.phase || "").trim().toLowerCase();
|
|
471
|
-
|
|
471
|
+
// Newer Codex sessions can omit `payload.phase` for final answers.
|
|
472
|
+
const normalizedPhase = phase === "commentary" ? "commentary" : "final_answer";
|
|
473
|
+
const relayablePhase = normalizedPhase === "final_answer" || (flowMode && normalizedPhase === "commentary");
|
|
472
474
|
if (!relayablePhase) {
|
|
473
475
|
return null;
|
|
474
476
|
}
|
|
@@ -481,7 +483,7 @@ function parseCodexAssistantLine(line, options = {}) {
|
|
|
481
483
|
return {
|
|
482
484
|
id: entry.payload.id || hashText(line),
|
|
483
485
|
text,
|
|
484
|
-
phase:
|
|
486
|
+
phase: normalizedPhase,
|
|
485
487
|
timestamp: entry.timestamp || entry.payload.timestamp || new Date().toISOString(),
|
|
486
488
|
};
|
|
487
489
|
} catch {
|
package/src/cli.js
CHANGED
|
@@ -102,7 +102,7 @@ function renderSeatStatus(seat) {
|
|
|
102
102
|
function renderLinkTargets(seat) {
|
|
103
103
|
const targets = Array.isArray(seat.continueTargets) ? seat.continueTargets : [];
|
|
104
104
|
return targets
|
|
105
|
-
.map((target) => `${target.targetSeatId}
|
|
105
|
+
.map((target) => `${target.targetSeatId} (flow ${target.flowMode})`)
|
|
106
106
|
.join(", ");
|
|
107
107
|
}
|
|
108
108
|
|
|
@@ -110,6 +110,20 @@ function parseSeatOptions(command, args) {
|
|
|
110
110
|
const seatId = normalizeSeatId(command);
|
|
111
111
|
let continueTargets = [];
|
|
112
112
|
let index = 0;
|
|
113
|
+
let seatFlowMode = null;
|
|
114
|
+
let hasExplicitTarget = false;
|
|
115
|
+
|
|
116
|
+
const flowToken = String(args[index] || "").trim().toLowerCase();
|
|
117
|
+
if (flowToken === "flow") {
|
|
118
|
+
const parsedSeatFlow = parseFlowModeToken("flow", args[index + 1]);
|
|
119
|
+
if (!parsedSeatFlow) {
|
|
120
|
+
throw new Error(
|
|
121
|
+
`\`muuuuse ${command} flow\` requires \`on\` or \`off\`.`
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
seatFlowMode = parsedSeatFlow;
|
|
125
|
+
index += 2;
|
|
126
|
+
}
|
|
113
127
|
|
|
114
128
|
while (index < args.length) {
|
|
115
129
|
const token = String(args[index] || "").trim().toLowerCase();
|
|
@@ -118,21 +132,45 @@ function parseSeatOptions(command, args) {
|
|
|
118
132
|
const parsedLinks = parseLinkTargets(args.slice(index + 1), seatId);
|
|
119
133
|
if (parsedLinks.consumed > 0) {
|
|
120
134
|
continueTargets = mergeTargets(continueTargets, parsedLinks.continueTargets);
|
|
135
|
+
hasExplicitTarget = true;
|
|
121
136
|
index += 1 + parsedLinks.consumed;
|
|
122
137
|
continue;
|
|
123
138
|
}
|
|
124
139
|
break;
|
|
125
140
|
}
|
|
126
141
|
|
|
142
|
+
if (token === "continue") {
|
|
143
|
+
const targetSeatId = normalizeSeatId(args[index + 1]);
|
|
144
|
+
if (!targetSeatId) {
|
|
145
|
+
break;
|
|
146
|
+
}
|
|
147
|
+
upsertTarget(continueTargets, {
|
|
148
|
+
targetSeatId,
|
|
149
|
+
flowMode: seatFlowMode || "on",
|
|
150
|
+
});
|
|
151
|
+
hasExplicitTarget = true;
|
|
152
|
+
index += 2;
|
|
153
|
+
continue;
|
|
154
|
+
}
|
|
155
|
+
|
|
127
156
|
break;
|
|
128
157
|
}
|
|
129
158
|
|
|
130
159
|
if (index === args.length) {
|
|
160
|
+
if (seatFlowMode && !hasExplicitTarget) {
|
|
161
|
+
const partnerSeatId = seatId % 2 === 0 ? seatId - 1 : seatId + 1;
|
|
162
|
+
if (partnerSeatId > 0) {
|
|
163
|
+
upsertTarget(continueTargets, {
|
|
164
|
+
targetSeatId: partnerSeatId,
|
|
165
|
+
flowMode: seatFlowMode,
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
}
|
|
131
169
|
return { continueTargets };
|
|
132
170
|
}
|
|
133
171
|
|
|
134
172
|
throw new Error(
|
|
135
|
-
`\`muuuuse ${command}\` accepts no extra arguments or \`link <seat> flow on [<seat> flow off ...]\`. Run it directly in the terminal you want to arm.`
|
|
173
|
+
`\`muuuuse ${command}\` accepts no extra arguments, optional \`flow on/off\`, \`continue <seat>\`, or \`link <seat> flow on [<seat> flow off ...]\`. Run it directly in the terminal you want to arm.`
|
|
136
174
|
);
|
|
137
175
|
}
|
|
138
176
|
|
package/src/runtime.js
CHANGED
|
@@ -58,11 +58,6 @@ function normalizeFlowMode(flowMode) {
|
|
|
58
58
|
return String(flowMode || "").trim().toLowerCase() === "on" ? "on" : "off";
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
-
function normalizeContinueSeatId(value) {
|
|
62
|
-
const seatId = normalizeSeatId(value);
|
|
63
|
-
return seatId || null;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
61
|
function resolveShell() {
|
|
67
62
|
const shell = String(process.env.SHELL || "").trim();
|
|
68
63
|
return shell || "/bin/bash";
|
|
@@ -132,6 +127,11 @@ function createSessionName(currentPath = process.cwd()) {
|
|
|
132
127
|
return `${getDefaultSessionName(currentPath)}-${createId(6)}`;
|
|
133
128
|
}
|
|
134
129
|
|
|
130
|
+
function getAnchorSeatId(seatId = 1) {
|
|
131
|
+
const normalizedSeatId = normalizeSeatId(seatId) || 1;
|
|
132
|
+
return normalizedSeatId % 2 === 0 ? normalizedSeatId - 1 : normalizedSeatId;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
135
|
function sleepSync(ms) {
|
|
136
136
|
if (!Number.isFinite(ms) || ms <= 0) {
|
|
137
137
|
return;
|
|
@@ -140,7 +140,8 @@ function sleepSync(ms) {
|
|
|
140
140
|
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
|
|
141
141
|
}
|
|
142
142
|
|
|
143
|
-
function findExistingSessionName(currentPath = process.cwd()) {
|
|
143
|
+
function findExistingSessionName(currentPath = process.cwd(), anchorSeatId = null) {
|
|
144
|
+
const targetAnchorSeatId = normalizeSeatId(anchorSeatId) || null;
|
|
144
145
|
const candidates = listSessionNames()
|
|
145
146
|
.map((sessionName) => {
|
|
146
147
|
const sessionPaths = getSessionPaths(sessionName);
|
|
@@ -158,6 +159,16 @@ function findExistingSessionName(currentPath = process.cwd()) {
|
|
|
158
159
|
return null;
|
|
159
160
|
}
|
|
160
161
|
|
|
162
|
+
if (targetAnchorSeatId) {
|
|
163
|
+
const controllerAnchorSeatId = normalizeSeatId(controller?.anchorSeatId);
|
|
164
|
+
if (controllerAnchorSeatId && controllerAnchorSeatId !== targetAnchorSeatId) {
|
|
165
|
+
return null;
|
|
166
|
+
}
|
|
167
|
+
if (!controllerAnchorSeatId && !getSeatDirIfExists(sessionName, targetAnchorSeatId)) {
|
|
168
|
+
return null;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
161
172
|
return {
|
|
162
173
|
sessionName,
|
|
163
174
|
createdAtMs,
|
|
@@ -169,10 +180,10 @@ function findExistingSessionName(currentPath = process.cwd()) {
|
|
|
169
180
|
return candidates[0]?.sessionName || null;
|
|
170
181
|
}
|
|
171
182
|
|
|
172
|
-
function waitForExistingSessionName(currentPath = process.cwd(), timeoutMs = SEAT_JOIN_WAIT_MS) {
|
|
183
|
+
function waitForExistingSessionName(currentPath = process.cwd(), timeoutMs = SEAT_JOIN_WAIT_MS, anchorSeatId = null) {
|
|
173
184
|
const deadline = Date.now() + timeoutMs;
|
|
174
185
|
while (Date.now() <= deadline) {
|
|
175
|
-
const sessionName = findExistingSessionName(currentPath);
|
|
186
|
+
const sessionName = findExistingSessionName(currentPath, anchorSeatId);
|
|
176
187
|
if (sessionName) {
|
|
177
188
|
return sessionName;
|
|
178
189
|
}
|
|
@@ -183,11 +194,19 @@ function waitForExistingSessionName(currentPath = process.cwd(), timeoutMs = SEA
|
|
|
183
194
|
}
|
|
184
195
|
|
|
185
196
|
function resolveSessionName(currentPath = process.cwd(), seatId = 1) {
|
|
186
|
-
const
|
|
197
|
+
const anchorSeatId = getAnchorSeatId(seatId);
|
|
198
|
+
const existing = findExistingSessionName(currentPath, anchorSeatId);
|
|
187
199
|
if (existing) {
|
|
188
200
|
return existing;
|
|
189
201
|
}
|
|
190
202
|
|
|
203
|
+
if (seatId % 2 === 0) {
|
|
204
|
+
const waited = waitForExistingSessionName(currentPath, SEAT_JOIN_WAIT_MS, anchorSeatId);
|
|
205
|
+
if (waited) {
|
|
206
|
+
return waited;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
191
210
|
return createSessionName(currentPath);
|
|
192
211
|
}
|
|
193
212
|
|
|
@@ -529,7 +548,7 @@ async function sendTextAndEnter(child, text, shouldAbort = () => false) {
|
|
|
529
548
|
}
|
|
530
549
|
|
|
531
550
|
try {
|
|
532
|
-
child.write("\
|
|
551
|
+
child.write("\n");
|
|
533
552
|
} catch {
|
|
534
553
|
return false;
|
|
535
554
|
}
|
|
@@ -540,26 +559,22 @@ async function sendTextAndEnter(child, text, shouldAbort = () => false) {
|
|
|
540
559
|
class ArmedSeat {
|
|
541
560
|
constructor(options) {
|
|
542
561
|
this.seatId = options.seatId;
|
|
562
|
+
this.anchorSeatId = getAnchorSeatId(this.seatId);
|
|
563
|
+
this.partnerSeatId = this.seatId % 2 === 0 ? this.seatId - 1 : this.seatId + 1;
|
|
543
564
|
this.continueTargets = Array.isArray(options.continueTargets) ? options.continueTargets : [];
|
|
544
565
|
this.cwd = normalizeWorkingPath(options.cwd);
|
|
545
566
|
|
|
546
|
-
// Auto-link partner seat for backwards compatibility (
|
|
547
|
-
// This preserves v5 behavior where odd seats (anchor) initiate relay to even (partner)
|
|
567
|
+
// Auto-link adjacent partner seat for backwards compatibility (1↔2, 3↔4, ...).
|
|
548
568
|
if (this.continueTargets.length === 0) {
|
|
549
|
-
if (this.
|
|
550
|
-
|
|
551
|
-
this.continueTargets.push({ targetSeatId: 2, flowMode: "on" });
|
|
552
|
-
} else if (this.seatId === 3) {
|
|
553
|
-
// Seat 3 relays to seat 4
|
|
554
|
-
this.continueTargets.push({ targetSeatId: 4, flowMode: "on" });
|
|
569
|
+
if (this.partnerSeatId > 0) {
|
|
570
|
+
this.continueTargets.push({ targetSeatId: this.partnerSeatId, flowMode: "on" });
|
|
555
571
|
}
|
|
556
|
-
// Even seats don't auto-link (they receive from odd partners)
|
|
557
572
|
}
|
|
558
573
|
|
|
559
574
|
if (this.continueTargets.some((t) => t.targetSeatId === this.seatId)) {
|
|
560
575
|
throw new Error(`\`muuuuse ${this.seatId}\` cannot relay to itself.`);
|
|
561
576
|
}
|
|
562
|
-
this.sessionName = resolveSessionName(this.cwd);
|
|
577
|
+
this.sessionName = resolveSessionName(this.cwd, this.seatId);
|
|
563
578
|
if (!this.sessionName) {
|
|
564
579
|
throw new Error(
|
|
565
580
|
`Failed to create or find session in ${this.cwd}.`
|
|
@@ -568,7 +583,28 @@ class ArmedSeat {
|
|
|
568
583
|
this.sessionPaths = getSessionPaths(this.sessionName);
|
|
569
584
|
this.paths = getSeatPaths(this.sessionName, this.seatId);
|
|
570
585
|
this.continueOffset = getFileSize(this.paths.continuePath);
|
|
571
|
-
|
|
586
|
+
|
|
587
|
+
// Per-target trust state, paths, and event offsets for the signed relay channel.
|
|
588
|
+
// Only create directories for same-session targets (same anchor pair).
|
|
589
|
+
// Cross-session targets use the continuation channel and don't need local seat dirs.
|
|
590
|
+
const ownAnchor = getAnchorSeatId(this.seatId);
|
|
591
|
+
this.targetTrust = {};
|
|
592
|
+
this.targetPaths = {};
|
|
593
|
+
this.targetOffsets = {};
|
|
594
|
+
for (const t of this.continueTargets) {
|
|
595
|
+
const sameSession = getAnchorSeatId(t.targetSeatId) === ownAnchor;
|
|
596
|
+
this.targetTrust[t.targetSeatId] = {
|
|
597
|
+
challenge: null,
|
|
598
|
+
peerPublicKey: null,
|
|
599
|
+
phase: "initializing",
|
|
600
|
+
pairedAt: null,
|
|
601
|
+
sameSession,
|
|
602
|
+
};
|
|
603
|
+
this.targetPaths[t.targetSeatId] = sameSession
|
|
604
|
+
? getSeatPaths(this.sessionName, t.targetSeatId)
|
|
605
|
+
: null;
|
|
606
|
+
this.targetOffsets[t.targetSeatId] = 0;
|
|
607
|
+
}
|
|
572
608
|
|
|
573
609
|
this.child = null;
|
|
574
610
|
this.childPid = null;
|
|
@@ -582,15 +618,11 @@ class ArmedSeat {
|
|
|
582
618
|
this.resizeCleanup = null;
|
|
583
619
|
this.forceKillTimer = null;
|
|
584
620
|
this.identity = null;
|
|
621
|
+
this.ownChallenge = null;
|
|
585
622
|
this.lastUserInputAtMs = 0;
|
|
586
623
|
this.pendingInboundContext = null;
|
|
587
624
|
this.recentInboundRelays = [];
|
|
588
625
|
this.recentEmittedAnswers = [];
|
|
589
|
-
this.trustState = {
|
|
590
|
-
challenge: null,
|
|
591
|
-
phase: "initialized",
|
|
592
|
-
createdAt: null,
|
|
593
|
-
};
|
|
594
626
|
this.liveState = {
|
|
595
627
|
type: null,
|
|
596
628
|
pid: null,
|
|
@@ -611,6 +643,8 @@ class ArmedSeat {
|
|
|
611
643
|
cwd: this.cwd,
|
|
612
644
|
createdAt: current.createdAt || this.startedAt,
|
|
613
645
|
updatedAt: new Date().toISOString(),
|
|
646
|
+
anchorSeatId: this.anchorSeatId,
|
|
647
|
+
partnerSeatId: this.partnerSeatId,
|
|
614
648
|
...extra,
|
|
615
649
|
});
|
|
616
650
|
}
|
|
@@ -649,19 +683,84 @@ class ArmedSeat {
|
|
|
649
683
|
|
|
650
684
|
initializeTrustMaterial() {
|
|
651
685
|
this.identity = loadOrCreateSeatIdentity(this.paths);
|
|
686
|
+
const ownChallenge = createId(48);
|
|
652
687
|
writeJson(this.paths.challengePath, {
|
|
653
688
|
sessionName: this.sessionName,
|
|
689
|
+
challenge: ownChallenge,
|
|
654
690
|
publicKey: this.identity.publicKey,
|
|
655
691
|
createdAt: new Date().toISOString(),
|
|
656
692
|
});
|
|
657
|
-
this.
|
|
658
|
-
this.trustState.createdAt = new Date().toISOString();
|
|
693
|
+
this.ownChallenge = ownChallenge;
|
|
659
694
|
}
|
|
660
695
|
|
|
661
|
-
|
|
696
|
+
syncTargetTrust() {
|
|
662
697
|
if (!this.identity) {
|
|
663
698
|
this.initializeTrustMaterial();
|
|
664
699
|
}
|
|
700
|
+
|
|
701
|
+
for (const target of this.continueTargets) {
|
|
702
|
+
this.syncOneTargetTrust(target.targetSeatId);
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
syncOneTargetTrust(targetSeatId) {
|
|
707
|
+
const trust = this.targetTrust[targetSeatId];
|
|
708
|
+
if (!trust || trust.phase === "paired") {
|
|
709
|
+
return;
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
// Cross-session target: relay goes through the continuation channel only.
|
|
713
|
+
if (!trust.sameSession) {
|
|
714
|
+
trust.phase = "paired";
|
|
715
|
+
trust.pairedAt = new Date().toISOString();
|
|
716
|
+
return;
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
// Same-session target: read their challenge.json to get their public key
|
|
720
|
+
// and challenge. One-way trust — we just need to verify their events.
|
|
721
|
+
const targetPaths = this.targetPaths[targetSeatId];
|
|
722
|
+
if (!targetPaths) {
|
|
723
|
+
return;
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
const targetChallenge = readSeatChallenge(targetPaths, this.sessionName);
|
|
727
|
+
if (!targetChallenge) {
|
|
728
|
+
trust.phase = "waiting_for_target";
|
|
729
|
+
return;
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
trust.challenge = targetChallenge.challenge;
|
|
733
|
+
trust.peerPublicKey = targetChallenge.publicKey;
|
|
734
|
+
trust.phase = "paired";
|
|
735
|
+
trust.pairedAt = new Date().toISOString();
|
|
736
|
+
|
|
737
|
+
// Initialize offset to current file size so we only read new events.
|
|
738
|
+
this.targetOffsets[targetSeatId] = getFileSize(targetPaths.eventsPath);
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
isTargetPaired(targetSeatId) {
|
|
742
|
+
const trust = this.targetTrust[targetSeatId];
|
|
743
|
+
return Boolean(trust && trust.phase === "paired");
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
getOverallTrustPhase() {
|
|
747
|
+
const targets = this.continueTargets;
|
|
748
|
+
if (targets.length === 0) {
|
|
749
|
+
return "paired";
|
|
750
|
+
}
|
|
751
|
+
const allPaired = targets.every((t) => this.isTargetPaired(t.targetSeatId));
|
|
752
|
+
if (allPaired) {
|
|
753
|
+
return "paired";
|
|
754
|
+
}
|
|
755
|
+
const anyPaired = targets.some((t) => this.isTargetPaired(t.targetSeatId));
|
|
756
|
+
if (anyPaired) {
|
|
757
|
+
return "partial";
|
|
758
|
+
}
|
|
759
|
+
return "initializing";
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
hasAnyPairedTarget() {
|
|
763
|
+
return this.continueTargets.some((t) => this.isTargetPaired(t.targetSeatId));
|
|
665
764
|
}
|
|
666
765
|
|
|
667
766
|
launchShell() {
|
|
@@ -669,6 +768,7 @@ class ArmedSeat {
|
|
|
669
768
|
fs.rmSync(this.paths.pipePath, { force: true });
|
|
670
769
|
clearStaleStopRequest(this.sessionPaths.stopPath, this.startedAtMs);
|
|
671
770
|
this.initializeTrustMaterial();
|
|
771
|
+
this.syncTargetTrust();
|
|
672
772
|
this.writeController();
|
|
673
773
|
|
|
674
774
|
const shell = resolveShell();
|
|
@@ -684,7 +784,7 @@ class ArmedSeat {
|
|
|
684
784
|
|
|
685
785
|
this.childPid = this.child.pid;
|
|
686
786
|
this.writeMeta();
|
|
687
|
-
this.writeStatus({ state: "running", trust: this.
|
|
787
|
+
this.writeStatus({ state: "running", trust: this.getOverallTrustPhase() });
|
|
688
788
|
|
|
689
789
|
this.child.onData((data) => {
|
|
690
790
|
fs.appendFileSync(this.paths.pipePath, data);
|
|
@@ -1145,7 +1245,11 @@ class ArmedSeat {
|
|
|
1145
1245
|
}
|
|
1146
1246
|
|
|
1147
1247
|
const payload = sanitizeRelayText(entry.text);
|
|
1148
|
-
if (!payload || !this.identity || !this.
|
|
1248
|
+
if (!payload || !this.identity || !this.ownChallenge) {
|
|
1249
|
+
return;
|
|
1250
|
+
}
|
|
1251
|
+
|
|
1252
|
+
if (!this.hasAnyPairedTarget()) {
|
|
1149
1253
|
return;
|
|
1150
1254
|
}
|
|
1151
1255
|
|
|
@@ -1162,8 +1266,10 @@ class ArmedSeat {
|
|
|
1162
1266
|
}
|
|
1163
1267
|
|
|
1164
1268
|
const pendingInboundContext = this.getPendingInboundContext();
|
|
1165
|
-
|
|
1166
1269
|
const entryId = entry.id || createId(12);
|
|
1270
|
+
|
|
1271
|
+
// Sign with OUR OWN challenge. Each reader verifies using our challenge
|
|
1272
|
+
// (which they obtained during the trust handshake as peerChallenge).
|
|
1167
1273
|
const signedEntry = {
|
|
1168
1274
|
id: entryId,
|
|
1169
1275
|
type: "answer",
|
|
@@ -1174,51 +1280,67 @@ class ArmedSeat {
|
|
|
1174
1280
|
createdAt: entry.createdAt || new Date().toISOString(),
|
|
1175
1281
|
chainId: pendingInboundContext?.chainId || entry.chainId || entryId,
|
|
1176
1282
|
hop: pendingInboundContext ? pendingInboundContext.hop + 1 : 0,
|
|
1177
|
-
challenge: this.
|
|
1283
|
+
challenge: this.ownChallenge,
|
|
1178
1284
|
publicKey: this.identity.publicKey,
|
|
1179
1285
|
};
|
|
1180
1286
|
signedEntry.signature = signText(
|
|
1181
|
-
buildAnswerSignaturePayload(this.sessionName, this.
|
|
1287
|
+
buildAnswerSignaturePayload(this.sessionName, this.ownChallenge, signedEntry),
|
|
1182
1288
|
this.identity.privateKey
|
|
1183
1289
|
);
|
|
1184
1290
|
appendJsonl(this.paths.eventsPath, signedEntry);
|
|
1185
|
-
this.forwardContinuation(signedEntry);
|
|
1186
|
-
this.rememberEmittedAnswer(answerKey);
|
|
1187
1291
|
|
|
1292
|
+
// Forward via continuation channel for cross-session targets.
|
|
1293
|
+
for (const target of this.continueTargets) {
|
|
1294
|
+
this.forwardContinuation(signedEntry, target);
|
|
1295
|
+
}
|
|
1296
|
+
|
|
1297
|
+
this.rememberEmittedAnswer(answerKey);
|
|
1188
1298
|
this.log(`[${this.seatId}] ${previewText(payload)}`);
|
|
1189
1299
|
}
|
|
1190
1300
|
|
|
1191
|
-
forwardContinuation(signedEntry) {
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
if (!shouldAcceptInboundEntry(targetEntry.flowMode, signedEntry)) {
|
|
1196
|
-
continue;
|
|
1197
|
-
}
|
|
1301
|
+
forwardContinuation(signedEntry, targetEntry) {
|
|
1302
|
+
if (!shouldAcceptInboundEntry(targetEntry.flowMode, signedEntry)) {
|
|
1303
|
+
return;
|
|
1304
|
+
}
|
|
1198
1305
|
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1306
|
+
// Same-session target: write directly to their continuation channel.
|
|
1307
|
+
const trust = this.targetTrust[targetEntry.targetSeatId];
|
|
1308
|
+
if (trust && trust.sameSession) {
|
|
1309
|
+
const targetPaths = this.targetPaths[targetEntry.targetSeatId];
|
|
1310
|
+
if (targetPaths) {
|
|
1311
|
+
const continuationEntry = buildContinuationEntry(this.sessionName, targetEntry.targetSeatId, signedEntry);
|
|
1312
|
+
appendJsonl(targetPaths.continuePath, continuationEntry);
|
|
1313
|
+
this.log(`[${this.seatId} => ${targetEntry.targetSeatId} (${targetEntry.flowMode})] ${previewText(continuationEntry.text)}`);
|
|
1203
1314
|
}
|
|
1315
|
+
return;
|
|
1316
|
+
}
|
|
1204
1317
|
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1318
|
+
// Cross-session target: find the target across sessions.
|
|
1319
|
+
const target = this.findContinuationTarget(targetEntry.targetSeatId);
|
|
1320
|
+
if (!target) {
|
|
1321
|
+
return;
|
|
1208
1322
|
}
|
|
1323
|
+
|
|
1324
|
+
const continuationEntry = buildContinuationEntry(this.sessionName, target.seatId, signedEntry);
|
|
1325
|
+
appendJsonl(target.paths.continuePath, continuationEntry);
|
|
1326
|
+
this.log(`[${this.seatId} => ${target.seatId} (${targetEntry.flowMode})] ${previewText(continuationEntry.text)}`);
|
|
1209
1327
|
}
|
|
1210
1328
|
|
|
1211
1329
|
async tick() {
|
|
1212
1330
|
if (this.stopRequested()) {
|
|
1213
1331
|
this.writeStatus({
|
|
1214
1332
|
state: "stopping",
|
|
1215
|
-
trust: this.
|
|
1333
|
+
trust: this.getOverallTrustPhase(),
|
|
1216
1334
|
});
|
|
1217
1335
|
this.requestStop("stop_requested");
|
|
1218
1336
|
return;
|
|
1219
1337
|
}
|
|
1220
1338
|
|
|
1221
|
-
this.
|
|
1339
|
+
this.syncTargetTrust();
|
|
1340
|
+
if (this.stopped || this.stopRequested()) {
|
|
1341
|
+
this.requestStop("stop_requested");
|
|
1342
|
+
return;
|
|
1343
|
+
}
|
|
1222
1344
|
await this.pullContinuationEvents();
|
|
1223
1345
|
if (this.stopped || this.stopRequested()) {
|
|
1224
1346
|
this.requestStop("stop_requested");
|
|
@@ -1236,8 +1358,8 @@ class ArmedSeat {
|
|
|
1236
1358
|
cwd: live.cwd,
|
|
1237
1359
|
log: live.log,
|
|
1238
1360
|
lastAnswerAt: live.lastAnswerAt,
|
|
1239
|
-
trust: this.
|
|
1240
|
-
challengeReady:
|
|
1361
|
+
trust: this.getOverallTrustPhase(),
|
|
1362
|
+
challengeReady: this.hasAnyPairedTarget(),
|
|
1241
1363
|
});
|
|
1242
1364
|
}
|
|
1243
1365
|
|
|
@@ -1251,7 +1373,7 @@ class ArmedSeat {
|
|
|
1251
1373
|
this.log("Use this shell normally. Codex, Claude, and Gemini relay automatically from their local session logs.");
|
|
1252
1374
|
if (this.continueTargets.length > 0) {
|
|
1253
1375
|
const targets = this.continueTargets.map((t) => `${t.targetSeatId} (flow ${t.flowMode})`).join(", ");
|
|
1254
|
-
this.log(`Seat ${this.seatId} relays to ${targets}.`);
|
|
1376
|
+
this.log(`Seat ${this.seatId} relays to ${targets}. Establishing trust.`);
|
|
1255
1377
|
}
|
|
1256
1378
|
this.log("Run `muuuuse status` or `muuuuse stop` from any terminal.");
|
|
1257
1379
|
|
package/src/util.js
CHANGED
|
@@ -9,7 +9,7 @@ const fs = require("node:fs");
|
|
|
9
9
|
const os = require("node:os");
|
|
10
10
|
const path = require("node:path");
|
|
11
11
|
|
|
12
|
-
const BRAND = "🔌Muuuuse
|
|
12
|
+
const BRAND = "🔌Muuuuse v7.0.1";
|
|
13
13
|
const POLL_MS = 220;
|
|
14
14
|
const MAX_RELAY_CHARS = 4000;
|
|
15
15
|
const SESSION_MATCH_WINDOW_MS = 5 * 60 * 1000;
|