agentgui 1.0.817 → 1.0.819

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/CHANGELOG.md CHANGED
@@ -1,4 +1,5 @@
1
1
  ## 2026-04-11
2
+ - refactor: split jsonl-watcher.js parse/event logic into jsonl-parser.js; watcher retains file watching/polling only
2
3
  - refactor: split tool-version.js into tool-version-check.js (sync) and tool-version-fetch.js (async/network)
3
4
 
4
5
  ## [Unreleased]
package/CLAUDE.md CHANGED
@@ -32,7 +32,8 @@ lib/codec.js msgpack encode/decode (pack/unpack wrappers)
32
32
  lib/db-queries.js All 88 query functions (createQueries factory, extracted from database.js)
33
33
  lib/execution-machine.js XState v5 machine per conversation: idle/streaming/draining/rate_limited states
34
34
  lib/gm-agent-configs.js GM agent configuration and spawning
35
- lib/jsonl-watcher.js Watches ~/.claude/projects for JSONL file changes
35
+ lib/jsonl-parser.js JSONL event parsing, session tracking, streaming state (extracted from jsonl-watcher.js)
36
+ lib/jsonl-watcher.js Watches ~/.claude/projects for JSONL file changes, delegates parsing to jsonl-parser.js
36
37
  lib/oauth-common.js Shared OAuth helpers (buildBaseUrl, isRemoteRequest, encodeOAuthState, result/relay pages)
37
38
  lib/oauth-gemini.js Gemini OAuth flow (credential discovery, token exchange, callback handling)
38
39
  lib/oauth-codex.js Codex CLI OAuth flow (PKCE S256, token exchange, callback handling)
@@ -42,8 +43,9 @@ lib/pm2-manager.js PM2 process management wrapper
42
43
  lib/speech.js Speech-to-text and text-to-speech via @huggingface/transformers
43
44
  lib/speech-manager.js TTS orchestration (eager TTS, voice cache, model download, broadcastModelProgress)
44
45
  lib/tool-install-machine.js XState v5 machine per tool: unchecked/checking/idle/installing/installed/updating/needs_update/failed states
45
- lib/tool-manager.js Tool facade - re-exports from tool-version, tool-spawner, tool-provisioner
46
- lib/tool-version.js Version detection for CLI tools and plugins (data-driven framework paths)
46
+ lib/tool-manager.js Tool facade - re-exports from tool-version-check, tool-version-fetch, tool-spawner, tool-provisioner
47
+ lib/tool-version-check.js Sync/local version detection: BIN_MAP, FRAMEWORK_PATHS, checkCliInstalled, getCliVersion, checkToolInstalled, compareVersions, getInstalledVersion
48
+ lib/tool-version-fetch.js Async/network version functions: getPublishedVersion, fetchPublishedVersion, clearVersionCache, checkToolViaBunx
47
49
  lib/tool-spawner.js npm/bun install/update spawn with timeout and heartbeat
48
50
  lib/tool-provisioner.js Auto-provisioning and periodic update checking
49
51
  lib/routes-speech.js Speech/TTS HTTP route handlers (stt, tts, voices, speech-status)
@@ -237,9 +239,9 @@ Current tools:
237
239
  - `cli-agent-browser`: bin=`agent-browser`, pkg=`agent-browser` — uses `-V` flag (not `--version`) for version detection
238
240
  - `gm-cc`, `gm-oc`, `gm-gc`, `gm-kilo`, `gm-codex`: plugin tools
239
241
 
240
- **BIN_MAP gotcha:** `lib/tool-version.js` has a single `BIN_MAP` constant shared by `checkCliInstalled()` and `getCliVersion()`. Any new CLI tool must be added there. `agent-browser` uses `-V` (not `--version`) — a `versionFlag` override handles this.
242
+ **BIN_MAP gotcha:** `lib/tool-version-check.js` has a single `BIN_MAP` constant shared by `checkCliInstalled()` and `getCliVersion()`. Any new CLI tool must be added there. `agent-browser` uses `-V` (not `--version`) — a `versionFlag` override handles this.
241
243
 
