ompchamber 0.7.0 → 1.0.0

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 (165) hide show
  1. package/dist/client/index.html +1 -1
  2. package/dist/client/static/css/index.e880517fcd.css +2 -0
  3. package/dist/client/static/js/async/{3y.4c2596f64c.js → 3y.71f2ccf3b6.js} +2 -2
  4. package/dist/client/static/js/async/{xw.ae67ba06fd.js → 5b.0d6ae50fe8.js} +10 -10
  5. package/dist/client/static/js/async/5bh.8b2363424f.js +1 -0
  6. package/dist/client/static/js/async/{vy.bbe114c4e0.js → bi.09688ba75c.js} +1 -2
  7. package/dist/client/static/js/async/c.797d933882.js +1 -0
  8. package/dist/client/static/js/async/{dc.6dc4b0a04b.js → dc.856fda6560.js} +2 -2
  9. package/dist/client/static/js/async/g.a06ca3b3e9.js +2 -0
  10. package/dist/client/static/js/async/g.a06ca3b3e9.js.LICENSE.txt +11 -0
  11. package/dist/client/static/js/async/{m.4b29d34f93.js → m.c038c71b9f.js} +2 -2
  12. package/dist/client/static/js/async/ma.2c261b04dd.js +2 -0
  13. package/dist/client/static/js/async/ms.98960af4eb.js +67 -0
  14. package/dist/client/static/js/async/nam.b5663e919f.js +10 -0
  15. package/dist/client/static/js/async/nam.b5663e919f.js.LICENSE.txt +11 -0
  16. package/dist/client/static/js/async/ot.84c71b308f.js +2 -0
  17. package/dist/client/static/js/async/ot.84c71b308f.js.LICENSE.txt +11 -0
  18. package/dist/client/static/js/async/{q.542c184fec.js → q.23af2a358f.js} +3 -3
  19. package/dist/client/static/js/async/{s.413c2a4683.js → s.222ee974f6.js} +2 -2
  20. package/dist/client/static/js/async/t.4dca0e2ba8.js +1 -0
  21. package/dist/client/static/js/async/uf.46ad6209fa.js +2 -0
  22. package/dist/client/static/js/async/xn.6360379f71.js +2 -0
  23. package/dist/client/static/js/async/xnl.feb0021e50.js +2 -0
  24. package/dist/client/static/js/async/xnl.feb0021e50.js.LICENSE.txt +12 -0
  25. package/dist/client/static/js/index.0de15c5e17.js +33 -0
  26. package/dist/client/static/js/{uy.2e8929ed0c.js → jp.50817896f3.js} +6 -6
  27. package/package.json +2 -1
  28. package/src/client/components/common/MarkdownRenderer.tsx +69 -27
  29. package/src/client/components/common/code-editor/history.test.ts +118 -0
  30. package/src/client/components/common/code-editor/history.ts +101 -0
  31. package/src/client/components/common/code-editor/index.tsx +56 -66
  32. package/src/client/components/common/code-surface/index.tsx +84 -11
  33. package/src/client/components/common/code-surface/measure.ts +66 -17
  34. package/src/client/components/common/diagram-viewer/index.tsx +258 -0
  35. package/src/client/components/common/diagram-viewer/read-block.ts +53 -0
  36. package/src/client/components/layout/session-sidebar/CategoryItem.tsx +2 -1
  37. package/src/client/components/layout/session-sidebar/SessionItem.tsx +11 -4
  38. package/src/client/components/layout/session-sidebar/SessionList.tsx +1 -1
  39. package/src/client/components/mobile/mobile-session-sidebar/Item.tsx +1 -1
  40. package/src/client/components/mobile/mobile-session-sidebar/List.tsx +1 -1
  41. package/src/client/components/mobile/mobile-session-sidebar/SessionRow.tsx +2 -2
  42. package/src/client/components/settings/LazyModal.tsx +1 -1
  43. package/src/client/components/settings/Modal.tsx +2 -1
  44. package/src/client/components/workspace/chat-timeline/ComposerDock.tsx +11 -4
  45. package/src/client/components/workspace/chat-timeline/QueueList.tsx +18 -14
  46. package/src/client/components/workspace/chat-timeline/ToolCallCard.tsx +8 -1
  47. package/src/client/components/workspace/chat-timeline/chat-input/index.tsx +11 -52
  48. package/src/client/components/workspace/chat-timeline/chat-input/selection.ts +57 -0
  49. package/src/client/components/workspace/chat-timeline/index.tsx +124 -108
  50. package/src/client/components/workspace/chat-timeline/tool-renderers/ask-panel/FrameControl.tsx +172 -0
  51. package/src/client/components/workspace/chat-timeline/tool-renderers/ask-panel/QuestionBlock.tsx +155 -0
  52. package/src/client/components/workspace/chat-timeline/tool-renderers/ask-panel/drafts.ts +113 -0
  53. package/src/client/components/workspace/chat-timeline/tool-renderers/ask-panel/index.tsx +92 -0
  54. package/src/client/components/workspace/chat-timeline/tool-renderers/{ask-dialog → extension-dialog}/Footer.tsx +3 -3
  55. package/src/client/components/workspace/chat-timeline/tool-renderers/{ask-dialog → extension-dialog}/Header.tsx +3 -3
  56. package/src/client/components/workspace/chat-timeline/tool-renderers/{ask-dialog → extension-dialog}/Lazy.tsx +8 -8
  57. package/src/client/components/workspace/chat-timeline/tool-renderers/{ask-dialog → extension-dialog}/SelectBody.tsx +3 -3
  58. package/src/client/components/workspace/chat-timeline/tool-renderers/{ask-dialog → extension-dialog}/index.tsx +16 -10
  59. package/src/client/components/workspace/chat-timeline/tool-renderers/index.tsx +2 -2
  60. package/src/client/components/workspace/file-explorer/index.tsx +18 -5
  61. package/src/client/components/workspace/git-panel/BranchToolbar.tsx +2 -2
  62. package/src/client/components/workspace/git-panel/index.tsx +5 -1
  63. package/src/client/components/workspace/terminal-panel/Header.tsx +51 -19
  64. package/src/client/components/workspace/terminal-panel/RealtimeXtermView.tsx +86 -282
  65. package/src/client/components/workspace/terminal-panel/index.tsx +76 -50
  66. package/src/client/components/workspace/terminal-panel/mount.ts +275 -0
  67. package/src/client/data/theme/terminal.ts +27 -29
  68. package/src/client/hooks/chat/omp/session-list.tsx +23 -2
  69. package/src/client/hooks/chat/omp/session-sidebar-controller.ts +1 -1
  70. package/src/client/hooks/chat/timeline/actions.ts +23 -34
  71. package/src/client/hooks/chat/timeline/ask-frames.ts +102 -0
  72. package/src/client/hooks/chat/timeline/extension-dialog.ts +12 -9
  73. package/src/client/hooks/chat/timeline/index.ts +18 -28
  74. package/src/client/hooks/chat/timeline/prompt-text.ts +35 -0
  75. package/src/client/hooks/chat/timeline/queue.ts +110 -46
  76. package/src/client/hooks/chat/timeline/send.ts +13 -27
  77. package/src/client/hooks/chat/timeline/session-load.ts +48 -106
  78. package/src/client/hooks/chat/timeline/session-pagination.ts +144 -0
  79. package/src/client/hooks/editor/use-code-window.ts +118 -0
  80. package/src/client/hooks/ui/input-required-alert.ts +43 -0
  81. package/src/client/hooks/ui/notification-sound.ts +65 -0
  82. package/src/client/hooks/ui/theme.ts +22 -6
  83. package/src/client/hooks/workspace/terminal/index.ts +217 -0
  84. package/src/client/hooks/workspace/terminal/socket.ts +227 -0
  85. package/src/client/tailwind.css +38 -12
  86. package/src/server/db.server.ts +19 -0
  87. package/src/server/lib/fs/git-sync.ts +191 -0
  88. package/src/server/lib/omp/rpc/manager.ts +72 -6
  89. package/src/server/lib/omp/rpc/session-commands.ts +6 -0
  90. package/src/server/lib/omp/rpc/session-registry.ts +18 -0
  91. package/src/server/lib/omp/session/sidebar-data.server.ts +10 -4
  92. package/src/server/lib/queue/delivery.server.ts +114 -0
  93. package/src/server/lib/queue/schema-migration.server.ts +50 -0
  94. package/src/server/lib/queue/store.server.ts +185 -0
  95. package/src/server/lib/terminal/foreground.ts +73 -0
  96. package/src/server/lib/terminal/history.test.ts +118 -0
  97. package/src/server/lib/terminal/history.ts +205 -0
  98. package/src/server/lib/terminal/runtime.server.ts +338 -0
  99. package/src/server/lib/terminal/session.server.ts +215 -0
  100. package/src/server/lib/terminal/shell.test.ts +97 -0
  101. package/src/server/lib/terminal/shell.ts +103 -0
  102. package/src/server/routes/fs/git.ts +22 -12
  103. package/src/server/routes/index.ts +2 -1
  104. package/src/server/routes/sessions/index.ts +11 -3
  105. package/src/server/routes/sessions/queue.ts +121 -0
  106. package/src/server/routes/sessions/session.ts +1 -132
  107. package/src/server/routes/settings/commands.ts +1 -1
  108. package/src/server/routes/terminal/index.ts +2 -2
  109. package/src/server/routes/terminal/run.ts +8 -100
  110. package/src/server/routes/terminal/sessions.ts +24 -0
  111. package/src/server/routes/terminal/ws.ts +149 -0
  112. package/src/shared/lib/chat/ask-questions.test.ts +169 -0
  113. package/src/shared/lib/chat/ask-questions.ts +264 -0
  114. package/src/shared/lib/chat/omp/agent-events.test.ts +37 -1
  115. package/src/shared/lib/chat/omp/agent-events.ts +30 -0
  116. package/src/shared/lib/chat/timeline/omp-callbacks.test.ts +9 -1
  117. package/src/shared/lib/chat/timeline/omp-callbacks.ts +45 -8
  118. package/src/shared/lib/code/lazy-window.test.ts +161 -0
  119. package/src/shared/lib/code/lazy-window.ts +156 -0
  120. package/src/shared/lib/code/syntax-highlight.test.ts +50 -1
  121. package/src/shared/lib/code/syntax-highlight.ts +14 -1
  122. package/src/shared/lib/code/windowed-highlight.ts +63 -0
  123. package/src/shared/lib/markdown/mermaid.ts +8 -6
  124. package/src/shared/lib/omp/session/stream-state.server.ts +2 -3
  125. package/src/shared/lib/workspace/session-state/store.ts +1 -4
  126. package/src/shared/lib/workspace/terminal/protocol.ts +222 -0
  127. package/src/shared/types/omp/agent.ts +14 -2
  128. package/src/shared/types/terminal.ts +17 -15
  129. package/src/shared/types/workspace.ts +6 -2
  130. package/dist/client/static/css/index.51f68210a5.css +0 -2
  131. package/dist/client/static/js/async/5b.d88304bc13.js +0 -1
  132. package/dist/client/static/js/async/c.cd45b0ac45.js +0 -1
  133. package/dist/client/static/js/async/fw.6a51998a8e.js +0 -6
  134. package/dist/client/static/js/async/g.86e2256a70.js +0 -2
  135. package/dist/client/static/js/async/kc.bbdb35b771.js +0 -1
  136. package/dist/client/static/js/async/mw.ca1512563c.js +0 -10
  137. package/dist/client/static/js/async/rf.d626afc367.js +0 -2
  138. package/dist/client/static/js/async/t.cda4891526.js +0 -1
  139. package/dist/client/static/js/async/uf.24ca3d49c3.js +0 -2
  140. package/dist/client/static/js/async/uf.24ca3d49c3.js.LICENSE.txt +0 -6
  141. package/dist/client/static/js/async/v0.394ed37c6b.js +0 -2
  142. package/dist/client/static/js/async/v0.394ed37c6b.js.LICENSE.txt +0 -6
  143. package/dist/client/static/js/async/vy.bbe114c4e0.js.LICENSE.txt +0 -6
  144. package/dist/client/static/js/async/xn.afecebbbd9.js +0 -2
  145. package/dist/client/static/js/index.eccad0b70a.js +0 -33
  146. package/dist/client/static/js/index.eccad0b70a.js.LICENSE.txt +0 -4
  147. package/src/client/components/workspace/chat-timeline/tool-renderers/panels/Ask.tsx +0 -205
  148. package/src/client/components/workspace/terminal-panel/InputBar.tsx +0 -82
  149. package/src/client/components/workspace/terminal-panel/LogEntry.tsx +0 -70
  150. package/src/client/hooks/workspace/terminal-output.ts +0 -66
  151. package/src/client/hooks/workspace/terminal.ts +0 -283
  152. package/src/server/lib/fs/terminal-cwd.ts +0 -49
  153. package/src/server/routes/terminal/stream.ts +0 -100
  154. /package/dist/client/static/js/async/{3y.4c2596f64c.js.LICENSE.txt → 3y.71f2ccf3b6.js.LICENSE.txt} +0 -0
  155. /package/dist/client/static/js/async/{g.86e2256a70.js.LICENSE.txt → 5b.0d6ae50fe8.js.LICENSE.txt} +0 -0
  156. /package/dist/client/static/js/async/{dc.6dc4b0a04b.js.LICENSE.txt → dc.856fda6560.js.LICENSE.txt} +0 -0
  157. /package/dist/client/static/js/async/{fw.6a51998a8e.js.LICENSE.txt → m.c038c71b9f.js.LICENSE.txt} +0 -0
  158. /package/dist/client/static/js/async/{m.4b29d34f93.js.LICENSE.txt → ma.2c261b04dd.js.LICENSE.txt} +0 -0
  159. /package/dist/client/static/js/async/{xn.afecebbbd9.js.LICENSE.txt → ms.98960af4eb.js.LICENSE.txt} +0 -0
  160. /package/dist/client/static/js/async/{mw.ca1512563c.js.LICENSE.txt → q.23af2a358f.js.LICENSE.txt} +0 -0
  161. /package/dist/client/static/js/async/{q.542c184fec.js.LICENSE.txt → s.222ee974f6.js.LICENSE.txt} +0 -0
  162. /package/dist/client/static/js/async/{rf.d626afc367.js.LICENSE.txt → uf.46ad6209fa.js.LICENSE.txt} +0 -0
  163. /package/dist/client/static/js/async/{s.413c2a4683.js.LICENSE.txt → xn.6360379f71.js.LICENSE.txt} +0 -0
  164. /package/dist/client/static/js/{async/xw.ae67ba06fd.js.LICENSE.txt → index.0de15c5e17.js.LICENSE.txt} +0 -0
  165. /package/dist/client/static/js/{uy.2e8929ed0c.js.LICENSE.txt → jp.50817896f3.js.LICENSE.txt} +0 -0
