@bytespell/shella 0.2.4 → 0.2.6
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/bundled-plugins/agent/AGENT_SPEC.md +611 -0
- package/bundled-plugins/agent/README.md +7 -0
- package/bundled-plugins/agent/components.json +24 -0
- package/bundled-plugins/agent/eslint.config.js +23 -0
- package/bundled-plugins/agent/index.html +13 -0
- package/bundled-plugins/agent/package-lock.json +12140 -0
- package/bundled-plugins/agent/package.json +62 -0
- package/bundled-plugins/agent/public/vite.svg +1 -0
- package/bundled-plugins/agent/server.js +631 -0
- package/bundled-plugins/agent/src/App.tsx +755 -0
- package/bundled-plugins/agent/src/assets/react.svg +1 -0
- package/bundled-plugins/agent/src/components/ui/alert-dialog.tsx +182 -0
- package/bundled-plugins/agent/src/components/ui/badge.tsx +45 -0
- package/bundled-plugins/agent/src/components/ui/button.tsx +60 -0
- package/bundled-plugins/agent/src/components/ui/card.tsx +94 -0
- package/bundled-plugins/agent/src/components/ui/combobox.tsx +294 -0
- package/bundled-plugins/agent/src/components/ui/dropdown-menu.tsx +253 -0
- package/bundled-plugins/agent/src/components/ui/field.tsx +225 -0
- package/bundled-plugins/agent/src/components/ui/input-group.tsx +147 -0
- package/bundled-plugins/agent/src/components/ui/input.tsx +19 -0
- package/bundled-plugins/agent/src/components/ui/label.tsx +24 -0
- package/bundled-plugins/agent/src/components/ui/select.tsx +185 -0
- package/bundled-plugins/agent/src/components/ui/separator.tsx +26 -0
- package/bundled-plugins/agent/src/components/ui/switch.tsx +31 -0
- package/bundled-plugins/agent/src/components/ui/textarea.tsx +18 -0
- package/bundled-plugins/agent/src/index.css +131 -0
- package/bundled-plugins/agent/src/lib/utils.ts +6 -0
- package/bundled-plugins/agent/src/main.tsx +11 -0
- package/bundled-plugins/agent/src/reducer.test.ts +359 -0
- package/bundled-plugins/agent/src/reducer.ts +255 -0
- package/bundled-plugins/agent/src/store.ts +379 -0
- package/bundled-plugins/agent/src/types.ts +98 -0
- package/bundled-plugins/agent/src/utils.test.ts +393 -0
- package/bundled-plugins/agent/src/utils.ts +158 -0
- package/bundled-plugins/agent/tsconfig.app.json +32 -0
- package/bundled-plugins/agent/tsconfig.json +13 -0
- package/bundled-plugins/agent/tsconfig.node.json +26 -0
- package/bundled-plugins/agent/vite.config.ts +14 -0
- package/bundled-plugins/agent/vitest.config.ts +17 -0
- package/bundled-plugins/terminal/README.md +7 -0
- package/bundled-plugins/terminal/index.html +24 -0
- package/bundled-plugins/terminal/package-lock.json +3346 -0
- package/bundled-plugins/terminal/package.json +38 -0
- package/bundled-plugins/terminal/server.ts +265 -0
- package/bundled-plugins/terminal/src/App.tsx +153 -0
- package/bundled-plugins/terminal/src/TERMINAL_SPEC.md +404 -0
- package/bundled-plugins/terminal/src/main.tsx +9 -0
- package/bundled-plugins/terminal/src/store.ts +114 -0
- package/bundled-plugins/terminal/tsconfig.json +22 -0
- package/bundled-plugins/terminal/vite.config.ts +10 -0
- package/dist/src/plugin-manager.js +1 -1
- package/dist/src/plugin-manager.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,379 @@
|
|
|
1
|
+
import { create } from 'zustand'
|
|
2
|
+
import type {
|
|
3
|
+
ContentBlock,
|
|
4
|
+
AgentMessage,
|
|
5
|
+
ActiveToolCall,
|
|
6
|
+
SessionState,
|
|
7
|
+
SessionStats,
|
|
8
|
+
AvailableModel,
|
|
9
|
+
ConnectionStatus,
|
|
10
|
+
ToolResultMessage,
|
|
11
|
+
Session,
|
|
12
|
+
} from './types'
|
|
13
|
+
|
|
14
|
+
interface AgentStore {
|
|
15
|
+
// Connection state
|
|
16
|
+
status: ConnectionStatus
|
|
17
|
+
error: string | null
|
|
18
|
+
currentCwd: string | null
|
|
19
|
+
|
|
20
|
+
// Session state
|
|
21
|
+
sessionState: SessionState | null
|
|
22
|
+
sessionStats: SessionStats | null
|
|
23
|
+
availableModels: AvailableModel[]
|
|
24
|
+
sessions: Session[]
|
|
25
|
+
|
|
26
|
+
// Messages
|
|
27
|
+
messages: AgentMessage[]
|
|
28
|
+
isStreaming: boolean
|
|
29
|
+
streamingMessageIndex: number | null
|
|
30
|
+
activeToolCalls: Map<string, ActiveToolCall>
|
|
31
|
+
|
|
32
|
+
// Actions
|
|
33
|
+
connect: () => void
|
|
34
|
+
disconnect: () => void
|
|
35
|
+
send: (data: unknown) => void
|
|
36
|
+
sendPrompt: (message: string) => void
|
|
37
|
+
abort: () => void
|
|
38
|
+
changeCwd: (path: string) => void
|
|
39
|
+
setError: (error: string | null) => void
|
|
40
|
+
|
|
41
|
+
// Commands
|
|
42
|
+
getState: () => void
|
|
43
|
+
getMessages: () => void
|
|
44
|
+
getAvailableModels: () => void
|
|
45
|
+
getSessionStats: () => void
|
|
46
|
+
getSessions: () => void
|
|
47
|
+
setModel: (provider: string, modelId: string) => void
|
|
48
|
+
setThinkingLevel: (level: string) => void
|
|
49
|
+
setAutoCompaction: (enabled: boolean) => void
|
|
50
|
+
compact: () => void
|
|
51
|
+
newSession: () => void
|
|
52
|
+
switchSession: (sessionId: string) => void
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// WebSocket singleton - lives outside React
|
|
56
|
+
let ws: WebSocket | null = null
|
|
57
|
+
let reconnectTimeout: number | null = null
|
|
58
|
+
let reconnectAttempts = 0
|
|
59
|
+
|
|
60
|
+
export const useAgentStore = create<AgentStore>((set, get) => ({
|
|
61
|
+
// Initial state
|
|
62
|
+
status: 'connecting',
|
|
63
|
+
error: null,
|
|
64
|
+
currentCwd: null,
|
|
65
|
+
sessionState: null,
|
|
66
|
+
sessionStats: null,
|
|
67
|
+
availableModels: [],
|
|
68
|
+
sessions: [],
|
|
69
|
+
messages: [],
|
|
70
|
+
isStreaming: false,
|
|
71
|
+
streamingMessageIndex: null,
|
|
72
|
+
activeToolCalls: new Map(),
|
|
73
|
+
|
|
74
|
+
// Connect to WebSocket
|
|
75
|
+
connect: () => {
|
|
76
|
+
// Don't reconnect if already open or connecting
|
|
77
|
+
if (ws && (ws.readyState === WebSocket.OPEN || ws.readyState === WebSocket.CONNECTING)) return
|
|
78
|
+
|
|
79
|
+
if (ws) {
|
|
80
|
+
ws.onclose = null // Prevent reconnect on intentional close
|
|
81
|
+
ws.close()
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
set({ status: 'connecting' })
|
|
85
|
+
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'
|
|
86
|
+
ws = new WebSocket(`${protocol}//${window.location.host}/ws`)
|
|
87
|
+
|
|
88
|
+
ws.onopen = () => {
|
|
89
|
+
set({ status: 'connected', error: null })
|
|
90
|
+
reconnectAttempts = 0
|
|
91
|
+
ws?.send(JSON.stringify({ type: 'start' }))
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
ws.onclose = () => {
|
|
95
|
+
set({ status: 'disconnected' })
|
|
96
|
+
ws = null
|
|
97
|
+
const delay = Math.min(1000 * Math.pow(2, reconnectAttempts), 10000)
|
|
98
|
+
reconnectAttempts++
|
|
99
|
+
reconnectTimeout = window.setTimeout(() => {
|
|
100
|
+
get().connect()
|
|
101
|
+
}, delay)
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
ws.onerror = () => {
|
|
105
|
+
set({ status: 'error', error: 'WebSocket connection failed' })
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
ws.onmessage = (event) => {
|
|
109
|
+
try {
|
|
110
|
+
const data = JSON.parse(event.data)
|
|
111
|
+
handleMessage(data, set, get)
|
|
112
|
+
} catch (err) {
|
|
113
|
+
console.error('Parse error:', err)
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
},
|
|
117
|
+
|
|
118
|
+
disconnect: () => {
|
|
119
|
+
if (reconnectTimeout) {
|
|
120
|
+
clearTimeout(reconnectTimeout)
|
|
121
|
+
reconnectTimeout = null
|
|
122
|
+
}
|
|
123
|
+
if (ws) {
|
|
124
|
+
ws.onclose = null
|
|
125
|
+
ws.close()
|
|
126
|
+
ws = null
|
|
127
|
+
}
|
|
128
|
+
set({ status: 'disconnected' })
|
|
129
|
+
},
|
|
130
|
+
|
|
131
|
+
send: (data) => {
|
|
132
|
+
ws?.send(JSON.stringify(data))
|
|
133
|
+
},
|
|
134
|
+
|
|
135
|
+
sendPrompt: (message) => {
|
|
136
|
+
const state = get()
|
|
137
|
+
if (!message.trim() || !ws || state.sessionState?.isStreaming) return
|
|
138
|
+
|
|
139
|
+
// Optimistically add user message
|
|
140
|
+
set({
|
|
141
|
+
messages: [...state.messages, { role: 'user', content: [{ type: 'text', text: message }] }]
|
|
142
|
+
})
|
|
143
|
+
ws.send(JSON.stringify({ type: 'prompt', message }))
|
|
144
|
+
},
|
|
145
|
+
|
|
146
|
+
abort: () => {
|
|
147
|
+
ws?.send(JSON.stringify({ type: 'abort' }))
|
|
148
|
+
},
|
|
149
|
+
|
|
150
|
+
changeCwd: (path) => {
|
|
151
|
+
ws?.send(JSON.stringify({ type: 'change_cwd', path }))
|
|
152
|
+
},
|
|
153
|
+
|
|
154
|
+
setError: (error) => set({ error }),
|
|
155
|
+
|
|
156
|
+
// Commands
|
|
157
|
+
getState: () => {
|
|
158
|
+
ws?.send(JSON.stringify({ type: 'command', command: { type: 'get_state' } }))
|
|
159
|
+
},
|
|
160
|
+
|
|
161
|
+
getMessages: () => {
|
|
162
|
+
ws?.send(JSON.stringify({ type: 'command', command: { type: 'get_messages' } }))
|
|
163
|
+
},
|
|
164
|
+
|
|
165
|
+
getAvailableModels: () => {
|
|
166
|
+
ws?.send(JSON.stringify({ type: 'command', command: { type: 'get_available_models' } }))
|
|
167
|
+
},
|
|
168
|
+
|
|
169
|
+
getSessionStats: () => {
|
|
170
|
+
ws?.send(JSON.stringify({ type: 'command', command: { type: 'get_session_stats' } }))
|
|
171
|
+
},
|
|
172
|
+
|
|
173
|
+
getSessions: () => {
|
|
174
|
+
ws?.send(JSON.stringify({ type: 'command', command: { type: 'get_sessions' } }))
|
|
175
|
+
},
|
|
176
|
+
|
|
177
|
+
setModel: (provider, modelId) => {
|
|
178
|
+
ws?.send(JSON.stringify({ type: 'command', command: { type: 'set_model', provider, modelId } }))
|
|
179
|
+
},
|
|
180
|
+
|
|
181
|
+
setThinkingLevel: (level) => {
|
|
182
|
+
ws?.send(JSON.stringify({ type: 'command', command: { type: 'set_thinking_level', level } }))
|
|
183
|
+
},
|
|
184
|
+
|
|
185
|
+
setAutoCompaction: (enabled) => {
|
|
186
|
+
ws?.send(JSON.stringify({ type: 'command', command: { type: 'set_auto_compaction', enabled } }))
|
|
187
|
+
},
|
|
188
|
+
|
|
189
|
+
compact: () => {
|
|
190
|
+
ws?.send(JSON.stringify({ type: 'command', command: { type: 'compact' } }))
|
|
191
|
+
},
|
|
192
|
+
|
|
193
|
+
newSession: () => {
|
|
194
|
+
ws?.send(JSON.stringify({ type: 'command', command: { type: 'new_session' } }))
|
|
195
|
+
},
|
|
196
|
+
|
|
197
|
+
switchSession: (sessionPath) => {
|
|
198
|
+
ws?.send(JSON.stringify({ type: 'command', command: { type: 'switch_session', sessionPath } }))
|
|
199
|
+
},
|
|
200
|
+
}))
|
|
201
|
+
|
|
202
|
+
// Handle incoming WebSocket messages
|
|
203
|
+
function handleMessage(
|
|
204
|
+
data: { type: string; event?: Record<string, unknown>; message?: string; cwd?: string },
|
|
205
|
+
set: (partial: Partial<AgentStore>) => void,
|
|
206
|
+
get: () => AgentStore
|
|
207
|
+
) {
|
|
208
|
+
if (data.type === 'ready') {
|
|
209
|
+
set({ status: 'connected' })
|
|
210
|
+
if (data.cwd) set({ currentCwd: data.cwd })
|
|
211
|
+
} else if (data.type === 'cwd_changed') {
|
|
212
|
+
if (data.cwd) set({ currentCwd: data.cwd })
|
|
213
|
+
set({ messages: [], sessionStats: null, activeToolCalls: new Map() })
|
|
214
|
+
} else if (data.type === 'error') {
|
|
215
|
+
set({ error: data.message || 'Unknown error' })
|
|
216
|
+
} else if (data.type === 'rpc_event' && data.event) {
|
|
217
|
+
handleRpcEvent(data.event, set, get)
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// Handle RPC events from pi-agent
|
|
222
|
+
function handleRpcEvent(
|
|
223
|
+
event: Record<string, unknown>,
|
|
224
|
+
set: (partial: Partial<AgentStore>) => void,
|
|
225
|
+
get: () => AgentStore
|
|
226
|
+
) {
|
|
227
|
+
const eventType = event.type as string
|
|
228
|
+
|
|
229
|
+
// Response events
|
|
230
|
+
if (eventType === 'response') {
|
|
231
|
+
const command = event.command as string
|
|
232
|
+
if (command === 'get_state' && event.success) {
|
|
233
|
+
set({ sessionState: event.data as SessionState })
|
|
234
|
+
} else if (command === 'get_messages' && event.success) {
|
|
235
|
+
const data = event.data as { messages: AgentMessage[] }
|
|
236
|
+
console.log('[store] get_messages response:', data.messages?.length, 'messages', data)
|
|
237
|
+
set({ messages: data.messages || [] })
|
|
238
|
+
} else if (command === 'get_available_models' && event.success) {
|
|
239
|
+
const data = event.data as { models: AvailableModel[] }
|
|
240
|
+
set({ availableModels: data.models || [] })
|
|
241
|
+
} else if (command === 'get_session_stats' && event.success) {
|
|
242
|
+
set({ sessionStats: event.data as SessionStats })
|
|
243
|
+
} else if (command === 'set_model' && event.success) {
|
|
244
|
+
get().getState()
|
|
245
|
+
} else if (command === 'set_thinking_level' && event.success) {
|
|
246
|
+
get().getState()
|
|
247
|
+
} else if (command === 'set_auto_compaction' && event.success) {
|
|
248
|
+
get().getState()
|
|
249
|
+
} else if (command === 'compact' && event.success) {
|
|
250
|
+
get().getState()
|
|
251
|
+
get().getSessionStats()
|
|
252
|
+
} else if (command === 'new_session' && event.success) {
|
|
253
|
+
set({ messages: [], sessionStats: null })
|
|
254
|
+
get().getState()
|
|
255
|
+
get().getSessions() // Refresh sessions list
|
|
256
|
+
} else if (command === 'get_sessions' && event.success) {
|
|
257
|
+
const data = event.data as { sessions: Session[] }
|
|
258
|
+
set({ sessions: data.sessions || [] })
|
|
259
|
+
} else if (command === 'switch_session' && event.success) {
|
|
260
|
+
// Session switched - clear current state and fetch new messages
|
|
261
|
+
set({ messages: [], sessionStats: null, activeToolCalls: new Map() })
|
|
262
|
+
get().getState()
|
|
263
|
+
get().getMessages()
|
|
264
|
+
get().getSessionStats()
|
|
265
|
+
get().getSessions() // Refresh to update current session indicator
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// Agent lifecycle events
|
|
270
|
+
else if (eventType === 'agent_start') {
|
|
271
|
+
set({ isStreaming: true, activeToolCalls: new Map() })
|
|
272
|
+
}
|
|
273
|
+
else if (eventType === 'agent_end') {
|
|
274
|
+
set({ isStreaming: false, activeToolCalls: new Map(), streamingMessageIndex: null })
|
|
275
|
+
get().getState()
|
|
276
|
+
get().getSessionStats()
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// Message events
|
|
280
|
+
else if (eventType === 'message_start') {
|
|
281
|
+
const msg = event.message as AgentMessage
|
|
282
|
+
console.log('[store] message_start:', msg?.role, msg)
|
|
283
|
+
if (msg) {
|
|
284
|
+
// Skip user messages - we add them optimistically in sendPrompt
|
|
285
|
+
if (msg.role === 'user') {
|
|
286
|
+
return
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
const messages = get().messages
|
|
290
|
+
const newMessages = [...messages, msg]
|
|
291
|
+
|
|
292
|
+
const updates: Partial<AgentStore> = { messages: newMessages }
|
|
293
|
+
|
|
294
|
+
if (msg.role === 'assistant') {
|
|
295
|
+
updates.streamingMessageIndex = newMessages.length - 1
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
if (msg.role === 'toolResult') {
|
|
299
|
+
const toolCallId = (msg as ToolResultMessage).toolCallId
|
|
300
|
+
const activeToolCalls = new Map(get().activeToolCalls)
|
|
301
|
+
activeToolCalls.delete(toolCallId)
|
|
302
|
+
updates.activeToolCalls = activeToolCalls
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
set(updates)
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
else if (eventType === 'message_update') {
|
|
309
|
+
const msg = event.message as AgentMessage
|
|
310
|
+
if (msg) {
|
|
311
|
+
const messages = get().messages
|
|
312
|
+
if (messages.length === 0) {
|
|
313
|
+
set({ messages: [msg] })
|
|
314
|
+
} else {
|
|
315
|
+
const updated = [...messages]
|
|
316
|
+
updated[updated.length - 1] = msg
|
|
317
|
+
set({ messages: updated })
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
else if (eventType === 'message_end') {
|
|
322
|
+
set({ streamingMessageIndex: null })
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
// Tool execution events
|
|
326
|
+
else if (eventType === 'tool_execution_start') {
|
|
327
|
+
const toolCallId = event.toolCallId as string
|
|
328
|
+
const activeToolCalls = new Map(get().activeToolCalls)
|
|
329
|
+
activeToolCalls.set(toolCallId, {
|
|
330
|
+
toolCallId,
|
|
331
|
+
toolName: event.toolName as string,
|
|
332
|
+
args: (event.args as Record<string, unknown>) || {},
|
|
333
|
+
status: 'running',
|
|
334
|
+
partialOutput: null,
|
|
335
|
+
})
|
|
336
|
+
set({ activeToolCalls })
|
|
337
|
+
}
|
|
338
|
+
else if (eventType === 'tool_execution_update') {
|
|
339
|
+
const toolCallId = event.toolCallId as string
|
|
340
|
+
const partialResult = event.partialResult as { content?: ContentBlock[] } | undefined
|
|
341
|
+
const activeToolCalls = new Map(get().activeToolCalls)
|
|
342
|
+
const existing = activeToolCalls.get(toolCallId)
|
|
343
|
+
if (existing) {
|
|
344
|
+
const text = partialResult?.content?.find((c): c is { type: 'text'; text: string } => c.type === 'text')?.text
|
|
345
|
+
activeToolCalls.set(toolCallId, { ...existing, partialOutput: text || null })
|
|
346
|
+
set({ activeToolCalls })
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
else if (eventType === 'tool_execution_end') {
|
|
350
|
+
const toolCallId = event.toolCallId as string
|
|
351
|
+
const result = event.result as { content?: ContentBlock[] } | undefined
|
|
352
|
+
const activeToolCalls = new Map(get().activeToolCalls)
|
|
353
|
+
const existing = activeToolCalls.get(toolCallId)
|
|
354
|
+
if (existing) {
|
|
355
|
+
const text = result?.content?.find((c): c is { type: 'text'; text: string } => c.type === 'text')?.text
|
|
356
|
+
activeToolCalls.set(toolCallId, {
|
|
357
|
+
...existing,
|
|
358
|
+
status: event.isError ? 'error' : 'done',
|
|
359
|
+
partialOutput: text || null,
|
|
360
|
+
})
|
|
361
|
+
set({ activeToolCalls })
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
// Reconnect on visibility change
|
|
367
|
+
if (typeof document !== 'undefined') {
|
|
368
|
+
document.addEventListener('visibilitychange', () => {
|
|
369
|
+
const { status, connect } = useAgentStore.getState()
|
|
370
|
+
if (document.visibilityState === 'visible' && (status === 'disconnected' || status === 'error')) {
|
|
371
|
+
if (reconnectTimeout) {
|
|
372
|
+
clearTimeout(reconnectTimeout)
|
|
373
|
+
reconnectTimeout = null
|
|
374
|
+
}
|
|
375
|
+
reconnectAttempts = 0
|
|
376
|
+
connect()
|
|
377
|
+
}
|
|
378
|
+
})
|
|
379
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
// =============================================================================
|
|
2
|
+
// Types - Following AGENT_SPEC.md
|
|
3
|
+
// =============================================================================
|
|
4
|
+
|
|
5
|
+
export type ContentBlock =
|
|
6
|
+
| { type: 'text'; text: string }
|
|
7
|
+
| { type: 'thinking'; thinking: string }
|
|
8
|
+
| { type: 'toolCall'; id: string; name: string; arguments: Record<string, unknown> }
|
|
9
|
+
| { type: 'image'; data: string; mimeType: string }
|
|
10
|
+
|
|
11
|
+
export interface UserMessage {
|
|
12
|
+
role: 'user'
|
|
13
|
+
content: ContentBlock[]
|
|
14
|
+
timestamp?: number
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface AssistantMessage {
|
|
18
|
+
role: 'assistant'
|
|
19
|
+
content: ContentBlock[]
|
|
20
|
+
model?: string
|
|
21
|
+
stopReason?: string
|
|
22
|
+
timestamp?: number
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface ToolResultMessage {
|
|
26
|
+
role: 'toolResult'
|
|
27
|
+
toolCallId: string
|
|
28
|
+
toolName: string
|
|
29
|
+
content: ContentBlock[]
|
|
30
|
+
isError?: boolean
|
|
31
|
+
timestamp?: number
|
|
32
|
+
details?: ToolResultDetails
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Tool-specific details
|
|
36
|
+
export type ToolResultDetails = EditToolDetails | BashToolDetails | unknown
|
|
37
|
+
|
|
38
|
+
export interface EditToolDetails {
|
|
39
|
+
diff: string
|
|
40
|
+
firstChangedLine?: number
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface BashToolDetails {
|
|
44
|
+
exitCode?: number
|
|
45
|
+
truncated?: boolean
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export type AgentMessage = UserMessage | AssistantMessage | ToolResultMessage
|
|
49
|
+
|
|
50
|
+
export interface ActiveToolCall {
|
|
51
|
+
toolCallId: string
|
|
52
|
+
toolName: string
|
|
53
|
+
args: Record<string, unknown>
|
|
54
|
+
status: 'running' | 'done' | 'error'
|
|
55
|
+
partialOutput: string | null
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export interface SessionState {
|
|
59
|
+
model?: { provider: string; id: string; name?: string; reasoning?: boolean }
|
|
60
|
+
thinkingLevel?: string
|
|
61
|
+
isStreaming: boolean
|
|
62
|
+
isCompacting?: boolean
|
|
63
|
+
autoCompactionEnabled?: boolean
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export interface SessionStats {
|
|
67
|
+
tokens?: { input: number; output: number; cacheRead?: number; cacheWrite?: number; total: number }
|
|
68
|
+
cost?: number
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export interface AvailableModel {
|
|
72
|
+
provider: string
|
|
73
|
+
id: string
|
|
74
|
+
name?: string
|
|
75
|
+
reasoning?: boolean
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export type ConnectionStatus = 'connecting' | 'connected' | 'disconnected' | 'error'
|
|
79
|
+
|
|
80
|
+
export interface Session {
|
|
81
|
+
id: string
|
|
82
|
+
name?: string | null
|
|
83
|
+
file?: string
|
|
84
|
+
messageCount?: number
|
|
85
|
+
created?: number | null
|
|
86
|
+
lastModified?: number
|
|
87
|
+
isCurrent?: boolean
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// =============================================================================
|
|
91
|
+
// Render Item Types - For splitting assistant messages
|
|
92
|
+
// =============================================================================
|
|
93
|
+
|
|
94
|
+
export type RenderItem =
|
|
95
|
+
| { type: 'user-message'; content: ContentBlock[] }
|
|
96
|
+
| { type: 'text-chunk'; blocks: ContentBlock[]; isStreaming: boolean }
|
|
97
|
+
| { type: 'tool-call'; id: string; name: string; args: Record<string, unknown> }
|
|
98
|
+
| { type: 'tool-result'; toolCallId: string; toolName: string; command?: string; output: string; isError: boolean; diff?: string; filePath?: string }
|