242
- **Framework paths:** `lib/tool-version.js` uses a `FRAMEWORK_PATHS` data table instead of per-framework if/else chains. Each framework entry defines pluginDir, versionFile, parseVersion, and optional markerFile/fallbackInstalled. Adding a new framework means adding one entry to this table.
244
+ **Framework paths:** `lib/tool-version-check.js` uses a `FRAMEWORK_PATHS` data table instead of per-framework if/else chains. Each framework entry defines pluginDir, versionFile, parseVersion, and optional markerFile/fallbackInstalled. Adding a new framework means adding one entry to this table.
243
245
 
244
246
  **Background provisioning:** `autoProvision()` runs at startup, checks/installs missing tools (~10s). `startPeriodicUpdateCheck()` runs every 6 hours in background to check for updates. Both broadcast tool status via WebSocket so UI stays in sync.
245
247
 
@@ -0,0 +1,179 @@
1
+ import path from 'path';
2
+
3
+ export class JsonlParser {
4
+ constructor({ broadcastSync, queries, ownedSessionIds }) {
5
+ this._bc = broadcastSync;
6
+ this._q = queries;
7
+ this._owned = ownedSessionIds;
8
+ this._convMap = new Map();
9
+ this._emitted = new Map();
10
+ this._seqs = new Map();
11
+ this._streaming = new Set();
12
+ this._sessions = new Map();
13
+ }
14
+
15
+ clear() {
16
+ this._convMap.clear();
17
+ this._emitted.clear();
18
+ this._seqs.clear();
19
+ this._streaming.clear();
20
+ this._sessions.clear();
21
+ }
22
+
23
+ removeSid(sid) {
24
+ this._convMap.delete(sid);
25
+ this._seqs.delete(sid);
26
+ this._streaming.delete(sid);
27
+ this._sessions.delete(sid);
28
+ for (const key of [...this._emitted.keys()]) if (key.startsWith(`${sid}:`)) this._emitted.delete(key);
29
+ }
30
+
31
+ endAllStreaming() {
32
+ for (const sid of [...this._streaming]) this._endStreaming(this._convMap.get(sid), sid);
33
+ }
34
+
35
+ _line(fp, line) {
36
+ line = line.trim(); if (!line) return;
37
+ let e; try { e = JSON.parse(line); } catch (_) { return; }
38
+ if (!e || !e.sessionId) return;
39
+ if (this._owned?.has(e.sessionId)) return;
40
+ const cid = this._conv(e.sessionId, e, fp);
41
+ if (cid) this._route(cid, e.sessionId, e);
42
+ }
43
+
44
+ _conv(sid, e) {
45
+ if (this._convMap.has(sid)) return this._convMap.get(sid);
46
+ const found = this._q.getConversations().find(c => c.claudeSessionId === sid);
47
+ if (found) { this._convMap.set(sid, found.id); return found.id; }
48
+ if (e.type === 'queue-operation' || e.type === 'last-prompt') return null;
49
+ if (e.type === 'user' && e.isMeta) return null;
50
+ const cwd = e.cwd || process.cwd();
51
+ const branch = e.gitBranch || '';
52
+ const base = path.basename(cwd);
53
+ const title = branch ? `${branch} @ ${base}` : base;
54
+ const conv = this._q.createConversation('claude-code', title, cwd);
55
+ this._q.setClaudeSessionId(conv.id, sid);
56
+ this._convMap.set(sid, conv.id);
57
+ this._bc({ type: 'conversation_created', conversation: conv, timestamp: Date.now() });
58
+ return conv.id;
59
+ }
60
+
61
+ _seq(sid) { const n = (this._seqs.get(sid) || 0) + 1; this._seqs.set(sid, n); return n; }
62
+
63
+ _dbSession(cid, sid) {
64
+ if (this._sessions.has(sid)) return this._sessions.get(sid);
65
+ const sess = this._q.createSession(cid);
66
+ this._q.updateSession(sess.id, { status: 'active' });
67
+ this._sessions.set(sid, sess.id);
68
+ return sess.id;
69
+ }
70
+
71
+ _emit(cid, sid, block, role, extra) {
72
+ const dbSid = this._dbSession(cid, sid);
73
+ const seq = this._seq(sid);
74
+ try { this._q.createChunk(dbSid, cid, seq, block.type || 'unknown', block); } catch (_) {}
75
+ this._bc({ type: 'streaming_progress', sessionId: dbSid, conversationId: cid, block, blockRole: role, seq, timestamp: Date.now(), ...extra });
76
+ }
77
+
78
+ _emitBlocks(cid, sid, blocks, role) {
79
+ for (const b of blocks) {
80
+ if (!b || !b.type) continue;
81
+ this._emit(cid, sid, b, role);
82
+ }
83
+ }
84
+
85
+ _startStreaming(cid, sid) {
86
+ if (this._streaming.has(sid)) return;
87
+ this._streaming.add(sid);
88
+ const dbSid = this._dbSession(cid, sid);
89
+ this._q.setIsStreaming(cid, true);
90
+ this._bc({ type: 'streaming_start', sessionId: dbSid, conversationId: cid, agentId: 'cli-claude', timestamp: Date.now() });
91
+ }
92
+
93
+ _endStreaming(cid, sid) {
94
+ if (!this._streaming.has(sid)) return;
95
+ this._streaming.delete(sid);
96
+ const dbSid = this._sessions.get(sid);
97
+ if (dbSid) {
98
+ try { this._q.updateSession(dbSid, { status: 'completed', completed_at: Date.now() }); } catch (_) {}
99
+ }
100
+ this._q.setIsStreaming(cid, false);
101
+ this._bc({ type: 'streaming_complete', sessionId: dbSid || sid, conversationId: cid, agentId: 'cli-claude', eventCount: 0, seq: this._seq(sid), timestamp: Date.now() });
102
+ this._sessions.delete(sid);
103
+ }
104
+
105
+ _route(cid, sid, e) {
106
+ if (e.type === 'queue-operation' || e.type === 'last-prompt' || (e.type === 'user' && e.isMeta)) return;
107
+
108
+ if (e.isApiErrorMessage && e.error === 'rate_limit') {
109
+ this._bc({ type: 'streaming_error', sessionId: this._sessions.get(sid) || sid, conversationId: cid, error: 'Rate limit hit', recoverable: true, timestamp: Date.now() });
110
+ return;
111
+ }
112
+
113
+ if (e.type === 'system') {
114
+ if (e.subtype === 'init') { this._startStreaming(cid, sid); return; }
115
+ if (e.subtype === 'turn_duration' || e.subtype === 'stop_hook_summary') { this._endStreaming(cid, sid); return; }
116
+ this._startStreaming(cid, sid);
117
+ this._emit(cid, sid, { type: 'system', subtype: e.subtype, model: e.model, cwd: e.cwd, tools: e.tools, preTokens: e.compactMetadata?.preTokens }, 'system');
118
+ return;
119
+ }
120
+
121
+ if (e.type === 'assistant' && e.message?.content) {
122
+ this._startStreaming(cid, sid);
123
+ const key = `${sid}:${e.message.id}`;
124
+ const prev = this._emitted.get(key) || 0;
125
+ const content = e.message.content;
126
+ const newBlocks = content.slice(prev);
127
+ if (newBlocks.length > 0) {
128
+ this._emitted.set(key, content.length);
129
+ this._emitBlocks(cid, sid, newBlocks, 'assistant');
130
+ }
131
+ if (e.message.stop_reason) this._emitted.delete(key);
132
+ return;
133
+ }
134
+
135
+ if (e.type === 'user' && e.message?.content) {
136
+ if (e.isCompactSummary) { this._emit(cid, sid, { type: 'compact_summary', content: e.message.content }, 'system'); return; }
137
+ this._startStreaming(cid, sid);
138
+ const content = e.message.content;
139
+ const textParts = Array.isArray(content)
140
+ ? content.filter(b => b.type === 'text' && b.text && !b.text.startsWith('<task-notification>')).map(b => b.text).join('\n')
141
+ : (typeof content === 'string' && !content.startsWith('<task-notification>') ? content : '');
142
+ if (textParts) {
143
+ try { this._q.createMessage(cid, 'user', textParts); } catch (_) {}
144
+ }
145
+ const blocks = Array.isArray(content) ? content : [];
146
+ for (const b of blocks) if (b.type === 'tool_result') this._emit(cid, sid, b, 'tool_result');
147
+ return;
148
+ }
149
+
150
+ if (e.type === 'progress') {
151
+ this._startStreaming(cid, sid);
152
+ const d = e.data || {};
153
+ if (d.type === 'agent_progress') {
154
+ const inner = d.message?.message || d.message;
155
+ const content = inner?.content;
156
+ if (Array.isArray(content)) {
157
+ const role = inner.role === 'user' ? 'tool_result' : 'assistant';
158
+ this._emitBlocks(cid, sid, content, role);
159
+ }
160
+ return;
161
+ }
162
+ if (d.type === 'hook_progress') {
163
+ this._emit(cid, sid, { type: 'hook_progress', hookEvent: d.hookEvent, hookName: d.hookName }, 'system');
164
+ return;
165
+ }
166
+ if (d.type === 'mcp_progress') {
167
+ this._emit(cid, sid, { type: 'mcp_progress', status: d.status, serverName: d.serverName, toolName: d.toolName }, 'system');
168
+ return;
169
+ }
170
+ this._emit(cid, sid, { type: d.type || e.subtype || 'progress', content: e.content || d }, 'progress');
171
+ return;
172
+ }
173
+
174
+ if (e.type === 'result') {
175
+ this._emit(cid, sid, { type: 'result', result: e.result, subtype: e.subtype, duration_ms: e.duration_ms, total_cost_usd: e.total_cost_usd, is_error: e.is_error || false }, 'result', { isResult: true });
176
+ this._endStreaming(cid, sid);
177
+ }
178
+ }
179
+ }
@@ -1,22 +1,16 @@
1
1
  import fs from 'fs';
