@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.
@@ -832,6 +832,7 @@ function countDoctorFailures(checks) {
832
832
 
833
833
  // packages/cli/src/commands/init.ts
834
834
  var RIG_CONFIG_PACKAGE_DIST_TAG = "latest";
835
+ var DEFAULT_REMOTE_RIG_URL = "https://where.rig-does.work";
835
836
  var RIG_CONFIG_DEV_DEPENDENCIES = {
836
837
  "@rig/core": `npm:@h-rig/core@${RIG_CONFIG_PACKAGE_DIST_TAG}`,
837
838
  "@rig/standard-plugin": `npm:@h-rig/standard-plugin@${RIG_CONFIG_PACKAGE_DIST_TAG}`
@@ -936,6 +937,19 @@ function readGhAuthToken() {
936
937
  }
937
938
  return result.stdout.trim();
938
939
  }
940
+ function refreshGhProjectScopesAndReadToken() {
941
+ const result = spawnSync("gh", ["auth", "refresh", "--scopes", "read:project"], {
942
+ encoding: "utf8",
943
+ stdio: ["inherit", "pipe", "pipe"]
944
+ });
945
+ if (result.status !== 0)
946
+ return null;
947
+ try {
948
+ return readGhAuthToken();
949
+ } catch {
950
+ return null;
951
+ }
952
+ }
939
953
  async function loadClackPrompts() {
940
954
  return await import("@clack/prompts");
941
955
  }
@@ -1031,12 +1045,27 @@ async function promptManualProjectStatusMapping(prompts) {
1031
1045
  }
1032
1046
  return statuses;
1033
1047
  }
