agentgui 1.0.40 → 1.0.41
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/acp-launcher.js +79 -226
- package/package.json +3 -7
- package/server.js +3 -3
- package/stream-handler.js +14 -9
- package/DELIVERABLES.txt +0 -212
package/acp-launcher.js
CHANGED
|
@@ -1,37 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import fs from 'fs';
|
|
3
|
-
import path from 'path';
|
|
4
|
-
import os from 'os';
|
|
5
|
-
|
|
6
|
-
// Common paths where claude-code-acp might be installed
|
|
7
|
-
const CLAUDE_CODE_ACP_PATHS = [
|
|
8
|
-
'/config/.gmweb/npm-global/bin/claude-code-acp',
|
|
9
|
-
'/usr/local/bin/claude-code-acp',
|
|
10
|
-
'/usr/bin/claude-code-acp',
|
|
11
|
-
path.join(os.homedir(), '.local/bin/claude-code-acp'),
|
|
12
|
-
path.join(os.homedir(), '.gmweb/npm-global/bin/claude-code-acp'),
|
|
13
|
-
'claude-code-acp', // fallback to PATH
|
|
14
|
-
];
|
|
15
|
-
|
|
16
|
-
// Common paths where opencode might be installed
|
|
17
|
-
const OPENCODE_PATHS = [
|
|
18
|
-
'/usr/local/bin/opencode',
|
|
19
|
-
'/usr/bin/opencode',
|
|
20
|
-
path.join(os.homedir(), '.local/bin/opencode'),
|
|
21
|
-
'opencode', // fallback to PATH
|
|
22
|
-
];
|
|
23
|
-
|
|
24
|
-
function findBinary(paths) {
|
|
25
|
-
for (const p of paths) {
|
|
26
|
-
try {
|
|
27
|
-
fs.accessSync(p, fs.constants.X_OK);
|
|
28
|
-
return p;
|
|
29
|
-
} catch (_) {
|
|
30
|
-
continue;
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
return null;
|
|
34
|
-
}
|
|
1
|
+
import { createClient } from 'claude-code-acp';
|
|
35
2
|
|
|
36
3
|
const RIPPLEUI_SYSTEM_PROMPT = `CRITICAL INSTRUCTION: You are responding in a web-based HTML interface. EVERY response must be formatted as beautiful, styled HTML using RippleUI and Tailwind CSS. This is NOT a text-based interface - users see raw HTML rendered in their browser.
|
|
37
4
|
|
|
@@ -94,221 +61,76 @@ MANDATORY RULES:
|
|
|
94
61
|
✓ Use color classes: text-gray-700, bg-blue-50, border-blue-500
|
|
95
62
|
✓ Make visual hierarchy clear: use different font sizes, colors, cards
|
|
96
63
|
|
|
97
|
-
EXAMPLES OF COMPLETE RESPONSES:
|
|
98
|
-
|
|
99
|
-
Example 1 - Answer:
|
|
100
|
-
<div class="space-y-4 p-6"><h2 class="text-2xl font-bold">Explanation</h2><p class="text-gray-700">Here is the detailed explanation...</p></div>
|
|
101
|
-
|
|
102
|
-
Example 2 - Code:
|
|
103
|
-
<div class="space-y-4 p-6"><h3 class="text-xl font-bold">JavaScript Function</h3><pre class="bg-gray-900 text-white p-4 rounded overflow-x-auto"><code>const greet = () => console.log('Hello');</code></pre></div>
|
|
104
|
-
|
|
105
|
-
Example 3 - Multiple sections:
|
|
106
|
-
<div class="space-y-4 p-6"><h2 class="text-2xl font-bold">Topic</h2><div class="card bg-white shadow p-4"><h3 class="font-bold">Section 1</h3><p>Content here</p></div><div class="card bg-white shadow p-4"><h3 class="font-bold">Section 2</h3><p>More content</p></div></div>
|
|
107
|
-
|
|
108
64
|
YOU MUST ALWAYS OUTPUT VALID, COMPLETE HTML.
|
|
109
65
|
The user's interface shows YOUR HTML directly - make it beautiful, well-organized, and professional.`;
|
|
110
66
|
|
|
111
67
|
export default class ACPConnection {
|
|
112
68
|
constructor() {
|
|
113
|
-
this.
|
|
114
|
-
this.buffer = '';
|
|
115
|
-
this.nextRequestId = 1;
|
|
116
|
-
this.pendingRequests = new Map();
|
|
69
|
+
this.client = null;
|
|
117
70
|
this.sessionId = null;
|
|
118
71
|
this.onUpdate = null;
|
|
119
|
-
this.cwd = '/config';
|
|
120
72
|
}
|
|
121
73
|
|
|
74
|
+
/**
|
|
75
|
+
* Connect to ACP bridge and create session
|
|
76
|
+
*/
|
|
122
77
|
async connect(agentType, cwd) {
|
|
123
|
-
this.cwd = cwd;
|
|
124
|
-
|
|
125
|
-
const acpSetup = async () => {
|
|
126
|
-
await this._spawnACP(agentType, cwd);
|
|
127
|
-
await this.sendRequest('initialize', {
|
|
128
|
-
protocolVersion: 1,
|
|
129
|
-
clientCapabilities: { fs: { readTextFile: true, writeTextFile: true } },
|
|
130
|
-
}, 10000);
|
|
131
|
-
const result = await this.sendRequest('session/new', { cwd, mcpServers: [] }, 30000);
|
|
132
|
-
this.sessionId = result.sessionId;
|
|
133
|
-
await this.sendRequest('session/set_mode', { sessionId: this.sessionId, modeId: 'bypassPermissions' }, 10000);
|
|
134
|
-
};
|
|
135
|
-
|
|
136
|
-
const deadline = new Promise((_, reject) => setTimeout(() => reject(new Error('ACP handshake timeout (60s)')), 60000));
|
|
137
|
-
|
|
138
78
|
try {
|
|
139
|
-
|
|
140
|
-
console.log(`[ACP] Connected via ACP bridge (${agentType})`);
|
|
141
|
-
} catch (acpErr) {
|
|
142
|
-
console.error(`[ACP] ❌ FATAL: Bridge failed: ${acpErr.message}`);
|
|
143
|
-
console.error(`[ACP] The ACP bridge is REQUIRED. Please install the bridge for ${agentType}.`);
|
|
144
|
-
if (this.child) {
|
|
145
|
-
try { this.child.kill('SIGTERM'); } catch (_) {}
|
|
146
|
-
this.child = null;
|
|
147
|
-
}
|
|
148
|
-
throw acpErr;
|
|
149
|
-
}
|
|
150
|
-
}
|
|
79
|
+
console.log(`[ACP] Connecting to ${agentType}...`);
|
|
151
80
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
delete env.NODE_INSPECT;
|
|
157
|
-
delete env.NODE_DEBUG;
|
|
158
|
-
|
|
159
|
-
// Ensure npm global bin directories are in PATH
|
|
160
|
-
const npmGlobalBins = [
|
|
161
|
-
'/config/.gmweb/npm-global/bin',
|
|
162
|
-
path.join(os.homedir(), '.gmweb/npm-global/bin'),
|
|
163
|
-
path.join(os.homedir(), '.local/bin'),
|
|
164
|
-
'/usr/local/bin',
|
|
165
|
-
];
|
|
166
|
-
const currentPath = env.PATH || '';
|
|
167
|
-
const newPathEntries = npmGlobalBins.filter(p => !currentPath.includes(p));
|
|
168
|
-
if (newPathEntries.length > 0) {
|
|
169
|
-
env.PATH = [...newPathEntries, currentPath].join(':');
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
try {
|
|
173
|
-
let cmd;
|
|
174
|
-
let args;
|
|
175
|
-
if (agentType === 'opencode') {
|
|
176
|
-
cmd = findBinary(OPENCODE_PATHS);
|
|
177
|
-
args = ['acp'];
|
|
178
|
-
} else {
|
|
179
|
-
cmd = findBinary(CLAUDE_CODE_ACP_PATHS);
|
|
180
|
-
args = [];
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
if (!cmd) {
|
|
184
|
-
reject(new Error(`Could not find ${agentType} ACP binary. Please ensure ${agentType === 'opencode' ? 'opencode' : 'claude-code-acp'} is installed and in your PATH.`));
|
|
185
|
-
return;
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
this.child = spawn(cmd, args, { cwd, stdio: ['pipe', 'pipe', 'pipe'], env, shell: false });
|
|
189
|
-
} catch (err) {
|
|
190
|
-
reject(new Error(`Failed to spawn ACP: ${err.message}`));
|
|
191
|
-
return;
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
this.child.stderr.on('data', d => console.error(`[ACP:stderr]`, d.toString().trim()));
|
|
195
|
-
this.child.on('error', err => reject(new Error(`ACP spawn error: ${err.message}`)));
|
|
196
|
-
this.child.on('exit', () => {
|
|
197
|
-
this.child = null;
|
|
198
|
-
for (const [id, req] of this.pendingRequests) {
|
|
199
|
-
req.reject(new Error('ACP process exited'));
|
|
200
|
-
clearTimeout(req.timeoutId);
|
|
201
|
-
}
|
|
202
|
-
this.pendingRequests.clear();
|
|
81
|
+
// Create client directly from npm module
|
|
82
|
+
this.client = await createClient({
|
|
83
|
+
agent: agentType === 'opencode' ? 'opencode' : 'claude-code',
|
|
84
|
+
cwd
|
|
203
85
|
});
|
|
204
86
|
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
this.buffer = lines.pop() || '';
|
|
210
|
-
for (const line of lines) {
|
|
211
|
-
if (!line.trim()) continue;
|
|
212
|
-
try { this.handleMessage(JSON.parse(line)); }
|
|
213
|
-
catch (e) { console.error('[ACP:parse]', line.substring(0, 200), e.message); }
|
|
214
|
-
}
|
|
215
|
-
});
|
|
216
|
-
|
|
217
|
-
setTimeout(resolve, 300);
|
|
218
|
-
});
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
handleMessage(msg) {
|
|
222
|
-
if (msg.method) { this.handleIncoming(msg); return; }
|
|
223
|
-
if (msg.id !== undefined && this.pendingRequests.has(msg.id)) {
|
|
224
|
-
const req = this.pendingRequests.get(msg.id);
|
|
225
|
-
this.pendingRequests.delete(msg.id);
|
|
226
|
-
clearTimeout(req.timeoutId);
|
|
227
|
-
if (msg.error) req.reject(new Error(msg.error.message || JSON.stringify(msg.error)));
|
|
228
|
-
else req.resolve(msg.result);
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
handleIncoming(msg) {
|
|
233
|
-
if (msg.method === 'session/update' && msg.params) {
|
|
234
|
-
if (this.onUpdate) this.onUpdate(msg.params);
|
|
235
|
-
this.resetPromptTimeout();
|
|
236
|
-
return;
|
|
237
|
-
}
|
|
238
|
-
if (msg.method === 'session/request_permission' && msg.id !== undefined) {
|
|
239
|
-
this.sendResponse(msg.id, { outcome: { outcome: 'selected', optionId: 'allow' } });
|
|
240
|
-
this.resetPromptTimeout();
|
|
241
|
-
return;
|
|
242
|
-
}
|
|
243
|
-
if (msg.method === 'fs/read_text_file' && msg.id !== undefined) {
|
|
244
|
-
try { this.sendResponse(msg.id, { content: fs.readFileSync(msg.params?.path, 'utf-8') }); }
|
|
245
|
-
catch (e) { this.sendError(msg.id, -32000, e.message); }
|
|
246
|
-
return;
|
|
247
|
-
}
|
|
248
|
-
if (msg.method === 'fs/write_text_file' && msg.id !== undefined) {
|
|
249
|
-
try { fs.writeFileSync(msg.params?.path, msg.params?.content, 'utf-8'); this.sendResponse(msg.id, null); }
|
|
250
|
-
catch (e) { this.sendError(msg.id, -32000, e.message); }
|
|
251
|
-
return;
|
|
87
|
+
console.log(`[ACP] ✅ Connected to ${agentType} (direct module)`);
|
|
88
|
+
} catch (err) {
|
|
89
|
+
console.error(`[ACP] ❌ FATAL: Connection failed: ${err.message}`);
|
|
90
|
+
throw new Error(`ACP connection failed for ${agentType}: ${err.message}`);
|
|
252
91
|
}
|
|
253
92
|
}
|
|
254
93
|
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
clearTimeout(req.timeoutId);
|
|
259
|
-
req.timeoutId = setTimeout(() => {
|
|
260
|
-
this.pendingRequests.delete(id);
|
|
261
|
-
req.reject(new Error('session/prompt timeout'));
|
|
262
|
-
}, 300000);
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
sendRequest(method, params, timeoutMs = 60000) {
|
|
268
|
-
return new Promise((resolve, reject) => {
|
|
269
|
-
if (!this.child) { reject(new Error('ACP not connected')); return; }
|
|
270
|
-
const id = this.nextRequestId++;
|
|
271
|
-
const timeoutId = setTimeout(() => {
|
|
272
|
-
this.pendingRequests.delete(id);
|
|
273
|
-
reject(new Error(`${method} timeout (${timeoutMs}ms)`));
|
|
274
|
-
}, timeoutMs);
|
|
275
|
-
this.pendingRequests.set(id, { resolve, reject, timeoutId, method });
|
|
276
|
-
this.child.stdin.write(JSON.stringify({ jsonrpc: '2.0', id, method, ...(params && { params }) }) + '\n');
|
|
277
|
-
});
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
sendResponse(id, result) {
|
|
281
|
-
if (!this.child) return;
|
|
282
|
-
this.child.stdin.write(JSON.stringify({ jsonrpc: '2.0', id, result }) + '\n');
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
sendError(id, code, message) {
|
|
286
|
-
if (!this.child) return;
|
|
287
|
-
this.child.stdin.write(JSON.stringify({ jsonrpc: '2.0', id, error: { code, message } }) + '\n');
|
|
288
|
-
}
|
|
289
|
-
|
|
94
|
+
/**
|
|
95
|
+
* Initialize ACP session
|
|
96
|
+
*/
|
|
290
97
|
async initialize() {
|
|
291
|
-
|
|
98
|
+
if (!this.client) throw new Error('ACP not connected');
|
|
99
|
+
return this.client.request('initialize', {
|
|
292
100
|
protocolVersion: 1,
|
|
293
|
-
clientCapabilities: { fs: { readTextFile: true, writeTextFile: true } }
|
|
101
|
+
clientCapabilities: { fs: { readTextFile: true, writeTextFile: true } }
|
|
294
102
|
});
|
|
295
103
|
}
|
|
296
104
|
|
|
105
|
+
/**
|
|
106
|
+
* Create new session
|
|
107
|
+
*/
|
|
297
108
|
async newSession(cwd) {
|
|
298
|
-
|
|
109
|
+
if (!this.client) throw new Error('ACP not connected');
|
|
110
|
+
const result = await this.client.request('session/new', { cwd, mcpServers: [] });
|
|
299
111
|
this.sessionId = result.sessionId;
|
|
300
112
|
return result;
|
|
301
113
|
}
|
|
302
114
|
|
|
115
|
+
/**
|
|
116
|
+
* Set session mode
|
|
117
|
+
*/
|
|
303
118
|
async setSessionMode(modeId) {
|
|
304
|
-
|
|
119
|
+
if (!this.client) throw new Error('ACP not connected');
|
|
120
|
+
return this.client.request('session/set_mode', { sessionId: this.sessionId, modeId });
|
|
305
121
|
}
|
|
306
122
|
|
|
123
|
+
/**
|
|
124
|
+
* Inject skills and system prompt
|
|
125
|
+
*/
|
|
307
126
|
async injectSkills(additionalContext = '') {
|
|
308
|
-
|
|
309
|
-
const systemPrompt = additionalContext ? `${RIPPLEUI_SYSTEM_PROMPT}\n\n---\n\n${additionalContext}` : RIPPLEUI_SYSTEM_PROMPT;
|
|
127
|
+
if (!this.client) throw new Error('ACP not connected');
|
|
310
128
|
|
|
311
|
-
|
|
129
|
+
const systemPrompt = additionalContext
|
|
130
|
+
? `${RIPPLEUI_SYSTEM_PROMPT}\n\n---\n\n${additionalContext}`
|
|
131
|
+
: RIPPLEUI_SYSTEM_PROMPT;
|
|
132
|
+
|
|
133
|
+
return this.client.request('session/skill_inject', {
|
|
312
134
|
sessionId: this.sessionId,
|
|
313
135
|
skills: [],
|
|
314
136
|
notification: [{ type: 'text', text: systemPrompt }]
|
|
@@ -316,30 +138,61 @@ export default class ACPConnection {
|
|
|
316
138
|
}
|
|
317
139
|
|
|
318
140
|
/**
|
|
319
|
-
* Inject system
|
|
141
|
+
* Inject system context
|
|
320
142
|
*/
|
|
321
143
|
async injectSystemContext() {
|
|
322
|
-
|
|
144
|
+
if (!this.client) throw new Error('ACP not connected');
|
|
145
|
+
|
|
146
|
+
return this.client.request('session/context', {
|
|
323
147
|
sessionId: this.sessionId,
|
|
324
148
|
context: RIPPLEUI_SYSTEM_PROMPT,
|
|
325
149
|
role: 'system'
|
|
326
150
|
});
|
|
327
151
|
}
|
|
328
152
|
|
|
153
|
+
/**
|
|
154
|
+
* Send prompt and stream updates
|
|
155
|
+
*/
|
|
329
156
|
async sendPrompt(prompt) {
|
|
157
|
+
if (!this.client) throw new Error('ACP not connected');
|
|
158
|
+
|
|
330
159
|
const promptContent = Array.isArray(prompt) ? prompt : [{ type: 'text', text: prompt }];
|
|
331
|
-
|
|
160
|
+
|
|
161
|
+
// Setup update handler before sending
|
|
162
|
+
if (this.onUpdate) {
|
|
163
|
+
this.client.on('update', (update) => {
|
|
164
|
+
// Forward updates immediately with no delay
|
|
165
|
+
this.onUpdate({ update });
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// Send prompt and get result
|
|
170
|
+
return this.client.request('session/prompt', {
|
|
171
|
+
sessionId: this.sessionId,
|
|
172
|
+
prompt: promptContent
|
|
173
|
+
}, 300000);
|
|
332
174
|
}
|
|
333
175
|
|
|
176
|
+
/**
|
|
177
|
+
* Check if connection is running
|
|
178
|
+
*/
|
|
334
179
|
isRunning() {
|
|
335
|
-
return this.
|
|
180
|
+
return this.client !== null;
|
|
336
181
|
}
|
|
337
182
|
|
|
183
|
+
/**
|
|
184
|
+
* Terminate connection
|
|
185
|
+
*/
|
|
338
186
|
async terminate() {
|
|
339
|
-
if (!this.
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
187
|
+
if (!this.client) return;
|
|
188
|
+
|
|
189
|
+
try {
|
|
190
|
+
await this.client.close();
|
|
191
|
+
} catch (err) {
|
|
192
|
+
console.error(`[ACP] Error during terminate: ${err.message}`);
|
|
193
|
+
} finally {
|
|
194
|
+
this.client = null;
|
|
195
|
+
this.sessionId = null;
|
|
196
|
+
}
|
|
344
197
|
}
|
|
345
198
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agentgui",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.41",
|
|
4
4
|
"description": "Multi-agent ACP client with real-time communication",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "server.js",
|
|
@@ -18,15 +18,11 @@
|
|
|
18
18
|
"homepage": "https://github.com/AnEntrypoint/agentgui#readme",
|
|
19
19
|
"scripts": {
|
|
20
20
|
"start": "node server.js",
|
|
21
|
-
"
|
|
22
|
-
"dev": "node server.js --watch",
|
|
23
|
-
"dev:bun": "bun run server-bun.js --watch",
|
|
24
|
-
"test": "node run-browser-tests.js",
|
|
25
|
-
"test:integration": "./test-integration.sh",
|
|
26
|
-
"test:all": "npm run test:integration && npm run test"
|
|
21
|
+
"dev": "node server.js --watch"
|
|
27
22
|
},
|
|
28
23
|
"dependencies": {
|
|
29
24
|
"better-sqlite3": "^12.6.2",
|
|
25
|
+
"claude-code-acp": "^1.0.0",
|
|
30
26
|
"ws": "^8.14.2"
|
|
31
27
|
}
|
|
32
28
|
}
|
package/server.js
CHANGED
|
@@ -551,10 +551,10 @@ async function processMessage(conversationId, messageId, sessionId, content, age
|
|
|
551
551
|
console.error(`[processMessage] State history: ${JSON.stringify(summary, null, 2)}`);
|
|
552
552
|
|
|
553
553
|
} finally {
|
|
554
|
-
// Cleanup: remove from state store
|
|
555
|
-
|
|
554
|
+
// Cleanup: remove from state store immediately (async to not block)
|
|
555
|
+
setImmediate(() => {
|
|
556
556
|
sessionStateStore.remove(sessionId);
|
|
557
|
-
}
|
|
557
|
+
});
|
|
558
558
|
|
|
559
559
|
// Log final state
|
|
560
560
|
console.log(`[processMessage] Final state: ${stateManager.getState()}`);
|
package/stream-handler.js
CHANGED
|
@@ -62,15 +62,9 @@ export class StreamHandler {
|
|
|
62
62
|
this.sequence = persistedUpdate.sequence;
|
|
63
63
|
this.updateCount++;
|
|
64
64
|
|
|
65
|
-
// Validate consistency after write
|
|
66
|
-
const validation = StateValidator.validateSession(this.sessionId);
|
|
67
|
-
if (!validation.valid) {
|
|
68
|
-
console.error(`[StreamHandler] State validation failed after update:`, validation);
|
|
69
|
-
// Log but continue - database is still source of truth
|
|
70
|
-
}
|
|
71
|
-
|
|
72
65
|
// CRITICAL: Broadcast happens AFTER database write confirms
|
|
73
66
|
// This ensures clients see data that's already persisted
|
|
67
|
+
// Broadcast immediately with zero delay
|
|
74
68
|
this.broadcastFn({
|
|
75
69
|
type: 'stream_update',
|
|
76
70
|
sessionId: this.sessionId,
|
|
@@ -79,8 +73,19 @@ export class StreamHandler {
|
|
|
79
73
|
update: persistedUpdate.content,
|
|
80
74
|
sequence: this.sequence,
|
|
81
75
|
persisted: true,
|
|
82
|
-
timestamp: persistedUpdate.created_at
|
|
83
|
-
|
|
76
|
+
timestamp: persistedUpdate.created_at
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
// Validate consistency asynchronously (don't block broadcast)
|
|
80
|
+
setImmediate(() => {
|
|
81
|
+
try {
|
|
82
|
+
const validation = StateValidator.validateSession(this.sessionId);
|
|
83
|
+
if (!validation.valid) {
|
|
84
|
+
console.error(`[StreamHandler] State validation failed: ${validation.error}`);
|
|
85
|
+
}
|
|
86
|
+
} catch (validationErr) {
|
|
87
|
+
console.error(`[StreamHandler] Validation error: ${validationErr.message}`);
|
|
88
|
+
}
|
|
84
89
|
});
|
|
85
90
|
} catch (err) {
|
|
86
91
|
console.error(`[StreamHandler] Error persisting update: ${err.message}`);
|
package/DELIVERABLES.txt
DELETED
|
@@ -1,212 +0,0 @@
|
|
|
1
|
-
================================================================================
|
|
2
|
-
STATE CONSISTENCY TEST DELIVERABLES
|
|
3
|
-
================================================================================
|
|
4
|
-
|
|
5
|
-
TEST COMPLETED: February 3, 2026
|
|
6
|
-
SYSTEM TESTED: BuildEsk LIVE (https://buildesk.acc.l-inc.co.za/gm/)
|
|
7
|
-
CREDENTIALS: abc / Test123456
|
|
8
|
-
|
|
9
|
-
================================================================================
|
|
10
|
-
DOCUMENTED FINDINGS
|
|
11
|
-
================================================================================
|
|
12
|
-
|
|
13
|
-
✓ VERIFIED - Conversation lists are IDENTICAL between windows
|
|
14
|
-
✓ VERIFIED - No console errors detected
|
|
15
|
-
✓ VERIFIED - Multi-session support working
|
|
16
|
-
✓ VERIFIED - Authentication system functional
|
|
17
|
-
|
|
18
|
-
⚠ PENDING - Real-time message synchronization (manual test needed)
|
|
19
|
-
⚠ PENDING - Timestamp consistency (manual test needed)
|
|
20
|
-
⚠ PENDING - Rapid message handling (manual test needed)
|
|
21
|
-
|
|
22
|
-
================================================================================
|
|
23
|
-
DOCUMENTATION FILES
|
|
24
|
-
================================================================================
|
|
25
|
-
|
|
26
|
-
MAIN DOCUMENTS:
|
|
27
|
-
→ TEST_README.md
|
|
28
|
-
Entry point - Quick overview and navigation
|
|
29
|
-
|
|
30
|
-
→ TEST_SUMMARY.md
|
|
31
|
-
Executive summary - Key findings and recommendations
|
|
32
|
-
|
|
33
|
-
→ STATE_CONSISTENCY_TEST_REPORT.md
|
|
34
|
-
Comprehensive report - Detailed procedures and technical details
|
|
35
|
-
|
|
36
|
-
→ STATE_CONSISTENCY_TEST_INDEX.md
|
|
37
|
-
Complete index - Navigation guide and quick reference
|
|
38
|
-
|
|
39
|
-
REFERENCE:
|
|
40
|
-
→ STATE_CONSISTENCY_GUARANTEE.md
|
|
41
|
-
Implementation details
|
|
42
|
-
|
|
43
|
-
→ DELIVERABLES.txt
|
|
44
|
-
This file
|
|
45
|
-
|
|
46
|
-
================================================================================
|
|
47
|
-
TEST ARTIFACTS
|
|
48
|
-
================================================================================
|
|
49
|
-
|
|
50
|
-
LOCATION: test-artifacts/
|
|
51
|
-
|
|
52
|
-
SCREENSHOTS (1280x720 PNG):
|
|
53
|
-
├── 01-window-a-initial.png
|
|
54
|
-
├── 01-window-b-initial.png
|
|
55
|
-
├── 02-window-a-after-send.png
|
|
56
|
-
└── 02-window-b-after-send.png
|
|
57
|
-
|
|
58
|
-
PAGE SNAPSHOTS:
|
|
59
|
-
├── snapshot-a-1.txt
|
|
60
|
-
└── snapshot-b-1.txt
|
|
61
|
-
|
|
62
|
-
CONSOLE LOGS:
|
|
63
|
-
├── console-a.log
|
|
64
|
-
└── console-b.log
|
|
65
|
-
|
|
66
|
-
VERIFICATION: diff snapshot-a-1.txt snapshot-b-1.txt → No differences ✓
|
|
67
|
-
|
|
68
|
-
================================================================================
|
|
69
|
-
QUICK START GUIDE
|
|
70
|
-
================================================================================
|
|
71
|
-
|
|
72
|
-
1. FOR QUICK OVERVIEW:
|
|
73
|
-
Read: TEST_README.md (2 min)
|
|
74
|
-
|
|
75
|
-
2. FOR MANUAL TESTING:
|
|
76
|
-
Read: STATE_CONSISTENCY_TEST_REPORT.md
|
|
77
|
-
Sections: "Manual Test Procedures" and "Commands for Manual Testing"
|
|
78
|
-
|
|
79
|
-
3. TO VERIFY FINDINGS:
|
|
80
|
-
Check: test-artifacts/ screenshots and snapshots
|
|
81
|
-
|
|
82
|
-
4. FOR TECHNICAL DETAILS:
|
|
83
|
-
Read: STATE_CONSISTENCY_TEST_REPORT.md
|
|
84
|
-
Section: "Appendix: Technical Details"
|
|
85
|
-
|
|
86
|
-
================================================================================
|
|
87
|
-
COMMAND REFERENCE
|
|
88
|
-
================================================================================
|
|
89
|
-
|
|
90
|
-
LAUNCH DUAL SESSIONS:
|
|
91
|
-
# Terminal 1
|
|
92
|
-
agent-browser --headed --session window-a \
|
|
93
|
-
--credentials abc Test123456 \
|
|
94
|
-
open https://buildesk.acc.l-inc.co.za/gm/
|
|
95
|
-
|
|
96
|
-
# Terminal 2
|
|
97
|
-
agent-browser --headed --session window-b \
|
|
98
|
-
--credentials abc Test123456 \
|
|
99
|
-
open https://buildesk.acc.l-inc.co.za/gm/
|
|
100
|
-
|
|
101
|
-
TAKE SCREENSHOTS:
|
|
102
|
-
agent-browser --session window-a screenshot --full manual-a.png
|
|
103
|
-
agent-browser --session window-b screenshot --full manual-b.png
|
|
104
|
-
|
|
105
|
-
CHECK CONSOLE:
|
|
106
|
-
agent-browser --session window-a console
|
|
107
|
-
agent-browser --session window-b console
|
|
108
|
-
|
|
109
|
-
GET PAGE SNAPSHOT:
|
|
110
|
-
agent-browser --session window-a snapshot -i -c
|
|
111
|
-
|
|
112
|
-
================================================================================
|
|
113
|
-
TEST RESULTS SUMMARY
|
|
114
|
-
================================================================================
|
|
115
|
-
|
|
116
|
-
AUTOMATED TEST RESULTS:
|
|
117
|
-
✓ Server Connectivity ..................... PASSED
|
|
118
|
-
✓ Session A Initialization ............... PASSED
|
|
119
|
-
✓ Session B Initialization ............... PASSED
|
|
120
|
-
✓ Authentication (both sessions) ......... PASSED
|
|
121
|
-
✓ Initial Conversation Lists Match ....... PASSED (IDENTICAL)
|
|
122
|
-
✓ Console Error Detection ................ PASSED (No errors)
|
|
123
|
-
✓ Page Snapshot Comparison ............... PASSED (Identical)
|
|
124
|
-
|
|
125
|
-
TOTAL: 7/7 PASSED ✓
|
|
126
|
-
|
|
127
|
-
MANUAL TEST STATUS:
|
|
128
|
-
⚠ New Conversation Sync ................. PENDING
|
|
129
|
-
⚠ Message Send Synchronization .......... PENDING
|
|
130
|
-
⚠ Timestamp Consistency ................. PENDING
|
|
131
|
-
⚠ Rapid Message Handling ................ PENDING
|
|
132
|
-
|
|
133
|
-
================================================================================
|
|
134
|
-
FINAL RECOMMENDATIONS
|
|
135
|
-
================================================================================
|
|
136
|
-
|
|
137
|
-
NEXT STEPS:
|
|
138
|
-
1. Review test artifacts in test-artifacts/
|
|
139
|
-
2. Execute manual test procedures from STATE_CONSISTENCY_TEST_REPORT.md
|
|
140
|
-
3. Document real-time sync behavior and latencies
|
|
141
|
-
4. Analyze console logs for state sync patterns
|
|
142
|
-
5. Validate timestamp consistency across windows
|
|
143
|
-
6. Test rapid message scenarios for race conditions
|
|
144
|
-
7. Create final consolidated test report
|
|
145
|
-
|
|
146
|
-
EXPECTED OUTCOMES:
|
|
147
|
-
- Measure message send latency (target: < 100ms)
|
|
148
|
-
- Verify timestamp updates propagate to both windows
|
|
149
|
-
- Confirm no lost messages under rapid sending
|
|
150
|
-
- Document WebSocket/polling implementation
|
|
151
|
-
- Validate connection resilience
|
|
152
|
-
|
|
153
|
-
================================================================================
|
|
154
|
-
FILE LOCATIONS
|
|
155
|
-
================================================================================
|
|
156
|
-
|
|
157
|
-
All files are located in: /config/workspace/agentgui/
|
|
158
|
-
|
|
159
|
-
Documentation:
|
|
160
|
-
- TEST_README.md
|
|
161
|
-
- TEST_SUMMARY.md
|
|
162
|
-
- STATE_CONSISTENCY_TEST_REPORT.md
|
|
163
|
-
- STATE_CONSISTENCY_TEST_INDEX.md
|
|
164
|
-
- STATE_CONSISTENCY_GUARANTEE.md
|
|
165
|
-
- DELIVERABLES.txt (this file)
|
|
166
|
-
|
|
167
|
-
Test Artifacts:
|
|
168
|
-
- test-artifacts/ (directory)
|
|
169
|
-
├── 4 PNG screenshots
|
|
170
|
-
├── 2 TXT snapshots
|
|
171
|
-
└── 2 console logs
|
|
172
|
-
|
|
173
|
-
================================================================================
|
|
174
|
-
VERIFICATION CHECKLIST
|
|
175
|
-
================================================================================
|
|
176
|
-
|
|
177
|
-
✓ Server is reachable and responding with HTTP 200
|
|
178
|
-
✓ Authentication credentials work correctly
|
|
179
|
-
✓ Both sessions connect without conflicts
|
|
180
|
-
✓ Conversation lists load and are identical
|
|
181
|
-
✓ Console logs collected (no errors)
|
|
182
|
-
✓ Screenshots captured for both windows
|
|
183
|
-
✓ Page snapshots created and compared
|
|
184
|
-
✓ Diff analysis shows identical content
|
|
185
|
-
✓ Test documentation complete
|
|
186
|
-
✓ Manual test procedures documented
|
|
187
|
-
✓ Test artifacts organized and available
|
|
188
|
-
|
|
189
|
-
================================================================================
|
|
190
|
-
SUPPORT & QUESTIONS
|
|
191
|
-
================================================================================
|
|
192
|
-
|
|
193
|
-
For detailed information, see:
|
|
194
|
-
- TEST_README.md for quick overview
|
|
195
|
-
- STATE_CONSISTENCY_TEST_REPORT.md for procedures
|
|
196
|
-
- STATE_CONSISTENCY_TEST_INDEX.md for navigation
|
|
197
|
-
|
|
198
|
-
To run manual tests:
|
|
199
|
-
- Follow commands in STATE_CONSISTENCY_TEST_REPORT.md section:
|
|
200
|
-
"Commands for Manual Testing"
|
|
201
|
-
|
|
202
|
-
To review evidence:
|
|
203
|
-
- Check screenshots in test-artifacts/
|
|
204
|
-
- Compare snapshots: test-artifacts/snapshot-*.txt
|
|
205
|
-
|
|
206
|
-
================================================================================
|
|
207
|
-
TEST EXECUTION: AUTOMATED ✓ COMPLETE
|
|
208
|
-
MANUAL PHASE: READY TO BEGIN ⚠
|
|
209
|
-
================================================================================
|
|
210
|
-
|
|
211
|
-
Report Generated: February 3, 2026
|
|
212
|
-
Status: All automated tests passed, manual phase ready for execution
|