@h-rig/cli 0.0.6-alpha.23 → 0.0.6-alpha.25

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/dist/bin/rig.js CHANGED
@@ -6703,6 +6703,15 @@ function appendTranscript(state, label, text2) {
6703
6703
  state.transcript.splice(0, state.transcript.length - MAX_TRANSCRIPT_LINES);
6704
6704
  }
6705
6705
  }
6706
+ function nativePiUi(ctx) {
6707
+ const ui = ctx.ui;
6708
+ return typeof ui.emitSessionEvent === "function" && typeof ui.appendSessionMessages === "function" ? ui : null;
6709
+ }
6710
+ function syncNativeDisplayCwd(ctx, state) {
6711
+ const ui = nativePiUi(ctx);
6712
+ if (ui?.setDisplayCwd && state.cwd)
6713
+ ui.setDisplayCwd(state.cwd);
6714
+ }
6706
6715
  function parseExtensionUiRequest(value) {
6707
6716
  const request = recordOf(value) ?? {};
6708
6717
  const requestId = String(request.requestId ?? request.id ?? `ui-${Date.now()}`);
@@ -6746,8 +6755,13 @@ function renderBridgeWidget(state) {
6746
6755
  function updatePiUi(ctx, state) {
6747
6756
  ctx.ui.setTitle("Pi \xB7 Rig worker daemon");
6748
6757
  ctx.ui.setStatus("rig-worker-pi", state.wsConnected ? "worker Pi WS live" : state.status);
6758
+ syncNativeDisplayCwd(ctx, state);
6759
+ if (state.nativeStream && nativePiUi(ctx)) {
6760
+ ctx.ui.setWidget("rig-worker-pi-transcript", undefined);
6761
+ return;
6762
+ }
6749
6763
  ctx.ui.setWorkingVisible(false);
6750
- ctx.ui.setWidget("rig-worker-pi-transcript", renderBridgeWidget(state), { placement: "aboveEditor" });
6764
+ ctx.ui.setWidget("rig-worker-pi-transcript", [`Worker Pi daemon bridge \xB7 degraded widget transcript`, ...renderBridgeWidget(state)], { placement: "aboveEditor" });
6751
6765
  }
6752
6766
  function applyStatus(state, payload) {
6753
6767
  const status = recordOf(payload.status) ?? payload;
@@ -6765,7 +6779,7 @@ function applyMessage(state, message2) {
6765
6779
  const label = role === "assistant" ? "Pi" : role === "user" ? "You" : role === "tool" || role === "toolResult" ? "Tool" : "System";
6766
6780
  appendTranscript(state, label, textFromContent(record.content ?? record.message ?? record.text ?? ""));
6767
6781
  }
6768
- function applyPiEvent(state, eventValue) {
6782
+ function applyPiEvent(ctx, state, eventValue) {
6769
6783
  const event = recordOf(eventValue);
6770
6784
  if (!event)
6771
6785
  return;
@@ -6773,11 +6787,20 @@ function applyPiEvent(state, eventValue) {
6773
6787
  if (type === "agent_start") {
6774
6788
  state.streaming = true;
6775
6789
  state.status = "streaming";
6790
+ } else if (type === "agent_end") {
6791
+ state.streaming = false;
6792
+ state.status = "idle";
6793
+ } else if (type === "queue_update") {
6794
+ const steering = Array.isArray(event.steering) ? event.steering.length : 0;
6795
+ const followUp = Array.isArray(event.followUp) ? event.followUp.length : 0;
6796
+ state.status = `queued \xB7 steer ${steering} \xB7 follow-up ${followUp}`;
6797
+ }
6798
+ const native = nativePiUi(ctx);
6799
+ if (state.nativeStream && native?.emitSessionEvent) {
6800
+ native.emitSessionEvent(eventValue);
6776
6801
  return;
6777
6802
  }
6778
6803
  if (type === "agent_end") {
6779
- state.streaming = false;
6780
- state.status = "idle";
6781
6804
  appendTranscript(state, "System", "Agent turn complete.");
6782
6805
  return;
6783
6806
  }
@@ -6802,29 +6825,60 @@ function applyPiEvent(state, eventValue) {
6802
6825
  }
6803
6826
  if (type === "tool_execution_end") {
6804
6827
  appendTranscript(state, event.isError === true ? "Error" : "Tool", asText(event.result ?? `${String(event.toolName ?? "tool")} complete`));
6805
- return;
6806
- }
6807
- if (type === "queue_update") {
6808
- const steering = Array.isArray(event.steering) ? event.steering.length : 0;
6809
- const followUp = Array.isArray(event.followUp) ? event.followUp.length : 0;
6810
- state.status = `queued \xB7 steer ${steering} \xB7 follow-up ${followUp}`;
6811
6828
  }
6812
6829
  }
6830
+ function firstPendingShell(state) {
6831
+ return state.pendingShells[0];
6832
+ }
6833
+ function finishPendingShell(state, shell, result) {
6834
+ const index = state.pendingShells.indexOf(shell);
6835
+ if (index !== -1)
6836
+ state.pendingShells.splice(index, 1);
6837
+ shell.resolve(result);
6838
+ }
6839
+ function failPendingShell(state, shell, error) {
6840
+ const index = state.pendingShells.indexOf(shell);
6841
+ if (index !== -1)
6842
+ state.pendingShells.splice(index, 1);
6843
+ shell.reject(error);
6844
+ }
6813
6845
  function applyUiEvent(state, value) {
6814
6846
  const event = recordOf(value);
6815
6847
  if (!event)
6816
6848
  return;
6817
6849
  const type = String(event.type ?? "ui");
6818
- if (type === "shell.start")
6819
- appendTranscript(state, "Tool", `$ ${asText(event.command)}`);
6820
- else if (type === "shell.chunk")
6821
- appendTranscript(state, "Tool", asText(event.chunk));
6822
- else if (type === "shell.end")
6823
- appendTranscript(state, event.isError === true ? "Error" : "Tool", asText(event.output ?? `exit ${String(event.exitCode ?? "")}`));
6824
- else
6825
- appendTranscript(state, "System", `${type}: ${asText(event)}`);
6850
+ if (type === "shell.chunk") {
6851
+ const pending = firstPendingShell(state);
6852
+ const chunk = asText(event.chunk);
6853
+ if (pending) {
6854
+ pending.sawChunk = true;
6855
+ pending.onData(Buffer.from(chunk));
6856
+ } else {
6857
+ appendTranscript(state, "Tool", chunk);
6858
+ }
6859
+ return;
6860
+ }
6861
+ if (type === "shell.end") {
6862
+ const pending = firstPendingShell(state);
6863
+ const output = asText(event.output ?? "");
6864
+ const exitCode = typeof event.exitCode === "number" ? event.exitCode : event.isError === true ? 1 : 0;
6865
+ if (pending) {
6866
+ if (output && !pending.sawChunk)
6867
+ pending.onData(Buffer.from(output));
6868
+ finishPendingShell(state, pending, { exitCode });
6869
+ } else {
6870
+ appendTranscript(state, event.isError === true ? "Error" : "Tool", output || `exit ${String(exitCode)}`);
6871
+ }
6872
+ return;
6873
+ }
6874
+ if (type === "shell.start") {
6875
+ if (!firstPendingShell(state))
6876
+ appendTranscript(state, "Tool", `$ ${asText(event.command)}`);
6877
+ return;
6878
+ }
6879
+ appendTranscript(state, "System", `${type}: ${asText(event)}`);
6826
6880
  }
6827
- function applyEnvelope(state, envelopeValue) {
6881
+ function applyEnvelope(ctx, state, envelopeValue) {
6828
6882
  const envelope = recordOf(envelopeValue);
6829
6883
  if (!envelope)
6830
6884
  return;
@@ -6833,7 +6887,8 @@ function applyEnvelope(state, envelopeValue) {
6833
6887
  const metadata = recordOf(envelope.metadata);
6834
6888
  state.cwd = typeof metadata?.cwd === "string" ? metadata.cwd : state.cwd;
6835
6889
  state.status = "worker Pi daemon ready";
6836
- appendTranscript(state, "System", "Connected to worker Pi daemon.");
6890
+ if (!state.nativeStream)
6891
+ appendTranscript(state, "System", "Connected to worker Pi daemon.");
6837
6892
  } else if (type === "status.update") {
6838
6893
  applyStatus(state, envelope);
6839
6894
  } else if (type === "activity.update") {
@@ -6845,10 +6900,11 @@ function applyEnvelope(state, envelopeValue) {
6845
6900
  } else if (type === "pi.ui_event") {
6846
6901
  applyUiEvent(state, envelope.event);
6847
6902
  } else if (type === "pi.event") {
6848
- applyPiEvent(state, envelope.event);
6903
+ applyPiEvent(ctx, state, envelope.event);
6849
6904
  } else if (type === "error") {
6850
6905
  appendTranscript(state, "Error", asText(envelope.message ?? envelope.detail ?? "unknown error"));
6851
6906
  }
6907
+ syncNativeDisplayCwd(ctx, state);
6852
6908
  }
6853
6909
  async function waitForWorkerReady(options, ctx, state) {
6854
6910
  while (true) {
@@ -6869,7 +6925,7 @@ async function waitForWorkerReady(options, ctx, state) {
6869
6925
  continue;
6870
6926
  }
6871
6927
  const sessionRecord = recordOf(session) ?? {};
6872
- applyEnvelope(state, { type: "ready", metadata: sessionRecord.metadata ?? sessionRecord });
6928
+ applyEnvelope(ctx, state, { type: "ready", metadata: sessionRecord.metadata ?? sessionRecord });
6873
6929
  updatePiUi(ctx, state);
6874
6930
  return true;
6875
6931
  }
@@ -6899,7 +6955,7 @@ async function connectWorkerStream(options, ctx, state) {
6899
6955
  if (!catchupDone)
6900
6956
  buffered.push(payload);
6901
6957
  else {
6902
- applyEnvelope(state, payload);
6958
+ applyEnvelope(ctx, state, payload);
6903
6959
  updatePiUi(ctx, state);
6904
6960
  }
6905
6961
  } catch (error) {
@@ -6922,8 +6978,12 @@ async function connectWorkerStream(options, ctx, state) {
6922
6978
  getRunPiCommandsViaServer(options.context, options.runId)
6923
6979
  ]);
6924
6980
  const messages = Array.isArray(messagesPayload.messages) ? messagesPayload.messages : [];
6925
- for (const message2 of messages)
6926
- applyMessage(state, message2);
6981
+ const native = nativePiUi(ctx);
6982
+ if (state.nativeStream && native?.appendSessionMessages)
6983
+ native.appendSessionMessages(messages);
6984
+ else
6985
+ for (const message2 of messages)
6986
+ applyMessage(state, message2);
6927
6987
  applyStatus(state, statusPayload);
6928
6988
  const commands = Array.isArray(commandsPayload.commands) ? commandsPayload.commands : [];
6929
6989
  state.commands = commands.flatMap((command) => {
@@ -6932,7 +6992,7 @@ async function connectWorkerStream(options, ctx, state) {
6932
6992
  });
6933
6993
  catchupDone = true;
6934
6994
  for (const payload of buffered.splice(0))
6935
- applyEnvelope(state, payload);
6995
+ applyEnvelope(ctx, state, payload);
6936
6996
  updatePiUi(ctx, state);
6937
6997
  } catch (error) {
6938
6998
  appendTranscript(state, "Error", `Worker Pi catch-up failed: ${error instanceof Error ? error.message : String(error)}`);
@@ -6941,6 +7001,51 @@ async function connectWorkerStream(options, ctx, state) {
6941
7001
  }
6942
7002
  await closePromise;
6943
7003
  }
7004
+ function createRemoteBashOperations(options, state, excludeFromContext) {
7005
+ return {
7006
+ exec(command, _cwd, execOptions) {
7007
+ return new Promise((resolve20, reject) => {
7008
+ const pending = {
7009
+ command,
7010
+ onData: execOptions.onData,
7011
+ resolve: resolve20,
7012
+ reject,
7013
+ sawChunk: false
7014
+ };
7015
+ const cleanup = () => {
7016
+ execOptions.signal?.removeEventListener("abort", onAbort);
7017
+ if (timer)
7018
+ clearTimeout(timer);
7019
+ };
7020
+ const onAbort = () => {
7021
+ cleanup();
7022
+ failPendingShell(state, pending, new Error("Remote worker shell command aborted locally."));
7023
+ };
7024
+ const timeoutMs = typeof execOptions.timeout === "number" && execOptions.timeout > 0 ? execOptions.timeout : 0;
7025
+ const timer = timeoutMs > 0 ? setTimeout(() => {
7026
+ cleanup();
7027
+ failPendingShell(state, pending, new Error(`Remote worker shell command timed out after ${timeoutMs}ms.`));
7028
+ }, timeoutMs) : null;
7029
+ const wrappedResolve = pending.resolve;
7030
+ const wrappedReject = pending.reject;
7031
+ pending.resolve = (result) => {
7032
+ cleanup();
7033
+ wrappedResolve(result);
7034
+ };
7035
+ pending.reject = (error) => {
7036
+ cleanup();
7037
+ wrappedReject(error);
7038
+ };
7039
+ execOptions.signal?.addEventListener("abort", onAbort, { once: true });
7040
+ state.pendingShells.push(pending);
7041
+ sendRunPiShellViaServer(options.context, options.runId, `${excludeFromContext ? "!!" : "!"}${command}`).catch((error) => {
7042
+ cleanup();
7043
+ failPendingShell(state, pending, error instanceof Error ? error : new Error(String(error)));
7044
+ });
7045
+ });
7046
+ }
7047
+ };
7048
+ }
6944
7049
  async function answerPendingUi(options, state, line) {
6945
7050
  const pending = state.pendingUi;
6946
7051
  if (!pending)
@@ -7006,13 +7111,22 @@ function createRigWorkerPiBridgeExtension(options) {
7006
7111
  commands: [],
7007
7112
  streaming: false,
7008
7113
  pendingUi: null,
7009
- wsConnected: false
7114
+ pendingShells: [],
7115
+ wsConnected: false,
7116
+ nativeStream: false
7010
7117
  };
7011
7118
  if (options.initialMessageSent)
7012
7119
  appendTranscript(state, "System", "Initial message sent to worker Pi daemon.");
7120
+ let nativePiUiContextAvailable = false;
7121
+ pi.on("user_bash", (event) => {
7122
+ state.nativeStream = Boolean(state.nativeStream || nativePiUiContextAvailable);
7123
+ return { operations: createRemoteBashOperations(options, state, event.excludeFromContext === true) };
7124
+ });
7013
7125
  pi.on("session_start", async (_event, ctx) => {
7126
+ nativePiUiContextAvailable = Boolean(nativePiUi(ctx));
7127
+ state.nativeStream = nativePiUiContextAvailable;
7014
7128
  updatePiUi(ctx, state);
7015
- ctx.ui.notify("Rig worker Pi bridge extension loaded", "info");
7129
+ ctx.ui.notify(nativePiUiContextAvailable ? "Rig worker Pi native stream bridge loaded" : "Rig worker Pi bridge extension loaded (degraded widget fallback)", "info");
7016
7130
  ctx.ui.onTerminalInput((data) => {
7017
7131
  if (data.includes("\x04")) {
7018
7132
  ctx.shutdown();
@@ -7026,6 +7140,8 @@ function createRigWorkerPiBridgeExtension(options) {
7026
7140
  const text2 = [editorText, inlineText].filter(Boolean).join(" ").trim();
7027
7141
  if (!text2)
7028
7142
  return;
7143
+ if (text2.startsWith("!"))
7144
+ return;
7029
7145
  ctx.ui.setEditorText("");
7030
7146
  routeInput(options, ctx, state, text2).catch((error) => {
7031
7147
  appendTranscript(state, "Error", error instanceof Error ? error.message : String(error));
@@ -522,6 +522,15 @@ function appendTranscript(state, label, text) {
522
522
  state.transcript.splice(0, state.transcript.length - MAX_TRANSCRIPT_LINES);
523
523
  }
524
524
  }
525
+ function nativePiUi(ctx) {
526
+ const ui = ctx.ui;
527
+ return typeof ui.emitSessionEvent === "function" && typeof ui.appendSessionMessages === "function" ? ui : null;
528
+ }
529
+ function syncNativeDisplayCwd(ctx, state) {
530
+ const ui = nativePiUi(ctx);
531
+ if (ui?.setDisplayCwd && state.cwd)
532
+ ui.setDisplayCwd(state.cwd);
533
+ }
525
534
  function parseExtensionUiRequest(value) {
526
535
  const request = recordOf(value) ?? {};
527
536
  const requestId = String(request.requestId ?? request.id ?? `ui-${Date.now()}`);
@@ -565,8 +574,13 @@ function renderBridgeWidget(state) {
565
574
  function updatePiUi(ctx, state) {
566
575
  ctx.ui.setTitle("Pi \xB7 Rig worker daemon");
567
576
  ctx.ui.setStatus("rig-worker-pi", state.wsConnected ? "worker Pi WS live" : state.status);
577
+ syncNativeDisplayCwd(ctx, state);
578
+ if (state.nativeStream && nativePiUi(ctx)) {
579
+ ctx.ui.setWidget("rig-worker-pi-transcript", undefined);
580
+ return;
581
+ }
568
582
  ctx.ui.setWorkingVisible(false);
569
- ctx.ui.setWidget("rig-worker-pi-transcript", renderBridgeWidget(state), { placement: "aboveEditor" });
583
+ ctx.ui.setWidget("rig-worker-pi-transcript", [`Worker Pi daemon bridge \xB7 degraded widget transcript`, ...renderBridgeWidget(state)], { placement: "aboveEditor" });
570
584
  }
571
585
  function applyStatus(state, payload) {
572
586
  const status = recordOf(payload.status) ?? payload;
@@ -584,7 +598,7 @@ function applyMessage(state, message) {
584
598
  const label = role === "assistant" ? "Pi" : role === "user" ? "You" : role === "tool" || role === "toolResult" ? "Tool" : "System";
585
599
  appendTranscript(state, label, textFromContent(record.content ?? record.message ?? record.text ?? ""));
586
600
  }
587
- function applyPiEvent(state, eventValue) {
601
+ function applyPiEvent(ctx, state, eventValue) {
588
602
  const event = recordOf(eventValue);
589
603
  if (!event)
590
604
  return;
@@ -592,11 +606,20 @@ function applyPiEvent(state, eventValue) {
592
606
  if (type === "agent_start") {
593
607
  state.streaming = true;
594
608
  state.status = "streaming";
609
+ } else if (type === "agent_end") {
610
+ state.streaming = false;
611
+ state.status = "idle";
612
+ } else if (type === "queue_update") {
613
+ const steering = Array.isArray(event.steering) ? event.steering.length : 0;
614
+ const followUp = Array.isArray(event.followUp) ? event.followUp.length : 0;
615
+ state.status = `queued \xB7 steer ${steering} \xB7 follow-up ${followUp}`;
616
+ }
617
+ const native = nativePiUi(ctx);
618
+ if (state.nativeStream && native?.emitSessionEvent) {
619
+ native.emitSessionEvent(eventValue);
595
620
  return;
596
621
  }
597
622
  if (type === "agent_end") {
598
- state.streaming = false;
599
- state.status = "idle";
600
623
  appendTranscript(state, "System", "Agent turn complete.");
601
624
  return;
602
625
  }
@@ -621,29 +644,60 @@ function applyPiEvent(state, eventValue) {
621
644
  }
622
645
  if (type === "tool_execution_end") {
623
646
  appendTranscript(state, event.isError === true ? "Error" : "Tool", asText(event.result ?? `${String(event.toolName ?? "tool")} complete`));
624
- return;
625
- }
626
- if (type === "queue_update") {
627
- const steering = Array.isArray(event.steering) ? event.steering.length : 0;
628
- const followUp = Array.isArray(event.followUp) ? event.followUp.length : 0;
629
- state.status = `queued \xB7 steer ${steering} \xB7 follow-up ${followUp}`;
630
647
  }
631
648
  }
649
+ function firstPendingShell(state) {
650
+ return state.pendingShells[0];
651
+ }
652
+ function finishPendingShell(state, shell, result) {
653
+ const index = state.pendingShells.indexOf(shell);
654
+ if (index !== -1)
655
+ state.pendingShells.splice(index, 1);
656
+ shell.resolve(result);
657
+ }
658
+ function failPendingShell(state, shell, error) {
659
+ const index = state.pendingShells.indexOf(shell);
660
+ if (index !== -1)
661
+ state.pendingShells.splice(index, 1);
662
+ shell.reject(error);
663
+ }
632
664
  function applyUiEvent(state, value) {
633
665
  const event = recordOf(value);
634
666
  if (!event)
635
667
  return;
636
668
  const type = String(event.type ?? "ui");
637
- if (type === "shell.start")
638
- appendTranscript(state, "Tool", `$ ${asText(event.command)}`);
639
- else if (type === "shell.chunk")
640
- appendTranscript(state, "Tool", asText(event.chunk));
641
- else if (type === "shell.end")
642
- appendTranscript(state, event.isError === true ? "Error" : "Tool", asText(event.output ?? `exit ${String(event.exitCode ?? "")}`));
643
- else
644
- appendTranscript(state, "System", `${type}: ${asText(event)}`);
645
- }
646
- function applyEnvelope(state, envelopeValue) {
669
+ if (type === "shell.chunk") {
670
+ const pending = firstPendingShell(state);
671
+ const chunk = asText(event.chunk);
672
+ if (pending) {
673
+ pending.sawChunk = true;
674
+ pending.onData(Buffer.from(chunk));
675
+ } else {
676
+ appendTranscript(state, "Tool", chunk);
677
+ }
678
+ return;
679
+ }
680
+ if (type === "shell.end") {
681
+ const pending = firstPendingShell(state);
682
+ const output = asText(event.output ?? "");
683
+ const exitCode = typeof event.exitCode === "number" ? event.exitCode : event.isError === true ? 1 : 0;
684
+ if (pending) {
685
+ if (output && !pending.sawChunk)
686
+ pending.onData(Buffer.from(output));
687
+ finishPendingShell(state, pending, { exitCode });
688
+ } else {
689
+ appendTranscript(state, event.isError === true ? "Error" : "Tool", output || `exit ${String(exitCode)}`);
690
+ }
691
+ return;
692
+ }
693
+ if (type === "shell.start") {
694
+ if (!firstPendingShell(state))
695
+ appendTranscript(state, "Tool", `$ ${asText(event.command)}`);
696
+ return;
697
+ }
698
+ appendTranscript(state, "System", `${type}: ${asText(event)}`);
699
+ }
700
+ function applyEnvelope(ctx, state, envelopeValue) {
647
701
  const envelope = recordOf(envelopeValue);
648
702
  if (!envelope)
649
703
  return;
@@ -652,7 +706,8 @@ function applyEnvelope(state, envelopeValue) {
652
706
  const metadata = recordOf(envelope.metadata);
653
707
  state.cwd = typeof metadata?.cwd === "string" ? metadata.cwd : state.cwd;
654
708
  state.status = "worker Pi daemon ready";
655
- appendTranscript(state, "System", "Connected to worker Pi daemon.");
709
+ if (!state.nativeStream)
710
+ appendTranscript(state, "System", "Connected to worker Pi daemon.");
656
711
  } else if (type === "status.update") {
657
712
  applyStatus(state, envelope);
658
713
  } else if (type === "activity.update") {
@@ -664,10 +719,11 @@ function applyEnvelope(state, envelopeValue) {
664
719
  } else if (type === "pi.ui_event") {
665
720
  applyUiEvent(state, envelope.event);
666
721
  } else if (type === "pi.event") {
667
- applyPiEvent(state, envelope.event);
722
+ applyPiEvent(ctx, state, envelope.event);
668
723
  } else if (type === "error") {
669
724
  appendTranscript(state, "Error", asText(envelope.message ?? envelope.detail ?? "unknown error"));
670
725
  }
726
+ syncNativeDisplayCwd(ctx, state);
671
727
  }
672
728
  async function waitForWorkerReady(options, ctx, state) {
673
729
  while (true) {
@@ -688,7 +744,7 @@ async function waitForWorkerReady(options, ctx, state) {
688
744
  continue;
689
745
  }
690
746
  const sessionRecord = recordOf(session) ?? {};
691
- applyEnvelope(state, { type: "ready", metadata: sessionRecord.metadata ?? sessionRecord });
747
+ applyEnvelope(ctx, state, { type: "ready", metadata: sessionRecord.metadata ?? sessionRecord });
692
748
  updatePiUi(ctx, state);
693
749
  return true;
694
750
  }
@@ -718,7 +774,7 @@ async function connectWorkerStream(options, ctx, state) {
718
774
  if (!catchupDone)
719
775
  buffered.push(payload);
720
776
  else {
721
- applyEnvelope(state, payload);
777
+ applyEnvelope(ctx, state, payload);
722
778
  updatePiUi(ctx, state);
723
779
  }
724
780
  } catch (error) {
@@ -741,8 +797,12 @@ async function connectWorkerStream(options, ctx, state) {
741
797
  getRunPiCommandsViaServer(options.context, options.runId)
742
798
  ]);
743
799
  const messages = Array.isArray(messagesPayload.messages) ? messagesPayload.messages : [];
744
- for (const message of messages)
745
- applyMessage(state, message);
800
+ const native = nativePiUi(ctx);
801
+ if (state.nativeStream && native?.appendSessionMessages)
802
+ native.appendSessionMessages(messages);
803
+ else
804
+ for (const message of messages)
805
+ applyMessage(state, message);
746
806
  applyStatus(state, statusPayload);
747
807
  const commands = Array.isArray(commandsPayload.commands) ? commandsPayload.commands : [];
748
808
  state.commands = commands.flatMap((command) => {
@@ -751,7 +811,7 @@ async function connectWorkerStream(options, ctx, state) {
751
811
  });
752
812
  catchupDone = true;
753
813
  for (const payload of buffered.splice(0))
754
- applyEnvelope(state, payload);
814
+ applyEnvelope(ctx, state, payload);
755
815
  updatePiUi(ctx, state);
756
816
  } catch (error) {
757
817
  appendTranscript(state, "Error", `Worker Pi catch-up failed: ${error instanceof Error ? error.message : String(error)}`);
@@ -760,6 +820,51 @@ async function connectWorkerStream(options, ctx, state) {
760
820
  }
761
821
  await closePromise;
762
822
  }
823
+ function createRemoteBashOperations(options, state, excludeFromContext) {
824
+ return {
825
+ exec(command, _cwd, execOptions) {
826
+ return new Promise((resolve3, reject) => {
827
+ const pending = {
828
+ command,
829
+ onData: execOptions.onData,
830
+ resolve: resolve3,
831
+ reject,
832
+ sawChunk: false
833
+ };
834
+ const cleanup = () => {
835
+ execOptions.signal?.removeEventListener("abort", onAbort);
836
+ if (timer)
837
+ clearTimeout(timer);
838
+ };
839
+ const onAbort = () => {
840
+ cleanup();
841
+ failPendingShell(state, pending, new Error("Remote worker shell command aborted locally."));
842
+ };
843
+ const timeoutMs = typeof execOptions.timeout === "number" && execOptions.timeout > 0 ? execOptions.timeout : 0;
844
+ const timer = timeoutMs > 0 ? setTimeout(() => {
845
+ cleanup();
846
+ failPendingShell(state, pending, new Error(`Remote worker shell command timed out after ${timeoutMs}ms.`));
847
+ }, timeoutMs) : null;
848
+ const wrappedResolve = pending.resolve;
849
+ const wrappedReject = pending.reject;
850
+ pending.resolve = (result) => {
851
+ cleanup();
852
+ wrappedResolve(result);
853
+ };
854
+ pending.reject = (error) => {
855
+ cleanup();
856
+ wrappedReject(error);
857
+ };
858
+ execOptions.signal?.addEventListener("abort", onAbort, { once: true });
859
+ state.pendingShells.push(pending);
860
+ sendRunPiShellViaServer(options.context, options.runId, `${excludeFromContext ? "!!" : "!"}${command}`).catch((error) => {
861
+ cleanup();
862
+ failPendingShell(state, pending, error instanceof Error ? error : new Error(String(error)));
863
+ });
864
+ });
865
+ }
866
+ };
867
+ }
763
868
  async function answerPendingUi(options, state, line) {
764
869
  const pending = state.pendingUi;
765
870
  if (!pending)
@@ -825,13 +930,22 @@ function createRigWorkerPiBridgeExtension(options) {
825
930
  commands: [],
826
931
  streaming: false,
827
932
  pendingUi: null,
828
- wsConnected: false
933
+ pendingShells: [],
934
+ wsConnected: false,
935
+ nativeStream: false
829
936
  };
830
937
  if (options.initialMessageSent)
831
938
  appendTranscript(state, "System", "Initial message sent to worker Pi daemon.");
939
+ let nativePiUiContextAvailable = false;
940
+ pi.on("user_bash", (event) => {
941
+ state.nativeStream = Boolean(state.nativeStream || nativePiUiContextAvailable);
942
+ return { operations: createRemoteBashOperations(options, state, event.excludeFromContext === true) };
943
+ });
832
944
  pi.on("session_start", async (_event, ctx) => {
945
+ nativePiUiContextAvailable = Boolean(nativePiUi(ctx));
946
+ state.nativeStream = nativePiUiContextAvailable;
833
947
  updatePiUi(ctx, state);
834
- ctx.ui.notify("Rig worker Pi bridge extension loaded", "info");
948
+ ctx.ui.notify(nativePiUiContextAvailable ? "Rig worker Pi native stream bridge loaded" : "Rig worker Pi bridge extension loaded (degraded widget fallback)", "info");
835
949
  ctx.ui.onTerminalInput((data) => {
836
950
  if (data.includes("\x04")) {
837
951
  ctx.shutdown();
@@ -845,6 +959,8 @@ function createRigWorkerPiBridgeExtension(options) {
845
959
  const text = [editorText, inlineText].filter(Boolean).join(" ").trim();
846
960
  if (!text)
847
961
  return;
962
+ if (text.startsWith("!"))
963
+ return;
848
964
  ctx.ui.setEditorText("");
849
965
  routeInput(options, ctx, state, text).catch((error) => {
850
966
  appendTranscript(state, "Error", error instanceof Error ? error.message : String(error));