muuuuse 3.1.1 → 4.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "muuuuse",
3
- "version": "3.1.1",
3
+ "version": "4.0.0",
4
4
  "description": "🔌Muuuuse arms regular terminals in isolated pairs and can continue relay output into any other armed seat.",
5
5
  "type": "commonjs",
6
6
  "bin": {
package/src/agents.js CHANGED
@@ -555,8 +555,7 @@ function selectClaudeSessionFile(currentPath, processStartedAtMs, options = {})
555
555
  return selectSessionCandidatePath(candidates, currentPath, processStartedAtMs);
556
556
  }
557
557
 
558
- function extractClaudeAssistantText(content, options = {}) {
559
- const flowMode = options.flowMode === true;
558
+ function extractClaudeAssistantText(content) {
560
559
  if (!Array.isArray(content)) {
561
560
  return "";
562
561
  }
@@ -569,7 +568,23 @@ function extractClaudeAssistantText(content, options = {}) {
569
568
  if (item.type === "text" && typeof item.text === "string") {
570
569
  return [item.text.trim()];
571
570
  }
572
- if (flowMode && item.type === "thinking" && typeof item.thinking === "string") {
571
+ return [];
572
+ })
573
+ .filter((text) => text.length > 0)
574
+ .join("\n");
575
+ }
576
+
577
+ function extractClaudeThinkingText(content) {
578
+ if (!Array.isArray(content)) {
579
+ return "";
580
+ }
581
+
582
+ return content
583
+ .flatMap((item) => {
584
+ if (!item || typeof item !== "object") {
585
+ return [];
586
+ }
587
+ if (item.type === "thinking" && typeof item.thinking === "string") {
573
588
  return [item.thinking.trim()];
574
589
  }
575
590
  return [];
@@ -586,28 +601,46 @@ function parseClaudeAssistantLine(line, options = {}) {
586
601
  return null;
587
602
  }
588
603
 
589
- if (!flowMode && entry.message?.stop_reason !== "end_turn") {
604
+ const isFinal = entry.message?.stop_reason === "end_turn";
605
+ if (!flowMode && !isFinal) {
590
606
  return null;
591
607
  }
592
608
 
593
- const text = sanitizeRelayText(extractClaudeAssistantText(entry.message.content, options));
594
- if (!text) {
595
- return null;
609
+ const id = entry.uuid || entry.message.id || hashText(line);
610
+ const timestamp = entry.timestamp || new Date().toISOString();
611
+ const results = [];
612
+
613
+ if (flowMode) {
614
+ const thinking = sanitizeRelayText(extractClaudeThinkingText(entry.message.content));
615
+ if (thinking) {
616
+ results.push({
617
+ id: `${id}-thinking`,
618
+ text: thinking,
619
+ phase: "commentary",
620
+ timestamp,
621
+ });
622
+ }
596
623
  }
597
624
 
598
- return {
599
- id: entry.uuid || entry.message.id || hashText(line),
600
- text,
601
- phase: flowMode && entry.message?.stop_reason !== "end_turn" ? "commentary" : "final_answer",
602
- timestamp: entry.timestamp || new Date().toISOString(),
603
- };
625
+ const text = sanitizeRelayText(extractClaudeAssistantText(entry.message.content));
626
+ if (text) {
627
+ results.push({
628
+ id,
629
+ text,
630
+ phase: isFinal ? "final_answer" : "commentary",
631
+ timestamp,
632
+ });
633
+ }
634
+
635
+ return results.length > 0 ? results : null;
604
636
  } catch {
605
637
  return null;
606
638
  }
607
639
  }
608
640
 
609
641
  function parseClaudeFinalLine(line) {
610
- return parseClaudeAssistantLine(line, { flowMode: false });
642
+ const results = parseClaudeAssistantLine(line, { flowMode: false });
643
+ return Array.isArray(results) ? results[0] || null : results;
611
644
  }
612
645
 
613
646
  function readClaudeAnswers(filePath, offset, sinceMs = null, options = {}) {
@@ -616,8 +649,10 @@ function readClaudeAnswers(filePath, offset, sinceMs = null, options = {}) {
616
649
  .split("\n")
617
650
  .map((line) => line.trim())
618
651
  .filter((line) => line.length > 0)
619
- .map((line) => parseClaudeAssistantLine(line, options))
620
- .filter((entry) => entry !== null)
652
+ .flatMap((line) => {
653
+ const result = parseClaudeAssistantLine(line, options);
654
+ return Array.isArray(result) ? result : result ? [result] : [];
655
+ })
621
656
  .filter((entry) => isAnswerNewEnough(entry, sinceMs));
622
657
 
623
658
  return { nextOffset, answers };
package/src/cli.js CHANGED
@@ -56,11 +56,10 @@ async function main(argv = process.argv.slice(2)) {
56
56
 
57
57
  const seatId = normalizeSeatId(command);
58
58
  if (seatId) {
59
- const { flowMode, continueSeatId, continueTargets } = parseSeatOptions(command, argv.slice(1));
59
+ const { flowMode, continueTargets } = parseSeatOptions(command, argv.slice(1));
60
60
  const seat = new ArmedSeat({
61
61
  cwd: process.cwd(),
62
62
  continueTargets,
63
- continueSeatId,
64
63
  flowMode,
65
64
  seatId,
66
65
  });
@@ -125,10 +124,9 @@ function renderLinkTargets(seat) {
125
124
  function parseSeatOptions(command, args) {
126
125
  const seatId = normalizeSeatId(command);
127
126
  let flowMode = "off";
128
- let continueSeatId = null;
129
127
  let continueTargets = [];
130
128
  if (args.length === 0) {
131
- return { flowMode, continueSeatId, continueTargets };
129
+ return { flowMode, continueTargets };
132
130
  }
133
131
 
134
132
  if (String(args[0] || "").trim().toLowerCase() === "link") {
@@ -136,7 +134,6 @@ function parseSeatOptions(command, args) {
136
134
  if (parsedLinks.consumed === args.length - 1 && parsedLinks.consumed > 0) {
137
135
  return {
138
136
  flowMode: parsedLinks.flowMode,
139
- continueSeatId: parsedLinks.continueTargets[0]?.targetSeatId || null,
140
137
  continueTargets: parsedLinks.continueTargets,
141
138
  };
142
139
  }
@@ -153,13 +150,12 @@ function parseSeatOptions(command, args) {
153
150
  const continueToken = String(args[index] || "").trim().toLowerCase();
154
151
  const targetSeatId = normalizeSeatId(args[index + 1]);
155
152
  if (continueToken === "continue" && targetSeatId) {
156
- continueSeatId = targetSeatId;
157
153
  continueTargets = [{ targetSeatId, flowMode }];
158
154
  index += 2;
159
155
  }
160
156
 
161
157
  if (index === args.length) {
162
- return { flowMode, continueSeatId, continueTargets };
158
+ return { flowMode, continueTargets };
163
159
  }
164
160
  }
165
161
 
package/src/runtime.js CHANGED
@@ -650,7 +650,6 @@ class ArmedSeat {
650
650
  ),
651
651
  this.flowMode
652
652
  );
653
- this.continueSeatId = this.continueTargets[0]?.targetSeatId || normalizeContinueSeatId(options.continueSeatId);
654
653
  this.cwd = normalizeWorkingPath(options.cwd);
655
654
  if (this.continueTargets.some((target) => target.targetSeatId === this.seatId)) {
656
655
  throw new Error(`\`muuuuse ${this.seatId}\` cannot continue to itself.`);
@@ -728,7 +727,6 @@ class ArmedSeat {
728
727
  partnerSeatId: this.partnerSeatId,
729
728
  sessionName: this.sessionName,
730
729
  flowMode: this.flowMode,
731
- continueSeatId: this.continueSeatId,
732
730
  continueTargets: this.continueTargets,
733
731
  cwd: this.cwd,
734
732
  pid: process.pid,
@@ -745,7 +743,6 @@ class ArmedSeat {
745
743
  partnerSeatId: this.partnerSeatId,
746
744
  sessionName: this.sessionName,
747
745
  flowMode: this.flowMode,
748
- continueSeatId: this.continueSeatId,
749
746
  continueTargets: this.continueTargets,
750
747
  cwd: this.cwd,
751
748
  pid: process.pid,
@@ -1525,11 +1522,16 @@ class ArmedSeat {
1525
1522
  buildAnswerSignaturePayload(this.sessionName, this.trustState.challenge, signedEntry),
1526
1523
  this.identity.privateKey
1527
1524
  );
1528
- appendJsonl(this.paths.eventsPath, signedEntry);
1525
+
1526
+ // Only write to partner events if local flow mode accepts this phase.
1527
+ // Commentary is gated here; continue targets filter independently.
1528
+ if (shouldAcceptInboundEntry(this.flowMode, signedEntry)) {
1529
+ appendJsonl(this.paths.eventsPath, signedEntry);
1530
+ this.log(`[${this.seatId}] ${previewText(payload)}`);
1531
+ }
1532
+
1529
1533
  this.forwardContinuation(signedEntry);
1530
1534
  this.rememberEmittedAnswer(answerKey);
1531
-
1532
- this.log(`[${this.seatId}] ${previewText(payload)}`);
1533
1535
  }
1534
1536
 
1535
1537
  forwardContinuation(signedEntry) {
@@ -1538,6 +1540,10 @@ class ArmedSeat {
1538
1540
  }
1539
1541
 
1540
1542
  for (const targetEntry of this.continueTargets) {
1543
+ if (!shouldAcceptInboundEntry(targetEntry.flowMode, signedEntry)) {
1544
+ continue;
1545
+ }
1546
+
1541
1547
  const target = this.findContinuationTarget(targetEntry.targetSeatId);
1542
1548
  if (!target) {
1543
1549
  this.log(`[${this.seatId}] continue ${targetEntry.targetSeatId} unavailable`);
@@ -1706,7 +1712,6 @@ function buildSeatReport(sessionName, seatId) {
1706
1712
  partnerSeatId: status?.partnerSeatId || meta?.partnerSeatId || getPartnerSeatId(seatId),
1707
1713
  state: wrapperLive ? status?.state || "running" : "orphaned_child",
1708
1714
  flowMode: status?.flowMode || meta?.flowMode || "off",
1709
- continueSeatId: status?.continueSeatId || meta?.continueSeatId || null,
1710
1715
  continueTargets: normalizeContinueTargets(
1711
1716
  status?.continueTargets || meta?.continueTargets || (
1712
1717
  (status?.continueSeatId || meta?.continueSeatId)