@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
@@ -8,7 +8,14 @@ export type StageLayout = {
8
8
  readonly typeBarTop: number;
9
9
  readonly footerTop: number;
10
10
  readonly compact: boolean;
11
+ /** Raw terminal is below the usable threshold; render a guidance message
12
+ * instead of a scene that would clip into nothing (#18). */
13
+ readonly tooSmall: boolean;
11
14
  };
15
+ /** Below these raw dimensions, ad-terminal panels collapse below their 4×4
16
+ * floor and the UI would blank silently. We show a "resize me" card instead. */
17
+ export declare const MIN_TERMINAL_WIDTH = 48;
18
+ export declare const MIN_TERMINAL_HEIGHT = 16;
12
19
  export declare function computeStageLayout(width: number, height: number): StageLayout;
13
20
  export declare function visibleWidth(text: string): number;
14
21
  export declare function alignText(text: string, width: number, align?: "left" | "center" | "right"): string;
@@ -1,17 +1,22 @@
1
1
  // @bun
2
2
  // packages/cli/src/app-opentui/layout.ts
3
+ var MIN_TERMINAL_WIDTH = 48;
4
+ var MIN_TERMINAL_HEIGHT = 16;
3
5
  function computeStageLayout(width, height) {
4
- const safeWidth = Math.max(40, Math.trunc(width || 80));
5
- const safeHeight = Math.max(18, Math.trunc(height || 24));
6
+ const rawWidth = Math.trunc(width || 80);
7
+ const rawHeight = Math.trunc(height || 24);
8
+ const tooSmall = rawWidth < MIN_TERMINAL_WIDTH || rawHeight < MIN_TERMINAL_HEIGHT;
9
+ const safeWidth = Math.max(40, rawWidth);
10
+ const safeHeight = Math.max(18, rawHeight);
6
11
  const compact = safeWidth < 92 || safeHeight < 30;
7
12
  const horizontalGutter = compact ? 1 : 2;
8
13
  const centerWidth = Math.max(36, safeWidth - horizontalGutter * 2);
9
14
  const centerLeft = horizontalGutter;
10
15
  const footerTop = Math.max(0, safeHeight - 1);
11
- const typeBarTop = footerTop;
16
+ const typeBarTop = Math.max(0, footerTop - 1);
12
17
  const centerTop = compact ? 1 : Math.max(1, Math.floor(safeHeight * 0.055));
13
- const centerHeight = Math.max(8, footerTop - centerTop);
14
- return { width: safeWidth, height: safeHeight, centerWidth, centerLeft, centerTop, centerHeight, typeBarTop, footerTop, compact };
18
+ const centerHeight = Math.max(6, typeBarTop - centerTop);
19
+ return { width: safeWidth, height: safeHeight, centerWidth, centerLeft, centerTop, centerHeight, typeBarTop, footerTop, compact, tooSmall };
15
20
  }
