@h-rig/cli 0.0.6-alpha.21 → 0.0.6-alpha.22

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/src/index.js CHANGED
@@ -6590,6 +6590,50 @@ async function promptForTaskSelection(question) {
6590
6590
 
6591
6591
  // packages/cli/src/commands/_operator-view.ts
6592
6592
  var TERMINAL_RUN_STATUSES = new Set(["completed", "failed", "stopped", "cancelled", "canceled", "closed", "merged", "needs_attention", "needs-attention"]);
6593
+ var CANONICAL_STAGES2 = [
6594
+ "Connect",
6595
+ "GitHub/task sync",
6596
+ "Prepare workspace",
6597
+ "Launch Pi",
6598
+ "Plan",
6599
+ "Implement",
6600
+ "Validate",
6601
+ "Commit",
6602
+ "Open PR",
6603
+ "Review/CI",
6604
+ "Merge",
6605
+ "Complete"
6606
+ ];
6607
+ var GREEN = "\x1B[32m";
6608
+ var BLUE = "\x1B[34m";
6609
+ var MAGENTA = "\x1B[35m";
6610
+ var YELLOW = "\x1B[33m";
6611
+ var RED = "\x1B[31m";
6612
+ var DIM = "\x1B[2m";
6613
+ var BOLD = "\x1B[1m";
6614
+ var RESET = "\x1B[0m";
6615
+ async function loadPiTuiRuntime() {
6616
+ try {
6617
+ return await import("@earendil-works/pi-tui");
6618
+ } catch {
6619
+ const base = new URL("../../../pi/packages/tui/src/", import.meta.url);
6620
+ const [tui, input, terminal, keys, utils] = await Promise.all([
6621
+ import(new URL("tui.ts", base).href),
6622
+ import(new URL("components/input.ts", base).href),
6623
+ import(new URL("terminal.ts", base).href),
6624
+ import(new URL("keys.ts", base).href),
6625
+ import(new URL("utils.ts", base).href)
6626
+ ]);
6627
+ return {
6628
+ Container: tui.Container,
6629
+ TUI: tui.TUI,
6630
+ Input: input.Input,
6631
+ ProcessTerminal: terminal.ProcessTerminal,
6632
+ matchesKey: keys.matchesKey,
6633
+ truncateToWidth: utils.truncateToWidth
6634
+ };
6635
+ }
6636
+ }
6593
6637
  function runStatusFromPayload(payload) {
6594
6638
  const run = payload.run && typeof payload.run === "object" && !Array.isArray(payload.run) ? payload.run : payload;
6595
6639
  return String(run.status ?? "unknown").toLowerCase();
@@ -6628,12 +6672,201 @@ async function readOperatorSnapshot(context, runId, options = {}) {
6628
6672
  const timelineCursor = typeof timelinePage.nextCursor === "string" ? timelinePage.nextCursor : options.timelineCursor ?? null;
6629
6673
  return { run, logs, timeline, timelineCursor, rendered: renderOperatorSnapshot({ run, logs, timeline }) };
6630
6674
  }
6675
+ function unwrapRun(runPayload) {
6676
+ return runPayload.run && typeof runPayload.run === "object" && !Array.isArray(runPayload.run) ? runPayload.run : runPayload;
6677
+ }
6678
+ function logDetail2(log3) {
6679
+ return typeof log3.detail === "string" ? log3.detail.trim() : "";
6680
+ }
6681
+ function logTitle(log3) {
6682
+ return typeof log3.title === "string" ? log3.title.trim() : "";
6683
+ }
6684
+ function renderAssistantTextFromTimeline(entries) {
6685
+ const assistant = entries.filter((entry) => entry.type === "assistant_message" && typeof entry.text === "string").at(-1);
6686
+ const text2 = typeof assistant?.text === "string" ? assistant.text.trimEnd() : "";
6687
+ if (!text2)
6688
+ return [];
6689
+ return [`${BLUE}${BOLD}Remote Pi assistant${RESET}`, ...text2.split(/\r?\n/).slice(-18)];
6690
+ }
6691
+ function renderToolLines(entries) {
6692
+ return entries.filter((entry) => entry.type === "tool_execution_start" || entry.type === "tool_execution_update" || entry.type === "tool_execution_end" || entry.type === "mcp_tool_call").slice(-8).map((entry) => `${DIM}[tool]${RESET} ${String(entry.toolName ?? entry.name ?? entry.title ?? entry.type)} ${String(entry.status ?? entry.state ?? "")}`.trim());
6693
+ }
6694
+ function renderStageLines(logs) {
6695
+ const latestByStage = new Map;
6696
+ for (const log3 of logs) {
6697
+ const title = logTitle(log3).toLowerCase();
6698
+ const stageName = String(log3.stage ?? "").toLowerCase();
6699
+ const stage = CANONICAL_STAGES2.find((candidate) => candidate.toLowerCase() === title || candidate.toLowerCase() === stageName);
6700
+ if (stage)
6701
+ latestByStage.set(stage, log3);
6702
+ }
6703
+ return CANONICAL_STAGES2.map((stage) => {
6704
+ const log3 = latestByStage.get(stage);
6705
+ const status = String(log3?.status ?? "pending");
6706
+ const detail = log3 ? logDetail2(log3) : "";
6707
+ const color = status === "completed" ? GREEN : status === "failed" || status === "rejected" ? RED : status === "pending" ? DIM : YELLOW;
6708
+ const mark = status === "completed" ? "\u2713" : status === "pending" ? "\xB7" : status === "failed" ? "\u2717" : "\u25B6";
6709
+ return `${color}${mark} ${stage}${RESET}${detail ? ` ${DIM}\u2014 ${detail.slice(0, 140)}${RESET}` : ""}`;
6710
+ });
6711
+ }
6712
+ function renderEventLines(logs) {
6713
+ return logs.filter((log3) => !CANONICAL_STAGES2.some((stage) => stage.toLowerCase() === logTitle(log3).toLowerCase())).slice(-12).flatMap((log3) => {
6714
+ const title = logTitle(log3) || "Rig event";
6715
+ const detail = logDetail2(log3);
6716
+ if (!detail)
6717
+ return [];
6718
+ const tone = String(log3.tone ?? "");
6719
+ const color = tone === "error" ? RED : tone === "tool" ? MAGENTA : DIM;
6720
+ return [`${color}[${title}]${RESET} ${detail.slice(0, 220)}`];
6721
+ });
6722
+ }
6723
+
6724
+ class RigRunComponent {
6725
+ truncateToWidth;
6726
+ snapshot = { run: {}, logs: [], timeline: [] };
6727
+ localEvents = [];
6728
+ constructor(truncateToWidth) {
6729
+ this.truncateToWidth = truncateToWidth;
6730
+ }
6731
+ update(snapshot) {
6732
+ this.snapshot = snapshot;
6733
+ }
6734
+ addLocalEvent(message2) {
6735
+ this.localEvents.push(`${new Date().toLocaleTimeString()} ${message2}`);
6736
+ this.localEvents = this.localEvents.slice(-8);
6737
+ }
6738
+ invalidate() {}
6739
+ render(width) {
6740
+ const run = unwrapRun(this.snapshot.run);
6741
+ const runId = String(run.runId ?? run.id ?? "run");
6742
+ const status = String(run.status ?? "unknown");
6743
+ const worker = typeof run.worktreePath === "string" && run.worktreePath.trim() ? run.worktreePath.trim() : typeof run.projectRoot === "string" && run.projectRoot.trim() ? run.projectRoot.trim() : "worker workspace pending";
6744
+ const lines = [
6745
+ `${BOLD}Rig Pi frontend${RESET} ${DIM}(local Pi TUI \u2192 Rig server \u2192 worker Pi backend)${RESET}`,
6746
+ `${BOLD}${runId}${RESET} \xB7 ${status} \xB7 ${DIM}${worker}${RESET}`,
6747
+ "",
6748
+ `${BOLD}Rig flow${RESET}`,
6749
+ ...renderStageLines(this.snapshot.logs ?? []),
6750
+ "",
6751
+ ...renderAssistantTextFromTimeline(this.snapshot.timeline ?? []),
6752
+ ...renderToolLines(this.snapshot.timeline ?? []),
6753
+ "",
6754
+ `${BOLD}Rig / Pi events${RESET}`,
6755
+ ...renderEventLines(this.snapshot.logs ?? []),
6756
+ ...this.localEvents.map((event) => `${GREEN}[frontend]${RESET} ${event}`),
6757
+ ""
6758
+ ];
6759
+ return lines.slice(-42).map((line) => this.truncateToWidth(line, Math.max(10, width)));
6760
+ }
6761
+ }
6762
+
6763
+ class RigInputComponent {
6764
+ matchesKey;
6765
+ truncateToWidth;
6766
+ input;
6767
+ status = "Type text, /skill:..., or remote Pi slash commands. Local: /stop /detach.";
6768
+ constructor(InputCtor, matchesKey, truncateToWidth, onSubmit, onEscape) {
6769
+ this.matchesKey = matchesKey;
6770
+ this.truncateToWidth = truncateToWidth;
6771
+ this.input = new InputCtor;
6772
+ this.input.onSubmit = (value) => {
6773
+ const text2 = value.trim();
6774
+ this.input.setValue("");
6775
+ if (text2)
6776
+ Promise.resolve(onSubmit(text2));
6777
+ };
6778
+ this.input.onEscape = onEscape;
6779
+ }
6780
+ handleInput(data) {
6781
+ if (this.matchesKey(data, "ctrl+d")) {
6782
+ this.input.onEscape?.();
6783
+ return;
6784
+ }
6785
+ this.input.handleInput?.(data);
6786
+ }
6787
+ setStatus(status) {
6788
+ this.status = status;
6789
+ }
6790
+ invalidate() {
6791
+ this.input.invalidate();
6792
+ }
6793
+ render(width) {
6794
+ return [
6795
+ `${DIM}${this.truncateToWidth(this.status, Math.max(10, width))}${RESET}`,
6796
+ `${GREEN}${BOLD}You \u2192 worker Pi:${RESET}`,
6797
+ ...this.input.render(width)
6798
+ ];
6799
+ }
6800
+ }
6801
+ async function attachRunPiTuiFrontend(context, input) {
6802
+ const piTui = await loadPiTuiRuntime();
6803
+ const terminal = new piTui.ProcessTerminal;
6804
+ const tui = new piTui.TUI(terminal);
6805
+ const root = new piTui.Container;
6806
+ const runView = new RigRunComponent(piTui.truncateToWidth);
6807
+ let detached = false;
6808
+ let steered = input.steered === true;
6809
+ let latest = await readOperatorSnapshot(context, input.runId);
6810
+ let timelineCursor = latest.timelineCursor;
6811
+ runView.update(latest);
6812
+ if (steered)
6813
+ runView.addLocalEvent("initial message queued to worker Pi.");
6814
+ const stop = () => {
6815
+ detached = true;
6816
+ tui.stop();
6817
+ };
6818
+ const inputView = new RigInputComponent(piTui.Input, piTui.matchesKey, piTui.truncateToWidth, async (line) => {
6819
+ if (line === "/detach" || line === "/quit" || line === "/q") {
6820
+ runView.addLocalEvent("detached from run.");
6821
+ stop();
6822
+ return;
6823
+ }
6824
+ if (line === "/stop") {
6825
+ await stopRunViaServer(context, input.runId);
6826
+ runView.addLocalEvent("stop requested.");
6827
+ stop();
6828
+ return;
6829
+ }
6830
+ await steerRunViaServer(context, input.runId, line);
6831
+ steered = true;
6832
+ runView.addLocalEvent(`queued to worker Pi: ${line.slice(0, 160)}`);
6833
+ tui.requestRender();
6834
+ }, stop);
6835
+ root.addChild(runView);
6836
+ root.addChild(inputView);
6837
+ tui.addChild(root);
6838
+ tui.setFocus(inputView.input);
6839
+ tui.start();
6840
+ tui.requestRender(true);
6841
+ const pollMs = Math.max(250, Math.trunc(input.pollMs ?? 1000));
6842
+ try {
6843
+ while (!detached && !TERMINAL_RUN_STATUSES.has(runStatusFromPayload(latest.run))) {
6844
+ await Bun.sleep(pollMs);
6845
+ latest = await readOperatorSnapshot(context, input.runId, { timelineCursor });
6846
+ timelineCursor = latest.timelineCursor;
6847
+ runView.update(latest);
6848
+ inputView.setStatus(`Remote worker ${runStatusFromPayload(latest.run)}. Input is forwarded to worker Pi; /stop /detach are local controls.`);
6849
+ tui.requestRender();
6850
+ }
6851
+ } finally {
6852
+ if (!detached)
6853
+ tui.stop();
6854
+ }
6855
+ return { ...latest, timelineCursor, steered, detached, rendered: renderOperatorSnapshot(latest) };
6856
+ }
6631
6857
  async function attachRunOperatorView(context, input) {
6632
6858
  let steered = false;
6633
6859
  if (input.message?.trim()) {
6634
6860
  await steerRunViaServer(context, input.runId, input.message.trim());
6635
6861
  steered = true;
6636
6862
  }
6863
+ if (input.follow && !input.once && input.interactive !== false && context.outputMode === "text" && Boolean(process.stdin.isTTY && process.stdout.isTTY)) {
6864
+ return attachRunPiTuiFrontend(context, {
6865
+ runId: input.runId,
6866
+ pollMs: input.pollMs,
6867
+ steered
6868
+ });
6869
+ }
6637
6870
  const surface = createOperatorSurface({ interactive: input.interactive !== false });
6638
6871
  let snapshot = await readOperatorSnapshot(context, input.runId);
6639
6872
  if (context.outputMode === "text") {
@@ -6774,92 +7007,6 @@ function formatSubmittedRun(input) {
6774
7007
  `);
6775
7008
  }
6776
7009
 
6777
- // packages/cli/src/commands/_pi-session.ts
6778
- import { spawn as spawn2 } from "child_process";
6779
- function buildPiRigSessionEnv(input) {
6780
- return {
6781
- RIG_PROJECT_ROOT: input.projectRoot,
6782
- PROJECT_RIG_ROOT: input.projectRoot,
6783
- RIG_RUN_ID: input.runId,
6784
- RIG_SERVER_RUN_ID: input.runId,
6785
- RIG_RUNTIME_ADAPTER: "pi",
6786
- RIG_SERVER_URL: input.serverUrl,
6787
- RIG_SERVER_BASE_URL: input.serverUrl,
6788
- RIG_STEERING_POLL_MS: process.env.RIG_STEERING_POLL_MS?.trim() || "1000",
6789
- RIG_PI_OPERATOR_SESSION: "1",
6790
- ...input.taskId ? { RIG_TASK_ID: input.taskId } : {},
6791
- ...input.authToken ? { RIG_AUTH_TOKEN: input.authToken } : {}
6792
- };
6793
- }
6794
- function shellBinary(name) {
6795
- const explicit = process.env.RIG_PI_BINARY?.trim();
6796
- if (explicit)
6797
- return explicit;
6798
- return Bun.which(name) || name;
6799
- }
6800
- function buildPiRigSessionCommand(input) {
6801
- const configuredExtension = input.extensionSource ?? process.env.RIG_PI_RIG_EXTENSION_SOURCE?.trim();
6802
- const extensionSource = configuredExtension && configuredExtension.length > 0 ? configuredExtension : resolvePiRigPackageSource(input.projectRoot);
6803
- const initialCommand = `/rig attach ${input.runId}`;
6804
- return [
6805
- shellBinary("pi"),
6806
- "--no-extensions",
6807
- "--extension",
6808
- extensionSource,
6809
- initialCommand
6810
- ];
6811
- }
6812
- async function launchPiRigSession(context, input) {
6813
- if (context.outputMode !== "text" || !process.stdin.isTTY || !process.stdout.isTTY) {
6814
- return { launched: false, exitCode: null, command: [] };
6815
- }
6816
- if (process.env.RIG_DISABLE_PI_LAUNCH === "1") {
6817
- return { launched: false, exitCode: null, command: [] };
6818
- }
6819
- const server = await ensureServerForCli(context.projectRoot);
6820
- const command = buildPiRigSessionCommand({ ...input, projectRoot: context.projectRoot });
6821
- const env = {
6822
- ...process.env,
6823
- ...buildPiRigSessionEnv({
6824
- projectRoot: context.projectRoot,
6825
- runId: input.runId,
6826
- taskId: input.taskId,
6827
- serverUrl: server.baseUrl,
6828
- authToken: server.authToken
6829
- })
6830
- };
6831
- process.stdout.write(`Launching Pi for Rig run ${input.runId}\u2026
6832
- `);
6833
- process.stdout.write(`Pi command: ${formatCommand(command)}
6834
- `);
6835
- const launchedAt = Date.now();
6836
- const child = spawn2(command[0], command.slice(1), {
6837
- cwd: context.projectRoot,
6838
- env,
6839
- stdio: "inherit"
6840
- });
6841
- const launchError = await new Promise((resolve20) => {
6842
- child.once("error", (error) => {
6843
- resolve20({ error: error.message });
6844
- });
6845
- child.once("close", (code) => resolve20({ code }));
6846
- });
6847
- if ("error" in launchError) {
6848
- process.stderr.write(`Failed to launch Pi; falling back to Rig attach view: ${launchError.error}
6849
- `);
6850
- return { launched: false, exitCode: null, command, error: launchError.error };
6851
- }
6852
- const exitCode = launchError.code;
6853
- const elapsedMs = Date.now() - launchedAt;
6854
- if (typeof exitCode === "number" && exitCode !== 0 && elapsedMs < 5000) {
6855
- const error = `Pi exited during startup with code ${exitCode}.`;
6856
- process.stderr.write(`${error} Falling back to Rig attach view.
6857
- `);
6858
- return { launched: false, exitCode, command, error };
6859
- }
6860
- return { launched: true, exitCode, command };
6861
- }
6862
-
6863
7010
  // packages/cli/src/commands/run.ts
6864
7011
  function normalizeRemoteRunDetails(payload) {
6865
7012
  const run = payload.run;
@@ -7084,20 +7231,13 @@ async function executeRun(context, args) {
7084
7231
  throw new CliError2("run attach requires a run id.", 2);
7085
7232
  }
7086
7233
  let steered = false;
7087
- const shouldTryPiAttach = context.outputMode === "text" && follow.value && !once.value && Boolean(process.stdin.isTTY && process.stdout.isTTY) && process.env.RIG_DISABLE_PI_LAUNCH !== "1";
7088
- if (shouldTryPiAttach && messageOption.value?.trim()) {
7234
+ if (messageOption.value?.trim()) {
7089
7235
  await steerRunViaServer(context, runId, messageOption.value.trim());
7090
7236
  steered = true;
7091
7237
  }
7092
- if (shouldTryPiAttach) {
7093
- const piSession = await launchPiRigSession(context, { runId });
7094
- if (piSession.launched) {
7095
- return { ok: true, group: "run", command, details: { runId, steered, mode: "pi", ...piSession } };
7096
- }
7097
- }
7098
7238
  const attached = await attachRunOperatorView(context, {
7099
7239
  runId,
7100
- message: shouldTryPiAttach ? null : messageOption.value ?? null,
7240
+ message: null,
7101
7241
  once: once.value,
7102
7242
  follow: follow.value,
7103
7243
  pollMs: parsePositiveInt(pollMs.value, "--poll-ms", 2000)
@@ -7824,20 +7964,7 @@ async function executeTask(context, args, options) {
7824
7964
  let attachDetails = null;
7825
7965
  if (!detachResult.value && context.outputMode === "text") {
7826
7966
  console.log(formatSubmittedRun({ runId: submitted.runId, task: selectedTask ? summarizeTask(selectedTask) : null }));
7827
- if (runtimeAdapter === "pi") {
7828
- const piSession = await launchPiRigSession(context, {
7829
- runId: submitted.runId,
7830
- taskId: selectedTaskId,
7831
- title: titleResult.value ?? readTaskString(selectedTask ?? {}, "title"),
7832
- runtimeAdapter
7833
- });
7834
- attachDetails = { mode: "pi", ...piSession };
7835
- if (!piSession.launched) {
7836
- attachDetails = await attachRunOperatorView(context, { runId: submitted.runId, follow: true });
7837
- }
7838
- } else {
7839
- attachDetails = await attachRunOperatorView(context, { runId: submitted.runId, follow: true });
7840
- }
7967
+ attachDetails = await attachRunOperatorView(context, { runId: submitted.runId, follow: true });
7841
7968
  } else if (context.outputMode === "text") {
7842
7969
  console.log(formatSubmittedRun({ runId: submitted.runId, task: selectedTask ? summarizeTask(selectedTask) : null }));
7843
7970
  }
@@ -7924,7 +8051,7 @@ async function executeTask(context, args, options) {
7924
8051
  // packages/cli/src/commands/task-run-driver.ts
7925
8052
  import { copyFileSync as copyFileSync3, existsSync as existsSync12, mkdirSync as mkdirSync8, readFileSync as readFileSync10, statSync as statSync2, writeFileSync as writeFileSync6 } from "fs";
7926
8053
  import { resolve as resolve21 } from "path";
7927
- import { spawn as spawn3, spawnSync as spawnSync4 } from "child_process";
8054
+ import { spawn as spawn2, spawnSync as spawnSync4 } from "child_process";
7928
8055
  import { createInterface as createLineInterface } from "readline";
7929
8056
  import { loadConfig as loadConfig2 } from "@rig/core/load-config";
7930
8057
  import {
@@ -7981,6 +8108,7 @@ function buildPiRigBridgeEnv(input) {
7981
8108
  RIG_SERVER_RUN_ID: input.runId,
7982
8109
  RIG_TASK_ID: input.taskId,
7983
8110
  RIG_RUNTIME_ADAPTER: "pi",
8111
+ RIG_STEERING_POLL_MS: "0",
7984
8112
  ...input.serverUrl ? { RIG_SERVER_URL: input.serverUrl } : {},
7985
8113
  ...input.authToken ? { RIG_AUTH_TOKEN: input.authToken } : {},
7986
8114
  ...githubBridgeEnv(githubToken)
@@ -8152,7 +8280,7 @@ async function runCheckedCommand(command, args, cwd, label = "git") {
8152
8280
  }
8153
8281
  function createCommandRunner(binary) {
8154
8282
  return async (args, options) => {
8155
- const child = spawn3(binary, [...args], {
8283
+ const child = spawn2(binary, [...args], {
8156
8284
  cwd: options?.cwd,
8157
8285
  stdio: ["ignore", "pipe", "pipe"]
8158
8286
  });
@@ -8576,6 +8704,69 @@ function appendAssistantTimelineFromRecord(input) {
8576
8704
  }
8577
8705
  return nextAssistantText;
8578
8706
  }
8707
+ function appendPiRpcProtocolLogFromRecord(input) {
8708
+ const type = typeof input.record.type === "string" ? input.record.type : "";
8709
+ if (type === "response") {
8710
+ const command = typeof input.record.command === "string" ? input.record.command : "rpc";
8711
+ const success = input.record.success !== false;
8712
+ if (success && command !== "prompt" && command !== "steer" && command !== "follow_up" && command !== "set_session_name") {
8713
+ return true;
8714
+ }
8715
+ appendRunLog(input.projectRoot, input.runId, {
8716
+ id: input.nextRunLogId(),
8717
+ title: success ? "Pi RPC response" : "Pi RPC error",
8718
+ detail: success ? `${command}: accepted` : `${command}: ${String(input.record.error ?? "failed")}`,
8719
+ tone: success ? "tool" : "error",
8720
+ status: input.status,
8721
+ payload: input.record,
8722
+ createdAt: new Date().toISOString()
8723
+ });
8724
+ emitServerRunEvent({ type: "log", runId: input.runId, title: success ? "Pi RPC response" : "Pi RPC error" });
8725
+ return true;
8726
+ }
8727
+ if (type !== "extension_ui_request")
8728
+ return false;
8729
+ const method = typeof input.record.method === "string" ? input.record.method : "ui";
8730
+ let title = "Pi UI event";
8731
+ let detail = method;
8732
+ let tone = "info";
8733
+ if (method === "notify") {
8734
+ title = "Pi notification";
8735
+ detail = String(input.record.message ?? "");
8736
+ tone = input.record.notifyType === "error" ? "error" : "info";
8737
+ } else if (method === "setStatus") {
8738
+ title = "Pi UI status";
8739
+ detail = `${String(input.record.statusKey ?? "status")}: ${String(input.record.statusText ?? "cleared")}`;
8740
+ tone = "tool";
8741
+ } else if (method === "setWidget") {
8742
+ title = "Pi UI widget";
8743
+ const lines = Array.isArray(input.record.widgetLines) ? input.record.widgetLines.map((line) => String(line)).join(" | ") : "cleared";
8744
+ detail = `${String(input.record.widgetKey ?? "widget")}: ${lines}`.slice(0, 500);
8745
+ tone = "tool";
8746
+ } else if (method === "setTitle") {
8747
+ title = "Pi UI title";
8748
+ detail = String(input.record.title ?? "");
8749
+ tone = "tool";
8750
+ } else if (method === "set_editor_text") {
8751
+ title = "Pi editor update";
8752
+ detail = String(input.record.text ?? "").slice(0, 500);
8753
+ tone = "tool";
8754
+ } else {
8755
+ title = "Pi UI request";
8756
+ detail = `${method}: ${String(input.record.title ?? input.record.message ?? "")}`.trim();
8757
+ }
8758
+ appendRunLog(input.projectRoot, input.runId, {
8759
+ id: input.nextRunLogId(),
8760
+ title,
8761
+ detail,
8762
+ tone,
8763
+ status: input.status,
8764
+ payload: input.record,
8765
+ createdAt: new Date().toISOString()
8766
+ });
8767
+ emitServerRunEvent({ type: "log", runId: input.runId, title });
8768
+ return true;
8769
+ }
8579
8770
  function appendPiToolTimelineFromRecord(input) {
8580
8771
  const type = typeof input.record.type === "string" ? input.record.type : "";
8581
8772
  if (type !== "tool_execution_start" && type !== "tool_execution_update" && type !== "tool_execution_end")
@@ -8594,7 +8785,7 @@ function appendPiToolTimelineFromRecord(input) {
8594
8785
  }
8595
8786
  function isNonRenderablePiProtocolRecord(record) {
8596
8787
  const type = typeof record.type === "string" ? record.type : "";
8597
- return type === "message_start" || type === "message_end" || type === "turn_start" || type === "turn_end" || type === "tool_result" || type === "message_update" && (!record.assistantMessageEvent || typeof record.assistantMessageEvent !== "object" || Array.isArray(record.assistantMessageEvent) || record.assistantMessageEvent.type !== "text_delta");
8788
+ return type === "agent_start" || type === "agent_end" || type === "message_start" || type === "message_end" || type === "turn_start" || type === "turn_end" || type === "tool_result" || type === "message_update" && (!record.assistantMessageEvent || typeof record.assistantMessageEvent !== "object" || Array.isArray(record.assistantMessageEvent) || record.assistantMessageEvent.type !== "text_delta");
8598
8789
  }
8599
8790
  function appendToolTimelineFromLog(input) {
8600
8791
  const title = typeof input.log.title === "string" ? input.log.title : "";
@@ -8757,11 +8948,8 @@ async function executeRigOwnedTaskRun(context, input) {
8757
8948
  ...input.model ? ["--model", input.model] : [],
8758
8949
  "--prompt"
8759
8950
  ] : input.runtimeAdapter === "pi" ? [
8760
- "--print",
8761
- "--verbose",
8762
8951
  "--mode",
8763
- "json",
8764
- "--no-session",
8952
+ "rpc",
8765
8953
  ...input.model ? ["--model", input.model] : []
8766
8954
  ] : [
8767
8955
  "--print",
@@ -8858,7 +9046,7 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
8858
9046
  projectRoot: context.projectRoot,
8859
9047
  runId: input.runId,
8860
9048
  stage,
8861
- detail: stage === "Launch Pi" ? "Pi runtime bridge starting with pi-rig environment." : stage === "Plan" ? `${planningClassification.planningRequired ? "recorded" : "skipped"} (${planningClassification.reason}; size=${planningClassification.size}; risk=${planningClassification.risk})` : stage === "Implement" ? "Pi implementation pass is running." : null,
9049
+ detail: stage === "Launch Pi" ? "Pi RPC runtime bridge starting with pi-rig environment." : stage === "Plan" ? `${planningClassification.planningRequired ? "recorded" : "skipped"} (${planningClassification.reason}; size=${planningClassification.size}; risk=${planningClassification.risk})` : stage === "Implement" ? "Pi implementation pass is running." : null,
8862
9050
  status: stage === "Implement" || stage === "Launch Pi" ? "running" : "completed"
8863
9051
  });
8864
9052
  }
@@ -8951,6 +9139,7 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
8951
9139
  detail: detail ?? "Verifier review is running."
8952
9140
  });
8953
9141
  };
9142
+ const nextRunLogId = createRunLogIdFactory(input.runId);
8954
9143
  const handleWrapperEvent = (rawPayload) => {
8955
9144
  let event = null;
8956
9145
  try {
@@ -9085,9 +9274,23 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
9085
9274
  }
9086
9275
  return true;
9087
9276
  }
9277
+ if (event.type === "pi.rpc.prompt.sent" || event.type === "pi.rpc.steering.delivered" || event.type === "pi.rpc.steering.poll.failed" || event.type === "pi.rpc.extension_ui.cancelled") {
9278
+ const title = event.type === "pi.rpc.prompt.sent" ? "Delivered initial prompt to worker Pi" : event.type === "pi.rpc.steering.delivered" ? "Delivered steering to worker Pi" : event.type === "pi.rpc.steering.poll.failed" ? "Worker Pi steering poll failed" : "Pi RPC UI request auto-cancelled";
9279
+ const detail = event.type === "pi.rpc.prompt.sent" ? `${String(payload.kind ?? "prompt")} prompt (${String(payload.bytes ?? "unknown")} bytes)` : event.type === "pi.rpc.steering.delivered" ? `${String(payload.actor ?? "operator")}: ${String(payload.message ?? "")}`.slice(0, 500) : event.type === "pi.rpc.steering.poll.failed" ? String(payload.error ?? "steering poll failed") : `${String(payload.method ?? "ui")}: ${String(payload.reason ?? "noninteractive worker session")}`;
9280
+ appendRunLog(context.projectRoot, input.runId, {
9281
+ id: nextRunLogId(),
9282
+ title,
9283
+ detail,
9284
+ tone: event.type === "pi.rpc.steering.poll.failed" ? "error" : "info",
9285
+ status: reviewStarted ? "reviewing" : verificationStarted ? "validating" : "running",
9286
+ payload,
9287
+ createdAt: new Date().toISOString()
9288
+ });
9289
+ emitServerRunEvent({ type: "log", runId: input.runId, title });
9290
+ return true;
9291
+ }
9088
9292
  return false;
9089
9293
  };
9090
- const nextRunLogId = createRunLogIdFactory(input.runId);
9091
9294
  const handleAgentStdoutLine = (line) => {
9092
9295
  const trimmed = line.trim();
9093
9296
  if (!trimmed)
@@ -9121,6 +9324,15 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
9121
9324
  try {
9122
9325
  const record = JSON.parse(trimmed);
9123
9326
  const liveLogStatus = reviewStarted ? "reviewing" : verificationStarted ? "validating" : "running";
9327
+ if (input.runtimeAdapter === "pi" && appendPiRpcProtocolLogFromRecord({
9328
+ projectRoot: context.projectRoot,
9329
+ runId: input.runId,
9330
+ record,
9331
+ status: liveLogStatus,
9332
+ nextRunLogId
9333
+ })) {
9334
+ return;
9335
+ }
9124
9336
  if (input.runtimeAdapter === "pi" && appendPiToolTimelineFromRecord({ projectRoot: context.projectRoot, runId: input.runId, record })) {
9125
9337
  emitServerRunEvent({ type: "timeline", runId: input.runId });
9126
9338
  return;
@@ -9266,7 +9478,7 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
9266
9478
  }
9267
9479
  for (let attempt = 1;!exit && attempt <= maxAttempts; attempt += 1) {
9268
9480
  const attemptHostAgentCommand = buildHostAgentCommand(currentPrompt);
9269
- const child = spawn3(attemptHostAgentCommand[0], attemptHostAgentCommand.slice(1), {
9481
+ const child = spawn2(attemptHostAgentCommand[0], attemptHostAgentCommand.slice(1), {
9270
9482
  cwd: context.projectRoot,
9271
9483
  env: childEnv,
9272
9484
  stdio: ["pipe", "pipe", "pipe"]
@@ -9613,7 +9825,7 @@ Failed to update task source for ${input.taskId ?? runtimeTaskId} to failed: ${e
9613
9825
  });
9614
9826
  emitServerRunEvent({ type: "log", runId: input.runId, title: "Steering Pi from PR feedback" });
9615
9827
  const feedbackCommand = buildHostAgentCommand(message2);
9616
- const child = spawn3(feedbackCommand[0], feedbackCommand.slice(1), {
9828
+ const child = spawn2(feedbackCommand[0], feedbackCommand.slice(1), {
9617
9829
  cwd: latestRuntimeWorkspace ?? context.projectRoot,
9618
9830
  env: childEnv,
9619
9831
  stdio: ["pipe", "pipe", "pipe"]
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@h-rig/cli",
3
- "version": "0.0.6-alpha.21",
3
+ "version": "0.0.6-alpha.22",
4
4
  "type": "module",
5
5
  "description": "Rig package",
6
6
  "license": "UNLICENSED",
@@ -23,10 +23,11 @@
23
23
  },
24
24
  "dependencies": {
25
25
  "@clack/prompts": "^1.2.0",
26
- "@rig/core": "npm:@h-rig/core@0.0.6-alpha.21",
27
- "@rig/runtime": "npm:@h-rig/runtime@0.0.6-alpha.21",
28
- "@rig/client": "npm:@h-rig/client@0.0.6-alpha.21",
29
- "@rig/server": "npm:@h-rig/server@0.0.6-alpha.21",
26
+ "@earendil-works/pi-tui": "^0.75.5",
27
+ "@rig/core": "npm:@h-rig/core@0.0.6-alpha.22",
28
+ "@rig/runtime": "npm:@h-rig/runtime@0.0.6-alpha.22",
29
+ "@rig/client": "npm:@h-rig/client@0.0.6-alpha.22",
30
+ "@rig/server": "npm:@h-rig/server@0.0.6-alpha.22",
30
31
  "picocolors": "^1.1.1"
31
32
  }
32
33
  }