@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.
@@ -6496,6 +6496,15 @@ function appendTranscript(state, label, text2) {
6496
6496
  state.transcript.splice(0, state.transcript.length - MAX_TRANSCRIPT_LINES);
6497
6497
  }
6498
6498
  }
6499
+ function nativePiUi(ctx) {
6500
+ const ui = ctx.ui;
6501
+ return typeof ui.emitSessionEvent === "function" && typeof ui.appendSessionMessages === "function" ? ui : null;
6502
+ }
6503
+ function syncNativeDisplayCwd(ctx, state) {
6504
+ const ui = nativePiUi(ctx);
6505
+ if (ui?.setDisplayCwd && state.cwd)
6506
+ ui.setDisplayCwd(state.cwd);
6507
+ }
6499
6508
  function parseExtensionUiRequest(value) {
6500
6509
  const request = recordOf(value) ?? {};
6501
6510
  const requestId = String(request.requestId ?? request.id ?? `ui-${Date.now()}`);
@@ -6539,8 +6548,13 @@ function renderBridgeWidget(state) {
6539
6548
  function updatePiUi(ctx, state) {
6540
6549
  ctx.ui.setTitle("Pi \xB7 Rig worker daemon");
6541
6550
  ctx.ui.setStatus("rig-worker-pi", state.wsConnected ? "worker Pi WS live" : state.status);
6551
+ syncNativeDisplayCwd(ctx, state);
6552
+ if (state.nativeStream && nativePiUi(ctx)) {
6553
+ ctx.ui.setWidget("rig-worker-pi-transcript", undefined);
6554
+ return;
6555
+ }
6542
6556
  ctx.ui.setWorkingVisible(false);
6543
- ctx.ui.setWidget("rig-worker-pi-transcript", renderBridgeWidget(state), { placement: "aboveEditor" });
6557
+ ctx.ui.setWidget("rig-worker-pi-transcript", [`Worker Pi daemon bridge \xB7 degraded widget transcript`, ...renderBridgeWidget(state)], { placement: "aboveEditor" });
6544
6558
  }
6545
6559
  function applyStatus(state, payload) {
6546
6560
  const status = recordOf(payload.status) ?? payload;
@@ -6558,7 +6572,7 @@ function applyMessage(state, message2) {
6558
6572
  const label = role === "assistant" ? "Pi" : role === "user" ? "You" : role === "tool" || role === "toolResult" ? "Tool" : "System";
6559
6573
  appendTranscript(state, label, textFromContent(record.content ?? record.message ?? record.text ?? ""));
6560
6574
  }
6561
- function applyPiEvent(state, eventValue) {
6575
+ function applyPiEvent(ctx, state, eventValue) {
6562
6576
  const event = recordOf(eventValue);
6563
6577
  if (!event)
6564
6578
  return;
@@ -6566,11 +6580,20 @@ function applyPiEvent(state, eventValue) {
6566
6580
  if (type === "agent_start") {
6567
6581
  state.streaming = true;
6568
6582
  state.status = "streaming";
6583
+ } else if (type === "agent_end") {
6584
+ state.streaming = false;
6585
+ state.status = "idle";
6586
+ } else if (type === "queue_update") {
6587
+ const steering = Array.isArray(event.steering) ? event.steering.length : 0;
6588
+ const followUp = Array.isArray(event.followUp) ? event.followUp.length : 0;
6589
+ state.status = `queued \xB7 steer ${steering} \xB7 follow-up ${followUp}`;
6590
+ }
6591
+ const native = nativePiUi(ctx);
6592
+ if (state.nativeStream && native?.emitSessionEvent) {
6593
+ native.emitSessionEvent(eventValue);
6569
6594
  return;
6570
6595
  }
6571
6596
  if (type === "agent_end") {
6572
- state.streaming = false;
6573
- state.status = "idle";
6574
6597
  appendTranscript(state, "System", "Agent turn complete.");
6575
6598
  return;
6576
6599
  }
@@ -6595,29 +6618,60 @@ function applyPiEvent(state, eventValue) {
6595
6618
  }
6596
6619
  if (type === "tool_execution_end") {
6597
6620
  appendTranscript(state, event.isError === true ? "Error" : "Tool", asText(event.result ?? `${String(event.toolName ?? "tool")} complete`));
6598
- return;
6599
- }
6600
- if (type === "queue_update") {
6601
- const steering = Array.isArray(event.steering) ? event.steering.length : 0;
6602
- const followUp = Array.isArray(event.followUp) ? event.followUp.length : 0;
6603
- state.status = `queued \xB7 steer ${steering} \xB7 follow-up ${followUp}`;
6604
6621
  }
6605
6622
  }
6623
+ function firstPendingShell(state) {
6624
+ return state.pendingShells[0];
6625
+ }
6626
+ function finishPendingShell(state, shell, result) {
6627
+ const index = state.pendingShells.indexOf(shell);
6628
+ if (index !== -1)
6629
+ state.pendingShells.splice(index, 1);
6630
+ shell.resolve(result);
6631
+ }
6632
+ function failPendingShell(state, shell, error) {
6633
+ const index = state.pendingShells.indexOf(shell);
6634
+ if (index !== -1)
6635
+ state.pendingShells.splice(index, 1);
6636
+ shell.reject(error);
6637
+ }
6606
6638
  function applyUiEvent(state, value) {
6607
6639
  const event = recordOf(value);
6608
6640
  if (!event)
6609
6641
  return;
6610
6642
  const type = String(event.type ?? "ui");
6611
- if (type === "shell.start")
6612
- appendTranscript(state, "Tool", `$ ${asText(event.command)}`);
6613
- else if (type === "shell.chunk")
6614
- appendTranscript(state, "Tool", asText(event.chunk));
6615
- else if (type === "shell.end")
6616
- appendTranscript(state, event.isError === true ? "Error" : "Tool", asText(event.output ?? `exit ${String(event.exitCode ?? "")}`));
6617
- else
6618
- appendTranscript(state, "System", `${type}: ${asText(event)}`);
6643
+ if (type === "shell.chunk") {
6644
+ const pending = firstPendingShell(state);
6645
+ const chunk = asText(event.chunk);
6646
+ if (pending) {
6647
+ pending.sawChunk = true;
6648
+ pending.onData(Buffer.from(chunk));
6649
+ } else {
6650
+ appendTranscript(state, "Tool", chunk);
6651
+ }
6652
+ return;
6653
+ }
6654
+ if (type === "shell.end") {
6655
+ const pending = firstPendingShell(state);
6656
+ const output = asText(event.output ?? "");
6657
+ const exitCode = typeof event.exitCode === "number" ? event.exitCode : event.isError === true ? 1 : 0;
6658
+ if (pending) {
6659
+ if (output && !pending.sawChunk)
6660
+ pending.onData(Buffer.from(output));
6661
+ finishPendingShell(state, pending, { exitCode });
6662
+ } else {
6663
+ appendTranscript(state, event.isError === true ? "Error" : "Tool", output || `exit ${String(exitCode)}`);
6664
+ }
6665
+ return;
6666
+ }
6667
+ if (type === "shell.start") {
6668
+ if (!firstPendingShell(state))
6669
+ appendTranscript(state, "Tool", `$ ${asText(event.command)}`);
6670
+ return;
6671
+ }
6672
+ appendTranscript(state, "System", `${type}: ${asText(event)}`);
6619
6673
  }
6620
- function applyEnvelope(state, envelopeValue) {
6674
+ function applyEnvelope(ctx, state, envelopeValue) {
6621
6675
  const envelope = recordOf(envelopeValue);
6622
6676
  if (!envelope)
6623
6677
  return;
@@ -6626,7 +6680,8 @@ function applyEnvelope(state, envelopeValue) {
6626
6680
  const metadata = recordOf(envelope.metadata);
6627
6681
  state.cwd = typeof metadata?.cwd === "string" ? metadata.cwd : state.cwd;
6628
6682
  state.status = "worker Pi daemon ready";
6629
- appendTranscript(state, "System", "Connected to worker Pi daemon.");
6683
+ if (!state.nativeStream)
6684
+ appendTranscript(state, "System", "Connected to worker Pi daemon.");
6630
6685
  } else if (type === "status.update") {
6631
6686
  applyStatus(state, envelope);
6632
6687
  } else if (type === "activity.update") {
@@ -6638,10 +6693,11 @@ function applyEnvelope(state, envelopeValue) {
6638
6693
  } else if (type === "pi.ui_event") {
6639
6694
  applyUiEvent(state, envelope.event);
6640
6695
  } else if (type === "pi.event") {
6641
- applyPiEvent(state, envelope.event);
6696
+ applyPiEvent(ctx, state, envelope.event);
6642
6697
  } else if (type === "error") {
6643
6698
  appendTranscript(state, "Error", asText(envelope.message ?? envelope.detail ?? "unknown error"));
6644
6699
  }
6700
+ syncNativeDisplayCwd(ctx, state);
6645
6701
  }
6646
6702
  async function waitForWorkerReady(options, ctx, state) {
6647
6703
  while (true) {
@@ -6662,7 +6718,7 @@ async function waitForWorkerReady(options, ctx, state) {
6662
6718
  continue;
6663
6719
  }
6664
6720
  const sessionRecord = recordOf(session) ?? {};
6665
- applyEnvelope(state, { type: "ready", metadata: sessionRecord.metadata ?? sessionRecord });
6721
+ applyEnvelope(ctx, state, { type: "ready", metadata: sessionRecord.metadata ?? sessionRecord });
6666
6722
  updatePiUi(ctx, state);
6667
6723
  return true;
6668
6724
  }
@@ -6692,7 +6748,7 @@ async function connectWorkerStream(options, ctx, state) {
6692
6748
  if (!catchupDone)
6693
6749
  buffered.push(payload);
6694
6750
  else {
6695
- applyEnvelope(state, payload);
6751
+ applyEnvelope(ctx, state, payload);
6696
6752
  updatePiUi(ctx, state);
6697
6753
  }
6698
6754
  } catch (error) {
@@ -6715,8 +6771,12 @@ async function connectWorkerStream(options, ctx, state) {
6715
6771
  getRunPiCommandsViaServer(options.context, options.runId)
6716
6772
  ]);
6717
6773
  const messages = Array.isArray(messagesPayload.messages) ? messagesPayload.messages : [];
6718
- for (const message2 of messages)
6719
- applyMessage(state, message2);
6774
+ const native = nativePiUi(ctx);
6775
+ if (state.nativeStream && native?.appendSessionMessages)
6776
+ native.appendSessionMessages(messages);
6777
+ else
6778
+ for (const message2 of messages)
6779
+ applyMessage(state, message2);
6720
6780
  applyStatus(state, statusPayload);
6721
6781
  const commands = Array.isArray(commandsPayload.commands) ? commandsPayload.commands : [];
6722
6782
  state.commands = commands.flatMap((command) => {
@@ -6725,7 +6785,7 @@ async function connectWorkerStream(options, ctx, state) {
6725
6785
  });
6726
6786
  catchupDone = true;
6727
6787
  for (const payload of buffered.splice(0))
6728
- applyEnvelope(state, payload);
6788
+ applyEnvelope(ctx, state, payload);
6729
6789
  updatePiUi(ctx, state);
6730
6790
  } catch (error) {
6731
6791
  appendTranscript(state, "Error", `Worker Pi catch-up failed: ${error instanceof Error ? error.message : String(error)}`);
@@ -6734,6 +6794,51 @@ async function connectWorkerStream(options, ctx, state) {
6734
6794
  }
6735
6795
  await closePromise;
6736
6796
  }
6797
+ function createRemoteBashOperations(options, state, excludeFromContext) {
6798
+ return {
6799
+ exec(command, _cwd, execOptions) {
6800
+ return new Promise((resolve19, reject) => {
6801
+ const pending = {
6802
+ command,
6803
+ onData: execOptions.onData,
6804
+ resolve: resolve19,
6805
+ reject,
6806
+ sawChunk: false
6807
+ };
6808
+ const cleanup = () => {
6809
+ execOptions.signal?.removeEventListener("abort", onAbort);
6810
+ if (timer)
6811
+ clearTimeout(timer);
6812
+ };
6813
+ const onAbort = () => {
6814
+ cleanup();
6815
+ failPendingShell(state, pending, new Error("Remote worker shell command aborted locally."));
6816
+ };
6817
+ const timeoutMs = typeof execOptions.timeout === "number" && execOptions.timeout > 0 ? execOptions.timeout : 0;
6818
+ const timer = timeoutMs > 0 ? setTimeout(() => {
6819
+ cleanup();
6820
+ failPendingShell(state, pending, new Error(`Remote worker shell command timed out after ${timeoutMs}ms.`));
6821
+ }, timeoutMs) : null;
6822
+ const wrappedResolve = pending.resolve;
6823
+ const wrappedReject = pending.reject;
6824
+ pending.resolve = (result) => {
6825
+ cleanup();
6826
+ wrappedResolve(result);
6827
+ };
6828
+ pending.reject = (error) => {
6829
+ cleanup();
6830
+ wrappedReject(error);
6831
+ };
6832
+ execOptions.signal?.addEventListener("abort", onAbort, { once: true });
6833
+ state.pendingShells.push(pending);
6834
+ sendRunPiShellViaServer(options.context, options.runId, `${excludeFromContext ? "!!" : "!"}${command}`).catch((error) => {
6835
+ cleanup();
6836
+ failPendingShell(state, pending, error instanceof Error ? error : new Error(String(error)));
6837
+ });
6838
+ });
6839
+ }
6840
+ };
6841
+ }
6737
6842
  async function answerPendingUi(options, state, line) {
6738
6843
  const pending = state.pendingUi;
6739
6844
  if (!pending)
@@ -6799,13 +6904,22 @@ function createRigWorkerPiBridgeExtension(options) {
6799
6904
  commands: [],
6800
6905
  streaming: false,
6801
6906
  pendingUi: null,
6802
- wsConnected: false
6907
+ pendingShells: [],
6908
+ wsConnected: false,
6909
+ nativeStream: false
6803
6910
  };
6804
6911
  if (options.initialMessageSent)
6805
6912
  appendTranscript(state, "System", "Initial message sent to worker Pi daemon.");
6913
+ let nativePiUiContextAvailable = false;
6914
+ pi.on("user_bash", (event) => {
6915
+ state.nativeStream = Boolean(state.nativeStream || nativePiUiContextAvailable);
6916
+ return { operations: createRemoteBashOperations(options, state, event.excludeFromContext === true) };
6917
+ });
6806
6918
  pi.on("session_start", async (_event, ctx) => {
6919
+ nativePiUiContextAvailable = Boolean(nativePiUi(ctx));
6920
+ state.nativeStream = nativePiUiContextAvailable;
6807
6921
  updatePiUi(ctx, state);
6808
- ctx.ui.notify("Rig worker Pi bridge extension loaded", "info");
6922
+ ctx.ui.notify(nativePiUiContextAvailable ? "Rig worker Pi native stream bridge loaded" : "Rig worker Pi bridge extension loaded (degraded widget fallback)", "info");
6809
6923
  ctx.ui.onTerminalInput((data) => {
6810
6924
  if (data.includes("\x04")) {
6811
6925
  ctx.shutdown();
@@ -6819,6 +6933,8 @@ function createRigWorkerPiBridgeExtension(options) {
6819
6933
  const text2 = [editorText, inlineText].filter(Boolean).join(" ").trim();
6820
6934
  if (!text2)
6821
6935
  return;
6936
+ if (text2.startsWith("!"))
6937
+ return;
6822
6938
  ctx.ui.setEditorText("");
6823
6939
  routeInput(options, ctx, state, text2).catch((error) => {
6824
6940
  appendTranscript(state, "Error", error instanceof Error ? error.message : String(error));
package/dist/src/index.js CHANGED
@@ -6699,6 +6699,15 @@ function appendTranscript(state, label, text2) {
6699
6699
  state.transcript.splice(0, state.transcript.length - MAX_TRANSCRIPT_LINES);
6700
6700
  }
6701
6701
  }
6702
+ function nativePiUi(ctx) {
6703
+ const ui = ctx.ui;
6704
+ return typeof ui.emitSessionEvent === "function" && typeof ui.appendSessionMessages === "function" ? ui : null;
6705
+ }
6706
+ function syncNativeDisplayCwd(ctx, state) {
6707
+ const ui = nativePiUi(ctx);
6708
+ if (ui?.setDisplayCwd && state.cwd)
6709
+ ui.setDisplayCwd(state.cwd);
6710
+ }
6702
6711
  function parseExtensionUiRequest(value) {
6703
6712
  const request = recordOf(value) ?? {};
6704
6713
  const requestId = String(request.requestId ?? request.id ?? `ui-${Date.now()}`);
@@ -6742,8 +6751,13 @@ function renderBridgeWidget(state) {
6742
6751
  function updatePiUi(ctx, state) {
6743
6752
  ctx.ui.setTitle("Pi \xB7 Rig worker daemon");
6744
6753
  ctx.ui.setStatus("rig-worker-pi", state.wsConnected ? "worker Pi WS live" : state.status);
6754
+ syncNativeDisplayCwd(ctx, state);
6755
+ if (state.nativeStream && nativePiUi(ctx)) {
6756
+ ctx.ui.setWidget("rig-worker-pi-transcript", undefined);
6757
+ return;
6758
+ }
6745
6759
  ctx.ui.setWorkingVisible(false);
6746
- ctx.ui.setWidget("rig-worker-pi-transcript", renderBridgeWidget(state), { placement: "aboveEditor" });
6760
+ ctx.ui.setWidget("rig-worker-pi-transcript", [`Worker Pi daemon bridge \xB7 degraded widget transcript`, ...renderBridgeWidget(state)], { placement: "aboveEditor" });
6747
6761
  }
6748
6762
  function applyStatus(state, payload) {
6749
6763
  const status = recordOf(payload.status) ?? payload;
@@ -6761,7 +6775,7 @@ function applyMessage(state, message2) {
6761
6775
  const label = role === "assistant" ? "Pi" : role === "user" ? "You" : role === "tool" || role === "toolResult" ? "Tool" : "System";
6762
6776
  appendTranscript(state, label, textFromContent(record.content ?? record.message ?? record.text ?? ""));
6763
6777
  }
6764
- function applyPiEvent(state, eventValue) {
6778
+ function applyPiEvent(ctx, state, eventValue) {
6765
6779
  const event = recordOf(eventValue);
6766
6780
  if (!event)
6767
6781
  return;
@@ -6769,11 +6783,20 @@ function applyPiEvent(state, eventValue) {
6769
6783
  if (type === "agent_start") {
6770
6784
  state.streaming = true;
6771
6785
  state.status = "streaming";
6786
+ } else if (type === "agent_end") {
6787
+ state.streaming = false;
6788
+ state.status = "idle";
6789
+ } else if (type === "queue_update") {
6790
+ const steering = Array.isArray(event.steering) ? event.steering.length : 0;
6791
+ const followUp = Array.isArray(event.followUp) ? event.followUp.length : 0;
6792
+ state.status = `queued \xB7 steer ${steering} \xB7 follow-up ${followUp}`;
6793
+ }
6794
+ const native = nativePiUi(ctx);
6795
+ if (state.nativeStream && native?.emitSessionEvent) {
6796
+ native.emitSessionEvent(eventValue);
6772
6797
  return;
6773
6798
  }
6774
6799
  if (type === "agent_end") {
6775
- state.streaming = false;
6776
- state.status = "idle";
6777
6800
  appendTranscript(state, "System", "Agent turn complete.");
6778
6801
  return;
6779
6802
  }
@@ -6798,29 +6821,60 @@ function applyPiEvent(state, eventValue) {
6798
6821
  }
6799
6822
  if (type === "tool_execution_end") {
6800
6823
  appendTranscript(state, event.isError === true ? "Error" : "Tool", asText(event.result ?? `${String(event.toolName ?? "tool")} complete`));
6801
- return;
6802
- }
6803
- if (type === "queue_update") {
6804
- const steering = Array.isArray(event.steering) ? event.steering.length : 0;
6805
- const followUp = Array.isArray(event.followUp) ? event.followUp.length : 0;
6806
- state.status = `queued \xB7 steer ${steering} \xB7 follow-up ${followUp}`;
6807
6824
  }
6808
6825
  }
6826
+ function firstPendingShell(state) {
6827
+ return state.pendingShells[0];
6828
+ }
6829
+ function finishPendingShell(state, shell, result) {
6830
+ const index = state.pendingShells.indexOf(shell);
6831
+ if (index !== -1)
6832
+ state.pendingShells.splice(index, 1);
6833
+ shell.resolve(result);
6834
+ }
6835
+ function failPendingShell(state, shell, error) {
6836
+ const index = state.pendingShells.indexOf(shell);
6837
+ if (index !== -1)
6838
+ state.pendingShells.splice(index, 1);
6839
+ shell.reject(error);
6840
+ }
6809
6841
  function applyUiEvent(state, value) {
6810
6842
  const event = recordOf(value);
6811
6843
  if (!event)
6812
6844
  return;
6813
6845
  const type = String(event.type ?? "ui");
6814
- if (type === "shell.start")
6815
- appendTranscript(state, "Tool", `$ ${asText(event.command)}`);
6816
- else if (type === "shell.chunk")
6817
- appendTranscript(state, "Tool", asText(event.chunk));
6818
- else if (type === "shell.end")
6819
- appendTranscript(state, event.isError === true ? "Error" : "Tool", asText(event.output ?? `exit ${String(event.exitCode ?? "")}`));
6820
- else
6821
- appendTranscript(state, "System", `${type}: ${asText(event)}`);
6846
+ if (type === "shell.chunk") {
6847
+ const pending = firstPendingShell(state);
6848
+ const chunk = asText(event.chunk);
6849
+ if (pending) {
6850
+ pending.sawChunk = true;
6851
+ pending.onData(Buffer.from(chunk));
6852
+ } else {
6853
+ appendTranscript(state, "Tool", chunk);
6854
+ }
6855
+ return;
6856
+ }
6857
+ if (type === "shell.end") {
6858
+ const pending = firstPendingShell(state);
6859
+ const output = asText(event.output ?? "");
6860
+ const exitCode = typeof event.exitCode === "number" ? event.exitCode : event.isError === true ? 1 : 0;
6861
+ if (pending) {
6862
+ if (output && !pending.sawChunk)
6863
+ pending.onData(Buffer.from(output));
6864
+ finishPendingShell(state, pending, { exitCode });
6865
+ } else {
6866
+ appendTranscript(state, event.isError === true ? "Error" : "Tool", output || `exit ${String(exitCode)}`);
6867
+ }
6868
+ return;
6869
+ }
6870
+ if (type === "shell.start") {
6871
+ if (!firstPendingShell(state))
6872
+ appendTranscript(state, "Tool", `$ ${asText(event.command)}`);
6873
+ return;
6874
+ }
6875
+ appendTranscript(state, "System", `${type}: ${asText(event)}`);
6822
6876
  }
6823
- function applyEnvelope(state, envelopeValue) {
6877
+ function applyEnvelope(ctx, state, envelopeValue) {
6824
6878
  const envelope = recordOf(envelopeValue);
6825
6879
  if (!envelope)
6826
6880
  return;
@@ -6829,7 +6883,8 @@ function applyEnvelope(state, envelopeValue) {
6829
6883
  const metadata = recordOf(envelope.metadata);
6830
6884
  state.cwd = typeof metadata?.cwd === "string" ? metadata.cwd : state.cwd;
6831
6885
  state.status = "worker Pi daemon ready";
6832
- appendTranscript(state, "System", "Connected to worker Pi daemon.");
6886
+ if (!state.nativeStream)
6887
+ appendTranscript(state, "System", "Connected to worker Pi daemon.");
6833
6888
  } else if (type === "status.update") {
6834
6889
  applyStatus(state, envelope);
6835
6890
  } else if (type === "activity.update") {
@@ -6841,10 +6896,11 @@ function applyEnvelope(state, envelopeValue) {
6841
6896
  } else if (type === "pi.ui_event") {
6842
6897
  applyUiEvent(state, envelope.event);
6843
6898
  } else if (type === "pi.event") {
6844
- applyPiEvent(state, envelope.event);
6899
+ applyPiEvent(ctx, state, envelope.event);
6845
6900
  } else if (type === "error") {
6846
6901
  appendTranscript(state, "Error", asText(envelope.message ?? envelope.detail ?? "unknown error"));
6847
6902
  }
6903
+ syncNativeDisplayCwd(ctx, state);
6848
6904
  }
6849
6905
  async function waitForWorkerReady(options, ctx, state) {
6850
6906
  while (true) {
@@ -6865,7 +6921,7 @@ async function waitForWorkerReady(options, ctx, state) {
6865
6921
  continue;
6866
6922
  }
6867
6923
  const sessionRecord = recordOf(session) ?? {};
6868
- applyEnvelope(state, { type: "ready", metadata: sessionRecord.metadata ?? sessionRecord });
6924
+ applyEnvelope(ctx, state, { type: "ready", metadata: sessionRecord.metadata ?? sessionRecord });
6869
6925
  updatePiUi(ctx, state);
6870
6926
  return true;
6871
6927
  }
@@ -6895,7 +6951,7 @@ async function connectWorkerStream(options, ctx, state) {
6895
6951
  if (!catchupDone)
6896
6952
  buffered.push(payload);
6897
6953
  else {
6898
- applyEnvelope(state, payload);
6954
+ applyEnvelope(ctx, state, payload);
6899
6955
  updatePiUi(ctx, state);
6900
6956
  }
6901
6957
  } catch (error) {
@@ -6918,8 +6974,12 @@ async function connectWorkerStream(options, ctx, state) {
6918
6974
  getRunPiCommandsViaServer(options.context, options.runId)
6919
6975
  ]);
6920
6976
  const messages = Array.isArray(messagesPayload.messages) ? messagesPayload.messages : [];
6921
- for (const message2 of messages)
6922
- applyMessage(state, message2);
6977
+ const native = nativePiUi(ctx);
6978
+ if (state.nativeStream && native?.appendSessionMessages)
6979
+ native.appendSessionMessages(messages);
6980
+ else
6981
+ for (const message2 of messages)
6982
+ applyMessage(state, message2);
6923
6983
  applyStatus(state, statusPayload);
6924
6984
  const commands = Array.isArray(commandsPayload.commands) ? commandsPayload.commands : [];
6925
6985
  state.commands = commands.flatMap((command) => {
@@ -6928,7 +6988,7 @@ async function connectWorkerStream(options, ctx, state) {
6928
6988
  });
6929
6989
  catchupDone = true;
6930
6990
  for (const payload of buffered.splice(0))
6931
- applyEnvelope(state, payload);
6991
+ applyEnvelope(ctx, state, payload);
6932
6992
  updatePiUi(ctx, state);
6933
6993
  } catch (error) {
6934
6994
  appendTranscript(state, "Error", `Worker Pi catch-up failed: ${error instanceof Error ? error.message : String(error)}`);
@@ -6937,6 +6997,51 @@ async function connectWorkerStream(options, ctx, state) {
6937
6997
  }
6938
6998
  await closePromise;
6939
6999
  }
7000
+ function createRemoteBashOperations(options, state, excludeFromContext) {
7001
+ return {
7002
+ exec(command, _cwd, execOptions) {
7003
+ return new Promise((resolve20, reject) => {
7004
+ const pending = {
7005
+ command,
7006
+ onData: execOptions.onData,
7007
+ resolve: resolve20,
7008
+ reject,
7009
+ sawChunk: false
7010
+ };
7011
+ const cleanup = () => {
7012
+ execOptions.signal?.removeEventListener("abort", onAbort);
7013
+ if (timer)
7014
+ clearTimeout(timer);
7015
+ };
7016
+ const onAbort = () => {
7017
+ cleanup();
7018
+ failPendingShell(state, pending, new Error("Remote worker shell command aborted locally."));
7019
+ };
7020
+ const timeoutMs = typeof execOptions.timeout === "number" && execOptions.timeout > 0 ? execOptions.timeout : 0;
7021
+ const timer = timeoutMs > 0 ? setTimeout(() => {
7022
+ cleanup();
7023
+ failPendingShell(state, pending, new Error(`Remote worker shell command timed out after ${timeoutMs}ms.`));
7024
+ }, timeoutMs) : null;
7025
+ const wrappedResolve = pending.resolve;
7026
+ const wrappedReject = pending.reject;
7027
+ pending.resolve = (result) => {
7028
+ cleanup();
7029
+ wrappedResolve(result);
7030
+ };
7031
+ pending.reject = (error) => {
7032
+ cleanup();
7033
+ wrappedReject(error);
7034
+ };
7035
+ execOptions.signal?.addEventListener("abort", onAbort, { once: true });
7036
+ state.pendingShells.push(pending);
7037
+ sendRunPiShellViaServer(options.context, options.runId, `${excludeFromContext ? "!!" : "!"}${command}`).catch((error) => {
7038
+ cleanup();
7039
+ failPendingShell(state, pending, error instanceof Error ? error : new Error(String(error)));
7040
+ });
7041
+ });
7042
+ }
7043
+ };
7044
+ }
6940
7045
  async function answerPendingUi(options, state, line) {
6941
7046
  const pending = state.pendingUi;
6942
7047
  if (!pending)
@@ -7002,13 +7107,22 @@ function createRigWorkerPiBridgeExtension(options) {
7002
7107
  commands: [],
7003
7108
  streaming: false,
7004
7109
  pendingUi: null,
7005
- wsConnected: false
7110
+ pendingShells: [],
7111
+ wsConnected: false,
7112
+ nativeStream: false
7006
7113
  };
7007
7114
  if (options.initialMessageSent)
7008
7115
  appendTranscript(state, "System", "Initial message sent to worker Pi daemon.");
7116
+ let nativePiUiContextAvailable = false;
7117
+ pi.on("user_bash", (event) => {
7118
+ state.nativeStream = Boolean(state.nativeStream || nativePiUiContextAvailable);
7119
+ return { operations: createRemoteBashOperations(options, state, event.excludeFromContext === true) };
7120
+ });
7009
7121
  pi.on("session_start", async (_event, ctx) => {
7122
+ nativePiUiContextAvailable = Boolean(nativePiUi(ctx));
7123
+ state.nativeStream = nativePiUiContextAvailable;
7010
7124
  updatePiUi(ctx, state);
7011
- ctx.ui.notify("Rig worker Pi bridge extension loaded", "info");
7125
+ ctx.ui.notify(nativePiUiContextAvailable ? "Rig worker Pi native stream bridge loaded" : "Rig worker Pi bridge extension loaded (degraded widget fallback)", "info");
7012
7126
  ctx.ui.onTerminalInput((data) => {
7013
7127
  if (data.includes("\x04")) {
7014
7128
  ctx.shutdown();
@@ -7022,6 +7136,8 @@ function createRigWorkerPiBridgeExtension(options) {
7022
7136
  const text2 = [editorText, inlineText].filter(Boolean).join(" ").trim();
7023
7137
  if (!text2)
7024
7138
  return;
7139
+ if (text2.startsWith("!"))
7140
+ return;
7025
7141
  ctx.ui.setEditorText("");
7026
7142
  routeInput(options, ctx, state, text2).catch((error) => {
7027
7143
  appendTranscript(state, "Error", error instanceof Error ? error.message : String(error));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@h-rig/cli",
3
- "version": "0.0.6-alpha.23",
3
+ "version": "0.0.6-alpha.25",
4
4
  "type": "module",
5
5
  "description": "Rig package",
6
6
  "license": "UNLICENSED",
@@ -23,11 +23,11 @@
23
23
  },
24
24
  "dependencies": {
25
25
  "@clack/prompts": "^1.2.0",
26
- "@earendil-works/pi-coding-agent": "npm:@h-rig/pi-coding-agent@0.0.6-alpha.23",
27
- "@rig/core": "npm:@h-rig/core@0.0.6-alpha.23",
28
- "@rig/runtime": "npm:@h-rig/runtime@0.0.6-alpha.23",
29
- "@rig/client": "npm:@h-rig/client@0.0.6-alpha.23",
30
- "@rig/server": "npm:@h-rig/server@0.0.6-alpha.23",
26
+ "@earendil-works/pi-coding-agent": "npm:@h-rig/pi-coding-agent@0.0.6-alpha.25",
27
+ "@rig/core": "npm:@h-rig/core@0.0.6-alpha.25",
28
+ "@rig/runtime": "npm:@h-rig/runtime@0.0.6-alpha.25",
29
+ "@rig/client": "npm:@h-rig/client@0.0.6-alpha.25",
30
+ "@rig/server": "npm:@h-rig/server@0.0.6-alpha.25",
31
31
  "picocolors": "^1.1.1"
32
32
  }
33
33
  }