ompchamber 0.8.0 → 1.1.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.b859cc1cc5.css +2 -0
- package/dist/client/static/js/async/{5b.0d6ae50fe8.js → 5b.a5ccbc19dc.js} +2 -2
- package/dist/client/static/js/async/g.a06ca3b3e9.js +2 -0
- package/dist/client/static/js/async/{m.0896dab823.js → m.c038c71b9f.js} +2 -2
- package/dist/client/static/js/async/ms.98960af4eb.js +67 -0
- package/dist/client/static/js/async/ms.98960af4eb.js.LICENSE.txt +12 -0
- package/dist/client/static/js/async/ot.84c71b308f.js +2 -0
- package/dist/client/static/js/async/{x4.69ec69c61b.js.LICENSE.txt → ot.84c71b308f.js.LICENSE.txt} +5 -0
- package/dist/client/static/js/index.6e065017eb.js +33 -0
- package/package.json +2 -1
- package/src/client/components/common/ComposerPicker.tsx +16 -4
- package/src/client/components/common/ComposerTextarea.tsx +1 -0
- package/src/client/components/layout/RightActivityBar.tsx +1 -1
- package/src/client/components/layout/desktop-layout/ResizeHandle.tsx +2 -2
- package/src/client/components/layout/desktop-layout/TopNavbar.tsx +13 -2
- package/src/client/components/layout/desktop-layout/WorkspacePanels.tsx +107 -56
- package/src/client/components/layout/desktop-layout/index.tsx +25 -96
- package/src/client/components/layout/desktop-layout/resizer/index.tsx +79 -118
- package/src/client/components/layout/desktop-layout/resizer/separator-drag.ts +140 -0
- package/src/client/components/layout/session-sidebar/CategoryItem.tsx +1 -1
- package/src/client/components/layout/session-sidebar/Header.tsx +1 -1
- package/src/client/components/layout/session-sidebar/SessionItem.tsx +27 -13
- package/src/client/components/layout/session-sidebar/SessionList.tsx +1 -1
- package/src/client/components/layout/session-sidebar/SubagentList.tsx +6 -4
- 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/workspace/chat-timeline/ComposerDock.tsx +14 -4
- package/src/client/components/workspace/chat-timeline/EmptyWorkspacePrompt.tsx +3 -0
- package/src/client/components/workspace/chat-timeline/QueueList.tsx +18 -14
- package/src/client/components/workspace/chat-timeline/SubagentView.tsx +3 -1
- package/src/client/components/workspace/chat-timeline/chat-input/index.tsx +23 -3
- package/src/client/components/workspace/chat-timeline/index.tsx +7 -2
- package/src/client/components/workspace/chat-timeline/tool-renderers/panels/Goal.tsx +3 -3
- 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/model-dropdown/groups.ts +35 -2
- package/src/client/components/workspace/model-dropdown/index.tsx +48 -7
- package/src/client/components/workspace/model-dropdown/use-catalog.ts +28 -4
- 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/composer/index.ts +28 -8
- package/src/client/hooks/chat/omp/session-list.tsx +19 -2
- package/src/client/hooks/chat/omp/session-sidebar-controller.ts +1 -1
- package/src/client/hooks/chat/timeline/actions.ts +47 -28
- package/src/client/hooks/chat/timeline/deferred-model.test.ts +219 -0
- package/src/client/hooks/chat/timeline/deferred-model.ts +92 -0
- package/src/client/hooks/chat/timeline/index.ts +25 -21
- package/src/client/hooks/chat/timeline/queue.ts +110 -46
- package/src/client/hooks/chat/timeline/send.ts +16 -5
- package/src/client/hooks/workspace/available-width.ts +33 -0
- package/src/client/hooks/workspace/panel-widths.ts +34 -37
- package/src/client/hooks/workspace/terminal/index.ts +217 -0
- package/src/client/hooks/workspace/terminal/socket.ts +227 -0
- package/src/server/db.server.ts +19 -0
- package/src/server/lib/fs/git-sync.ts +191 -0
- package/src/server/lib/models/preferences.server.ts +110 -0
- package/src/server/lib/models/registry.server.ts +206 -0
- package/src/server/lib/omp/rpc/manager.ts +58 -3
- package/src/server/lib/omp/rpc/session-commands.ts +6 -0
- package/src/server/lib/omp/rpc/utility.ts +7 -1
- package/src/server/lib/omp/session/sidebar-data.server.ts +3 -3
- package/src/server/lib/queue/delivery.server.test.ts +203 -0
- package/src/server/lib/queue/delivery.server.ts +144 -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/models/root.ts +88 -215
- 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 +59 -18
- 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/composer/client.ts +17 -8
- package/src/shared/lib/chat/composer/commands.ts +290 -0
- package/src/shared/lib/chat/composer/composer.test.ts +157 -0
- package/src/shared/lib/chat/composer/filter.ts +21 -7
- package/src/shared/lib/chat/composer/trigger.ts +109 -20
- 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/models/preferences.test.ts +131 -0
- package/src/shared/lib/models/preferences.ts +88 -0
- package/src/shared/lib/models/provider-models.ts +8 -1
- package/src/shared/lib/omp/session/stream-state.server.ts +2 -3
- package/src/shared/lib/omp/subagent/label.test.ts +94 -0
- package/src/shared/lib/omp/subagent/label.ts +84 -0
- package/src/shared/lib/workspace/panel-widths.test.ts +113 -18
- package/src/shared/lib/workspace/panel-widths.ts +161 -30
- package/src/shared/lib/workspace/right-panels.ts +50 -11
- package/src/shared/lib/workspace/session-state/store.ts +4 -4
- package/src/shared/lib/workspace/terminal/protocol.ts +222 -0
- package/src/shared/types/chat.ts +38 -65
- package/src/shared/types/index.ts +1 -0
- package/src/shared/types/models.ts +101 -0
- package/src/shared/types/omp/agent.ts +12 -0
- package/src/shared/types/omp/subagent.ts +4 -0
- package/src/shared/types/settings/command.ts +13 -0
- package/src/shared/types/terminal.ts +17 -15
- package/src/shared/types/workspace.ts +2 -2
- package/dist/client/static/css/index.5c0447cefe.css +0 -2
- package/dist/client/static/js/async/g.d1c2fe09aa.js +0 -2
- package/dist/client/static/js/async/x4.69ec69c61b.js +0 -6
- package/dist/client/static/js/index.dc5f5fabe8.js +0 -33
- 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/{5b.0d6ae50fe8.js.LICENSE.txt → 5b.a5ccbc19dc.js.LICENSE.txt} +0 -0
- /package/dist/client/static/js/async/{g.d1c2fe09aa.js.LICENSE.txt → g.a06ca3b3e9.js.LICENSE.txt} +0 -0
- /package/dist/client/static/js/async/{m.0896dab823.js.LICENSE.txt → m.c038c71b9f.js.LICENSE.txt} +0 -0
- /package/dist/client/static/js/{index.dc5f5fabe8.js.LICENSE.txt → index.6e065017eb.js.LICENSE.txt} +0 -0
|
@@ -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
|
+
}
|
package/src/shared/types/chat.ts
CHANGED
|
@@ -187,71 +187,6 @@ export interface QueuedMessage {
|
|
|
187
187
|
model: QueuedMessageModel | null;
|
|
188
188
|
}
|
|
189
189
|
|
|
190
|
-
export interface AIModelOption {
|
|
191
|
-
id: string;
|
|
192
|
-
name: string;
|
|
193
|
-
provider: string;
|
|
194
|
-
providerIcon?: string;
|
|
195
|
-
contextWindow?: string | number;
|
|
196
|
-
isCmdAgent?: boolean;
|
|
197
|
-
isFavorite?: boolean;
|
|
198
|
-
isRecent?: boolean;
|
|
199
|
-
thinkingLevel?: string;
|
|
200
|
-
/** Baked thinking ladder for this model: `["off", ...efforts]`. */
|
|
201
|
-
thinkingLevels?: string[];
|
|
202
|
-
capabilities?: string[];
|
|
203
|
-
input?: string;
|
|
204
|
-
output?: string;
|
|
205
|
-
cost?: {
|
|
206
|
-
input: string | number;
|
|
207
|
-
output: string | number;
|
|
208
|
-
cacheRead?: string | number;
|
|
209
|
-
cacheWrite?: string | number;
|
|
210
|
-
};
|
|
211
|
-
description?: string;
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
// ── Real omp model registry (forked from omp-web) ──────────────────────────
|
|
215
|
-
|
|
216
|
-
/** One pickable model from the omp catalog (`/api/models` modelList). */
|
|
217
|
-
export interface ModelEntry {
|
|
218
|
-
id: string;
|
|
219
|
-
name: string;
|
|
220
|
-
provider: string;
|
|
221
|
-
supportsFastMode?: boolean;
|
|
222
|
-
contextWindow?: number;
|
|
223
|
-
maxTokens?: number;
|
|
224
|
-
/** Baked thinking ladder for this model: `["off", ...efforts]`. */
|
|
225
|
-
thinkingLevels?: string[];
|
|
226
|
-
/** Cost in $/1M tokens (omp catalog). */
|
|
227
|
-
cost?: { input?: number; output?: number; cacheRead?: number; cacheWrite?: number };
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
/** A model selected by provider + modelId (omp's set_model shape). */
|
|
231
|
-
export interface SelectedModel {
|
|
232
|
-
provider: string;
|
|
233
|
-
modelId: string;
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
/** Model-level thinking metadata read off the live session state. */
|
|
237
|
-
export interface ThinkingModelMeta {
|
|
238
|
-
provider: string;
|
|
239
|
-
modelId: string;
|
|
240
|
-
name?: string;
|
|
241
|
-
reasoning?: boolean;
|
|
242
|
-
thinking?: { efforts?: string[] };
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
/** Response shape of GET /api/models (mirrors omp-web ModelsData). */
|
|
246
|
-
export interface ModelsData {
|
|
247
|
-
models: Record<string, string>;
|
|
248
|
-
modelList: ModelEntry[];
|
|
249
|
-
defaultModel: SelectedModel | null;
|
|
250
|
-
thinkingLevels: Record<string, string[]>;
|
|
251
|
-
connectedProviders?: { id: string; name: string; disabled: boolean }[];
|
|
252
|
-
modelError?: string;
|
|
253
|
-
}
|
|
254
|
-
|
|
255
190
|
// ── Composer autocomplete (@file/agent / /command+skill) ───────────────────
|
|
256
191
|
|
|
257
192
|
/** Trigger group that opened the composer autocomplete: `@` → mention, `/` → command. */
|
|
@@ -259,6 +194,13 @@ export type ComposerPickKind = 'mention' | 'command';
|
|
|
259
194
|
/** Origin of a pickable item: an `@` mention is an agent or a workspace file. */
|
|
260
195
|
export type ComposerPickSource = 'agent' | 'file' | 'command' | 'skill';
|
|
261
196
|
|
|
197
|
+
/**
|
|
198
|
+
* Which part of a slash invocation the popup is completing. oh-my-pi splits
|
|
199
|
+
* these the same way: `name` completes the command token itself, `args`
|
|
200
|
+
* completes a declarative subcommand after `<command> `.
|
|
201
|
+
*/
|
|
202
|
+
export type ComposerTriggerPhase = 'name' | 'args';
|
|
203
|
+
|
|
262
204
|
/** A detected trigger: the `@`/`/` char plus the query typed after it. */
|
|
263
205
|
export interface ComposerTrigger {
|
|
264
206
|
kind: ComposerPickKind;
|
|
@@ -267,6 +209,18 @@ export interface ComposerTrigger {
|
|
|
267
209
|
start: number;
|
|
268
210
|
/** Caret index when detected. */
|
|
269
211
|
end: number;
|
|
212
|
+
/** Command completion phase; `mention` triggers are always `name`. */
|
|
213
|
+
phase: ComposerTriggerPhase;
|
|
214
|
+
/** Index the accepted text replaces from — equals `start` in the `name` phase. */
|
|
215
|
+
replaceFrom: number;
|
|
216
|
+
/** Command the `args` phase belongs to (name or alias as typed). */
|
|
217
|
+
command?: string;
|
|
218
|
+
/**
|
|
219
|
+
* The `/token` sits mid-prompt (prose precedes it on the line or an earlier
|
|
220
|
+
* line exists), so only skill entries may surface — oh-my-pi's
|
|
221
|
+
* `buildMidPromptSkillCompletions` gate.
|
|
222
|
+
*/
|
|
223
|
+
midPrompt?: boolean;
|
|
270
224
|
}
|
|
271
225
|
|
|
272
226
|
/** One autocomplete item surfaced in the composer popover. */
|
|
@@ -281,6 +235,25 @@ export interface ComposerPickItem {
|
|
|
281
235
|
token: string;
|
|
282
236
|
/** Workspace-relative path (file items only). */
|
|
283
237
|
path?: string;
|
|
238
|
+
/**
|
|
239
|
+
* Suppress the trailing space on insertion. Set on the collapsed `/skill:`
|
|
240
|
+
* namespace row so the popup reopens with the individual skills, matching
|
|
241
|
+
* oh-my-pi's accept path.
|
|
242
|
+
*/
|
|
243
|
+
insertWithoutSpace?: boolean;
|
|
244
|
+
/** Command aliases from oh-my-pi (`/models` for `/model`). */
|
|
245
|
+
aliases?: string[];
|
|
246
|
+
/** Declarative subcommands; drives the `args` phase. */
|
|
247
|
+
subcommands?: ComposerSubcommand[];
|
|
248
|
+
/** Argument hint (`[on|off|status]`), prefixed to the description like omp. */
|
|
249
|
+
inputHint?: string;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/** One declarative subcommand of an oh-my-pi slash command. */
|
|
253
|
+
export interface ComposerSubcommand {
|
|
254
|
+
name: string;
|
|
255
|
+
description?: string;
|
|
256
|
+
usage?: string;
|
|
284
257
|
}
|
|
285
258
|
|
|
286
259
|
/** A pick item after filtering, with the matched range within `name` (or null). */
|
|
@@ -2,6 +2,7 @@ export * from '@/shared/types/workspace';
|
|
|
2
2
|
export * from '@/shared/types/fs';
|
|
3
3
|
export * from '@/shared/types/git';
|
|
4
4
|
export * from '@/shared/types/chat';
|
|
5
|
+
export * from '@/shared/types/models';
|
|
5
6
|
export * from '@/shared/types/terminal';
|
|
6
7
|
export * from '@/shared/types/settings/state';
|
|
7
8
|
export * from '@/shared/types/tokenUsage';
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Model catalog types shared by the composer picker, the settings pages and
|
|
8
|
+
* the `/api/models` route: the picker row (`AIModelOption`), the registry entry
|
|
9
|
+
* (`ModelEntry`), the selection reference omp's `set_model` takes, and the
|
|
10
|
+
* stored favorite/recent preference pair.
|
|
11
|
+
*
|
|
12
|
+
* Split out of `chat.ts` so that file stays under the per-file ceiling — the
|
|
13
|
+
* picker's row shape is not part of the chat message model it was nested in.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
export interface AIModelOption {
|
|
17
|
+
id: string;
|
|
18
|
+
name: string;
|
|
19
|
+
provider: string;
|
|
20
|
+
providerIcon?: string;
|
|
21
|
+
contextWindow?: string | number;
|
|
22
|
+
isCmdAgent?: boolean;
|
|
23
|
+
/**
|
|
24
|
+
* Favorite / last-used flag. On a picker row these are DERIVED from the
|
|
25
|
+
* stored preference keys (`ModelPreferences`), never read off the registry —
|
|
26
|
+
* a favorite is a chamber-local preference, not a property of omp's config.
|
|
27
|
+
* On the MOCK catalog they are the seed for a fresh install's rails.
|
|
28
|
+
*/
|
|
29
|
+
isFavorite?: boolean;
|
|
30
|
+
/** MOCK seed only — the RECENT rail is ordered by `ModelPreferences.recentKeys`. */
|
|
31
|
+
isRecent?: boolean;
|
|
32
|
+
thinkingLevel?: string;
|
|
33
|
+
/** Baked thinking ladder for this model: `["off", ...efforts]`. */
|
|
34
|
+
thinkingLevels?: string[];
|
|
35
|
+
capabilities?: string[];
|
|
36
|
+
input?: string;
|
|
37
|
+
output?: string;
|
|
38
|
+
cost?: {
|
|
39
|
+
input: string | number;
|
|
40
|
+
output: string | number;
|
|
41
|
+
cacheRead?: string | number;
|
|
42
|
+
cacheWrite?: string | number;
|
|
43
|
+
};
|
|
44
|
+
description?: string;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// ── Real omp model registry (forked from omp-web) ──────────────────────────
|
|
48
|
+
|
|
49
|
+
/** One pickable model from the omp catalog (`/api/models` modelList). */
|
|
50
|
+
export interface ModelEntry {
|
|
51
|
+
id: string;
|
|
52
|
+
name: string;
|
|
53
|
+
provider: string;
|
|
54
|
+
supportsFastMode?: boolean;
|
|
55
|
+
contextWindow?: number;
|
|
56
|
+
maxTokens?: number;
|
|
57
|
+
/** Baked thinking ladder for this model: `["off", ...efforts]`. */
|
|
58
|
+
thinkingLevels?: string[];
|
|
59
|
+
/** Cost in $/1M tokens (omp catalog). */
|
|
60
|
+
cost?: { input?: number; output?: number; cacheRead?: number; cacheWrite?: number };
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** A model selected by provider + modelId (omp's set_model shape). */
|
|
64
|
+
export interface SelectedModel {
|
|
65
|
+
provider: string;
|
|
66
|
+
modelId: string;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** Model-level thinking metadata read off the live session state. */
|
|
70
|
+
export interface ThinkingModelMeta {
|
|
71
|
+
provider: string;
|
|
72
|
+
modelId: string;
|
|
73
|
+
name?: string;
|
|
74
|
+
reasoning?: boolean;
|
|
75
|
+
thinking?: { efforts?: string[] };
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* The user's favorite + last-used models, keyed by the composite `provider:id`
|
|
80
|
+
* identity (`modelKey`). Stored separately from the model registry because the
|
|
81
|
+
* registry is a read-only view of omp's config — a favorite is a chamber-local
|
|
82
|
+
* preference, and an id-only key would light up every provider serving it.
|
|
83
|
+
*/
|
|
84
|
+
export interface ModelPreferences {
|
|
85
|
+
/** Composite keys in the order the FAVORITES rail renders them. */
|
|
86
|
+
favorites: string[];
|
|
87
|
+
/** Composite keys, most recently used first, capped at `RECENT_MODELS_LIMIT`. */
|
|
88
|
+
recentKeys: string[];
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/** Response shape of GET /api/models (mirrors omp-web ModelsData). */
|
|
92
|
+
export interface ModelsData {
|
|
93
|
+
models: Record<string, string>;
|
|
94
|
+
modelList: ModelEntry[];
|
|
95
|
+
defaultModel: SelectedModel | null;
|
|
96
|
+
thinkingLevels: Record<string, string[]>;
|
|
97
|
+
connectedProviders?: { id: string; name: string; disabled: boolean }[];
|
|
98
|
+
modelError?: string;
|
|
99
|
+
/** Absent when the settings row has never been written. */
|
|
100
|
+
modelPreferences?: ModelPreferences;
|
|
101
|
+
}
|
|
@@ -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
|
|
@@ -150,6 +150,10 @@ export interface SubagentInfo {
|
|
|
150
150
|
detached?: boolean;
|
|
151
151
|
progress?: SubagentProgress;
|
|
152
152
|
lastUpdate?: number;
|
|
153
|
+
/** Model role the run resolved to. On-disk history entries carry it here
|
|
154
|
+
* (folded from the parent's task toolResult); live snapshots carry it on
|
|
155
|
+
* `progress` instead, because it arrives with the progress frame. */
|
|
156
|
+
modelRole?: string;
|
|
153
157
|
/** Settled result for history entries (SingleResult-derived). */
|
|
154
158
|
result?: SubagentHistoryResult;
|
|
155
159
|
/** Roster origin: live frames/snapshots (default) vs on-disk history. */
|
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
export type CommandScope = 'system' | 'user' | 'project';
|
|
2
2
|
|
|
3
|
+
/** Declarative subcommand of an oh-my-pi builtin (drives `/cmd <sub>` completion). */
|
|
4
|
+
export interface CommandSubcommand {
|
|
5
|
+
name: string;
|
|
6
|
+
description?: string;
|
|
7
|
+
usage?: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
3
10
|
export interface CommandItem {
|
|
4
11
|
id: string;
|
|
5
12
|
name: string;
|
|
@@ -9,4 +16,10 @@ export interface CommandItem {
|
|
|
9
16
|
overrideModel?: string;
|
|
10
17
|
template: string;
|
|
11
18
|
isBuiltIn?: boolean;
|
|
19
|
+
/** oh-my-pi aliases (`/models` for `/model`); live agent commands only. */
|
|
20
|
+
aliases?: string[];
|
|
21
|
+
/** Declarative subcommands; live agent commands only. */
|
|
22
|
+
subcommands?: CommandSubcommand[];
|
|
23
|
+
/** Argument hint (`[on|off|status]`); live agent commands only. */
|
|
24
|
+
inputHint?: string;
|
|
12
25
|
}
|
|
@@ -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,8 @@ 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
20
|
/** Live-only: the session's omp process is BLOCKED on an ask/approval dialog
|
|
21
21
|
* waiting for a user answer. Derived from the running process registry, never
|
|
22
22
|
* persisted — the process it describes is the same thing that owns the flag. */
|