1034
- async function promptGitHubProjectConfig(context, prompts, repoSlug, githubToken) {
1048
+ function projectScopeError(value) {
1049
+ const text = typeof value === "string" ? value : JSON.stringify(value ?? "");
1050
+ return /INSUFFICIENT_SCOPES|read:project|required scopes/i.test(text);
1051
+ }
1052
+ function optionName(option) {
1053
+ return String(option.name ?? option.label ?? option.id ?? "").trim();
1054
+ }
1055
+ function autoProjectStatusValue(options, key, label) {
1056
+ const candidates = [DEFAULT_PROJECT_STATUS_OPTIONS[key], label].filter((value) => Boolean(value)).map((value) => value.trim().toLowerCase());
1057
+ const match = options.find((option) => candidates.includes(optionName(option).toLowerCase()));
1058
+ if (!match)
1059
+ return null;
1060
+ return String(match.id ?? match.name);
1061
+ }
1062
+ async function promptGitHubProjectConfig(context, prompts, repoSlug, githubToken, refreshProjectToken) {
1035
1063
  const projectChoice = await promptSelect(prompts, {
1036
1064
  message: "GitHub Projects status sync",
1065
+ initialValue: "select",
1037
1066
  options: [
1038
- { value: "off", label: "Off" },
1039
1067
  { value: "select", label: "Select accessible ProjectV2" },
1068
+ { value: "off", label: "Off" },
1040
1069
  { value: "manual", label: "Enter ProjectV2 ids manually" }
1041
1070
  ]
1042
1071
  });
@@ -1052,16 +1081,22 @@ async function promptGitHubProjectConfig(context, prompts, repoSlug, githubToken
1052
1081
  const owner = repoOwnerFromSlug(repoSlug);
1053
1082
  if (!owner)
1054
1083
  throw new CliError2(`Cannot derive GitHub owner from repo slug ${repoSlug}.`, 1);
1055
- const projectsPayload = await listGitHubProjectsForInit(context, owner, githubToken).catch((error) => ({ ok: false, error: error instanceof Error ? error.message : String(error), projects: [] }));
1056
- const projects = recordArray(projectsPayload, "projects");
1084
+ let activeToken = githubToken?.trim() || null;
1085
+ let projectsPayload = await listGitHubProjectsForInit(context, owner, activeToken).catch((error) => ({ ok: false, error: error instanceof Error ? error.message : String(error), projects: [] }));
1086
+ let projects = recordArray(projectsPayload, "projects");
1087
+ if (projects.length === 0 && projectScopeError(projectsPayload.error) && refreshProjectToken) {
1088
+ prompts.outro?.("GitHub token is missing read:project; refreshing gh auth scopes and retrying Projects.");
1089
+ const refreshedToken = refreshProjectToken();
1090
+ if (refreshedToken) {
1091
+ activeToken = refreshedToken;
1092
+ projectsPayload = await listGitHubProjectsForInit(context, owner, activeToken).catch((error) => ({ ok: false, error: error instanceof Error ? error.message : String(error), projects: [] }));
1093
+ projects = recordArray(projectsPayload, "projects");
1094
+ }
1095
+ }
1057
1096
  if (projects.length === 0) {
1058
- const error = typeof projectsPayload.error === "string" ? ` (${projectsPayload.error})` : "";
1059
- prompts.outro?.(`No accessible GitHub Projects were returned${error}; falling back to manual ProjectV2 ids.`);
1060
- return {
1061
- githubProject: await promptRequiredText(prompts, { message: "GitHub ProjectV2 id", placeholder: "PVT_..." }),
1062
- githubProjectStatusField: await promptRequiredText(prompts, { message: "Project Status field id", placeholder: "field_status" }),
1063
- githubProjectStatuses: await promptManualProjectStatusMapping(prompts)
1064
- };
1097
+ const error = typeof projectsPayload.error === "string" ? ` (${String(projectsPayload.error).replace(/\s+/g, " ").slice(0, 240)})` : "";
1098
+ prompts.outro?.(`No accessible GitHub Projects were returned${error}; continuing with GitHub Projects status sync off.`);
1099
+ return { githubProject: "off", ...activeToken ? { githubToken: activeToken } : {} };
1065
1100
  }
1066
1101
  const selectedProjectId = await promptSelect(prompts, {
1067
1102
  message: "GitHub ProjectV2 project",
@@ -1075,23 +1110,34 @@ async function promptGitHubProjectConfig(context, prompts, repoSlug, githubToken
1075
1110
  ]
1076
1111
  });
1077
1112
  const projectId = selectedProjectId === "manual" ? await promptRequiredText(prompts, { message: "GitHub ProjectV2 id", placeholder: "PVT_..." }) : selectedProjectId;
1078
- const fieldPayload = await getGitHubProjectStatusFieldForInit(context, projectId, githubToken).catch((error) => ({ ok: false, error: error instanceof Error ? error.message : String(error) }));
1113
+ const fieldPayload = await getGitHubProjectStatusFieldForInit(context, projectId, activeToken).catch((error) => ({ ok: false, error: error instanceof Error ? error.message : String(error) }));
1079
1114
  const fieldPayloadRecord = fieldPayload && typeof fieldPayload === "object" && !Array.isArray(fieldPayload) ? fieldPayload : {};
1080
1115
  const rawField = fieldPayloadRecord.field;
1081
1116
  const field = rawField && typeof rawField === "object" && !Array.isArray(rawField) ? rawField : null;
1082
1117
  const fieldId = typeof field?.id === "string" && field.id.trim() ? field.id : await promptRequiredText(prompts, { message: "Project Status field id", placeholder: "field_status" });
1083
1118
  const options = Array.isArray(field?.options) ? field.options.filter((entry) => Boolean(entry && typeof entry === "object" && !Array.isArray(entry))) : [];
1084
1119
  if (options.length === 0) {
1085
- return { githubProject: projectId, githubProjectStatusField: fieldId, githubProjectStatuses: await promptManualProjectStatusMapping(prompts) };
1120
+ return {
1121
+ githubProject: projectId,
1122
+ githubProjectStatusField: fieldId,
1123
+ githubProjectStatuses: await promptManualProjectStatusMapping(prompts),
1124
+ ...activeToken ? { githubToken: activeToken } : {}
1125
+ };
1086
1126
  }
1087
1127
  const statuses = {};
1088
1128
  for (const [key, label] of Object.entries(PROJECT_STATUS_PROMPTS)) {
1089
- statuses[key] = await promptSelect(prompts, {
1129
+ const auto = autoProjectStatusValue(options, key, label);
1130
+ statuses[key] = auto ?? await promptSelect(prompts, {
1090
1131
  message: `Project status option for ${label}`,
1091
- options: options.map((option) => ({ value: String(option.id ?? option.name), label: String(option.name ?? option.id) }))
1132
+ options: options.map((option) => ({ value: String(option.id ?? option.name), label: optionName(option) }))
1092
1133
  });
1093
1134
  }
1094
- return { githubProject: projectId, githubProjectStatusField: fieldId, githubProjectStatuses: Object.keys(statuses).length > 0 ? statuses : undefined };
1135
+ return {
1136
+ githubProject: projectId,
1137
+ githubProjectStatusField: fieldId,
1138
+ githubProjectStatuses: Object.keys(statuses).length > 0 ? statuses : undefined,
1139
+ ...activeToken ? { githubToken: activeToken } : {}
1140
+ };
1095
1141
  }
1096
1142
  function sleep2(ms) {
1097
1143
  return new Promise((resolve7) => setTimeout(resolve7, ms));
@@ -1422,12 +1468,13 @@ async function runInteractiveControlPlaneInit(context, prompts) {
1422
1468
  });
1423
1469
  const serverChoice = await promptSelect(prompts, {
1424
1470
  message: "Rig server",
1471
+ initialValue: "remote",
1425
1472
  options: [
1426
- { value: "local", label: "Local server", hint: "run on this machine" },
1427
- { value: "remote", label: "Remote server", hint: "connect to an HTTPS Rig server" }
1473
+ { value: "remote", label: "Remote server", hint: "connect to an HTTPS Rig server" },
1474
+ { value: "local", label: "Local server", hint: "run on this machine" }
1428
1475
  ]
1429
1476
  });
1430
- const remoteUrl = serverChoice === "remote" ? await promptRequiredText(prompts, { message: "Remote Rig server URL", placeholder: "https://rig.example.com" }) : undefined;
1477
+ const remoteUrl = serverChoice === "remote" ? await promptRequiredText(prompts, { message: "Remote Rig server URL", placeholder: DEFAULT_REMOTE_RIG_URL, initialValue: DEFAULT_REMOTE_RIG_URL }) : undefined;
1431
1478
  let remoteCheckout;
1432
1479
  if (serverChoice === "remote") {
1433
1480
  const checkout = await promptSelect(prompts, {
@@ -1459,31 +1506,35 @@ async function runInteractiveControlPlaneInit(context, prompts) {
1459
1506
  { value: "skip", label: "Skip for now" }
1460
1507
  ]
1461
1508
  });
1509
+ let remoteGhTokenConfirmed = false;
1462
1510
  if (serverChoice === "remote" && authMethod === "gh") {
1463
1511
  if (!prompts.confirm)
1464
1512
  throw new CliError2("Remote gh-token import requires explicit confirmation.", 1);
1465
1513
  const confirmed = await prompts.confirm({
1466
1514
  message: `This sends a GitHub token from this machine to ${remoteUrl}. Continue?`,
1467
- initialValue: false
1515
+ initialValue: true
1468
1516
  });
1469
1517
  if (prompts.isCancel(confirmed) || confirmed !== true) {
1470
1518
  throw new CliError2("Remote gh-token import cancelled.", 1);
1471
1519
  }
1520
+ remoteGhTokenConfirmed = true;
1472
1521
  }
1473
1522
  const githubToken = authMethod === "token" ? await promptRequiredText(prompts, { message: "GitHub token", placeholder: "ghp_..." }) : authMethod === "gh" ? readGhAuthToken() : undefined;
1474
- const projectConfig = await promptGitHubProjectConfig(context, prompts, repoSlug, githubToken);
1523
+ const projectConfig = await promptGitHubProjectConfig(context, prompts, repoSlug, githubToken, authMethod === "gh" ? refreshGhProjectScopesAndReadToken : undefined);
1524
+ const effectiveGithubToken = projectConfig.githubToken ?? githubToken;
1475
1525
  const result = await runControlPlaneInit(context, {
1476
1526
  server: serverChoice,
1477
1527
  remoteUrl,
1478
1528
  repoSlug,
1479
- githubToken,
1529
+ githubToken: effectiveGithubToken,
1480
1530
  githubAuthMethod: authMethod,
1481
1531
  githubProject: projectConfig.githubProject,
1482
1532
  githubProjectStatusField: projectConfig.githubProjectStatusField,
1483
1533
  githubProjectStatuses: projectConfig.githubProjectStatuses,
1484
1534
  remoteCheckout,
1485
1535
  repair,
1486
- privateStateOnly
1536
+ privateStateOnly,
1537
+ yes: remoteGhTokenConfirmed || undefined
1487
1538
  });
1488
1539
  const details = result.details && typeof result.details === "object" && !Array.isArray(result.details) ? result.details : {};
1489
1540
  const deviceAuth = details.deviceAuth && typeof details.deviceAuth === "object" && !Array.isArray(details.deviceAuth) ? details.deviceAuth : null;
@@ -259,6 +259,14 @@ async function requestServerJson(context, pathname, init = {}) {
259
259
  }
260
260
  return payload;
261
261
  }
262
+ async function listRunsViaServer(context, options = {}) {
263
+ const url = new URL("http://rig.local/api/runs");
264
+ if (options.limit !== undefined)
265
+ url.searchParams.set("limit", String(options.limit));
266
+ const payload = await requestServerJson(context, `${url.pathname}${url.search}`);
267
+ const runs = Array.isArray(payload) ? payload : payload && typeof payload === "object" && !Array.isArray(payload) && Array.isArray(payload.runs) ? payload.runs : [];
268
+ return runs.filter((entry) => Boolean(entry && typeof entry === "object" && !Array.isArray(entry)));
269
+ }
262
270
  async function getRunDetailsViaServer(context, runId) {
263
271
  const payload = await requestServerJson(context, `/api/runs/${encodeURIComponent(runId)}`);
264
272
  return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : {};
@@ -317,6 +325,42 @@ var CANONICAL_STAGES = [
317
325
  function logDetail(log) {
318
326
  return typeof log.detail === "string" ? log.detail.trim() : "";
319
327
  }
328
+ function parseProviderProtocolLog(title, detail) {
329
+ if (title.trim().toLowerCase() !== "agent output")
330
+ return null;
331
+ if (!detail.startsWith("{") || !detail.endsWith("}"))
332
+ return null;
333
+ try {
334
+ const record = JSON.parse(detail);
335
+ if (!record || typeof record !== "object" || Array.isArray(record))
336
+ return null;
337
+ const type = record.type;
338
+ return typeof type === "string" && [
339
+ "assistant",
340
+ "message_start",
341
+ "message_update",
342
+ "message_end",
343
+ "stream_event",
344
+ "tool_result",
345
+ "tool_execution_start",
346
+ "tool_execution_update",
347
+ "tool_execution_end",
348
+ "turn_start",
349
+ "turn_end"
350
+ ].includes(type) ? record : null;
351
+ } catch {
352
+ return null;
353
+ }
354
+ }
355
+ function renderProviderProtocolLog(record) {
356
+ const type = typeof record.type === "string" ? record.type : "";
357
+ if (type === "tool_execution_start" || type === "tool_execution_update" || type === "tool_execution_end") {
358
+ const toolName = String(record.toolName ?? record.name ?? "tool");
359
+ 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";
360
+ return `[Pi tool] ${toolName} ${status}`;
361
+ }
362
+ return null;
363
+ }
320
364
  function entryId(entry, fallback) {
321
365
  return typeof entry.id === "string" && entry.id.trim() ? entry.id : fallback;
322
366
  }
@@ -361,12 +405,16 @@ function createPiRunStreamRenderer(output = process.stdout) {
361
405
  if (entry.type === "assistant_message" && typeof entry.text === "string") {
362
406
  const text = entry.text;
363
407
  const previousText = assistantTextById.get(id) ?? "";
408
+ if (!previousText && text.trim()) {
409
+ writeLine("[Pi assistant]");
410
+ }
364
411
  if (text.startsWith(previousText)) {
365
412
  const delta = text.slice(previousText.length);
366
413
  if (delta)
367
414
  output.write(delta);
368
415
  } else if (text.trim() && text !== previousText) {
369
- writeLine(`
416
+ if (previousText)
417
+ writeLine(`
370
418
  [Pi assistant]`);
371
419
  output.write(text);
372
420
  }
@@ -397,6 +445,13 @@ function createPiRunStreamRenderer(output = process.stdout) {
397
445
  const detail = logDetail(entry);
398
446
  if (!detail)
399
447
  continue;
448
+ const protocolRecord = parseProviderProtocolLog(title, detail);
449
+ if (protocolRecord) {
450
+ const protocolLine = renderProviderProtocolLog(protocolRecord);
451
+ if (protocolLine)
452
+ writeLine(protocolLine);
453
+ continue;
454
+ }
400
455
  writeLine(`[${title || "Rig log"}] ${detail}`);
401
456
  }
402
457
  }
@@ -524,6 +579,28 @@ function normalizeRemoteRunDetails(payload) {
524
579
  ...Array.isArray(payload.userInputs) ? { userInputs: payload.userInputs } : {}
525
580
  };
526
581
  }
582
+ var REMOTE_TERMINAL_RUN_STATUSES = new Set(["completed", "failed", "stopped", "cancelled", "canceled", "closed", "merged"]);
583
+ function isRemoteConnectionSelected(projectRoot) {
584
+ return resolveSelectedConnection(projectRoot)?.connection.kind === "remote";
585
+ }
586
+ async function listRunsForSelectedConnection(context, options = {}) {
587
+ if (isRemoteConnectionSelected(context.projectRoot)) {
588
+ return { runs: await listRunsViaServer(context, options), source: "server" };
589
+ }
590
+ return { runs: listAuthorityRuns(context.projectRoot), source: "local" };
591
+ }
592
+ function runStringField(run, key, fallback = "") {
593
+ const value = run[key];
594
+ return typeof value === "string" && value.trim() ? value : fallback;
595
+ }
596
+ function runDisplayTitle(run) {
597
+ return runStringField(run, "title", runStringField(run, "taskId", "(untitled)"));
598
+ }
599
+ function buildServerRunStatus(runs) {
600
+ const activeRuns = runs.filter((run) => !REMOTE_TERMINAL_RUN_STATUSES.has(runStringField(run, "status").toLowerCase()));
601
+ const recentRuns = runs.filter((run) => REMOTE_TERMINAL_RUN_STATUSES.has(runStringField(run, "status").toLowerCase()));
602
+ return { activeRuns, recentRuns, runs };
603
+ }
527
604
  function shouldPromptForEpicSelection(context, command, promptEpic, noEpicPrompt) {
528
605
  if (noEpicPrompt) {
529
606
  return false;
@@ -589,17 +666,17 @@ async function executeRun(context, args) {
589
666
  switch (command) {
590
667
  case "list": {
591
668
  requireNoExtraArgs(rest, "bun run rig run list");
592
- const runs = listAuthorityRuns(context.projectRoot);
669
+ const { runs, source } = await listRunsForSelectedConnection(context, { limit: 100 });
593
670
  if (context.outputMode === "text") {
594
671
  if (runs.length === 0) {
595
- console.log("No runs recorded in .rig/runs.");
672
+ console.log(source === "server" ? "No runs recorded on the selected Rig server." : "No runs recorded in .rig/runs.");
596
673
  } else {
597
674
  for (const run of runs) {
598
- console.log(`- ${run.runId} \xB7 ${run.status} \xB7 ${run.title}`);
675
+ console.log(`- ${runStringField(run, "runId", "(unknown-run)")} \xB7 ${runStringField(run, "status", "unknown")} \xB7 ${runDisplayTitle(run)}`);
599
676
  }
600
677
  }
601
678
  }
602
- return { ok: true, group: "run", command, details: { runs } };
679
+ return { ok: true, group: "run", command, details: { runs, source } };
603
680
  }
604
681
  case "delete": {
605
682
  let pending = rest;
@@ -736,17 +813,19 @@ async function executeRun(context, args) {
736
813
  }
737
814
  return { ok: true, group: "run", command };
738
815
  }
739
- const summary = runStatus(context.projectRoot, runtimeContext);
816
+ const summary = isRemoteConnectionSelected(context.projectRoot) ? buildServerRunStatus(await listRunsViaServer(context, { limit: 100 })) : runStatus(context.projectRoot, runtimeContext);
817
+ const activeRuns = Array.isArray(summary.activeRuns) ? summary.activeRuns.filter((run) => Boolean(run && typeof run === "object" && !Array.isArray(run))) : [];
818
+ const recentRuns = Array.isArray(summary.recentRuns) ? summary.recentRuns.filter((run) => Boolean(run && typeof run === "object" && !Array.isArray(run))) : [];
740
819
  if (context.outputMode === "text") {
741
- console.log(`Active runs: ${summary.activeRuns.length}`);
742
- for (const run of summary.activeRuns) {
743
- console.log(`- ${run.runId} \xB7 ${run.status} \xB7 ${run.taskId ?? run.title}`);
820
+ console.log(`Active runs: ${activeRuns.length}`);
821
+ for (const run of activeRuns) {
822
+ console.log(`- ${runStringField(run, "runId", "(unknown-run)")} \xB7 ${runStringField(run, "status", "unknown")} \xB7 ${runStringField(run, "taskId", runDisplayTitle(run))}`);
744
823
  }
745
- if (summary.recentRuns.length > 0) {
824
+ if (recentRuns.length > 0) {
746
825
  console.log("");
747
826
  console.log("Recent runs:");
748
- for (const run of summary.recentRuns) {
749
- console.log(`- ${run.runId} \xB7 ${run.status} \xB7 ${run.taskId ?? run.title}`);
827
+ for (const run of recentRuns) {
828
+ console.log(`- ${runStringField(run, "runId", "(unknown-run)")} \xB7 ${runStringField(run, "status", "unknown")} \xB7 ${runStringField(run, "taskId", runDisplayTitle(run))}`);
750
829
  }
751
830
  }
752
831
  }
@@ -1220,6 +1220,26 @@ function appendAssistantTimelineFromRecord(input) {
1220
1220
  }
1221
1221
  return nextAssistantText;
1222
1222
  }
1223
+ function appendPiToolTimelineFromRecord(input) {
1224
+ const type = typeof input.record.type === "string" ? input.record.type : "";
1225
+ if (type !== "tool_execution_start" && type !== "tool_execution_update" && type !== "tool_execution_end")
1226
+ return false;
1227
+ const toolCallId = typeof input.record.toolCallId === "string" && input.record.toolCallId.trim() ? input.record.toolCallId.trim() : `${Date.now()}`;
1228
+ const toolName = typeof input.record.toolName === "string" && input.record.toolName.trim() ? input.record.toolName.trim() : "tool";
1229
+ const result = input.record.result && typeof input.record.result === "object" && !Array.isArray(input.record.result) ? input.record.result : null;
1230
+ appendRunTimeline(input.projectRoot, input.runId, {
1231
+ id: `tool:${toolCallId}:${type}`,
1232
+ type,
1233
+ toolName,
1234
+ status: type === "tool_execution_end" ? input.record.isError === true || result?.isError === true ? "failed" : "completed" : "running",
1235
+ createdAt: new Date().toISOString()
1236
+ });
1237
+ return true;
1238
+ }
1239
+ function isNonRenderablePiProtocolRecord(record) {
1240
+ const type = typeof record.type === "string" ? record.type : "";
1241
+ 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");
1242
+ }
1223
1243
  function appendToolTimelineFromLog(input) {
1224
1244
  const title = typeof input.log.title === "string" ? input.log.title : "";
1225
1245
  if (title !== "Tool activity")
@@ -1745,6 +1765,10 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
1745
1765
  try {
1746
1766
  const record = JSON.parse(trimmed);
1747
1767
  const liveLogStatus = reviewStarted ? "reviewing" : verificationStarted ? "validating" : "running";
1768
+ if (input.runtimeAdapter === "pi" && appendPiToolTimelineFromRecord({ projectRoot: context.projectRoot, runId: input.runId, record })) {
1769
+ emitServerRunEvent({ type: "timeline", runId: input.runId });
1770
+ return;
1771
+ }
1748
1772
  const providerLogs = input.runtimeAdapter === "codex" ? buildCodexLogsFromRecord({
1749
1773
  runId: input.runId,
1750
1774
  record,
@@ -1816,6 +1840,9 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
1816
1840
  return;
1817
1841
  }
1818
1842
  }
1843
+ if (input.runtimeAdapter === "pi" && isNonRenderablePiProtocolRecord(record)) {
1844
+ return;
1845
+ }
1819
1846
  if (record.type === "assistant") {
1820
1847
  const message = record.message && typeof record.message === "object" ? record.message : record;
1821
1848
  const content = Array.isArray(message.content) ? message.content : [];
@@ -738,6 +738,42 @@ var CANONICAL_STAGES = [
738
738
  function logDetail(log) {
739
739
  return typeof log.detail === "string" ? log.detail.trim() : "";
740
740
  }
741
+ function parseProviderProtocolLog(title, detail) {
742
+ if (title.trim().toLowerCase() !== "agent output")
743
+ return null;
744
+ if (!detail.startsWith("{") || !detail.endsWith("}"))
745
+ return null;
746
+ try {
747
+ const record = JSON.parse(detail);
748
+ if (!record || typeof record !== "object" || Array.isArray(record))
749
+ return null;
750
+ const type = record.type;
751
+ return typeof type === "string" && [
752
+ "assistant",
753
+ "message_start",
754
+ "message_update",
755
+ "message_end",
756
+ "stream_event",
757
+ "tool_result",
758
+ "tool_execution_start",
759
+ "tool_execution_update",
760
+ "tool_execution_end",
761
+ "turn_start",
762
+ "turn_end"
763
+ ].includes(type) ? record : null;
764
+ } catch {
765
+ return null;
766
+ }
767
+ }
768
+ function renderProviderProtocolLog(record) {
769
+ const type = typeof record.type === "string" ? record.type : "";
770
+ if (type === "tool_execution_start" || type === "tool_execution_update" || type === "tool_execution_end") {
771
+ const toolName = String(record.toolName ?? record.name ?? "tool");
772
+ 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";
773
+ return `[Pi tool] ${toolName} ${status}`;
774
+ }
775
+ return null;
776
+ }
741
777
  function entryId(entry, fallback) {
742
778
  return typeof entry.id === "string" && entry.id.trim() ? entry.id : fallback;
743
779
  }
@@ -782,12 +818,16 @@ function createPiRunStreamRenderer(output = process.stdout) {
782
818
  if (entry.type === "assistant_message" && typeof entry.text === "string") {
783
819
  const text = entry.text;
784
820
  const previousText = assistantTextById.get(id) ?? "";
821
+ if (!previousText && text.trim()) {
822
+ writeLine("[Pi assistant]");
823
+ }
785
824
  if (text.startsWith(previousText)) {
786
825
  const delta = text.slice(previousText.length);
787
826
  if (delta)
788
827
  output.write(delta);
789
828
  } else if (text.trim() && text !== previousText) {
790
- writeLine(`
829
+ if (previousText)
830
+ writeLine(`
791
831
  [Pi assistant]`);
792
832
  output.write(text);
793
833
  }
@@ -818,6 +858,13 @@ function createPiRunStreamRenderer(output = process.stdout) {
818
858
  const detail = logDetail(entry);
819
859
  if (!detail)
820
860
  continue;
861
+ const protocolRecord = parseProviderProtocolLog(title, detail);
862
+ if (protocolRecord) {
863
+ const protocolLine = renderProviderProtocolLog(protocolRecord);
864
+ if (protocolLine)
865
+ writeLine(protocolLine);
866
+ continue;
867
+ }
821
868
  writeLine(`[${title || "Rig log"}] ${detail}`);
822
869
  }
823
870
  }