16
21
  function visibleWidth(text) {
17
22
  return [...text].length;
@@ -40,5 +45,7 @@ export {
40
45
  visibleWidth,
41
46
  truncateText,
42
47
  computeStageLayout,
43
- alignText
48
+ alignText,
49
+ MIN_TERMINAL_WIDTH,
50
+ MIN_TERMINAL_HEIGHT
44
51
  };
@@ -6,3 +6,27 @@ export declare function listSearchQuery(state: AppState): string;
6
6
  export declare function filterRunsForSearch(runs: readonly AppRunRecord[], query: string): AppRunRecord[];
7
7
  export declare function filterTasksForSearch(tasks: readonly AppTaskRecord[], query: string): AppTaskRecord[];
8
8
  export declare function searchSummary(kind: "runs" | "tasks", query: string, visible: number, total: number): string;
9
+ export type SortDir = "asc" | "desc";
10
+ export type RunSortKey = "status" | "runId" | "title" | "age";
11
+ export type RunSortSpec = {
12
+ readonly key: RunSortKey;
13
+ readonly dir: SortDir;
14
+ };
15
+ export type TaskSortKey = "status" | "id" | "priority" | "assignee" | "title";
16
+ export type TaskSortSpec = {
17
+ readonly key: TaskSortKey;
18
+ readonly dir: SortDir;
19
+ };
20
+ /** Read a typed run sort spec off the data bag, narrowing the `unknown` slice. */
21
+ export declare function runSortSpec(value: unknown): RunSortSpec | null;
22
+ /** Read a typed task sort spec off the data bag, narrowing the `unknown` slice. */
23
+ export declare function taskSortSpec(value: unknown): TaskSortSpec | null;
24
+ /** Cycle a column's sort: clicking the active column flips asc→desc→default;
25
+ * clicking a new column starts it ascending. `null` means "back to default". */
26
+ export declare function cycleRunSort(current: RunSortSpec | null, key: RunSortKey): RunSortSpec | null;
27
+ export declare function cycleTaskSort(current: TaskSortSpec | null, key: TaskSortKey): TaskSortSpec | null;
28
+ /** Sort runs by a column. `spec === null` returns the input order unchanged. */
29
+ export declare function sortRuns(runs: readonly AppRunRecord[], spec: RunSortSpec | null): AppRunRecord[];
30
+ /** Sort tasks by a column, client-side over already-fetched records. `spec ===
31
+ * null` returns the input order unchanged (server/adapter order is preserved). */
32
+ export declare function sortTasks(tasks: readonly AppTaskRecord[], spec: TaskSortSpec | null): AppTaskRecord[];
@@ -34,10 +34,97 @@ function filterTasksForSearch(tasks, query) {
34
34
  function searchSummary(kind, query, visible, total) {
35
35
  return query.trim() ? `search ${JSON.stringify(query)} \xB7 ${visible}/${total} ${kind}` : `${total} ${kind}`;
36
36
  }
37
+ function runSortSpec(value) {
38
+ if (!value || typeof value !== "object" || Array.isArray(value))
39
+ return null;
40
+ const record = value;
41
+ const key = record.key;
42
+ const dir = record.dir;
43
+ if ((key === "status" || key === "runId" || key === "title" || key === "age") && (dir === "asc" || dir === "desc")) {
44
+ return { key, dir };
45
+ }
46
+ return null;
47
+ }
48
+ function taskSortSpec(value) {
49
+ if (!value || typeof value !== "object" || Array.isArray(value))
50
+ return null;
51
+ const record = value;
52
+ const key = record.key;
53
+ const dir = record.dir;
54
+ if ((key === "status" || key === "id" || key === "priority" || key === "assignee" || key === "title") && (dir === "asc" || dir === "desc")) {
55
+ return { key, dir };
56
+ }
57
+ return null;
58
+ }
59
+ function cycleRunSort(current, key) {
60
+ if (!current || current.key !== key)
61
+ return { key, dir: "asc" };
62
+ if (current.dir === "asc")
63
+ return { key, dir: "desc" };
64
+ return null;
65
+ }
66
+ function cycleTaskSort(current, key) {
67
+ if (!current || current.key !== key)
68
+ return { key, dir: "asc" };
69
+ if (current.dir === "asc")
70
+ return { key, dir: "desc" };
71
+ return null;
72
+ }
73
+ function ageValue(updatedAt) {
74
+ const parsed = Date.parse(updatedAt ?? "");
75
+ return Number.isFinite(parsed) ? parsed : 0;
76
+ }
77
+ function applyDir(rows, compare, dir) {
78
+ const sorted = [...rows].sort(compare);
79
+ return dir === "desc" ? sorted.reverse() : sorted;
80
+ }
81
+ function sortRuns(runs, spec) {
82
+ if (!spec)
83
+ return [...runs];
84
+ const { key, dir } = spec;
85
+ if (key === "age") {
86
+ const byNewest = applyDir(runs, (a, b) => ageValue(b.updatedAt) - ageValue(a.updatedAt) || a.runId.localeCompare(b.runId), "asc");
87
+ return dir === "asc" ? byNewest : [...byNewest].reverse();
88
+ }
89
+ const compare = (a, b) => {
90
+ if (key === "status")
91
+ return a.status.localeCompare(b.status) || a.runId.localeCompare(b.runId);
92
+ if (key === "title")
93
+ return a.title.localeCompare(b.title, undefined, { sensitivity: "base" }) || a.runId.localeCompare(b.runId);
94
+ return a.runId.localeCompare(b.runId, undefined, { numeric: true });
95
+ };
96
+ return applyDir(runs, compare, dir);
97
+ }
98
+ function sortTasks(tasks, spec) {
99
+ if (!spec)
100
+ return [...tasks];
101
+ const { key, dir } = spec;
102
+ const compare = (a, b) => {
103
+ if (key === "status")
104
+ return a.status.localeCompare(b.status) || a.id.localeCompare(b.id, undefined, { numeric: true });
105
+ if (key === "priority") {
106
+ const left = a.priority ?? Number.NEGATIVE_INFINITY;
107
+ const right = b.priority ?? Number.NEGATIVE_INFINITY;
108
+ return right - left || a.id.localeCompare(b.id, undefined, { numeric: true });
109
+ }
110
+ if (key === "assignee")
111
+ return (a.assignees[0] ?? "").localeCompare(b.assignees[0] ?? "") || a.id.localeCompare(b.id, undefined, { numeric: true });
112
+ if (key === "title")
113
+ return a.title.localeCompare(b.title, undefined, { sensitivity: "base" }) || a.id.localeCompare(b.id, undefined, { numeric: true });
114
+ return a.id.localeCompare(b.id, undefined, { numeric: true });
115
+ };
116
+ return applyDir(tasks, compare, dir);
117
+ }
37
118
  export {
119
+ taskSortSpec,
120
+ sortTasks,
121
+ sortRuns,
38
122
  searchSummary,
123
+ runSortSpec,
39
124
  listSearchQuery,
40
125
  isListSearchScene,
41
126
  filterTasksForSearch,
42
- filterRunsForSearch
127
+ filterRunsForSearch,
128
+ cycleTaskSort,
129
+ cycleRunSort
43
130
  };
@@ -587,6 +587,14 @@ async function withSpinner(label, work, options = {}) {
587
587
  }
588
588
 
589
589
  // packages/cli/src/commands/_pi-frontend.ts
590
+ var TERMINAL_RUN_STATUSES = new Set(["completed", "failed", "stopped", "cancelled", "canceled", "closed", "merged", "needs_attention", "needs-attention"]);
591
+
592
+ class OperatorTranscriptUnavailableError extends Error {
593
+ constructor(message) {
594
+ super(message);
595
+ this.name = "OperatorTranscriptUnavailableError";
596
+ }
597
+ }
590
598
  function setTemporaryEnv(updates) {
591
599
  const previous = new Map;
592
600
  for (const [key, value] of Object.entries(updates)) {
@@ -614,6 +622,23 @@ function buildOperatorPiEnv(input) {
614
622
  ...input.serverProjectRoot ? { RIG_PROJECT_ROOT: input.serverProjectRoot } : {}
615
623
  };
616
624
  }
625
+ function shouldRequireOperatorTranscript(status) {
626
+ return typeof status === "string" && TERMINAL_RUN_STATUSES.has(status.trim().toLowerCase());
627
+ }
628
+ function missingOperatorTranscriptMessage(runId, status) {
629
+ const shortRun = runId.trim().slice(0, 8) || "unknown";
630
+ const statusText = typeof status === "string" && status.trim() ? status.trim() : "terminal";
631
+ 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.`;
632
+ }
633
+ function statusFromRunDetails(run) {
634
+ if (!run || typeof run !== "object" || Array.isArray(run))
635
+ return;
636
+ const record = run;
637
+ if (typeof record.status === "string")
638
+ return record.status;
639
+ const nested = record.run;
640
+ return nested && typeof nested === "object" && !Array.isArray(nested) ? nested.status : undefined;
641
+ }
617
642
  async function prepareOperatorConsole(context, runId, tempSessionDir) {
618
643
  const server = await ensureServerForCli(context.projectRoot);
619
644
  const localCwd = join(homedir2(), ".rig", "drones", runId.slice(0, 8));
@@ -643,6 +668,13 @@ async function prepareOperatorConsole(context, runId, tempSessionDir) {
643
668
  sessionFileArg = ["--session", localSessionPath];
644
669
  }
645
670
  } catch {}
671
+ if (sessionFileArg.length === 0) {
672
+ const run = await getRunDetailsViaServer(context, runId).catch(() => null);
673
+ const status = statusFromRunDetails(run);
674
+ if (shouldRequireOperatorTranscript(status)) {
675
+ throw new OperatorTranscriptUnavailableError(missingOperatorTranscriptMessage(runId, status));
676
+ }
677
+ }
646
678
  return { server, sessionFileArg };
647
679
  }
648
680
  var RIG_PI_THEME = {
@@ -815,7 +847,7 @@ if (import.meta.main) {
815
847
  try {
816
848
  await runOpenTuiPiHostChild();
817
849
  } catch (error) {
818
- const message = error instanceof Error ? error.stack || error.message : String(error);
850
+ const message = error instanceof Error ? error.name === "OperatorTranscriptUnavailableError" ? error.message : error.stack || error.message : String(error);
819
851
  process.stderr.write(`
820
852
  Pi host failed:
821
853
  ${message}
@@ -17,6 +17,7 @@ export type PiPtyHostSnapshot = {
17
17
  readonly lines: readonly string[];
18
18
  readonly styledLines?: readonly StyledText[];
19
19
  readonly message?: string;
20
+ readonly resizeError?: string;
20
21
  readonly exitCode?: number;
21
22
  readonly signal?: string | null;
22
23
  };
@@ -49,9 +50,13 @@ export declare class PiPtyHost {
49
50
  private cols;
50
51
  private rows;
51
52
  private message;
53
+ private lastResizeError;
52
54
  private exitCode;
53
55
  private signal;
54
56
  private notifyTimer;
57
+ /** Last streamed buffer content, so the onWriteParsed handler only emits a
58
+ * snapshot when the rendered content actually changed. */
59
+ private lastStreamKey;
55
60
  private _disposed;
56
61
  constructor(options: PiPtyHostOptions);
57
62
  get disposed(): boolean;
@@ -158,9 +158,11 @@ class PiPtyHost {
158
158
  cols;
159
159
  rows;
160
160
  message = "starting bundled Pi";
161
+ lastResizeError = null;
161
162
  exitCode;
162
163
  signal;
163
164
  notifyTimer = null;
165
+ lastStreamKey = "";
164
166
  _disposed = false;
165
167
  constructor(options) {
166
168
  this.runId = options.runId;
@@ -177,6 +179,17 @@ class PiPtyHost {
177
179
  scrollback: 1000
178
180
  });
179
181
  this.registerTerminalResponders();
182
+ this.disposables.push(this.terminal.onWriteParsed(() => {
183
+ if (this._disposed)
184
+ return;
185
+ const snapshot = this.createSnapshot();
186
+ const key = snapshot.lines.join(`
187
+ `);
188
+ if (key === this.lastStreamKey)
189
+ return;
190
+ this.lastStreamKey = key;
191
+ this.onSnapshot?.(snapshot);
192
+ }));
180
193
  }
181
194
  get disposed() {
182
195
  return this._disposed;
@@ -263,7 +276,10 @@ class PiPtyHost {
263
276
  this.terminal.resize(nextCols, nextRows);
264
277
  try {
265
278
  this.pty?.resize(nextCols, nextRows);
266
- } catch {}
279
+ this.lastResizeError = null;
280
+ } catch (error) {
281
+ this.lastResizeError = error instanceof Error ? error.message : String(error);
282
+ }
267
283
  this.emitSnapshotSoon(0);
268
284
  }
269
285
  detach() {
@@ -305,7 +321,7 @@ class PiPtyHost {
305
321
  return;
306
322
  const text = this.decoder.decode(data, { stream: true });
307
323
  this.respondToRawTerminalQueries(text);
308
- this.terminal.write(data, () => this.emitSnapshotSoon());
324
+ this.terminal.write(data);
309
325
  }
310
326
  emitSnapshotSoon(delayMs = SNAPSHOT_DELAY_MS) {
311
327
  if (this._disposed || this.notifyTimer)
@@ -334,6 +350,7 @@ class PiPtyHost {
334
350
  lines.push("");
335
351
  styledLines[lines.length - 1] = new StyledText([{ __isChunk: true, text: "" }]);
336
352
  }
353
+ const resolvedMessage = this.lastResizeError ? `resize degraded: ${this.lastResizeError}` : message;
337
354
  return {
338
355
  runId: this.runId,
339
356
  status: this.status,
@@ -341,7 +358,8 @@ class PiPtyHost {
341
358
  rows: this.rows,
342
359
  lines,
343
360
  styledLines,
344
- message,
361
+ message: resolvedMessage,
362
+ ...this.lastResizeError ? { resizeError: this.lastResizeError } : {},
345
363
  ...this.exitCode !== undefined ? { exitCode: this.exitCode } : {},
346
364
  ...this.signal !== undefined ? { signal: this.signal } : {}
347
365
  };
@@ -0,0 +1,9 @@
1
+ import { computeStageLayout } from "../layout";
2
+ import { entryIntentForScene } from "../runtime";
3
+ import { PRIMARY_NAV } from "./nav";
4
+ import type { AppSceneFrame, AppSceneId, AppState } from "../types";
5
+ type SceneRenderer = (state: AppState, layout: ReturnType<typeof computeStageLayout>) => AppSceneFrame;
6
+ export declare function App(props: {
7
+ sceneRenderers?: Partial<Record<AppSceneId, SceneRenderer>>;
8
+ }): React.ReactNode;
9
+ export { PRIMARY_NAV, entryIntentForScene };