2
2
  import path from 'path';
3
3
  import os from 'os';
4
+ import { JsonlParser } from './jsonl-parser.js';
4
5
 
5
6
  const PROJECTS_DIR = path.join(os.homedir(), '.claude', 'projects');
6
7
  const DEBOUNCE_MS = 16;
7
8
 
8
9
  export class JsonlWatcher {
9
10
  constructor({ broadcastSync, queries, ownedSessionIds }) {
10
- this._bc = broadcastSync;
11
- this._q = queries;
12
- this._owned = ownedSessionIds;
11
+ this._parser = new JsonlParser({ broadcastSync, queries, ownedSessionIds });
13
12
  this._tails = new Map();
14
- this._convMap = new Map();
15
- this._emitted = new Map();
16
13
  this._timers = new Map();
17
- this._seqs = new Map();
18
- this._streaming = new Set();
19
- this._sessions = new Map();
20
14
  this._watcher = null;
21
15
  }
22
16
 
@@ -24,7 +18,7 @@ export class JsonlWatcher {
24
18
  if (!fs.existsSync(PROJECTS_DIR)) return;
25
19
  this._scanDir(PROJECTS_DIR, 0);
26
20
  for (const [fp, t] of this._timers.entries()) { clearTimeout(t); this._timers.delete(fp); this._read(fp); }
27
- for (const sid of [...this._streaming]) this._endStreaming(this._convMap.get(sid), sid);
21
+ this._parser.endAllStreaming();
28
22
  try {
29
23
  this._watcher = fs.watch(PROJECTS_DIR, { recursive: true }, (_, f) => {
30
24
  if (f && f.endsWith('.jsonl')) this._debounce(path.join(PROJECTS_DIR, f));
@@ -40,13 +34,9 @@ export class JsonlWatcher {
40
34
  }
41
35
 
42
36
  removeConversation(conversationId) {
43
- const sids = [...this._convMap.entries()].filter(([, cid]) => cid === conversationId).map(([sid]) => sid);
37
+ const sids = [...this._parser._convMap.entries()].filter(([, cid]) => cid === conversationId).map(([sid]) => sid);
44
38
  for (const sid of sids) {
45
- this._convMap.delete(sid);
46
- this._seqs.delete(sid);
47
- this._streaming.delete(sid);
48
- this._sessions.delete(sid);
49
- for (const key of [...this._emitted.keys()]) if (key.startsWith(`${sid}:`)) this._emitted.delete(key);
39
+ this._parser.removeSid(sid);
50
40
  for (const [fp, s] of this._tails.entries()) {
51
41
  if (!fp.includes(sid)) continue;
52
42
  if (s.fd !== null) try { fs.closeSync(s.fd); } catch (_) {}
@@ -60,9 +50,9 @@ export class JsonlWatcher {
60
50
  removeAllConversations() {
61
51
  for (const s of this._tails.values()) if (s.fd !== null) try { fs.closeSync(s.fd); } catch (_) {}
62
52
  for (const t of this._timers.values()) clearTimeout(t);
63
- this._tails.clear(); this._convMap.clear(); this._emitted.clear();
64
- this._timers.clear(); this._seqs.clear(); this._streaming.clear();
65
- this._sessions.clear();
53
+ this._tails.clear();
54
+ this._timers.clear();
55
+ this._parser.clear();
66
56
  }
67
57
 
68
58
  _scanDir(dir, depth) {
@@ -99,155 +89,10 @@ export class JsonlWatcher {
99
89
  s.partial = text.slice(start);
100
90
  const ms = Number(process.hrtime.bigint() - t0) / 1e6;
101
91
  if (ms > 5) console.warn(`[JsonlWatcher] hot path ${ms.toFixed(1)}ms (${lines.length} lines)`);
102
- for (const l of lines) this._line(fp, l);
92
+ for (const l of lines) this._parser._line(fp, l);
103
93
  } catch (e) {
104
94
  if (e.code !== 'ENOENT') console.error('[JsonlWatcher] read error:', e.message);
105
95
  if (s.fd !== null) { try { fs.closeSync(s.fd); } catch (_) {} s.fd = null; }
106
96
  }
107
97
  }
108
-
109
- _line(fp, line) {
110
- line = line.trim(); if (!line) return;
111
- let e; try { e = JSON.parse(line); } catch (_) { return; }
112
- if (!e || !e.sessionId) return;
113
- if (this._owned?.has(e.sessionId)) return;
114
- const cid = this._conv(e.sessionId, e, fp);
115
- if (cid) this._route(cid, e.sessionId, e);
116
- }
117
-
118
- _conv(sid, e) {
119
- if (this._convMap.has(sid)) return this._convMap.get(sid);
120
- const found = this._q.getConversations().find(c => c.claudeSessionId === sid);
121
- if (found) { this._convMap.set(sid, found.id); return found.id; }
122
- if (e.type === 'queue-operation' || e.type === 'last-prompt') return null;
123
- if (e.type === 'user' && e.isMeta) return null;
124
- const cwd = e.cwd || process.cwd();
125
- const branch = e.gitBranch || '';
126
- const base = path.basename(cwd);
127
- const title = branch ? `${branch} @ ${base}` : base;
128
- const conv = this._q.createConversation('claude-code', title, cwd);
129
- this._q.setClaudeSessionId(conv.id, sid);
130
- this._convMap.set(sid, conv.id);
131
- this._bc({ type: 'conversation_created', conversation: conv, timestamp: Date.now() });
132
- return conv.id;
133
- }
134
-
135
- _seq(sid) { const n = (this._seqs.get(sid) || 0) + 1; this._seqs.set(sid, n); return n; }
136
-
137
- _dbSession(cid, sid) {
138
- if (this._sessions.has(sid)) return this._sessions.get(sid);
139
- const sess = this._q.createSession(cid);
140
- this._q.updateSession(sess.id, { status: 'active' });
141
- this._sessions.set(sid, sess.id);
142
- return sess.id;
143
- }
144
-
145
- _emit(cid, sid, block, role, extra) {
146
- const dbSid = this._dbSession(cid, sid);
147
- const seq = this._seq(sid);
148
- try { this._q.createChunk(dbSid, cid, seq, block.type || 'unknown', block); } catch (_) {}
149
- this._bc({ type: 'streaming_progress', sessionId: dbSid, conversationId: cid, block, blockRole: role, seq, timestamp: Date.now(), ...extra });
150
- }
151
-
152
- _emitBlocks(cid, sid, blocks, role) {
153
- for (const b of blocks) {
154
- if (!b || !b.type) continue;
155
- this._emit(cid, sid, b, role);
156
- }
157
- }
158
-
159
- _startStreaming(cid, sid) {
160
- if (this._streaming.has(sid)) return;
161
- this._streaming.add(sid);
162
- const dbSid = this._dbSession(cid, sid);
163
- this._q.setIsStreaming(cid, true);
164
- this._bc({ type: 'streaming_start', sessionId: dbSid, conversationId: cid, agentId: 'cli-claude', timestamp: Date.now() });
165
- }
166
-
167
- _endStreaming(cid, sid) {
168
- if (!this._streaming.has(sid)) return;
169
- this._streaming.delete(sid);
170
- const dbSid = this._sessions.get(sid);
171
- if (dbSid) {
172
- try { this._q.updateSession(dbSid, { status: 'completed', completed_at: Date.now() }); } catch (_) {}
173
- }
174
- this._q.setIsStreaming(cid, false);
175
- this._bc({ type: 'streaming_complete', sessionId: dbSid || sid, conversationId: cid, agentId: 'cli-claude', eventCount: 0, seq: this._seq(sid), timestamp: Date.now() });
176
- this._sessions.delete(sid);
177
- }
178
-
179
- _route(cid, sid, e) {
180
- if (e.type === 'queue-operation' || e.type === 'last-prompt' || (e.type === 'user' && e.isMeta)) return;
181
-
182
- if (e.isApiErrorMessage && e.error === 'rate_limit') {
183
- this._bc({ type: 'streaming_error', sessionId: this._sessions.get(sid) || sid, conversationId: cid, error: 'Rate limit hit', recoverable: true, timestamp: Date.now() });
184
- return;
185
- }
186
-
187
- if (e.type === 'system') {
188
- if (e.subtype === 'init') { this._startStreaming(cid, sid); return; }
189
- if (e.subtype === 'turn_duration' || e.subtype === 'stop_hook_summary') { this._endStreaming(cid, sid); return; }
190
- this._startStreaming(cid, sid);
191
- this._emit(cid, sid, { type: 'system', subtype: e.subtype, model: e.model, cwd: e.cwd, tools: e.tools, preTokens: e.compactMetadata?.preTokens }, 'system');
192
- return;
193
- }
194
-
195
- if (e.type === 'assistant' && e.message?.content) {
196
- this._startStreaming(cid, sid);
197
- const key = `${sid}:${e.message.id}`;
198
- const prev = this._emitted.get(key) || 0;
199
- const content = e.message.content;
200
- const newBlocks = content.slice(prev);
201
- if (newBlocks.length > 0) {
202
- this._emitted.set(key, content.length);
203
- this._emitBlocks(cid, sid, newBlocks, 'assistant');
204
- }
205
- if (e.message.stop_reason) this._emitted.delete(key);
206
- return;
207
- }
208
-
209
- if (e.type === 'user' && e.message?.content) {
210
- if (e.isCompactSummary) { this._emit(cid, sid, { type: 'compact_summary', content: e.message.content }, 'system'); return; }
211
- this._startStreaming(cid, sid);
212
- const content = e.message.content;
213
- const textParts = Array.isArray(content)
214
- ? content.filter(b => b.type === 'text' && b.text && !b.text.startsWith('<task-notification>')).map(b => b.text).join('\n')
215
- : (typeof content === 'string' && !content.startsWith('<task-notification>') ? content : '');
216
- if (textParts) {
217
- try { this._q.createMessage(cid, 'user', textParts); } catch (_) {}
218
- }
219
- const blocks = Array.isArray(content) ? content : [];
220
- for (const b of blocks) if (b.type === 'tool_result') this._emit(cid, sid, b, 'tool_result');
221
- return;
222
- }
223
-
224
- if (e.type === 'progress') {
225
- this._startStreaming(cid, sid);
226
- const d = e.data || {};
227
- if (d.type === 'agent_progress') {
228
- const inner = d.message?.message || d.message;
229
- const content = inner?.content;
230
- if (Array.isArray(content)) {
231
- const role = inner.role === 'user' ? 'tool_result' : 'assistant';
232
- this._emitBlocks(cid, sid, content, role);
233
- }
234
- return;
235
- }
236
- if (d.type === 'hook_progress') {
237
- this._emit(cid, sid, { type: 'hook_progress', hookEvent: d.hookEvent, hookName: d.hookName }, 'system');
238
- return;
239
- }
240
- if (d.type === 'mcp_progress') {
241
- this._emit(cid, sid, { type: 'mcp_progress', status: d.status, serverName: d.serverName, toolName: d.toolName }, 'system');
242
- return;
243
- }
244
- this._emit(cid, sid, { type: d.type || e.subtype || 'progress', content: e.content || d }, 'progress');
245
- return;
246
- }
247
-
248
- if (e.type === 'result') {
249
- this._emit(cid, sid, { type: 'result', result: e.result, subtype: e.subtype, duration_ms: e.duration_ms, total_cost_usd: e.total_cost_usd, is_error: e.is_error || false }, 'result', { isResult: true });
250
- this._endStreaming(cid, sid);
251
- }
252
- }
253
98
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentgui",
3
- "version": "1.0.817",
3
+ "version": "1.0.819",
4
4
  "description": "Multi-agent ACP client with real-time communication",
5
5
  "type": "module",
6
6
  "main": "electron/main.js",