@@ -11,7 +11,6 @@
11
11
  * stream — a run is in flight (prompt dispatch → agent_start)
12
12
  * finish — the run ended normally (agent_end), not yet seen
13
13
  * abort — the user stopped it, not yet seen
14
- * error — the run failed (prompt_error / process exit), not yet seen
15
14
  *
16
15
  * A terminal status is a one-shot badge: opening the session acknowledges it
17
16
  * (`markStreamSeen` deletes the row), which is exactly the "check appears
@@ -25,7 +24,7 @@
25
24
 
26
25
  import { getDb } from '@/server/db.server';
27
26
 
28
- export type SessionStreamStatus = 'stream' | 'finish' | 'abort' | 'error';
27
+ export type SessionStreamStatus = 'stream' | 'finish' | 'abort';
29
28
 
30
29
  export async function markStreamStatus(sessionId: string, status: SessionStreamStatus): Promise<void> {
31
30
  try {
@@ -87,7 +86,7 @@ export async function loadStreamStatuses(): Promise<Record<string, SessionStream
87
86
  }[];
88
87
  const map: Record<string, SessionStreamStatus> = {};
89
88
  for (const row of rows) {
90
- if (row.status === 'stream' || row.status === 'finish' || row.status === 'abort' || row.status === 'error') {
89
+ if (row.status === 'stream' || row.status === 'finish' || row.status === 'abort') {
91
90
  map[row.session_id] = row.status;
92
91
  }
93
92
  }
@@ -28,10 +28,7 @@
28
28
  * - userBrowser.viewportMode string
29
29
  * - userBrowser.zoomLevel number
30
30
  * - terminal.activeRepo string
31
- * - terminal.cwd string
32
- * - terminal.commandHistory string[]
33
- * - terminal.input string
34
- * - terminal.output string (trimmed tail)
31
+ * - terminal.id string (server-side PTY id; reattached on reload)
35
32
  * - git.viewMode 'flat' | 'tree'
36
33
  * - git.commitDraft string
37
34
  * - git.stagedExpanded boolean
@@ -0,0 +1,222 @@
1
+ /**
2
+ * @license
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+
6
+ /**
7
+ * Wire contract for the terminal socket — the only transport a terminal panel
8
+ * uses.
9
+ *
10
+ * Two frame kinds share one duplex socket:
11
+ *
12
+ * - **text** frames are JSON control (`attach`, `resize`, `close` upstream;
13
+ * `ready`, `exit`, `error` downstream). Elysia hands inbound JSON to the
14
+ * handler already parsed and inbound binary as a `Uint8Array`, so the split
15
+ * is unambiguous on both ends.
16
+ * - **binary** frames are raw PTY bytes in both directions. Keystrokes are
17
+ * never JSON-escaped, so `\x03`, `\x1b[A` and paste payloads reach the shell
18
+ * byte-for-byte, and output is written straight into xterm without a decode
19
+ * step in between.
20
+ *
21
+ * The first binary frame after `ready` is the retained scrollback (it may be
22
+ * empty); everything after it is live output.
23
+ */
24
+
25
+ import { isRecord } from '@/shared/lib/util/guards';
26
+
27
+ /** Grid bounds both ends clamp to, so a bogus size can never reach the PTY. */
28
+ export const TERMINAL_MIN_COLS = 2;
29
+ export const TERMINAL_MAX_COLS = 1000;
30
+ export const TERMINAL_MIN_ROWS = 1;
31
+ export const TERMINAL_MAX_ROWS = 500;
32
+
33
+ /** Largest single input frame accepted from a client (keystrokes or paste). */
34
+ export const TERMINAL_MAX_INPUT_BYTES = 64 * 1024;
35
+
36
+ /** Ids key a server-side map and appear in a path segment. */
37
+ const TERMINAL_ID_PATTERN = /^[A-Za-z0-9_-]{1,64}$/;
38
+
39
+ export type TerminalStatus = 'running' | 'exited';
40
+
41
+ export interface TerminalSnapshot {
42
+ id: string;
43
+ /** Working directory the shell was launched in. */
44
+ cwd: string;
45
+ shell: string;
46
+ cols: number;
47
+ rows: number;
48
+ status: TerminalStatus;
49
+ exitCode: number | null;
50
+ signal: string | null;
51
+ bunVersion: string;
52
+ nodeVersion: string;
53
+ /**
54
+ * True when a command holds the terminal's foreground. An idle shell sitting
55
+ * at its prompt is running but not busy, which is the distinction the cap and
56
+ * the status badge both use.
57
+ */
58
+ busy?: boolean;
59
+ /**
60
+ * Size the retained scrollback was drawn for. The client replays at this
61
+ * size and only then fits its own viewport: re-wrapping history drawn for
62
+ * another width leaves fragments the shell's own redraw never clears.
63
+ */
64
+ replayCols: number;
65
+ replayRows: number;
66
+ }
67
+
68
+ export interface TerminalAttachFrame {
69
+ t: 'attach';
70
+ cols: number;
71
+ rows: number;
72
+ root?: string;
73
+ repo?: string;
74
+ theme?: 'light' | 'dark';
75
+ }
76
+
77
+ export interface TerminalResizeFrame {
78
+ t: 'resize';
79
+ cols: number;
80
+ rows: number;
81
+ }
82
+
83
+ export interface TerminalCloseFrame {
84
+ t: 'close';
85
+ }
86
+
87
+ export type TerminalClientFrame = TerminalAttachFrame | TerminalResizeFrame | TerminalCloseFrame;
88
+
89
+ export interface TerminalReadyFrame extends TerminalSnapshot {
90
+ t: 'ready';
91
+ /**
92
+ * Length of the replay frame that follows. Zero means the scrollback is
93
+ * empty; either way the client counts these bytes before it treats anything
94
+ * as live output, so it can replay at `replayCols`/`replayRows` first.
95
+ */
96
+ replayBytes: number;
97
+ }
98
+
99
+ export interface TerminalExitFrame {
100
+ t: 'exit';
101
+ exitCode: number | null;
102
+ signal: string | null;
103
+ }
104
+
105
+ export type TerminalErrorCode = 'capacity' | 'cwd' | 'shell' | 'spawn' | 'unknown';
106
+
107
+ export interface TerminalErrorFrame {
108
+ t: 'error';
109
+ code: TerminalErrorCode;
110
+ message: string;
111
+ }
112
+
113
+ export type TerminalServerFrame = TerminalReadyFrame | TerminalExitFrame | TerminalErrorFrame;
114
+
115
+ /**
116
+ * Terminal ids are client-generated. `randomUUID` needs a secure context, and
117
+ * the chamber is reachable over plain http on a LAN address (`--lan`), so the
118
+ * fallback is what keeps that deployment working.
119
+ */
120
+ export function newTerminalId(): string {
121
+ if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') return crypto.randomUUID();
122
+ return `t-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;
123
+ }
124
+
125
+ export function isValidTerminalId(value: string): boolean {
126
+ return TERMINAL_ID_PATTERN.test(value);
127
+ }
128
+
129
+ /** Clamp a proposed grid to the bounds the PTY runtime accepts. */
130
+ export function clampTerminalSize(cols: number, rows: number): { cols: number; rows: number } {
131
+ return {
132
+ cols: clampDimension(cols, TERMINAL_MIN_COLS, TERMINAL_MAX_COLS),
133
+ rows: clampDimension(rows, TERMINAL_MIN_ROWS, TERMINAL_MAX_ROWS),
134
+ };
135
+ }
136
+
137
+ export function encodeServerFrame(frame: TerminalServerFrame): string {
138
+ return JSON.stringify(frame);
139
+ }
140
+
141
+ export function encodeClientFrame(frame: TerminalClientFrame): string {
142
+ return JSON.stringify(frame);
143
+ }
144
+
145
+ /** Decode a control frame from either a parsed object or raw JSON text. */
146
+ export function decodeClientFrame(raw: unknown): TerminalClientFrame | null {
147
+ const value = typeof raw === 'string' ? tryParse(raw) : raw;
148
+ if (!isRecord(value)) return null;
149
+
150
+ if (value.t === 'attach') {
151
+ const cols = readDimension(value.cols);
152
+ const rows = readDimension(value.rows);
153
+ if (cols === null || rows === null) return null;
154
+ const frame: TerminalAttachFrame = { t: 'attach', cols, rows };
155
+ if (typeof value.root === 'string') frame.root = value.root;
156
+ if (typeof value.repo === 'string') frame.repo = value.repo;
157
+ if (value.theme === 'light' || value.theme === 'dark') frame.theme = value.theme;
158
+ return frame;
159
+ }
160
+
161
+ if (value.t === 'resize') {
162
+ const cols = readDimension(value.cols);
163
+ const rows = readDimension(value.rows);
164
+ if (cols === null || rows === null) return null;
165
+ return { t: 'resize', cols, rows };
166
+ }
167
+
168
+ if (value.t === 'close') return { t: 'close' };
169
+ return null;
170
+ }
171
+
172
+ export function decodeServerFrame(raw: string): TerminalServerFrame | null {
173
+ const value = tryParse(raw);
174
+ if (!isRecord(value)) return null;
175
+
176
+ if (value.t === 'ready') {
177
+ if (typeof value.id !== 'string' || typeof value.cwd !== 'string') return null;
178
+ const replayBytes = typeof value.replayBytes === 'number' ? value.replayBytes : 0;
179
+ return { ...(value as unknown as TerminalSnapshot), t: 'ready', replayBytes };
180
+ }
181
+
182
+ if (value.t === 'exit') {
183
+ return {
184
+ t: 'exit',
185
+ exitCode: typeof value.exitCode === 'number' ? value.exitCode : null,
186
+ signal: typeof value.signal === 'string' ? value.signal : null,
187
+ };
188
+ }
189
+
190
+ if (value.t === 'error' && typeof value.message === 'string') {
191
+ return { t: 'error', code: readErrorCode(value.code), message: value.message };
192
+ }
193
+
194
+ return null;
195
+ }
196
+
197
+ /** WebSocket endpoint for one terminal. Browser-only: reads `window.location`. */
198
+ export function terminalSocketUrl(terminalId: string): string {
199
+ const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
200
+ return `${protocol}//${window.location.host}/api/terminal/${encodeURIComponent(terminalId)}/ws`;
201
+ }
202
+
203
+ function clampDimension(value: number, min: number, max: number): number {
204
+ if (!Number.isFinite(value)) return min;
205
+ return Math.min(max, Math.max(min, Math.trunc(value)));
206
+ }
207
+
208
+ function readDimension(value: unknown): number | null {
209
+ return typeof value === 'number' && Number.isFinite(value) ? Math.trunc(value) : null;
210
+ }
211
+
212
+ function readErrorCode(value: unknown): TerminalErrorCode {
213
+ return value === 'capacity' || value === 'cwd' || value === 'shell' || value === 'spawn' ? value : 'unknown';
214
+ }
215
+
216
+ function tryParse(raw: string): unknown {
217
+ try {
218
+ return JSON.parse(raw);
219
+ } catch {
220
+ return null;
221
+ }
222
+ }
@@ -5,8 +5,8 @@
5
5
 
6
6
  /**
7
7
  * omp agent event surface types shared by useOmpAgent, the chamber timeline
8
- * and the AskDialog renderer. Kept separate so the hook file stays under the
9
- * repo's per-file size ceiling.
8
+ * and the extension-dialog renderer. Kept separate so the hook file stays under
9
+ * the repo's per-file size ceiling.
10
10
  */
11
11
 
12
12
  import type { ChatMessageData } from '@/shared/types/chat';
@@ -22,10 +22,22 @@ export interface OmpAgentEvent {
22
22
  * into ChatMessageData lands on one of these. */
23
23
  export interface OmpAgentCallbacks {
24
24
  onAgentStart?: () => void;
25
+ /** A single turn inside the run began (multi-turn runs emit several). */
26
+ onTurnStart?: () => void;
25
27
  onMessageUpdate?: (msg: ChatMessageData) => void;
26
28
  onMessageEnd?: (msg: ChatMessageData) => void;
27
29
  onAgentEnd?: (info: { errorMessage?: string; message?: string }) => void;
28
30
  onPromptError?: (errorMessage: string) => void;
31
+ /** The dispatched prompt ran no agent turn: omp executed a built-in slash
32
+ * command itself (ack `agentInvoked:false`, e.g. /usage, /compact). No
33
+ * agent_start/agent_end follows, so the timeline must clear the optimistic
34
+ * generating state here or the AI placeholder spins forever. */
35
+ onPromptSettled?: () => void;
36
+ /** Output of a built-in slash command omp executed on the prompt path
37
+ * (`command_output` frame — /usage, /model, /compact result, …). Lives only
38
+ * on the live stream: it never enters get_state.messages or the session
39
+ * JSONL, so the timeline must render it from this frame or it is lost. */
40
+ onCommandOutput?: (text: string) => void;
29
41
  onNotice?: (level: string, message: string) => void;
30
42
  onConnected?: () => void;
31
43
  /** Mount-time probe found the session mid-run → the stream was reattached
@@ -1,19 +1,21 @@
1
- export interface TerminalLogItem {
2
- id: string;
3
- command: string;
4
- stdout?: string;
5
- stderr?: string;
6
- exitCode: number;
7
- durationMs?: number;
8
- timestamp: string;
9
- cwd?: string;
1
+ /**
2
+ * Live terminal inventory, as served by `GET /api/terminal/sessions`.
3
+ *
4
+ * The frame types themselves live in `@/shared/lib/workspace/terminal/protocol`
5
+ * — they are the wire contract the socket encodes and decodes, not domain data.
6
+ */
7
+
8
+ import type { TerminalSnapshot } from '@/shared/lib/workspace/terminal/protocol';
9
+
10
+ export interface TerminalSessionsResponse {
11
+ terminals: TerminalSnapshot[];
10
12
  }
11
13
 
12
- export interface TerminalRunResponse {
13
- stdout: string;
14
- stderr: string;
15
- exitCode: number;
16
- durationMs: number;
14
+ /** Launch context for the terminal header (`GET /api/terminal/run`). */
15
+ export interface TerminalContextResponse {
16
+ bunVersion: string;
17
+ nodeVersion: string;
18
+ gitBranch: string;
17
19
  cwd: string;
18
- error?: string;
20
+ relativePath: string;
19
21
  }
@@ -15,8 +15,12 @@ export interface SessionItemData {
15
15
  hasSubagents?: boolean;
16
16
  subagentCount?: number;
17
17
  /** Server-tracked stream state: `stream` while a run is in flight, then a
18
- * one-shot `finish`/`abort`/`error` badge cleared when the session opens. */
19
- streamStatus?: 'stream' | 'finish' | 'abort' | 'error';
18
+ * one-shot `finish`/`abort` badge cleared when the session opens. */
19
+ streamStatus?: 'stream' | 'finish' | 'abort';
20
+ /** Live-only: the session's omp process is BLOCKED on an ask/approval dialog
21
+ * waiting for a user answer. Derived from the running process registry, never
22
+ * persisted — the process it describes is the same thing that owns the flag. */
23
+ awaitingInput?: boolean;
20
24
  }
21
25
 
22
26
  /**