@h-rig/cli 0.0.6-alpha.17 → 0.0.6-alpha.19

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
@@ -2886,6 +2886,14 @@ async function switchServerProjectRootViaServer(context, projectRoot, options =
2886
2886
  }
2887
2887
  throw new CliError2(`Rig server did not switch to ${projectRoot} before timeout (${lastError instanceof Error ? lastError.message : String(lastError ?? "no status")}).`, 1);
2888
2888
  }
2889
+ async function listRunsViaServer(context, options = {}) {
2890
+ const url = new URL("http://rig.local/api/runs");
2891
+ if (options.limit !== undefined)
2892
+ url.searchParams.set("limit", String(options.limit));
2893
+ const payload = await requestServerJson(context, `${url.pathname}${url.search}`);
2894
+ const runs = Array.isArray(payload) ? payload : payload && typeof payload === "object" && !Array.isArray(payload) && Array.isArray(payload.runs) ? payload.runs : [];
2895
+ return runs.filter((entry) => Boolean(entry && typeof entry === "object" && !Array.isArray(entry)));
2896
+ }
2889
2897
  async function getRunDetailsViaServer(context, runId) {
2890
2898
  const payload = await requestServerJson(context, `/api/runs/${encodeURIComponent(runId)}`);
2891
2899
  return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : {};
@@ -4529,6 +4537,7 @@ function countDoctorFailures(checks) {
4529
4537
 
4530
4538
  // packages/cli/src/commands/init.ts
4531
4539
  var RIG_CONFIG_PACKAGE_DIST_TAG = "latest";
4540
+ var DEFAULT_REMOTE_RIG_URL = "https://where.rig-does.work";
4532
4541
  var RIG_CONFIG_DEV_DEPENDENCIES = {
4533
4542
  "@rig/core": `npm:@h-rig/core@${RIG_CONFIG_PACKAGE_DIST_TAG}`,
4534
4543
  "@rig/standard-plugin": `npm:@h-rig/standard-plugin@${RIG_CONFIG_PACKAGE_DIST_TAG}`
@@ -4633,6 +4642,19 @@ function readGhAuthToken() {
4633
4642
  }
4634
4643
  return result.stdout.trim();
4635
4644
  }
4645
+ function refreshGhProjectScopesAndReadToken() {
4646
+ const result = spawnSync("gh", ["auth", "refresh", "--scopes", "read:project"], {
4647
+ encoding: "utf8",
4648
+ stdio: ["inherit", "pipe", "pipe"]
4649
+ });
4650
+ if (result.status !== 0)
4651
+ return null;
4652
+ try {
4653
+ return readGhAuthToken();
4654
+ } catch {
4655
+ return null;
4656
+ }
4657
+ }
4636
4658
  async function loadClackPrompts() {
4637
4659
  return await import("@clack/prompts");
4638
4660
  }
@@ -4728,12 +4750,27 @@ async function promptManualProjectStatusMapping(prompts) {
4728
4750
  }
4729
4751
  return statuses;
4730
4752
  }
4731
- async function promptGitHubProjectConfig(context, prompts, repoSlug, githubToken) {
4753
+ function projectScopeError(value) {
4754
+ const text2 = typeof value === "string" ? value : JSON.stringify(value ?? "");
4755
+ return /INSUFFICIENT_SCOPES|read:project|required scopes/i.test(text2);
4756
+ }
4757
+ function optionName(option) {
4758
+ return String(option.name ?? option.label ?? option.id ?? "").trim();
4759
+ }
4760
+ function autoProjectStatusValue(options, key, label) {
4761
+ const candidates = [DEFAULT_PROJECT_STATUS_OPTIONS[key], label].filter((value) => Boolean(value)).map((value) => value.trim().toLowerCase());
4762
+ const match = options.find((option) => candidates.includes(optionName(option).toLowerCase()));
4763
+ if (!match)
4764
+ return null;
4765
+ return String(match.id ?? match.name);
4766
+ }
4767
+ async function promptGitHubProjectConfig(context, prompts, repoSlug, githubToken, refreshProjectToken) {
4732
4768
  const projectChoice = await promptSelect(prompts, {
4733
4769
  message: "GitHub Projects status sync",
4770
+ initialValue: "select",
4734
4771
  options: [
4735
- { value: "off", label: "Off" },
4736
4772
  { value: "select", label: "Select accessible ProjectV2" },
4773
+ { value: "off", label: "Off" },
4737
4774
  { value: "manual", label: "Enter ProjectV2 ids manually" }
4738
4775
  ]
4739
4776
  });
@@ -4749,16 +4786,22 @@ async function promptGitHubProjectConfig(context, prompts, repoSlug, githubToken
4749
4786
  const owner = repoOwnerFromSlug(repoSlug);
4750
4787
  if (!owner)
4751
4788
  throw new CliError2(`Cannot derive GitHub owner from repo slug ${repoSlug}.`, 1);
4752
- const projectsPayload = await listGitHubProjectsForInit(context, owner, githubToken).catch((error) => ({ ok: false, error: error instanceof Error ? error.message : String(error), projects: [] }));
4753
- const projects = recordArray(projectsPayload, "projects");
4789
+ let activeToken = githubToken?.trim() || null;
4790
+ let projectsPayload = await listGitHubProjectsForInit(context, owner, activeToken).catch((error) => ({ ok: false, error: error instanceof Error ? error.message : String(error), projects: [] }));
4791
+ let projects = recordArray(projectsPayload, "projects");
4792
+ if (projects.length === 0 && projectScopeError(projectsPayload.error) && refreshProjectToken) {
4793
+ prompts.outro?.("GitHub token is missing read:project; refreshing gh auth scopes and retrying Projects.");
4794
+ const refreshedToken = refreshProjectToken();
4795
+ if (refreshedToken) {
4796
+ activeToken = refreshedToken;
4797
+ projectsPayload = await listGitHubProjectsForInit(context, owner, activeToken).catch((error) => ({ ok: false, error: error instanceof Error ? error.message : String(error), projects: [] }));
4798
+ projects = recordArray(projectsPayload, "projects");
4799
+ }
4800
+ }
4754
4801
  if (projects.length === 0) {
4755
- const error = typeof projectsPayload.error === "string" ? ` (${projectsPayload.error})` : "";
4756
- prompts.outro?.(`No accessible GitHub Projects were returned${error}; falling back to manual ProjectV2 ids.`);
4757
- return {
4758
- githubProject: await promptRequiredText(prompts, { message: "GitHub ProjectV2 id", placeholder: "PVT_..." }),
4759
- githubProjectStatusField: await promptRequiredText(prompts, { message: "Project Status field id", placeholder: "field_status" }),
4760
- githubProjectStatuses: await promptManualProjectStatusMapping(prompts)
4761
- };
4802
+ const error = typeof projectsPayload.error === "string" ? ` (${String(projectsPayload.error).replace(/\s+/g, " ").slice(0, 240)})` : "";
4803
+ prompts.outro?.(`No accessible GitHub Projects were returned${error}; continuing with GitHub Projects status sync off.`);
4804
+ return { githubProject: "off", ...activeToken ? { githubToken: activeToken } : {} };
4762
4805
  }
4763
4806
  const selectedProjectId = await promptSelect(prompts, {
4764
4807
  message: "GitHub ProjectV2 project",
@@ -4772,23 +4815,34 @@ async function promptGitHubProjectConfig(context, prompts, repoSlug, githubToken
4772
4815
  ]
4773
4816
  });
4774
4817
  const projectId = selectedProjectId === "manual" ? await promptRequiredText(prompts, { message: "GitHub ProjectV2 id", placeholder: "PVT_..." }) : selectedProjectId;
4775
- const fieldPayload = await getGitHubProjectStatusFieldForInit(context, projectId, githubToken).catch((error) => ({ ok: false, error: error instanceof Error ? error.message : String(error) }));
4818
+ const fieldPayload = await getGitHubProjectStatusFieldForInit(context, projectId, activeToken).catch((error) => ({ ok: false, error: error instanceof Error ? error.message : String(error) }));
4776
4819
  const fieldPayloadRecord = fieldPayload && typeof fieldPayload === "object" && !Array.isArray(fieldPayload) ? fieldPayload : {};
4777
4820
  const rawField = fieldPayloadRecord.field;
4778
4821
  const field = rawField && typeof rawField === "object" && !Array.isArray(rawField) ? rawField : null;
4779
4822
  const fieldId = typeof field?.id === "string" && field.id.trim() ? field.id : await promptRequiredText(prompts, { message: "Project Status field id", placeholder: "field_status" });
4780
4823
  const options = Array.isArray(field?.options) ? field.options.filter((entry) => Boolean(entry && typeof entry === "object" && !Array.isArray(entry))) : [];
4781
4824
  if (options.length === 0) {
4782
- return { githubProject: projectId, githubProjectStatusField: fieldId, githubProjectStatuses: await promptManualProjectStatusMapping(prompts) };
4825
+ return {
4826
+ githubProject: projectId,
4827
+ githubProjectStatusField: fieldId,
4828
+ githubProjectStatuses: await promptManualProjectStatusMapping(prompts),
4829
+ ...activeToken ? { githubToken: activeToken } : {}
4830
+ };
4783
4831
  }
4784
4832
  const statuses = {};
4785
4833
  for (const [key, label] of Object.entries(PROJECT_STATUS_PROMPTS)) {
4786
- statuses[key] = await promptSelect(prompts, {
4834
+ const auto = autoProjectStatusValue(options, key, label);
4835
+ statuses[key] = auto ?? await promptSelect(prompts, {
4787
4836
  message: `Project status option for ${label}`,
4788
- options: options.map((option) => ({ value: String(option.id ?? option.name), label: String(option.name ?? option.id) }))
4837
+ options: options.map((option) => ({ value: String(option.id ?? option.name), label: optionName(option) }))
4789
4838
  });
4790
4839
  }
4791
- return { githubProject: projectId, githubProjectStatusField: fieldId, githubProjectStatuses: Object.keys(statuses).length > 0 ? statuses : undefined };
4840
+ return {
4841
+ githubProject: projectId,
4842
+ githubProjectStatusField: fieldId,
4843
+ githubProjectStatuses: Object.keys(statuses).length > 0 ? statuses : undefined,
4844
+ ...activeToken ? { githubToken: activeToken } : {}
4845
+ };
4792
4846
  }
4793
4847
  function sleep2(ms) {
4794
4848
  return new Promise((resolve18) => setTimeout(resolve18, ms));
@@ -5119,12 +5173,13 @@ async function runInteractiveControlPlaneInit(context, prompts) {
5119
5173
  });
5120
5174
  const serverChoice = await promptSelect(prompts, {
5121
5175
  message: "Rig server",
5176
+ initialValue: "remote",
5122
5177
  options: [
5123
- { value: "local", label: "Local server", hint: "run on this machine" },
5124
- { value: "remote", label: "Remote server", hint: "connect to an HTTPS Rig server" }
5178
+ { value: "remote", label: "Remote server", hint: "connect to an HTTPS Rig server" },
5179
+ { value: "local", label: "Local server", hint: "run on this machine" }
5125
5180
  ]
5126
5181
  });
5127
- const remoteUrl = serverChoice === "remote" ? await promptRequiredText(prompts, { message: "Remote Rig server URL", placeholder: "https://rig.example.com" }) : undefined;
5182
+ const remoteUrl = serverChoice === "remote" ? await promptRequiredText(prompts, { message: "Remote Rig server URL", placeholder: DEFAULT_REMOTE_RIG_URL, initialValue: DEFAULT_REMOTE_RIG_URL }) : undefined;
5128
5183
  let remoteCheckout;
5129
5184
  if (serverChoice === "remote") {
5130
5185
  const checkout = await promptSelect(prompts, {
@@ -5156,31 +5211,35 @@ async function runInteractiveControlPlaneInit(context, prompts) {
5156
5211
  { value: "skip", label: "Skip for now" }
5157
5212
  ]
5158
5213
  });
5214
+ let remoteGhTokenConfirmed = false;
5159
5215
  if (serverChoice === "remote" && authMethod === "gh") {
5160
5216
  if (!prompts.confirm)
5161
5217
  throw new CliError2("Remote gh-token import requires explicit confirmation.", 1);
5162
5218
  const confirmed = await prompts.confirm({
5163
5219
  message: `This sends a GitHub token from this machine to ${remoteUrl}. Continue?`,
5164
- initialValue: false
5220
+ initialValue: true
5165
5221
  });
5166
5222
  if (prompts.isCancel(confirmed) || confirmed !== true) {
5167
5223
  throw new CliError2("Remote gh-token import cancelled.", 1);
5168
5224
  }
5225
+ remoteGhTokenConfirmed = true;
5169
5226
  }
5170
5227
  const githubToken = authMethod === "token" ? await promptRequiredText(prompts, { message: "GitHub token", placeholder: "ghp_..." }) : authMethod === "gh" ? readGhAuthToken() : undefined;
5171
- const projectConfig = await promptGitHubProjectConfig(context, prompts, repoSlug, githubToken);
5228
+ const projectConfig = await promptGitHubProjectConfig(context, prompts, repoSlug, githubToken, authMethod === "gh" ? refreshGhProjectScopesAndReadToken : undefined);
5229
+ const effectiveGithubToken = projectConfig.githubToken ?? githubToken;
5172
5230
  const result = await runControlPlaneInit(context, {
5173
5231
  server: serverChoice,
5174
5232
  remoteUrl,
5175
5233
  repoSlug,
5176
- githubToken,
5234
+ githubToken: effectiveGithubToken,
5177
5235
  githubAuthMethod: authMethod,
5178
5236
  githubProject: projectConfig.githubProject,
5179
5237
  githubProjectStatusField: projectConfig.githubProjectStatusField,
5180
5238
  githubProjectStatuses: projectConfig.githubProjectStatuses,
5181
5239
  remoteCheckout,
5182
5240
  repair,
5183
- privateStateOnly
5241
+ privateStateOnly,
5242
+ yes: remoteGhTokenConfirmed || undefined
5184
5243
  });
5185
5244
  const details = result.details && typeof result.details === "object" && !Array.isArray(result.details) ? result.details : {};
5186
5245
  const deviceAuth = details.deviceAuth && typeof details.deviceAuth === "object" && !Array.isArray(details.deviceAuth) ? details.deviceAuth : null;
@@ -6355,6 +6414,42 @@ var CANONICAL_STAGES = [
6355
6414
  function logDetail(log3) {
6356
6415
  return typeof log3.detail === "string" ? log3.detail.trim() : "";
6357
6416
  }
6417
+ function parseProviderProtocolLog(title, detail) {
6418
+ if (title.trim().toLowerCase() !== "agent output")
6419
+ return null;
6420
+ if (!detail.startsWith("{") || !detail.endsWith("}"))
6421
+ return null;
6422
+ try {
6423
+ const record = JSON.parse(detail);
6424
+ if (!record || typeof record !== "object" || Array.isArray(record))
6425
+ return null;
6426
+ const type = record.type;
6427
+ return typeof type === "string" && [
6428
+ "assistant",
6429
+ "message_start",
6430
+ "message_update",
6431
+ "message_end",
6432
+ "stream_event",
6433
+ "tool_result",
6434
+ "tool_execution_start",
6435
+ "tool_execution_update",
6436
+ "tool_execution_end",
6437
+ "turn_start",
6438
+ "turn_end"
6439
+ ].includes(type) ? record : null;
6440
+ } catch {
6441
+ return null;
6442
+ }
6443
+ }
6444
+ function renderProviderProtocolLog(record) {
6445
+ const type = typeof record.type === "string" ? record.type : "";
6446
+ if (type === "tool_execution_start" || type === "tool_execution_update" || type === "tool_execution_end") {
6447
+ const toolName = String(record.toolName ?? record.name ?? "tool");
6448
+ const status = type === "tool_execution_start" ? "started" : type === "tool_execution_end" ? record.isError === true || record.result && typeof record.result === "object" && !Array.isArray(record.result) && record.result.isError === true ? "failed" : "completed" : "running";
6449
+ return `[Pi tool] ${toolName} ${status}`;
6450
+ }
6451
+ return null;
6452
+ }
6358
6453
  function entryId(entry, fallback) {
6359
6454
  return typeof entry.id === "string" && entry.id.trim() ? entry.id : fallback;
6360
6455
  }
@@ -6399,12 +6494,16 @@ function createPiRunStreamRenderer(output = process.stdout) {
6399
6494
  if (entry.type === "assistant_message" && typeof entry.text === "string") {
6400
6495
  const text2 = entry.text;
6401
6496
  const previousText = assistantTextById.get(id) ?? "";
6497
+ if (!previousText && text2.trim()) {
6498
+ writeLine("[Pi assistant]");
6499
+ }
6402
6500
  if (text2.startsWith(previousText)) {
6403
6501
  const delta = text2.slice(previousText.length);
6404
6502
  if (delta)
6405
6503
  output.write(delta);
6406
6504
  } else if (text2.trim() && text2 !== previousText) {
6407
- writeLine(`
6505
+ if (previousText)
6506
+ writeLine(`
6408
6507
  [Pi assistant]`);
6409
6508
  output.write(text2);
6410
6509
  }
@@ -6435,6 +6534,13 @@ function createPiRunStreamRenderer(output = process.stdout) {
6435
6534
  const detail = logDetail(entry);
6436
6535
  if (!detail)
6437
6536
  continue;
6537
+ const protocolRecord = parseProviderProtocolLog(title, detail);
6538
+ if (protocolRecord) {
6539
+ const protocolLine = renderProviderProtocolLog(protocolRecord);
6540
+ if (protocolLine)
6541
+ writeLine(protocolLine);
6542
+ continue;
6543
+ }
6438
6544
  writeLine(`[${title || "Rig log"}] ${detail}`);
6439
6545
  }
6440
6546
  }
@@ -6582,6 +6688,28 @@ function normalizeRemoteRunDetails(payload) {
6582
6688
  ...Array.isArray(payload.userInputs) ? { userInputs: payload.userInputs } : {}
6583
6689
  };
6584
6690
  }
6691
+ var REMOTE_TERMINAL_RUN_STATUSES = new Set(["completed", "failed", "stopped", "cancelled", "canceled", "closed", "merged"]);
6692
+ function isRemoteConnectionSelected(projectRoot) {
6693
+ return resolveSelectedConnection(projectRoot)?.connection.kind === "remote";
6694
+ }
6695
+ async function listRunsForSelectedConnection(context, options = {}) {
6696
+ if (isRemoteConnectionSelected(context.projectRoot)) {
6697
+ return { runs: await listRunsViaServer(context, options), source: "server" };
6698
+ }
6699
+ return { runs: listAuthorityRuns3(context.projectRoot), source: "local" };
6700
+ }
6701
+ function runStringField(run, key, fallback = "") {
6702
+ const value = run[key];
6703
+ return typeof value === "string" && value.trim() ? value : fallback;
6704
+ }
6705
+ function runDisplayTitle(run) {
6706
+ return runStringField(run, "title", runStringField(run, "taskId", "(untitled)"));
6707
+ }
6708
+ function buildServerRunStatus(runs) {
6709
+ const activeRuns = runs.filter((run) => !REMOTE_TERMINAL_RUN_STATUSES.has(runStringField(run, "status").toLowerCase()));
6710
+ const recentRuns = runs.filter((run) => REMOTE_TERMINAL_RUN_STATUSES.has(runStringField(run, "status").toLowerCase()));
6711
+ return { activeRuns, recentRuns, runs };
6712
+ }
6585
6713
  function shouldPromptForEpicSelection(context, command, promptEpic, noEpicPrompt) {
6586
6714
  if (noEpicPrompt) {
6587
6715
  return false;
@@ -6647,17 +6775,17 @@ async function executeRun(context, args) {
6647
6775
  switch (command) {
6648
6776
  case "list": {
6649
6777
  requireNoExtraArgs(rest, "bun run rig run list");
6650
- const runs = listAuthorityRuns3(context.projectRoot);
6778
+ const { runs, source } = await listRunsForSelectedConnection(context, { limit: 100 });
6651
6779
  if (context.outputMode === "text") {
6652
6780
  if (runs.length === 0) {
6653
- console.log("No runs recorded in .rig/runs.");
6781
+ console.log(source === "server" ? "No runs recorded on the selected Rig server." : "No runs recorded in .rig/runs.");
6654
6782
  } else {
6655
6783
  for (const run of runs) {
6656
- console.log(`- ${run.runId} \xB7 ${run.status} \xB7 ${run.title}`);
6784
+ console.log(`- ${runStringField(run, "runId", "(unknown-run)")} \xB7 ${runStringField(run, "status", "unknown")} \xB7 ${runDisplayTitle(run)}`);
6657
6785
  }
6658
6786
  }
6659
6787
  }
6660
- return { ok: true, group: "run", command, details: { runs } };
6788
+ return { ok: true, group: "run", command, details: { runs, source } };
6661
6789
  }
6662
6790
  case "delete": {
6663
6791
  let pending = rest;
@@ -6794,17 +6922,19 @@ async function executeRun(context, args) {
6794
6922
  }
6795
6923
  return { ok: true, group: "run", command };
6796
6924
  }
6797
- const summary = runStatus(context.projectRoot, runtimeContext);
6925
+ const summary = isRemoteConnectionSelected(context.projectRoot) ? buildServerRunStatus(await listRunsViaServer(context, { limit: 100 })) : runStatus(context.projectRoot, runtimeContext);
6926
+ const activeRuns = Array.isArray(summary.activeRuns) ? summary.activeRuns.filter((run) => Boolean(run && typeof run === "object" && !Array.isArray(run))) : [];
6927
+ const recentRuns = Array.isArray(summary.recentRuns) ? summary.recentRuns.filter((run) => Boolean(run && typeof run === "object" && !Array.isArray(run))) : [];
6798
6928
  if (context.outputMode === "text") {
6799
- console.log(`Active runs: ${summary.activeRuns.length}`);
6800
- for (const run of summary.activeRuns) {
6801
- console.log(`- ${run.runId} \xB7 ${run.status} \xB7 ${run.taskId ?? run.title}`);
6929
+ console.log(`Active runs: ${activeRuns.length}`);
6930
+ for (const run of activeRuns) {
6931
+ console.log(`- ${runStringField(run, "runId", "(unknown-run)")} \xB7 ${runStringField(run, "status", "unknown")} \xB7 ${runStringField(run, "taskId", runDisplayTitle(run))}`);
6802
6932
  }
6803
- if (summary.recentRuns.length > 0) {
6933
+ if (recentRuns.length > 0) {
6804
6934
  console.log("");
6805
6935
  console.log("Recent runs:");
6806
- for (const run of summary.recentRuns) {
6807
- console.log(`- ${run.runId} \xB7 ${run.status} \xB7 ${run.taskId ?? run.title}`);
6936
+ for (const run of recentRuns) {
6937
+ console.log(`- ${runStringField(run, "runId", "(unknown-run)")} \xB7 ${runStringField(run, "status", "unknown")} \xB7 ${runStringField(run, "taskId", runDisplayTitle(run))}`);
6808
6938
  }
6809
6939
  }
6810
6940
  }
@@ -8240,6 +8370,26 @@ function appendAssistantTimelineFromRecord(input) {
8240
8370
  }
8241
8371
  return nextAssistantText;
8242
8372
  }
8373
+ function appendPiToolTimelineFromRecord(input) {
8374
+ const type = typeof input.record.type === "string" ? input.record.type : "";
8375
+ if (type !== "tool_execution_start" && type !== "tool_execution_update" && type !== "tool_execution_end")
8376
+ return false;
8377
+ const toolCallId = typeof input.record.toolCallId === "string" && input.record.toolCallId.trim() ? input.record.toolCallId.trim() : `${Date.now()}`;
8378
+ const toolName = typeof input.record.toolName === "string" && input.record.toolName.trim() ? input.record.toolName.trim() : "tool";
8379
+ const result = input.record.result && typeof input.record.result === "object" && !Array.isArray(input.record.result) ? input.record.result : null;
8380
+ appendRunTimeline(input.projectRoot, input.runId, {
8381
+ id: `tool:${toolCallId}:${type}`,
8382
+ type,
8383
+ toolName,
8384
+ status: type === "tool_execution_end" ? input.record.isError === true || result?.isError === true ? "failed" : "completed" : "running",
8385
+ createdAt: new Date().toISOString()
8386
+ });
8387
+ return true;
8388
+ }
8389
+ function isNonRenderablePiProtocolRecord(record) {
8390
+ const type = typeof record.type === "string" ? record.type : "";
8391
+ 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");
8392
+ }
8243
8393
  function appendToolTimelineFromLog(input) {
8244
8394
  const title = typeof input.log.title === "string" ? input.log.title : "";
8245
8395
  if (title !== "Tool activity")
@@ -8765,6 +8915,10 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
8765
8915
  try {
8766
8916
  const record = JSON.parse(trimmed);
8767
8917
  const liveLogStatus = reviewStarted ? "reviewing" : verificationStarted ? "validating" : "running";
8918
+ if (input.runtimeAdapter === "pi" && appendPiToolTimelineFromRecord({ projectRoot: context.projectRoot, runId: input.runId, record })) {
8919
+ emitServerRunEvent({ type: "timeline", runId: input.runId });
8920
+ return;
8921
+ }
8768
8922
  const providerLogs = input.runtimeAdapter === "codex" ? buildCodexLogsFromRecord({
8769
8923
  runId: input.runId,
8770
8924
  record,
@@ -8836,6 +8990,9 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
8836
8990
  return;
8837
8991
  }
8838
8992
  }
8993
+ if (input.runtimeAdapter === "pi" && isNonRenderablePiProtocolRecord(record)) {
8994
+ return;
8995
+ }
8839
8996
  if (record.type === "assistant") {
8840
8997
  const message2 = record.message && typeof record.message === "object" ? record.message : record;
8841
8998
  const content = Array.isArray(message2.content) ? message2.content : [];
@@ -19,6 +19,42 @@ var CANONICAL_STAGES = [
19
19
  function logDetail(log) {
20
20
  return typeof log.detail === "string" ? log.detail.trim() : "";
21
21
  }
22
+ function parseProviderProtocolLog(title, detail) {
23
+ if (title.trim().toLowerCase() !== "agent output")
24
+ return null;
25
+ if (!detail.startsWith("{") || !detail.endsWith("}"))
26
+ return null;
27
+ try {
28
+ const record = JSON.parse(detail);
29
+ if (!record || typeof record !== "object" || Array.isArray(record))
30
+ return null;
31
+ const type = record.type;
32
+ return typeof type === "string" && [
33
+ "assistant",
34
+ "message_start",
35
+ "message_update",
36
+ "message_end",
37
+ "stream_event",
38
+ "tool_result",
39
+ "tool_execution_start",
40
+ "tool_execution_update",
41
+ "tool_execution_end",
42
+ "turn_start",
43
+ "turn_end"
44
+ ].includes(type) ? record : null;
45
+ } catch {
46
+ return null;
47
+ }
48
+ }
49
+ function renderProviderProtocolLog(record) {
50
+ const type = typeof record.type === "string" ? record.type : "";
51
+ if (type === "tool_execution_start" || type === "tool_execution_update" || type === "tool_execution_end") {
52
+ const toolName = String(record.toolName ?? record.name ?? "tool");
53
+ const status = type === "tool_execution_start" ? "started" : type === "tool_execution_end" ? record.isError === true || record.result && typeof record.result === "object" && !Array.isArray(record.result) && record.result.isError === true ? "failed" : "completed" : "running";
54
+ return `[Pi tool] ${toolName} ${status}`;
55
+ }
56
+ return null;
57
+ }
22
58
  function entryId(entry, fallback) {
23
59
  return typeof entry.id === "string" && entry.id.trim() ? entry.id : fallback;
24
60
  }
@@ -63,12 +99,16 @@ function createPiRunStreamRenderer(output = process.stdout) {
63
99
  if (entry.type === "assistant_message" && typeof entry.text === "string") {
64
100
  const text = entry.text;
65
101
  const previousText = assistantTextById.get(id) ?? "";
102
+ if (!previousText && text.trim()) {
103
+ writeLine("[Pi assistant]");
104
+ }
66
105
  if (text.startsWith(previousText)) {
67
106
  const delta = text.slice(previousText.length);
68
107
  if (delta)
69
108
  output.write(delta);
70
109
  } else if (text.trim() && text !== previousText) {
71
- writeLine(`
110
+ if (previousText)
111
+ writeLine(`
72
112
  [Pi assistant]`);
73
113
  output.write(text);
74
114
  }
@@ -99,6 +139,13 @@ function createPiRunStreamRenderer(output = process.stdout) {
99
139
  const detail = logDetail(entry);
100
140
  if (!detail)
101
141
  continue;
142
+ const protocolRecord = parseProviderProtocolLog(title, detail);
143
+ if (protocolRecord) {
144
+ const protocolLine = renderProviderProtocolLog(protocolRecord);
145
+ if (protocolLine)
146
+ writeLine(protocolLine);
147
+ continue;
148
+ }
102
149
  writeLine(`[${title || "Rig log"}] ${detail}`);
103
150
  }
104
151
  }
@@ -247,6 +247,42 @@ var CANONICAL_STAGES = [
247
247
  function logDetail(log) {
248
248
  return typeof log.detail === "string" ? log.detail.trim() : "";
249
249
  }
250
+ function parseProviderProtocolLog(title, detail) {
251
+ if (title.trim().toLowerCase() !== "agent output")
252
+ return null;
253
+ if (!detail.startsWith("{") || !detail.endsWith("}"))
254
+ return null;
255
+ try {
256
+ const record = JSON.parse(detail);
257
+ if (!record || typeof record !== "object" || Array.isArray(record))
258
+ return null;
259
+ const type = record.type;
260
+ return typeof type === "string" && [
261
+ "assistant",
262
+ "message_start",
263
+ "message_update",
264
+ "message_end",
265
+ "stream_event",
266
+ "tool_result",
267
+ "tool_execution_start",
268
+ "tool_execution_update",
269
+ "tool_execution_end",
270
+ "turn_start",
271
+ "turn_end"
272
+ ].includes(type) ? record : null;
273
+ } catch {
274
+ return null;
275
+ }
276
+ }
277
+ function renderProviderProtocolLog(record) {
278
+ const type = typeof record.type === "string" ? record.type : "";
279
+ if (type === "tool_execution_start" || type === "tool_execution_update" || type === "tool_execution_end") {
280
+ const toolName = String(record.toolName ?? record.name ?? "tool");
281
+ const status = type === "tool_execution_start" ? "started" : type === "tool_execution_end" ? record.isError === true || record.result && typeof record.result === "object" && !Array.isArray(record.result) && record.result.isError === true ? "failed" : "completed" : "running";
282
+ return `[Pi tool] ${toolName} ${status}`;
283
+ }
284
+ return null;
285
+ }
250
286
  function entryId(entry, fallback) {
251
287
  return typeof entry.id === "string" && entry.id.trim() ? entry.id : fallback;
252
288
  }
@@ -291,12 +327,16 @@ function createPiRunStreamRenderer(output = process.stdout) {
291
327
  if (entry.type === "assistant_message" && typeof entry.text === "string") {
292
328
  const text = entry.text;
293
329
  const previousText = assistantTextById.get(id) ?? "";
330
+ if (!previousText && text.trim()) {
331
+ writeLine("[Pi assistant]");
332
+ }
294
333
  if (text.startsWith(previousText)) {
295
334
  const delta = text.slice(previousText.length);
296
335
  if (delta)
297
336
  output.write(delta);
298
337
  } else if (text.trim() && text !== previousText) {
299
- writeLine(`
338
+ if (previousText)
339
+ writeLine(`
300
340
  [Pi assistant]`);
301
341
  output.write(text);
302
342
  }
@@ -327,6 +367,13 @@ function createPiRunStreamRenderer(output = process.stdout) {
327
367
  const detail = logDetail(entry);
328
368
  if (!detail)
329
369
  continue;
370
+ const protocolRecord = parseProviderProtocolLog(title, detail);
371
+ if (protocolRecord) {
372
+ const protocolLine = renderProviderProtocolLog(protocolRecord);
373
+ if (protocolLine)
374
+ writeLine(protocolLine);
375
+ continue;
376
+ }
330
377
  writeLine(`[${title || "Rig log"}] ${detail}`);
331
378
  }
332
379
  }
@@ -310,6 +310,14 @@ async function switchServerProjectRootViaServer(context, projectRoot, options =
310
310
  }
311
311
  throw new CliError2(`Rig server did not switch to ${projectRoot} before timeout (${lastError instanceof Error ? lastError.message : String(lastError ?? "no status")}).`, 1);
312
312
  }
313
+ async function listRunsViaServer(context, options = {}) {
314
+ const url = new URL("http://rig.local/api/runs");
315
+ if (options.limit !== undefined)
316
+ url.searchParams.set("limit", String(options.limit));
317
+ const payload = await requestServerJson(context, `${url.pathname}${url.search}`);
318
+ const runs = Array.isArray(payload) ? payload : payload && typeof payload === "object" && !Array.isArray(payload) && Array.isArray(payload.runs) ? payload.runs : [];
319
+ return runs.filter((entry) => Boolean(entry && typeof entry === "object" && !Array.isArray(entry)));
320
+ }
313
321
  async function getRunDetailsViaServer(context, runId) {
314
322
  const payload = await requestServerJson(context, `/api/runs/${encodeURIComponent(runId)}`);
315
323
  return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : {};
@@ -414,6 +422,7 @@ export {
414
422
  prepareRemoteCheckoutViaServer,
415
423
  postGitHubTokenViaServer,
416
424
  listWorkspaceTasksViaServer,
425
+ listRunsViaServer,
417
426
  listGitHubProjectsViaServer,
418
427
  getWorkspaceTaskViaServer,
419
428
  getRunTimelineViaServer,