@h-rig/cli 0.0.6-alpha.83 → 0.0.6-alpha.85

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.
Files changed (163) hide show
  1. package/dist/bin/rig.js +22060 -14620
  2. package/dist/src/app/board.js +155 -28
  3. package/dist/src/app-opentui/adapters/doctor.d.ts +0 -2
  4. package/dist/src/app-opentui/adapters/doctor.js +1 -25
  5. package/dist/src/app-opentui/adapters/family.d.ts +62 -0
  6. package/dist/src/app-opentui/adapters/family.js +14305 -0
  7. package/dist/src/app-opentui/adapters/fleet.d.ts +0 -2
  8. package/dist/src/app-opentui/adapters/fleet.js +6 -40
  9. package/dist/src/app-opentui/adapters/inbox.d.ts +12 -2
  10. package/dist/src/app-opentui/adapters/inbox.js +54 -59
  11. package/dist/src/app-opentui/adapters/init.d.ts +0 -2
  12. package/dist/src/app-opentui/adapters/init.js +0 -26
  13. package/dist/src/app-opentui/adapters/inspect.d.ts +52 -0
  14. package/dist/src/app-opentui/adapters/inspect.js +1024 -0
  15. package/dist/src/app-opentui/adapters/pi-attach.d.ts +15 -6
  16. package/dist/src/app-opentui/adapters/pi-attach.js +348 -93
  17. package/dist/src/app-opentui/adapters/pi.d.ts +23 -0
  18. package/dist/src/app-opentui/adapters/pi.js +363 -0
  19. package/dist/src/app-opentui/adapters/plugin.d.ts +84 -0
  20. package/dist/src/app-opentui/adapters/plugin.js +544 -0
  21. package/dist/src/app-opentui/adapters/repo.d.ts +37 -0
  22. package/dist/src/app-opentui/adapters/repo.js +186 -0
  23. package/dist/src/app-opentui/adapters/run-detail.d.ts +9 -2
  24. package/dist/src/app-opentui/adapters/run-detail.js +97 -44
  25. package/dist/src/app-opentui/adapters/server.d.ts +10 -2
  26. package/dist/src/app-opentui/adapters/server.js +96 -27
  27. package/dist/src/app-opentui/adapters/tasks.d.ts +0 -2
  28. package/dist/src/app-opentui/adapters/tasks.js +437 -105
  29. package/dist/src/app-opentui/adapters/workspace.d.ts +49 -0
  30. package/dist/src/app-opentui/adapters/workspace.js +333 -0
  31. package/dist/src/app-opentui/autocomplete.d.ts +20 -0
  32. package/dist/src/app-opentui/autocomplete.js +576 -0
  33. package/dist/src/app-opentui/bootstrap.js +24313 -16963
  34. package/dist/src/app-opentui/command-palette.d.ts +3 -0
  35. package/dist/src/app-opentui/command-palette.js +1010 -0
  36. package/dist/src/app-opentui/drone.js +8 -6
  37. package/dist/src/app-opentui/fleet-stats.d.ts +32 -0
  38. package/dist/src/app-opentui/fleet-stats.js +114 -0
  39. package/dist/src/app-opentui/index.js +3438 -1724
  40. package/dist/src/app-opentui/intent.js +71 -48
  41. package/dist/src/app-opentui/keymap.d.ts +6 -5
  42. package/dist/src/app-opentui/keymap.js +1064 -23
  43. package/dist/src/app-opentui/layout.d.ts +7 -0
  44. package/dist/src/app-opentui/layout.js +13 -6
  45. package/dist/src/app-opentui/list-search.d.ts +24 -0
  46. package/dist/src/app-opentui/list-search.js +88 -1
  47. package/dist/src/app-opentui/pi-host-child.js +33 -1
  48. package/dist/src/app-opentui/pi-pty-host.d.ts +5 -0
  49. package/dist/src/app-opentui/pi-pty-host.js +21 -3
  50. package/dist/src/app-opentui/react/App.d.ts +9 -0
  51. package/dist/src/app-opentui/react/App.js +5099 -0
  52. package/dist/src/app-opentui/react/Backdrop.d.ts +5 -0
  53. package/dist/src/app-opentui/react/Backdrop.js +829 -0
  54. package/dist/src/app-opentui/react/ChromeHost.d.ts +5 -0
  55. package/dist/src/app-opentui/react/ChromeHost.js +2942 -0
  56. package/dist/src/app-opentui/react/SceneFrameView.d.ts +7 -0
  57. package/dist/src/app-opentui/react/SceneFrameView.js +529 -0
  58. package/dist/src/app-opentui/react/context.d.ts +17 -0
  59. package/dist/src/app-opentui/react/context.js +37 -0
  60. package/dist/src/app-opentui/react/launch.d.ts +2 -0
  61. package/dist/src/app-opentui/react/launch.js +5698 -0
  62. package/dist/src/app-opentui/react/nav.d.ts +18 -0
  63. package/dist/src/app-opentui/react/nav.js +54 -0
  64. package/dist/src/app-opentui/react/scroll.d.ts +12 -0
  65. package/dist/src/app-opentui/react/scroll.js +21 -0
  66. package/dist/src/app-opentui/react/syntax.d.ts +2 -0
  67. package/dist/src/app-opentui/react/syntax.js +64 -0
  68. package/dist/src/app-opentui/registry.js +19923 -5151
  69. package/dist/src/app-opentui/render/constants.d.ts +31 -0
  70. package/dist/src/app-opentui/render/constants.js +66 -0
  71. package/dist/src/app-opentui/render/graphics.d.ts +2 -1
  72. package/dist/src/app-opentui/render/graphics.js +106 -39
  73. package/dist/src/app-opentui/render/image-visual-layer-worker.js +108 -20
  74. package/dist/src/app-opentui/render/image-visual-layer.d.ts +7 -0
  75. package/dist/src/app-opentui/render/image-visual-layer.js +109 -20
  76. package/dist/src/app-opentui/render/native-host.d.ts +37 -0
  77. package/dist/src/app-opentui/render/native-host.js +179 -0
  78. package/dist/src/app-opentui/render/panels.js +18 -11
  79. package/dist/src/app-opentui/render/preloader.js +8 -6
  80. package/dist/src/app-opentui/render/scene.d.ts +50 -1
  81. package/dist/src/app-opentui/render/scene.js +183 -6
  82. package/dist/src/app-opentui/render/text.d.ts +7 -1
  83. package/dist/src/app-opentui/render/text.js +10 -7
  84. package/dist/src/app-opentui/render/type-bar.js +69 -30
  85. package/dist/src/app-opentui/runtime.d.ts +44 -1
  86. package/dist/src/app-opentui/runtime.js +3231 -1363
  87. package/dist/src/app-opentui/scenes/command.js +20 -6
  88. package/dist/src/app-opentui/scenes/doctor.js +176 -11
  89. package/dist/src/app-opentui/scenes/error.js +20 -10
  90. package/dist/src/app-opentui/scenes/family-domains/agent.d.ts +2 -0
  91. package/dist/src/app-opentui/scenes/family-domains/agent.js +348 -0
  92. package/dist/src/app-opentui/scenes/family-domains/browser.d.ts +2 -0
  93. package/dist/src/app-opentui/scenes/family-domains/browser.js +195 -0
  94. package/dist/src/app-opentui/scenes/family-domains/dist.d.ts +2 -0
  95. package/dist/src/app-opentui/scenes/family-domains/dist.js +243 -0
  96. package/dist/src/app-opentui/scenes/family-domains/git.d.ts +2 -0
  97. package/dist/src/app-opentui/scenes/family-domains/git.js +195 -0
  98. package/dist/src/app-opentui/scenes/family-domains/github.d.ts +2 -0
  99. package/dist/src/app-opentui/scenes/family-domains/github.js +274 -0
  100. package/dist/src/app-opentui/scenes/family-domains/harness.d.ts +2 -0
  101. package/dist/src/app-opentui/scenes/family-domains/harness.js +152 -0
  102. package/dist/src/app-opentui/scenes/family-domains/index.d.ts +4 -0
  103. package/dist/src/app-opentui/scenes/family-domains/index.js +1679 -0
  104. package/dist/src/app-opentui/scenes/family-domains/kit.d.ts +76 -0
  105. package/dist/src/app-opentui/scenes/family-domains/kit.js +305 -0
  106. package/dist/src/app-opentui/scenes/family-domains/profile.d.ts +2 -0
  107. package/dist/src/app-opentui/scenes/family-domains/profile.js +212 -0
  108. package/dist/src/app-opentui/scenes/family-domains/queue.d.ts +2 -0
  109. package/dist/src/app-opentui/scenes/family-domains/queue.js +146 -0
  110. package/dist/src/app-opentui/scenes/family-domains/remote.d.ts +2 -0
  111. package/dist/src/app-opentui/scenes/family-domains/remote.js +518 -0
  112. package/dist/src/app-opentui/scenes/family-domains/review.d.ts +2 -0
  113. package/dist/src/app-opentui/scenes/family-domains/review.js +280 -0
  114. package/dist/src/app-opentui/scenes/family-domains/setup.d.ts +2 -0
  115. package/dist/src/app-opentui/scenes/family-domains/setup.js +267 -0
  116. package/dist/src/app-opentui/scenes/family-domains/stats.d.ts +2 -0
  117. package/dist/src/app-opentui/scenes/family-domains/stats.js +370 -0
  118. package/dist/src/app-opentui/scenes/family.d.ts +3 -0
  119. package/dist/src/app-opentui/scenes/family.js +2144 -0
  120. package/dist/src/app-opentui/scenes/fleet.js +521 -31
  121. package/dist/src/app-opentui/scenes/handoff.js +204 -12
  122. package/dist/src/app-opentui/scenes/help.js +609 -40
  123. package/dist/src/app-opentui/scenes/inbox.js +278 -17
  124. package/dist/src/app-opentui/scenes/init.js +84 -39
  125. package/dist/src/app-opentui/scenes/inspect.d.ts +3 -0
  126. package/dist/src/app-opentui/scenes/inspect.js +793 -0
  127. package/dist/src/app-opentui/scenes/main.js +218 -39
  128. package/dist/src/app-opentui/scenes/pi.d.ts +3 -0
  129. package/dist/src/app-opentui/scenes/pi.js +508 -0
  130. package/dist/src/app-opentui/scenes/plugin.d.ts +3 -0
  131. package/dist/src/app-opentui/scenes/plugin.js +486 -0
  132. package/dist/src/app-opentui/scenes/repo.d.ts +3 -0
  133. package/dist/src/app-opentui/scenes/repo.js +424 -0
  134. package/dist/src/app-opentui/scenes/run-detail.js +333 -20
  135. package/dist/src/app-opentui/scenes/server.js +181 -15
  136. package/dist/src/app-opentui/scenes/tasks.js +486 -31
  137. package/dist/src/app-opentui/scenes/workspace.d.ts +3 -0
  138. package/dist/src/app-opentui/scenes/workspace.js +426 -0
  139. package/dist/src/app-opentui/selectable.d.ts +19 -0
  140. package/dist/src/app-opentui/selectable.js +79 -0
  141. package/dist/src/app-opentui/state.js +83 -30
  142. package/dist/src/app-opentui/surface-catalog.d.ts +20 -0
  143. package/dist/src/app-opentui/surface-catalog.js +540 -0
  144. package/dist/src/app-opentui/theme.d.ts +28 -6
  145. package/dist/src/app-opentui/theme.js +61 -8
  146. package/dist/src/app-opentui/types.d.ts +121 -4
  147. package/dist/src/commands/_authority-runs.d.ts +1 -1
  148. package/dist/src/commands/_authority-runs.js +2 -12
  149. package/dist/src/commands/_help-catalog.js +95 -15
  150. package/dist/src/commands/_operator-view.js +34 -2
  151. package/dist/src/commands/_pi-frontend.d.ts +2 -0
  152. package/dist/src/commands/_pi-frontend.js +34 -0
  153. package/dist/src/commands/_server-events.d.ts +26 -0
  154. package/dist/src/commands/_server-events.js +310 -0
  155. package/dist/src/commands/agent.js +2 -12
  156. package/dist/src/commands/run.js +34 -2
  157. package/dist/src/commands/server.js +2 -12
  158. package/dist/src/commands/stats.js +95 -15
  159. package/dist/src/commands/task-run-driver.js +2 -12
  160. package/dist/src/commands/task.js +131 -29
  161. package/dist/src/commands.js +192 -31
  162. package/dist/src/index.js +192 -31
  163. package/package.json +11 -9
