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.
- package/dist/client/index.html +1 -1
- package/dist/client/static/css/index.e880517fcd.css +2 -0
- package/dist/client/static/js/async/{3y.4c2596f64c.js → 3y.71f2ccf3b6.js} +2 -2
- package/dist/client/static/js/async/{xw.ae67ba06fd.js → 5b.0d6ae50fe8.js} +10 -10
- package/dist/client/static/js/async/5bh.8b2363424f.js +1 -0
- package/dist/client/static/js/async/{vy.bbe114c4e0.js → bi.09688ba75c.js} +1 -2
- package/dist/client/static/js/async/c.797d933882.js +1 -0
- package/dist/client/static/js/async/{dc.6dc4b0a04b.js → dc.856fda6560.js} +2 -2
- package/dist/client/static/js/async/g.a06ca3b3e9.js +2 -0
- package/dist/client/static/js/async/g.a06ca3b3e9.js.LICENSE.txt +11 -0
- package/dist/client/static/js/async/{m.4b29d34f93.js → m.c038c71b9f.js} +2 -2
- package/dist/client/static/js/async/ma.2c261b04dd.js +2 -0
- package/dist/client/static/js/async/ms.98960af4eb.js +67 -0
- package/dist/client/static/js/async/nam.b5663e919f.js +10 -0
- package/dist/client/static/js/async/nam.b5663e919f.js.LICENSE.txt +11 -0
- package/dist/client/static/js/async/ot.84c71b308f.js +2 -0
- package/dist/client/static/js/async/ot.84c71b308f.js.LICENSE.txt +11 -0
- package/dist/client/static/js/async/{q.542c184fec.js → q.23af2a358f.js} +3 -3
- package/dist/client/static/js/async/{s.413c2a4683.js → s.222ee974f6.js} +2 -2
- package/dist/client/static/js/async/t.4dca0e2ba8.js +1 -0
- package/dist/client/static/js/async/uf.46ad6209fa.js +2 -0
- package/dist/client/static/js/async/xn.6360379f71.js +2 -0
- package/dist/client/static/js/async/xnl.feb0021e50.js +2 -0
- package/dist/client/static/js/async/xnl.feb0021e50.js.LICENSE.txt +12 -0
- package/dist/client/static/js/index.0de15c5e17.js +33 -0
- package/dist/client/static/js/{uy.2e8929ed0c.js → jp.50817896f3.js} +6 -6
- package/package.json +2 -1
- package/src/client/components/common/MarkdownRenderer.tsx +69 -27
- package/src/client/components/common/code-editor/history.test.ts +118 -0
- package/src/client/components/common/code-editor/history.ts +101 -0
- package/src/client/components/common/code-editor/index.tsx +56 -66
- package/src/client/components/common/code-surface/index.tsx +84 -11
- package/src/client/components/common/code-surface/measure.ts +66 -17
- package/src/client/components/common/diagram-viewer/index.tsx +258 -0
- package/src/client/components/common/diagram-viewer/read-block.ts +53 -0
- package/src/client/components/layout/session-sidebar/CategoryItem.tsx +2 -1
- package/src/client/components/layout/session-sidebar/SessionItem.tsx +11 -4
- package/src/client/components/layout/session-sidebar/SessionList.tsx +1 -1
- package/src/client/components/mobile/mobile-session-sidebar/Item.tsx +1 -1
- package/src/client/components/mobile/mobile-session-sidebar/List.tsx +1 -1
- package/src/client/components/mobile/mobile-session-sidebar/SessionRow.tsx +2 -2
- package/src/client/components/settings/LazyModal.tsx +1 -1
- package/src/client/components/settings/Modal.tsx +2 -1
- package/src/client/components/workspace/chat-timeline/ComposerDock.tsx +11 -4
- package/src/client/components/workspace/chat-timeline/QueueList.tsx +18 -14
- package/src/client/components/workspace/chat-timeline/ToolCallCard.tsx +8 -1
- package/src/client/components/workspace/chat-timeline/chat-input/index.tsx +11 -52
- package/src/client/components/workspace/chat-timeline/chat-input/selection.ts +57 -0
- package/src/client/components/workspace/chat-timeline/index.tsx +124 -108
- package/src/client/components/workspace/chat-timeline/tool-renderers/ask-panel/FrameControl.tsx +172 -0
- package/src/client/components/workspace/chat-timeline/tool-renderers/ask-panel/QuestionBlock.tsx +155 -0
- package/src/client/components/workspace/chat-timeline/tool-renderers/ask-panel/drafts.ts +113 -0
- package/src/client/components/workspace/chat-timeline/tool-renderers/ask-panel/index.tsx +92 -0
- package/src/client/components/workspace/chat-timeline/tool-renderers/{ask-dialog → extension-dialog}/Footer.tsx +3 -3
- package/src/client/components/workspace/chat-timeline/tool-renderers/{ask-dialog → extension-dialog}/Header.tsx +3 -3
- package/src/client/components/workspace/chat-timeline/tool-renderers/{ask-dialog → extension-dialog}/Lazy.tsx +8 -8
- package/src/client/components/workspace/chat-timeline/tool-renderers/{ask-dialog → extension-dialog}/SelectBody.tsx +3 -3
- package/src/client/components/workspace/chat-timeline/tool-renderers/{ask-dialog → extension-dialog}/index.tsx +16 -10
- package/src/client/components/workspace/chat-timeline/tool-renderers/index.tsx +2 -2
- package/src/client/components/workspace/file-explorer/index.tsx +18 -5
- package/src/client/components/workspace/git-panel/BranchToolbar.tsx +2 -2
- package/src/client/components/workspace/git-panel/index.tsx +5 -1
- package/src/client/components/workspace/terminal-panel/Header.tsx +51 -19
- package/src/client/components/workspace/terminal-panel/RealtimeXtermView.tsx +86 -282
- package/src/client/components/workspace/terminal-panel/index.tsx +76 -50
- package/src/client/components/workspace/terminal-panel/mount.ts +275 -0
- package/src/client/data/theme/terminal.ts +27 -29
- package/src/client/hooks/chat/omp/session-list.tsx +23 -2
- package/src/client/hooks/chat/omp/session-sidebar-controller.ts +1 -1
- package/src/client/hooks/chat/timeline/actions.ts +23 -34
- package/src/client/hooks/chat/timeline/ask-frames.ts +102 -0
- package/src/client/hooks/chat/timeline/extension-dialog.ts +12 -9
- package/src/client/hooks/chat/timeline/index.ts +18 -28
- package/src/client/hooks/chat/timeline/prompt-text.ts +35 -0
- package/src/client/hooks/chat/timeline/queue.ts +110 -46
- package/src/client/hooks/chat/timeline/send.ts +13 -27
- package/src/client/hooks/chat/timeline/session-load.ts +48 -106
- package/src/client/hooks/chat/timeline/session-pagination.ts +144 -0
- package/src/client/hooks/editor/use-code-window.ts +118 -0
- package/src/client/hooks/ui/input-required-alert.ts +43 -0
- package/src/client/hooks/ui/notification-sound.ts +65 -0
- package/src/client/hooks/ui/theme.ts +22 -6
- package/src/client/hooks/workspace/terminal/index.ts +217 -0
- package/src/client/hooks/workspace/terminal/socket.ts +227 -0
- package/src/client/tailwind.css +38 -12
- package/src/server/db.server.ts +19 -0
- package/src/server/lib/fs/git-sync.ts +191 -0
- package/src/server/lib/omp/rpc/manager.ts +72 -6
- package/src/server/lib/omp/rpc/session-commands.ts +6 -0
- package/src/server/lib/omp/rpc/session-registry.ts +18 -0
- package/src/server/lib/omp/session/sidebar-data.server.ts +10 -4
- package/src/server/lib/queue/delivery.server.ts +114 -0
- package/src/server/lib/queue/schema-migration.server.ts +50 -0
- package/src/server/lib/queue/store.server.ts +185 -0
- package/src/server/lib/terminal/foreground.ts +73 -0
- package/src/server/lib/terminal/history.test.ts +118 -0
- package/src/server/lib/terminal/history.ts +205 -0
- package/src/server/lib/terminal/runtime.server.ts +338 -0
- package/src/server/lib/terminal/session.server.ts +215 -0
- package/src/server/lib/terminal/shell.test.ts +97 -0
- package/src/server/lib/terminal/shell.ts +103 -0
- package/src/server/routes/fs/git.ts +22 -12
- package/src/server/routes/index.ts +2 -1
- package/src/server/routes/sessions/index.ts +11 -3
- package/src/server/routes/sessions/queue.ts +121 -0
- package/src/server/routes/sessions/session.ts +1 -132
- package/src/server/routes/settings/commands.ts +1 -1
- package/src/server/routes/terminal/index.ts +2 -2
- package/src/server/routes/terminal/run.ts +8 -100
- package/src/server/routes/terminal/sessions.ts +24 -0
- package/src/server/routes/terminal/ws.ts +149 -0
- package/src/shared/lib/chat/ask-questions.test.ts +169 -0
- package/src/shared/lib/chat/ask-questions.ts +264 -0
- package/src/shared/lib/chat/omp/agent-events.test.ts +37 -1
- package/src/shared/lib/chat/omp/agent-events.ts +30 -0
- package/src/shared/lib/chat/timeline/omp-callbacks.test.ts +9 -1
- package/src/shared/lib/chat/timeline/omp-callbacks.ts +45 -8
- package/src/shared/lib/code/lazy-window.test.ts +161 -0
- package/src/shared/lib/code/lazy-window.ts +156 -0
- package/src/shared/lib/code/syntax-highlight.test.ts +50 -1
- package/src/shared/lib/code/syntax-highlight.ts +14 -1
- package/src/shared/lib/code/windowed-highlight.ts +63 -0
- package/src/shared/lib/markdown/mermaid.ts +8 -6
- package/src/shared/lib/omp/session/stream-state.server.ts +2 -3
- package/src/shared/lib/workspace/session-state/store.ts +1 -4
- package/src/shared/lib/workspace/terminal/protocol.ts +222 -0
- package/src/shared/types/omp/agent.ts +14 -2
- package/src/shared/types/terminal.ts +17 -15
- package/src/shared/types/workspace.ts +6 -2
- package/dist/client/static/css/index.51f68210a5.css +0 -2
- package/dist/client/static/js/async/5b.d88304bc13.js +0 -1
- package/dist/client/static/js/async/c.cd45b0ac45.js +0 -1
- package/dist/client/static/js/async/fw.6a51998a8e.js +0 -6
- package/dist/client/static/js/async/g.86e2256a70.js +0 -2
- package/dist/client/static/js/async/kc.bbdb35b771.js +0 -1
- package/dist/client/static/js/async/mw.ca1512563c.js +0 -10
- package/dist/client/static/js/async/rf.d626afc367.js +0 -2
- package/dist/client/static/js/async/t.cda4891526.js +0 -1
- package/dist/client/static/js/async/uf.24ca3d49c3.js +0 -2
- package/dist/client/static/js/async/uf.24ca3d49c3.js.LICENSE.txt +0 -6
- package/dist/client/static/js/async/v0.394ed37c6b.js +0 -2
- package/dist/client/static/js/async/v0.394ed37c6b.js.LICENSE.txt +0 -6
- package/dist/client/static/js/async/vy.bbe114c4e0.js.LICENSE.txt +0 -6
- package/dist/client/static/js/async/xn.afecebbbd9.js +0 -2
- package/dist/client/static/js/index.eccad0b70a.js +0 -33
- package/dist/client/static/js/index.eccad0b70a.js.LICENSE.txt +0 -4
- package/src/client/components/workspace/chat-timeline/tool-renderers/panels/Ask.tsx +0 -205
- package/src/client/components/workspace/terminal-panel/InputBar.tsx +0 -82
- package/src/client/components/workspace/terminal-panel/LogEntry.tsx +0 -70
- package/src/client/hooks/workspace/terminal-output.ts +0 -66
- package/src/client/hooks/workspace/terminal.ts +0 -283
- package/src/server/lib/fs/terminal-cwd.ts +0 -49
- package/src/server/routes/terminal/stream.ts +0 -100
- /package/dist/client/static/js/async/{3y.4c2596f64c.js.LICENSE.txt → 3y.71f2ccf3b6.js.LICENSE.txt} +0 -0
- /package/dist/client/static/js/async/{g.86e2256a70.js.LICENSE.txt → 5b.0d6ae50fe8.js.LICENSE.txt} +0 -0
- /package/dist/client/static/js/async/{dc.6dc4b0a04b.js.LICENSE.txt → dc.856fda6560.js.LICENSE.txt} +0 -0
- /package/dist/client/static/js/async/{fw.6a51998a8e.js.LICENSE.txt → m.c038c71b9f.js.LICENSE.txt} +0 -0
- /package/dist/client/static/js/async/{m.4b29d34f93.js.LICENSE.txt → ma.2c261b04dd.js.LICENSE.txt} +0 -0
- /package/dist/client/static/js/async/{xn.afecebbbd9.js.LICENSE.txt → ms.98960af4eb.js.LICENSE.txt} +0 -0
- /package/dist/client/static/js/async/{mw.ca1512563c.js.LICENSE.txt → q.23af2a358f.js.LICENSE.txt} +0 -0
- /package/dist/client/static/js/async/{q.542c184fec.js.LICENSE.txt → s.222ee974f6.js.LICENSE.txt} +0 -0
- /package/dist/client/static/js/async/{rf.d626afc367.js.LICENSE.txt → uf.46ad6209fa.js.LICENSE.txt} +0 -0
- /package/dist/client/static/js/async/{s.413c2a4683.js.LICENSE.txt → xn.6360379f71.js.LICENSE.txt} +0 -0
- /package/dist/client/static/js/{async/xw.ae67ba06fd.js.LICENSE.txt → index.0de15c5e17.js.LICENSE.txt} +0 -0
- /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'
|
|
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'
|
|
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.
|
|
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
|
|
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
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
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
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
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
|
-
|
|
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
|
|
19
|
-
streamStatus?: 'stream' | 'finish' | 'abort'
|
|
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
|
/**
|