@@ -16,15 +16,6 @@ var __export = (target, all) => {
16
16
  var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
17
17
 
18
18
  // packages/cli/src/app-opentui/adapters/common.ts
19
- function adapterContextFromRuntime(runtime, renderer) {
20
- return {
21
- projectRoot: runtime.getState().projectRoot,
22
- renderer,
23
- ensureRuntime: () => runtime.getRunnerContext(),
24
- emit: (event) => runtime.emit(event),
25
- getState: () => runtime.getState()
26
- };
27
- }
28
19
  function projectRootOf(ctx) {
29
20
  const root = ctx.rig?.projectRoot ?? ctx.projectRoot ?? ctx.getState().projectRoot;
30
21
  if (typeof root === "string" && root.trim())
@@ -74,6 +65,17 @@ function patchData(ctx, data) {
74
65
  function patchFooter(ctx, footer) {
75
66
  ctx.emit({ type: "footer.patch", footer });
76
67
  }
68
+ function arrayFromPayload(value) {
69
+ if (Array.isArray(value)) {
70
+ return value.filter((entry) => Boolean(entry && typeof entry === "object" && !Array.isArray(entry)));
71
+ }
72
+ if (value && typeof value === "object" && !Array.isArray(value)) {
73
+ const entries = value.entries;
74
+ if (Array.isArray(entries))
75
+ return arrayFromPayload(entries);
76
+ }
77
+ return [];
78
+ }
77
79
  function stringField(record, keys, fallback = "") {
78
80
  for (const key of keys) {
79
81
  const value = record[key];
@@ -93,10 +95,6 @@ function compactRecord(record, keys) {
93
95
  }
94
96
  return out;
95
97
  }
96
- function payloadString(intent, key) {
97
- const value = intent.action.payload?.[key];
98
- return typeof value === "string" && value.trim() ? value.trim() : undefined;
99
- }
100
98
 
101
99
  // packages/cli/src/runner.ts
102
100
  import { EventBus } from "@rig/runtime/control-plane/runtime/events";
@@ -1125,18 +1123,8 @@ import {
1125
1123
  readJsonlFile,
1126
1124
  writeAuthorityRunRecord
1127
1125
  } from "@rig/runtime/control-plane/authority-files";
1128
- function normalizeRuntimeAdapter(value) {
1129
- const normalized = value?.trim().toLowerCase();
1130
- if (!normalized) {
1131
- return "pi";
1132
- }
1133
- if (normalized === "codex" || normalized === "codex-cli" || normalized === "codex-app-server" || normalized === "gpt-codex") {
1134
- return "codex";
1135
- }
1136
- if (normalized === "pi" || normalized === "rig-pi" || normalized === "@rig/pi") {
1137
- return "pi";
1138
- }
1139
- return "claude-code";
1126
+ function normalizeRuntimeAdapter(_value) {
1127
+ return "pi";
1140
1128
  }
1141
1129
  var init__authority_runs = __esm(() => {
1142
1130
  init__paths();
@@ -1758,6 +1746,23 @@ function buildOperatorPiEnv(input) {
1758
1746
  ...input.serverProjectRoot ? { RIG_PROJECT_ROOT: input.serverProjectRoot } : {}
1759
1747
  };
1760
1748
  }
1749
+ function shouldRequireOperatorTranscript(status) {
1750
+ return typeof status === "string" && TERMINAL_RUN_STATUSES.has(status.trim().toLowerCase());
1751
+ }
1752
+ function missingOperatorTranscriptMessage(runId, status) {
1753
+ const shortRun = runId.trim().slice(0, 8) || "unknown";
1754
+ const statusText = typeof status === "string" && status.trim() ? status.trim() : "terminal";
1755
+ return `Run ${shortRun} is ${statusText}, but no worker Pi session transcript is recorded for this run. It likely failed before Pi started or predates full-session capture. Use \`rig run show ${runId}\` and \`rig run logs ${runId}\` for the lifecycle details.`;
1756
+ }
1757
+ function statusFromRunDetails(run) {
1758
+ if (!run || typeof run !== "object" || Array.isArray(run))
1759
+ return;
1760
+ const record = run;
1761
+ if (typeof record.status === "string")
1762
+ return record.status;
1763
+ const nested = record.run;
1764
+ return nested && typeof nested === "object" && !Array.isArray(nested) ? nested.status : undefined;
1765
+ }
1761
1766
  async function prepareOperatorConsole(context, runId, tempSessionDir) {
1762
1767
  const server = await ensureServerForCli(context.projectRoot);
1763
1768
  const localCwd = join(homedir2(), ".rig", "drones", runId.slice(0, 8));
@@ -1787,6 +1792,13 @@ async function prepareOperatorConsole(context, runId, tempSessionDir) {
1787
1792
  sessionFileArg = ["--session", localSessionPath];
1788
1793
  }
1789
1794
  } catch {}
1795
+ if (sessionFileArg.length === 0) {
1796
+ const run = await getRunDetailsViaServer(context, runId).catch(() => null);
1797
+ const status = statusFromRunDetails(run);
1798
+ if (shouldRequireOperatorTranscript(status)) {
1799
+ throw new OperatorTranscriptUnavailableError(missingOperatorTranscriptMessage(runId, status));
1800
+ }
1801
+ }
1790
1802
  return { server, sessionFileArg };
1791
1803
  }
1792
1804
  function trustDroneCwd(localCwd) {
@@ -1858,10 +1870,17 @@ async function attachRunBundledPiFrontend(context, input) {
1858
1870
  rendered: "stock Pi operator console with the pi-rig extension"
1859
1871
  };
1860
1872
  }
1861
- var RIG_PI_THEME;
1873
+ var TERMINAL_RUN_STATUSES, OperatorTranscriptUnavailableError, RIG_PI_THEME;
1862
1874
  var init__pi_frontend = __esm(() => {
1863
1875
  init__server_client();
1864
1876
  init__async_ui();
1877
+ TERMINAL_RUN_STATUSES = new Set(["completed", "failed", "stopped", "cancelled", "canceled", "closed", "merged", "needs_attention", "needs-attention"]);
1878
+ OperatorTranscriptUnavailableError = class OperatorTranscriptUnavailableError extends Error {
1879
+ constructor(message2) {
1880
+ super(message2);
1881
+ this.name = "OperatorTranscriptUnavailableError";
1882
+ }
1883
+ };
1865
1884
  RIG_PI_THEME = {
1866
1885
  $schema: "https://raw.githubusercontent.com/earendil-works/pi/main/packages/coding-agent/src/modes/interactive/theme/theme-schema.json",
1867
1886
  name: "rig",
@@ -2016,7 +2035,7 @@ async function attachRunOperatorView(context, input) {
2016
2035
  }
2017
2036
  const pollMs = Math.max(250, Math.trunc(input.pollMs ?? 2000));
2018
2037
  let timelineCursor = snapshot.timelineCursor;
2019
- while (!detached && !TERMINAL_RUN_STATUSES.has(runStatusFromPayload(snapshot.run))) {
2038
+ while (!detached && !TERMINAL_RUN_STATUSES2.has(runStatusFromPayload(snapshot.run))) {
2020
2039
  await Bun.sleep(pollMs);
2021
2040
  snapshot = await readOperatorSnapshot(context, input.runId, { timelineCursor });
2022
2041
  timelineCursor = snapshot.timelineCursor;
@@ -2028,13 +2047,13 @@ async function attachRunOperatorView(context, input) {
2028
2047
  }
2029
2048
  return { ...snapshot, steered, detached };
2030
2049
  }
2031
- var TERMINAL_RUN_STATUSES;
2050
+ var TERMINAL_RUN_STATUSES2;
2032
2051
  var init__operator_view = __esm(() => {
2033
2052
  init__server_client();
2034
2053
  init__operator_surface();
2035
2054
  init__pi_frontend();
2036
2055
  init__async_ui();
2037
- TERMINAL_RUN_STATUSES = new Set(["completed", "failed", "stopped", "cancelled", "canceled", "closed", "merged", "needs_attention", "needs-attention"]);
2056
+ TERMINAL_RUN_STATUSES2 = new Set(["completed", "failed", "stopped", "cancelled", "canceled", "closed", "merged", "needs_attention", "needs-attention"]);
2038
2057
  });
2039
2058
 
2040
2059
  // packages/cli/src/commands/_cli-format.ts
@@ -2564,30 +2583,62 @@ var init__help_catalog = __esm(() => {
2564
2583
  }
2565
2584
  ];
2566
2585
  ADVANCED_GROUPS = [
2567
- { name: "setup", summary: "Bootstrap/check local setup.", usage: ["rig setup <bootstrap|check|preflight>"], commands: [{ command: "bootstrap|check|preflight", description: "Setup helpers." }] },
2568
- { name: "profile", summary: "Runtime profile/model defaults.", usage: ["rig profile <show|set>"], commands: [{ command: "show", description: "Show active profile." }] },
2586
+ {
2587
+ name: "setup",
2588
+ summary: "Bootstrap/check local setup.",
2589
+ usage: ["rig setup <bootstrap|check|preflight>"],
2590
+ commands: [
2591
+ { command: "bootstrap", description: "Bootstrap local setup dependencies.", primary: true },
2592
+ { command: "check", description: "Check local setup state (toolchain, deps, config).", primary: true },
2593
+ { command: "preflight", description: "Run preflight checks before a run.", primary: true }
2594
+ ]
2595
+ },
2596
+ {
2597
+ name: "profile",
2598
+ summary: "Runtime profile/model defaults.",
2599
+ usage: ["rig profile <show|set>"],
2600
+ commands: [
2601
+ { command: "show", description: "Show the active execution profile.", primary: true },
2602
+ { command: "set [--model <model>] [--runtime <runtime>] [--plugin <plugin>]", description: "Set model/runtime/plugin profile defaults.", primary: true }
2603
+ ]
2604
+ },
2569
2605
  {
2570
2606
  name: "review",
2571
2607
  summary: "Inspect or change completion review gate policy.",
2572
2608
  usage: ["rig review <show|set>"],
2573
2609
  commands: [
2574
2610
  { command: "show", description: "Show current review gate settings." },
2575
- { command: "set <off|advisory|required> [--provider greptile]", description: "Change review strictness/provider." }
2611
+ { command: "set <off|advisory|required> [--provider <provider>]", description: "Change review strictness/provider (e.g. --provider greptile)." }
2576
2612
  ],
2577
2613
  examples: ["rig review show", "rig review set required --provider greptile"],
2578
2614
  next: ["Use `rig inbox approvals` for blocked run handoffs."]
2579
2615
  },
2580
2616
  {
2581
2617
  name: "browser",
2582
- summary: "Browser/app diagnostics for browser-required tasks.",
2583
- usage: ["rig browser <help|explain|demo|app|hp-next> [options]"],
2618
+ summary: "Browser workstation actions for browser-required tasks: launch, check, CDP probe, profile reset, and app-specific smokes.",
2619
+ usage: ["rig browser <help|explain|demo|hp-next|cdp-probe|profile-persistence|profile-lock-check|smoke-test> [options]"],
2584
2620
  commands: [
2585
- { command: "help", description: "Rich browser command help (canonical: `rig browser help`)." },
2586
- { command: "explain", description: "Explain the browser-required task contract." },
2587
- { command: "demo", description: "Run browser demo flows against a local page." },
2588
- { command: "app", description: "Launch the Rig Browser workstation app." },
2589
- { command: "hp-next <dev|check|e2e|reset>", description: "Drive the hp-next browser test harness." }
2590
- ]
2621
+ { command: "help", description: "Rich browser command help (canonical: `rig browser help`).", primary: true },
2622
+ { command: "explain", description: "Explain the browser-required task contract and runtime helper commands.", primary: true },
2623
+ { command: "demo [--port <n>] [--profile <name>] [--state-dir <path>] [--target-url <url>] [--keep-open] [--no-build]", description: "Run a guided real browser/agent demo flow.", primary: true },
2624
+ { command: "hp-next dev", description: "Start hp-next Rig Browser in dev/watch mode.", primary: true },
2625
+ { command: "hp-next start", description: "Build/start the hp-next browser workstation without watch mode.", primary: true },
2626
+ { command: "hp-next check", description: "Validate the hp-next CDP endpoint on the configured port.", primary: true },
2627
+ { command: "hp-next e2e", description: "Run hp-next browser e2e smoke checks through Rig Browser.", primary: true },
2628
+ { command: "hp-next reset", description: "Reset the hp-next browser profile when stale browser state is suspected.", primary: true },
2629
+ { command: "cdp-probe", description: "Run an isolated CDP attach/evaluate/layout probe." },
2630
+ { command: "profile-persistence", description: "Verify a named browser profile persists browser state." },
2631
+ { command: "profile-lock-check", description: "Verify concurrent launches reject the same profile." },
2632
+ { command: "smoke-test", description: "Run the browser package smoke test." }
2633
+ ],
2634
+ examples: [
2635
+ "rig browser help",
2636
+ "rig browser hp-next check",
2637
+ "rig browser hp-next e2e",
2638
+ "rig browser cdp-probe",
2639
+ "rig browser profile-lock-check"
2640
+ ],
2641
+ next: ["Inside a task run, use rig-browser-launch, rig-browser-check, and rig-browser-attach-info from the worker PATH."]
2591
2642
  },
2592
2643
  {
2593
2644
  name: "pi",
@@ -2602,12 +2653,60 @@ var init__help_catalog = __esm(() => {
2602
2653
  examples: ["rig pi search subagents", "rig pi add pi-subagents", "rig pi list"],
2603
2654
  next: ["Config-managed extensions: declare `runtime: { pi: { packages: [...] } }` in rig.config.ts \u2014 workers pick them up automatically."]
2604
2655
  },
2605
- { name: "queue", summary: "Run task queues locally.", usage: ["rig queue run [options]"], commands: [{ command: "run", description: "Process queue work." }] },
2606
- { name: "agent", summary: "Runtime agent workspace helpers.", usage: ["rig agent <list|prepare|run|cleanup>"], commands: [{ command: "list", description: "List prepared agents." }] },
2656
+ {
2657
+ name: "queue",
2658
+ summary: "Run task queues locally.",
2659
+ usage: ["rig queue run [--workers <n>] [--max-tasks <n>] [--action validate|verify|pipeline] [--isolation off|worktree] [--no-runtime-reuse] [--fail-fast] [--skip-project-sync]"],
2660
+ commands: [
2661
+ { command: "run [--workers <n>] [--max-tasks <n>] [--action validate|verify|pipeline] [--isolation off|worktree] [--no-runtime-reuse] [--fail-fast] [--skip-project-sync]", description: "Process queue work: drain matching tasks with N workers, optional per-task action and isolation.", primary: true }
2662
+ ]
2663
+ },
2664
+ {
2665
+ name: "agent",
2666
+ summary: "Runtime agent workspace helpers.",
2667
+ usage: ["rig agent <list|prepare|run|cleanup>"],
2668
+ commands: [
2669
+ { command: "list", description: "List prepared agent runtimes.", primary: true },
2670
+ { command: "prepare [--id <id>] [--mode <mode>] [--task <task>]", description: "Prepare an isolated agent runtime workspace.", primary: true },
2671
+ { command: "run [--id <id>] [--mode <mode>] [--task <task>] [--skip-project-sync]", description: "Prepare (if needed) and run an agent in its workspace.", primary: true },
2672
+ { command: "cleanup [--all] [--id <id>]", description: "Remove prepared agent workspaces.", primary: true }
2673
+ ]
2674
+ },
2607
2675
  { name: "inspector", summary: "Event stream and drift scanners.", usage: ["rig inspector <stream|scan-upstream-drift>"], commands: [{ command: "stream", description: "Stream events." }] },
2608
- { name: "dist", summary: "Build/install packaged Rig CLI.", usage: ["rig dist <build|install|doctor>"], commands: [{ command: "build", description: "Build distribution." }] },
2676
+ {
2677
+ name: "dist",
2678
+ summary: "Build/install packaged Rig CLI.",
2679
+ usage: ["rig dist <build|install|doctor|rebuild-agent>"],
2680
+ commands: [
2681
+ { command: "build [--output-dir <dir>]", description: "Build the distributable Rig CLI.", primary: true },
2682
+ { command: "install [--scope <scope>] [--path <path>]", description: "Install the built CLI to a scope/path.", primary: true },
2683
+ { command: "doctor", description: "Diagnose the dist toolchain and install state.", primary: true },
2684
+ { command: "rebuild-agent", description: "Rebuild the agent runtime image, pruning stale images.", primary: true }
2685
+ ]
2686
+ },
2609
2687
  { name: "workspace", summary: "Workspace topology/service helpers.", usage: ["rig workspace <summary|topology|remote-hosts>"], commands: [{ command: "summary", description: "Show workspace summary." }] },
2610
- { name: "remote", summary: "Compatibility remote orchestration controls.", usage: ["rig remote <status|watch|pause|resume|...>"], commands: [{ command: "status", description: "Show remote state." }] },
2688
+ {
2689
+ name: "remote",
2690
+ summary: "Compatibility remote orchestration controls.",
2691
+ usage: ["rig remote <status|tasks|watch|pause|resume|continue|stop|refresh|add-iterations|remove-iterations|endpoint ...>"],
2692
+ commands: [
2693
+ { command: "status [--remote <alias>]", description: "Show the remote orchestration state.", primary: true },
2694
+ { command: "tasks [--remote <alias>]", description: "List the remote's tracked tasks.", primary: true },
2695
+ { command: "watch [--remote <alias>] [--seconds <n>] [--event <type>]", description: "Stream remote orchestration events.", primary: true },
2696
+ { command: "pause [--remote <alias>]", description: "Pause the running remote orchestration.", primary: true },
2697
+ { command: "resume [--remote <alias>] [--max-workers <n>] [--max-iterations <n>] [--direct-merge]", description: "Resume the remote with optional tuning.", primary: true },
2698
+ { command: "continue [--remote <alias>]", description: "Continue a paused remote orchestration.", primary: true },
2699
+ { command: "stop [--remote <alias>]", description: "Stop the remote orchestration.", primary: true },
2700
+ { command: "refresh [--remote <alias>]", description: "Refresh remote state.", primary: true },
2701
+ { command: "add-iterations [--count <n>] [--remote <alias>]", description: "Grant the remote more iterations.", primary: true },
2702
+ { command: "remove-iterations [--count <n>] [--remote <alias>]", description: "Reduce the remote's iteration budget.", primary: true },
2703
+ { command: "endpoint list", description: "List configured remote endpoints.", primary: true },
2704
+ { command: "endpoint add --alias <alias> --host <host> --port <n> [--token <token>]", description: "Register a remote endpoint.", primary: true },
2705
+ { command: "endpoint remove --alias <alias>", description: "Remove a remote endpoint.", primary: true },
2706
+ { command: "endpoint test [--alias <alias>]", description: "Test connectivity to a remote endpoint.", primary: true },
2707
+ { command: "endpoint doctor", description: "Diagnose remote endpoint configuration.", primary: true }
2708
+ ]
2709
+ },
2611
2710
  { name: "git", summary: "Pass through to Rig git-flow helper.", usage: ["rig git <args...>"], commands: [{ command: "<args...>", description: "Advanced git flow operations." }] },
2612
2711
  { name: "harness", summary: "Pass through to runtime harness CLI.", usage: ["rig harness <args...>"], commands: [{ command: "<args...>", description: "Advanced harness operations." }] },
2613
2712
  { name: "test", summary: "Project test wrappers.", usage: ["rig test <unit|e2e|all>"], commands: [{ command: "all", description: "Run configured project tests." }] }
@@ -3280,9 +3379,11 @@ class PiPtyHost {
3280
3379
  cols;
3281
3380
  rows;
3282
3381
  message = "starting bundled Pi";
3382
+ lastResizeError = null;
3283
3383
  exitCode;
3284
3384
  signal;
3285
3385
  notifyTimer = null;
3386
+ lastStreamKey = "";
3286
3387
  _disposed = false;
3287
3388
  constructor(options) {
3288
3389
  this.runId = options.runId;
@@ -3299,6 +3400,17 @@ class PiPtyHost {
3299
3400
  scrollback: 1000
3300
3401
  });
3301
3402
  this.registerTerminalResponders();
3403
+ this.disposables.push(this.terminal.onWriteParsed(() => {
3404
+ if (this._disposed)
3405
+ return;
3406
+ const snapshot = this.createSnapshot();
3407
+ const key = snapshot.lines.join(`
3408
+ `);
3409
+ if (key === this.lastStreamKey)
3410
+ return;
3411
+ this.lastStreamKey = key;
3412
+ this.onSnapshot?.(snapshot);
3413
+ }));
3302
3414
  }
3303
3415
  get disposed() {
3304
3416
  return this._disposed;
@@ -3385,7 +3497,10 @@ class PiPtyHost {
3385
3497
  this.terminal.resize(nextCols, nextRows);
3386
3498
  try {
3387
3499
  this.pty?.resize(nextCols, nextRows);
3388
- } catch {}
3500
+ this.lastResizeError = null;
3501
+ } catch (error) {
3502
+ this.lastResizeError = error instanceof Error ? error.message : String(error);
3503
+ }
3389
3504
  this.emitSnapshotSoon(0);
3390
3505
  }
3391
3506
  detach() {
@@ -3427,7 +3542,7 @@ class PiPtyHost {
3427
3542
  return;
3428
3543
  const text = this.decoder.decode(data, { stream: true });
3429
3544
  this.respondToRawTerminalQueries(text);
3430
- this.terminal.write(data, () => this.emitSnapshotSoon());
3545
+ this.terminal.write(data);
3431
3546
  }
3432
3547
  emitSnapshotSoon(delayMs = SNAPSHOT_DELAY_MS) {
3433
3548
  if (this._disposed || this.notifyTimer)
@@ -3456,6 +3571,7 @@ class PiPtyHost {
3456
3571
  lines.push("");
3457
3572
  styledLines[lines.length - 1] = new StyledText([{ __isChunk: true, text: "" }]);
3458
3573
  }
3574
+ const resolvedMessage = this.lastResizeError ? `resize degraded: ${this.lastResizeError}` : message2;
3459
3575
  return {
3460
3576
  runId: this.runId,
3461
3577
  status: this.status,
@@ -3463,7 +3579,8 @@ class PiPtyHost {
3463
3579
  rows: this.rows,
3464
3580
  lines,
3465
3581
  styledLines,
3466
- message: message2,
3582
+ message: resolvedMessage,
3583
+ ...this.lastResizeError ? { resizeError: this.lastResizeError } : {},
3467
3584
  ...this.exitCode !== undefined ? { exitCode: this.exitCode } : {},
3468
3585
  ...this.signal !== undefined ? { signal: this.signal } : {}
3469
3586
  };
@@ -3537,7 +3654,7 @@ async function refreshFleet(ctx, options = {}) {
3537
3654
  const entry = normalizeRunRecord(run);
3538
3655
  return entry ? [entry] : [];
3539
3656
  });
3540
- patchData(ctx, { fleet: { runs: normalized, refreshedAt: new Date().toISOString() } });
3657
+ patchData(ctx, { fleet: { runs: normalized, refreshedAt: new Date().toISOString() }, lastRefreshError: undefined });
3541
3658
  patchFooter(ctx, { server: serverLabel, runs: String(normalized.length) });
3542
3659
  emitCompleted(ctx, label, { count: normalized.length });
3543
3660
  return normalized;
@@ -3548,32 +3665,235 @@ async function refreshFleet(ctx, options = {}) {
3548
3665
  }
3549
3666
  var init_fleet = () => {};
3550
3667
 
3668
+ // packages/cli/src/app-opentui/adapters/inspect.ts
3669
+ var exports_inspect = {};
3670
+ __export(exports_inspect, {
3671
+ loadInspectRuns: () => loadInspectRuns,
3672
+ loadInspect: () => loadInspect,
3673
+ INSPECT_VIEWS: () => INSPECT_VIEWS
3674
+ });
3675
+ import { execFile } from "child_process";
3676
+ import { existsSync as existsSync4, readFileSync as readFileSync5, readdirSync, statSync } from "fs";
3677
+ import { resolve as resolve4 } from "path";
3678
+ import { promisify } from "util";
3679
+ import { resolveTaskArtifactDirs } from "@rig/runtime/control-plane/authority-files";
3680
+ import { readTaskArtifactPreview } from "@rig/runtime/control-plane/native/workspace-ops";
3681
+ import { resolveHarnessPaths, resolveMonorepoRoot as resolveMonorepoRoot2, runCapture } from "@rig/runtime/control-plane/native/utils";
3682
+ function isFailureEntry(entry) {
3683
+ const status = `${entry.status ?? entry.level ?? entry.type ?? ""}`.toLowerCase();
3684
+ return status.includes("fail") || status.includes("error");
3685
+ }
3686
+ async function gitDiff(projectRoot) {
3687
+ try {
3688
+ const { stdout } = await execFileAsync("git", ["diff", "--no-color"], { cwd: projectRoot, maxBuffer: 8 * 1024 * 1024 });
3689
+ return { diff: stdout };
3690
+ } catch (error) {
3691
+ return { diff: "", error: normalizeAppError(error).message };
3692
+ }
3693
+ }
3694
+ function listArtifacts(projectRoot, taskId3) {
3695
+ if (!taskId3)
3696
+ return { artifacts: [], error: "run has no task id \u2014 artifacts are resolved per task" };
3697
+ try {
3698
+ const dirs = resolveTaskArtifactDirs(projectRoot, taskId3).filter((dir) => existsSync4(dir));
3699
+ const out = [];
3700
+ const seen = new Set;
3701
+ for (const dir of dirs) {
3702
+ for (const name of readdirSync(dir)) {
3703
+ if (seen.has(name))
3704
+ continue;
3705
+ seen.add(name);
3706
+ const path = resolve4(dir, name);
3707
+ try {
3708
+ const stat = statSync(path);
3709
+ out.push({ name, path, sizeBytes: stat.size, isDir: stat.isDirectory() });
3710
+ } catch {
3711
+ out.push({ name, path, sizeBytes: 0, isDir: false });
3712
+ }
3713
+ }
3714
+ }
3715
+ out.sort((left, right) => left.name.localeCompare(right.name));
3716
+ return { artifacts: out };
3717
+ } catch (error) {
3718
+ return { artifacts: [], error: normalizeAppError(error).message };
3719
+ }
3720
+ }
3721
+ function readArtifact(projectRoot, taskId3, fileName) {
3722
+ if (!taskId3)
3723
+ return;
3724
+ try {
3725
+ const preview = readTaskArtifactPreview(projectRoot, taskId3, fileName);
3726
+ return { name: fileName, contents: preview.contents, truncated: preview.truncated, sizeBytes: preview.sizeBytes };
3727
+ } catch (error) {
3728
+ return { name: fileName, contents: `Failed to read artifact: ${normalizeAppError(error).message}`, truncated: false, sizeBytes: 0 };
3729
+ }
3730
+ }
3731
+ function loadGraph(projectRoot) {
3732
+ try {
3733
+ const result = runCapture(["br", "--no-db", "list", "--pretty"], resolveMonorepoRoot2(projectRoot));
3734
+ if (result.exitCode !== 0)
3735
+ return { graph: "", error: result.stderr || result.stdout || "failed to inspect graph" };
3736
+ return { graph: result.stdout };
3737
+ } catch (error) {
3738
+ return { graph: "", error: normalizeAppError(error).message };
3739
+ }
3740
+ }
3741
+ function loadAudit(projectRoot) {
3742
+ try {
3743
+ const auditPath = resolve4(resolveHarnessPaths(projectRoot).logsDir, "audit.jsonl");
3744
+ if (!existsSync4(auditPath))
3745
+ return [];
3746
+ return readFileSync5(auditPath, "utf-8").split(/\r?\n/).filter(Boolean).slice(-50).flatMap((raw) => {
3747
+ try {
3748
+ const parsed = JSON.parse(raw);
3749
+ return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? [parsed] : [{ detail: raw }];
3750
+ } catch {
3751
+ return [{ detail: raw }];
3752
+ }
3753
+ });
3754
+ } catch {
3755
+ return [];
3756
+ }
3757
+ }
3758
+ async function loadInspectRuns(ctx, options = {}) {
3759
+ const label = "Loading runs to inspect";
3760
+ emitStarted(ctx, label);
3761
+ try {
3762
+ const projectRoot = projectRootOf(ctx);
3763
+ const { listRunsViaServer: listRunsViaServer2 } = await Promise.resolve().then(() => (init__server_client(), exports__server_client));
3764
+ emitProgress(ctx, label, "listing recent runs");
3765
+ const runs = await listRunsViaServer2({ projectRoot }, { limit: options.limit ?? 50 });
3766
+ const options_ = runs.flatMap((record) => {
3767
+ const runId = stringField(record, ["runId", "id"]);
3768
+ if (!runId)
3769
+ return [];
3770
+ const taskId3 = stringField(record, ["taskId"]);
3771
+ const updatedAt = stringField(record, ["updatedAt", "createdAt"]);
3772
+ return [{
3773
+ runId,
3774
+ status: stringField(record, ["status", "state"], "unknown"),
3775
+ title: stringField(record, ["title", "taskTitle", "taskId"], "untitled run"),
3776
+ ...taskId3 ? { taskId: taskId3 } : {},
3777
+ ...updatedAt ? { updatedAt } : {}
3778
+ }];
3779
+ });
3780
+ patchData(ctx, { inspectRuns: options_, lastRefreshError: undefined });
3781
+ emitCompleted(ctx, label, { count: options_.length });
3782
+ return options_;
3783
+ } catch (error) {
3784
+ emitFailed(ctx, label, error);
3785
+ throw error;
3786
+ }
3787
+ }
3788
+ async function loadInspect(ctx, runId) {
3789
+ const cleanRunId = runId.trim();
3790
+ const label = `Inspecting run ${cleanRunId.slice(0, 8)}`;
3791
+ if (!cleanRunId) {
3792
+ await loadInspectRuns(ctx).catch(() => []);
3793
+ const error = new Error("Inspect requires a run id.");
3794
+ throw error;
3795
+ }
3796
+ emitStarted(ctx, label);
3797
+ try {
3798
+ const projectRoot = projectRootOf(ctx);
3799
+ const { getRunDetailsViaServer: getRunDetailsViaServer2, getRunLogsViaServer: getRunLogsViaServer2, getRunTimelineViaServer: getRunTimelineViaServer2 } = await Promise.resolve().then(() => (init__server_client(), exports__server_client));
3800
+ const context = { projectRoot };
3801
+ emitProgress(ctx, label, "loading logs, timeline, diff, artifacts, graph, and audit");
3802
+ const [runPayload, logsPayload, timelinePayload, diffResult] = await Promise.all([
3803
+ getRunDetailsViaServer2(context, cleanRunId).catch((error) => ({ error: normalizeAppError(error).message })),
3804
+ getRunLogsViaServer2(context, cleanRunId, { limit: 500 }).catch(() => []),
3805
+ getRunTimelineViaServer2(context, cleanRunId, { limit: 500 }).catch(() => []),
3806
+ gitDiff(projectRoot)
3807
+ ]);
3808
+ const run = runPayload && typeof runPayload === "object" && "run" in runPayload && runPayload.run && typeof runPayload.run === "object" ? runPayload.run : runPayload;
3809
+ const logs = arrayFromPayload(logsPayload);
3810
+ const timeline = arrayFromPayload(timelinePayload);
3811
+ const taskId3 = stringField(run, ["taskId"]) || undefined;
3812
+ const { artifacts, error: artifactsError } = listArtifacts(projectRoot, taskId3);
3813
+ const { graph, error: graphError } = loadGraph(projectRoot);
3814
+ const audit = loadAudit(projectRoot);
3815
+ const state = ctx.getState();
3816
+ const selectedArtifact = typeof state.data?.inspectArtifact === "string" ? state.data.inspectArtifact : undefined;
3817
+ const artifactView = selectedArtifact ? readArtifact(projectRoot, taskId3, selectedArtifact) : undefined;
3818
+ const snapshot = {
3819
+ runId: cleanRunId,
3820
+ status: stringField(run, ["status", "state"], "unknown"),
3821
+ title: stringField(run, ["title", "taskId", "id"], "untitled run"),
3822
+ logs,
3823
+ timeline,
3824
+ diff: diffResult.diff,
3825
+ failures: [...logs, ...timeline].filter(isFailureEntry),
3826
+ artifacts,
3827
+ graph,
3828
+ audit,
3829
+ refreshedAt: Date.now(),
3830
+ ...taskId3 ? { taskId: taskId3 } : {},
3831
+ ...artifactView ? { artifactView } : {},
3832
+ ...diffResult.error ? { diffError: diffResult.error } : {},
3833
+ ...graphError ? { graphError } : {},
3834
+ ...artifactsError ? { artifactsError } : {}
3835
+ };
3836
+ patchData(ctx, { inspect: snapshot, lastRefreshError: undefined });
3837
+ emitCompleted(ctx, label, {
3838
+ runId: cleanRunId,
3839
+ logs: logs.length,
3840
+ timeline: timeline.length,
3841
+ diffBytes: snapshot.diff.length,
3842
+ artifacts: artifacts.length,
3843
+ audit: audit.length
3844
+ });
3845
+ return snapshot;
3846
+ } catch (error) {
3847
+ emitFailed(ctx, label, error, { runId: cleanRunId });
3848
+ throw error;
3849
+ }
3850
+ }
3851
+ var execFileAsync, INSPECT_VIEWS;
3852
+ var init_inspect = __esm(() => {
3853
+ execFileAsync = promisify(execFile);
3854
+ INSPECT_VIEWS = [
3855
+ "logs",
3856
+ "timeline",
3857
+ "artifacts",
3858
+ "artifact",
3859
+ "diff",
3860
+ "failures",
3861
+ "graph",
3862
+ "audit"
3863
+ ];
3864
+ });
3865
+
3551
3866
  // packages/cli/src/app-opentui/adapters/pi-attach.ts
3552
3867
  var exports_pi_attach = {};
3553
3868
  __export(exports_pi_attach, {
3554
3869
  preparePiAttachHandoff: () => preparePiAttachHandoff,
3555
- createPiAttachAdapter: () => createPiAttachAdapter,
3556
3870
  attachRunWithBundledPi: () => attachRunWithBundledPi
3557
3871
  });
3558
- function createPiAttachAdapter(renderer) {
3559
- return {
3560
- id: "pi-attach",
3561
- async handleIntent(runtime, intent) {
3562
- if (intent.scene !== "handoff" && intent.action.kind !== "run-attach")
3563
- return false;
3564
- const ctx = adapterContextFromRuntime(runtime, renderer);
3565
- const runId = payloadString(intent, "runId") ?? runtime.getState().data.selectedRunId;
3566
- if (intent.action.kind === "run-attach" && runId) {
3567
- await attachRunWithBundledPi(ctx, runId);
3568
- return true;
3569
- }
3570
- if (runId) {
3571
- await preparePiAttachHandoff(ctx, runId);
3572
- return true;
3573
- }
3574
- return false;
3575
- }
3576
- };
3872
+ function currentAttachState(ctx, runId) {
3873
+ const value = ctx.getState().data?.piAttach;
3874
+ if (!value || typeof value !== "object" || Array.isArray(value))
3875
+ return null;
3876
+ const state = value;
3877
+ return state.runId === runId ? state : null;
3878
+ }
3879
+ function recordStep(ctx, runId, label, emitLabel) {
3880
+ const now = Date.now();
3881
+ const previous = currentAttachState(ctx, runId);
3882
+ const priorSteps = previous?.steps ?? [];
3883
+ const settled = priorSteps.map((step) => step.status === "running" ? { ...step, status: "done", endedAtMs: step.endedAtMs ?? now } : step);
3884
+ const steps = [...settled, { label, startedAtMs: now, status: "running" }];
3885
+ patchData(ctx, {
3886
+ piAttach: { ...previous ?? { runId, status: "preparing", message: label }, runId, steps }
3887
+ });
3888
+ emitProgress(ctx, emitLabel, label);
3889
+ }
3890
+ function settleSteps(ctx, runId) {
3891
+ const now = Date.now();
3892
+ const previous = currentAttachState(ctx, runId);
3893
+ if (!previous?.steps)
3894
+ return;
3895
+ const steps = previous.steps.map((step) => step.status === "running" ? { ...step, status: "done", endedAtMs: step.endedAtMs ?? now } : step);
3896
+ patchData(ctx, { piAttach: { ...previous, steps } });
3577
3897
  }
3578
3898
  async function preparePiAttachHandoff(ctx, runId) {
3579
3899
  const cleanRunId = runId.trim();
@@ -3582,7 +3902,7 @@ async function preparePiAttachHandoff(ctx, runId) {
3582
3902
  try {
3583
3903
  const { getRunDetailsViaServer: getRunDetailsViaServer2, getRunPiStatusViaServer: getRunPiStatusViaServer2, resolveServerConnectionLabel: resolveServerConnectionLabel2 } = await Promise.resolve().then(() => (init__server_client(), exports__server_client));
3584
3904
  const projectRoot = projectRootOf(ctx);
3585
- emitProgress(ctx, label, "checking run and Pi session readiness");
3905
+ recordStep(ctx, cleanRunId, "connecting to server \xB7 checking run and Pi session readiness", label);
3586
3906
  const [run, piStatus, server] = await Promise.all([
3587
3907
  getRunDetailsViaServer2({ projectRoot }, cleanRunId).catch((error) => ({ error: error instanceof Error ? error.message : String(error) })),
3588
3908
  getRunPiStatusViaServer2({ projectRoot }, cleanRunId).catch((error) => ({ error: error instanceof Error ? error.message : String(error) })),
@@ -3593,13 +3913,21 @@ async function preparePiAttachHandoff(ctx, runId) {
3593
3913
  status: "preparing",
3594
3914
  message: "ready to enter worker Pi",
3595
3915
  result: { run, piStatus, server },
3596
- ready: true
3916
+ ready: true,
3917
+ ...currentAttachState(ctx, cleanRunId)?.steps ? { steps: currentAttachState(ctx, cleanRunId).steps } : {}
3597
3918
  };
3598
- patchData(ctx, { piAttach: state });
3919
+ patchData(ctx, { piAttach: state, lastRefreshError: undefined });
3599
3920
  emitCompleted(ctx, label, { runId: cleanRunId, server });
3600
3921
  return state;
3601
3922
  } catch (error) {
3602
- const state = { runId: cleanRunId, status: "failed", message: error instanceof Error ? error.message : String(error) };
3923
+ const reason = error instanceof Error ? error.message : String(error);
3924
+ const state = {
3925
+ runId: cleanRunId,
3926
+ status: "failed",
3927
+ message: reason,
3928
+ failureReason: reason,
3929
+ ...currentAttachState(ctx, cleanRunId)?.steps ? { steps: currentAttachState(ctx, cleanRunId).steps } : {}
3930
+ };
3603
3931
  patchData(ctx, { piAttach: state });
3604
3932
  emitFailed(ctx, label, error, { runId: cleanRunId });
3605
3933
  throw error;
@@ -3614,32 +3942,52 @@ async function attachRunWithBundledPi(ctx, runId) {
3614
3942
  throw error;
3615
3943
  }
3616
3944
  emitStarted(ctx, label, { piAttach: { runId: cleanRunId, status: "entering-pi" } });
3617
- patchData(ctx, { piAttach: { runId: cleanRunId, status: "entering-pi", message: "starting bundled Pi" } });
3945
+ patchData(ctx, { piAttach: { runId: cleanRunId, status: "entering-pi", message: "starting bundled Pi", steps: [] } });
3618
3946
  try {
3619
- await preparePiAttachHandoff(ctx, cleanRunId).catch(() => null);
3620
- emitProgress(ctx, label, "starting Bun PTY host for bundled Pi");
3947
+ recordStep(ctx, cleanRunId, "connecting to server and fetching run transcript", label);
3948
+ await preparePiAttachHandoff(ctx, cleanRunId).catch((error) => {
3949
+ patchData(ctx, { lastRefreshError: normalizeAppError(error).message });
3950
+ return null;
3951
+ });
3952
+ recordStep(ctx, cleanRunId, "spawning Bun PTY host for bundled Pi", label);
3621
3953
  const projectRoot = projectRootOf(ctx);
3622
3954
  const cols = typeof process.stdout.columns === "number" ? process.stdout.columns : 100;
3623
3955
  const rows = Math.max(1, (typeof process.stdout.rows === "number" ? process.stdout.rows : 35) - 1);
3624
3956
  const patchTerminal = (snapshot) => {
3957
+ const previousSteps = currentAttachState(ctx, cleanRunId)?.steps;
3958
+ const failed = snapshot.status === "failed";
3959
+ const reason = failed ? snapshot.message ?? `bundled Pi exited with code ${snapshot.exitCode ?? "unknown"}` : undefined;
3625
3960
  patchData(ctx, {
3626
3961
  piTerminal: snapshot,
3627
3962
  piAttach: {
3628
3963
  runId: cleanRunId,
3629
- status: snapshot.status === "failed" ? "failed" : snapshot.status === "exited" ? "returned" : "entering-pi",
3964
+ status: failed ? "failed" : snapshot.status === "exited" ? "returned" : "entering-pi",
3630
3965
  message: snapshot.message ?? "bundled Pi running",
3631
- result: { runId: cleanRunId, status: snapshot.status, exitCode: snapshot.exitCode }
3966
+ result: { runId: cleanRunId, status: snapshot.status, exitCode: snapshot.exitCode },
3967
+ ...previousSteps ? { steps: previousSteps } : {},
3968
+ ...reason ? { failureReason: reason } : {}
3632
3969
  }
3633
3970
  });
3634
3971
  };
3972
+ recordStep(ctx, cleanRunId, "starting bundled Pi", label);
3635
3973
  await startPiPtyHost({
3636
3974
  runId: cleanRunId,
3637
3975
  projectRoot,
3638
3976
  cols,
3639
3977
  rows,
3640
- onSnapshot: patchTerminal,
3978
+ onSnapshot: (snapshot) => {
3979
+ if (snapshot.status === "running" && snapshot.lines.some((entry) => entry.trim().length > 0)) {
3980
+ settleSteps(ctx, cleanRunId);
3981
+ }
3982
+ patchTerminal(snapshot);
3983
+ },
3641
3984
  onExit: (snapshot) => {
3642
3985
  patchTerminal(snapshot);
3986
+ if (snapshot.status === "failed") {
3987
+ Promise.resolve().then(() => (init_inspect(), exports_inspect)).then(({ loadInspect: loadInspect2 }) => loadInspect2(ctx, cleanRunId)).catch(() => null);
3988
+ emitFailed(ctx, label, new Error(snapshot.message ?? `bundled Pi exited with code ${snapshot.exitCode ?? "unknown"}`), { runId: cleanRunId, result: { status: snapshot.status, exitCode: snapshot.exitCode } });
3989
+ return;
3990
+ }
3643
3991
  emitCompleted(ctx, label, { runId: cleanRunId, result: { status: snapshot.status, exitCode: snapshot.exitCode } });
3644
3992
  ctx.emit({ type: "scene.change", scene: "fleet", intent: { scene: "fleet", argv: ["runs"], action: { kind: "refresh", label: "Opening runs" } } });
3645
3993
  refreshFleet(ctx).catch((error) => emitFailed(ctx, "Refresh runs", error));
@@ -3657,7 +4005,16 @@ async function attachRunWithBundledPi(ctx, runId) {
3657
4005
  emitProgress(ctx, label, "bundled Pi is running", { piAttach: { runId: cleanRunId, status: "entering-pi", message: "bundled Pi running", result: record } });
3658
4006
  return record;
3659
4007
  } catch (error) {
3660
- patchData(ctx, { piAttach: { runId: cleanRunId, status: "failed", message: error instanceof Error ? error.message : String(error) } });
4008
+ const reason = error instanceof Error ? error.message : String(error);
4009
+ patchData(ctx, {
4010
+ piAttach: {
4011
+ runId: cleanRunId,
4012
+ status: "failed",
4013
+ message: reason,
4014
+ failureReason: reason,
4015
+ ...currentAttachState(ctx, cleanRunId)?.steps ? { steps: currentAttachState(ctx, cleanRunId).steps } : {}
4016
+ }
4017
+ });
3661
4018
  emitFailed(ctx, label, error, { runId: cleanRunId });
3662
4019
  throw error;
3663
4020
  }
@@ -3669,30 +4026,6 @@ var init_pi_attach = __esm(() => {
3669
4026
 
3670
4027
  // packages/cli/src/app-opentui/adapters/tasks.ts
3671
4028
  import { randomUUID } from "crypto";
3672
- function createTasksAdapter() {
3673
- return {
3674
- id: "tasks",
3675
- async handleIntent(runtime, intent) {
3676
- if (intent.scene !== "tasks" && intent.action.kind !== "task-run-next" && intent.action.kind !== "task-run-id")
3677
- return false;
3678
- const ctx = adapterContextFromRuntime(runtime);
3679
- const detach = intent.action.payload?.detach === true;
3680
- if (intent.action.kind === "task-run-next") {
3681
- await submitNextTaskRun(ctx, {}, { detach });
3682
- return true;
3683
- }
3684
- if (intent.action.kind === "task-run-id") {
3685
- await submitTaskRun(ctx, { taskId: payloadString(intent, "task"), detach });
3686
- return true;
3687
- }
3688
- if (intent.action.kind === "refresh" || intent.action.kind === "navigate") {
3689
- await refreshTasks(ctx, { limit: 20 });
3690
- return true;
3691
- }
3692
- return false;
3693
- }
3694
- };
3695
- }
3696
4029
  function rawRecord(record, key) {
3697
4030
  const direct = record[key];
3698
4031
  if (direct !== undefined && direct !== null)
@@ -4096,7 +4429,7 @@ async function loadTaskPreview(ctx, taskId3) {
4096
4429
  const { getWorkspaceTaskViaServer: getWorkspaceTaskViaServer2 } = await Promise.resolve().then(() => (init__server_client(), exports__server_client));
4097
4430
  const task = await getWorkspaceTaskViaServer2({ projectRoot: projectRootOf(ctx) }, taskId3);
4098
4431
  const normalized = task ? normalizeTaskRecord(task) : null;
4099
- patchData(ctx, { selectedTaskId: taskId3, taskPreview: normalized, taskPreviewRaw: task ? compactRecord(task, ["id", "title", "status", "body", "url"]) : null });
4432
+ patchData(ctx, { selectedTaskId: taskId3, taskDetailId: taskId3, taskPreview: normalized, taskPreviewRaw: task ? compactRecord(task, ["id", "title", "status", "body", "url"]) : null });
4100
4433
  emitCompleted(ctx, label, { taskId: taskId3, found: Boolean(normalized) });
4101
4434
  return normalized;
4102
4435
  } catch (error) {
@@ -4114,7 +4447,6 @@ export {
4114
4447
  normalizeTaskRecord,
4115
4448
  loadTaskPreview,
4116
4449
  ensureTaskRuntime,
4117
- createTasksAdapter,
4118
4450
  activeRunIdFromTaskRunError,
4119
4451
  activeRunIdFromTaskLike
4120
4452
  };