agentgui 1.0.40 → 1.0.42
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 +132 -219
- 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,36 +1,31 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { createClient } from 'claude-code-acp';
|
|
2
2
|
import fs from 'fs';
|
|
3
3
|
import path from 'path';
|
|
4
4
|
import os from 'os';
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
];
|
|
15
|
-
|
|
16
|
-
|
|
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) {
|
|
6
|
+
/**
|
|
7
|
+
* Load CLI configuration to ensure identical behavior
|
|
8
|
+
*/
|
|
9
|
+
function loadCLIConfig() {
|
|
10
|
+
const configPaths = [
|
|
11
|
+
path.join(os.homedir(), '.claude', 'config.json'),
|
|
12
|
+
path.join(os.homedir(), '.claude-code', 'config.json'),
|
|
13
|
+
path.join(process.env.XDG_CONFIG_HOME || path.join(os.homedir(), '.config'), 'claude', 'config.json')
|
|
14
|
+
];
|
|
15
|
+
|
|
16
|
+
for (const configPath of configPaths) {
|
|
26
17
|
try {
|
|
27
|
-
fs.
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
18
|
+
if (fs.existsSync(configPath)) {
|
|
19
|
+
const config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
|
|
20
|
+
console.log(`[ACP] Loaded CLI config from ${configPath}`);
|
|
21
|
+
return config;
|
|
22
|
+
}
|
|
23
|
+
} catch (e) {
|
|
24
|
+
// Config file doesn't exist or is invalid, continue
|
|
31
25
|
}
|
|
32
26
|
}
|
|
33
|
-
|
|
27
|
+
|
|
28
|
+
return {};
|
|
34
29
|
}
|
|
35
30
|
|
|
36
31
|
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.
|
|
@@ -94,221 +89,108 @@ MANDATORY RULES:
|
|
|
94
89
|
✓ Use color classes: text-gray-700, bg-blue-50, border-blue-500
|
|
95
90
|
✓ Make visual hierarchy clear: use different font sizes, colors, cards
|
|
96
91
|
|
|
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
92
|
YOU MUST ALWAYS OUTPUT VALID, COMPLETE HTML.
|
|
109
93
|
The user's interface shows YOUR HTML directly - make it beautiful, well-organized, and professional.`;
|
|
110
94
|
|
|
111
95
|
export default class ACPConnection {
|
|
112
96
|
constructor() {
|
|
113
|
-
this.
|
|
114
|
-
this.buffer = '';
|
|
115
|
-
this.nextRequestId = 1;
|
|
116
|
-
this.pendingRequests = new Map();
|
|
97
|
+
this.client = null;
|
|
117
98
|
this.sessionId = null;
|
|
118
99
|
this.onUpdate = null;
|
|
119
|
-
this.cwd = '/config';
|
|
120
100
|
}
|
|
121
101
|
|
|
102
|
+
/**
|
|
103
|
+
* Connect to ACP bridge and create session
|
|
104
|
+
* Uses identical configuration to CLI version
|
|
105
|
+
*/
|
|
122
106
|
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
107
|
try {
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
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();
|
|
203
|
-
});
|
|
204
|
-
|
|
205
|
-
this.child.stdout.setEncoding('utf8');
|
|
206
|
-
this.child.stdout.on('data', data => {
|
|
207
|
-
this.buffer += data;
|
|
208
|
-
const lines = this.buffer.split('\n');
|
|
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
|
-
}
|
|
108
|
+
console.log(`[ACP] Connecting to ${agentType}...`);
|
|
109
|
+
|
|
110
|
+
// Load CLI configuration for identical behavior
|
|
111
|
+
const cliConfig = loadCLIConfig();
|
|
112
|
+
|
|
113
|
+
// Create client with CLI-identical configuration
|
|
114
|
+
// Pass through all environment for OAuth and plugin support
|
|
115
|
+
const clientConfig = {
|
|
116
|
+
agent: agentType === 'opencode' ? 'opencode' : 'claude-code',
|
|
117
|
+
cwd,
|
|
118
|
+
// Use same environment as CLI (HOME, PATH, etc.)
|
|
119
|
+
env: process.env,
|
|
120
|
+
// Load plugins just like CLI does
|
|
121
|
+
plugins: true,
|
|
122
|
+
// Use OAuth for authentication (same as CLI)
|
|
123
|
+
oauth: true,
|
|
124
|
+
// Use model preferences from CLI config
|
|
125
|
+
modelPreferences: cliConfig.modelPreferences || undefined,
|
|
126
|
+
// Enable all capabilities that CLI enables
|
|
127
|
+
capabilities: {
|
|
128
|
+
fs: true,
|
|
129
|
+
mcp: true,
|
|
130
|
+
web: true,
|
|
131
|
+
terminal: true
|
|
132
|
+
},
|
|
133
|
+
// Pass through any other CLI settings
|
|
134
|
+
...cliConfig
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
// Remove potential conflicting fields
|
|
138
|
+
delete clientConfig.agent; // Re-add below
|
|
139
|
+
delete clientConfig.cwd; // Re-add below
|
|
140
|
+
|
|
141
|
+
this.client = await createClient({
|
|
142
|
+
agent: clientConfig.agent || (agentType === 'opencode' ? 'opencode' : 'claude-code'),
|
|
143
|
+
cwd,
|
|
144
|
+
...clientConfig
|
|
215
145
|
});
|
|
216
146
|
|
|
217
|
-
|
|
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;
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
resetPromptTimeout() {
|
|
256
|
-
for (const [id, req] of this.pendingRequests) {
|
|
257
|
-
if (req.method === 'session/prompt') {
|
|
258
|
-
clearTimeout(req.timeoutId);
|
|
259
|
-
req.timeoutId = setTimeout(() => {
|
|
260
|
-
this.pendingRequests.delete(id);
|
|
261
|
-
req.reject(new Error('session/prompt timeout'));
|
|
262
|
-
}, 300000);
|
|
263
|
-
}
|
|
147
|
+
console.log(`[ACP] ✅ Connected to ${agentType} (CLI-identical mode)`);
|
|
148
|
+
} catch (err) {
|
|
149
|
+
console.error(`[ACP] ❌ FATAL: Connection failed: ${err.message}`);
|
|
150
|
+
throw new Error(`ACP connection failed for ${agentType}: ${err.message}`);
|
|
264
151
|
}
|
|
265
152
|
}
|
|
266
153
|
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
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
|
-
|
|
154
|
+
/**
|
|
155
|
+
* Initialize ACP session
|
|
156
|
+
*/
|
|
290
157
|
async initialize() {
|
|
291
|
-
|
|
158
|
+
if (!this.client) throw new Error('ACP not connected');
|
|
159
|
+
return this.client.request('initialize', {
|
|
292
160
|
protocolVersion: 1,
|
|
293
|
-
clientCapabilities: { fs: { readTextFile: true, writeTextFile: true } }
|
|
161
|
+
clientCapabilities: { fs: { readTextFile: true, writeTextFile: true } }
|
|
294
162
|
});
|
|
295
163
|
}
|
|
296
164
|
|
|
165
|
+
/**
|
|
166
|
+
* Create new session
|
|
167
|
+
*/
|
|
297
168
|
async newSession(cwd) {
|
|
298
|
-
|
|
169
|
+
if (!this.client) throw new Error('ACP not connected');
|
|
170
|
+
const result = await this.client.request('session/new', { cwd, mcpServers: [] });
|
|
299
171
|
this.sessionId = result.sessionId;
|
|
300
172
|
return result;
|
|
301
173
|
}
|
|
302
174
|
|
|
175
|
+
/**
|
|
176
|
+
* Set session mode
|
|
177
|
+
*/
|
|
303
178
|
async setSessionMode(modeId) {
|
|
304
|
-
|
|
179
|
+
if (!this.client) throw new Error('ACP not connected');
|
|
180
|
+
return this.client.request('session/set_mode', { sessionId: this.sessionId, modeId });
|
|
305
181
|
}
|
|
306
182
|
|
|
183
|
+
/**
|
|
184
|
+
* Inject skills and system prompt
|
|
185
|
+
*/
|
|
307
186
|
async injectSkills(additionalContext = '') {
|
|
308
|
-
|
|
309
|
-
|
|
187
|
+
if (!this.client) throw new Error('ACP not connected');
|
|
188
|
+
|
|
189
|
+
const systemPrompt = additionalContext
|
|
190
|
+
? `${RIPPLEUI_SYSTEM_PROMPT}\n\n---\n\n${additionalContext}`
|
|
191
|
+
: RIPPLEUI_SYSTEM_PROMPT;
|
|
310
192
|
|
|
311
|
-
return this.
|
|
193
|
+
return this.client.request('session/skill_inject', {
|
|
312
194
|
sessionId: this.sessionId,
|
|
313
195
|
skills: [],
|
|
314
196
|
notification: [{ type: 'text', text: systemPrompt }]
|
|
@@ -316,30 +198,61 @@ export default class ACPConnection {
|
|
|
316
198
|
}
|
|
317
199
|
|
|
318
200
|
/**
|
|
319
|
-
* Inject system
|
|
201
|
+
* Inject system context
|
|
320
202
|
*/
|
|
321
203
|
async injectSystemContext() {
|
|
322
|
-
|
|
204
|
+
if (!this.client) throw new Error('ACP not connected');
|
|
205
|
+
|
|
206
|
+
return this.client.request('session/context', {
|
|
323
207
|
sessionId: this.sessionId,
|
|
324
208
|
context: RIPPLEUI_SYSTEM_PROMPT,
|
|
325
209
|
role: 'system'
|
|
326
210
|
});
|
|
327
211
|
}
|
|
328
212
|
|
|
213
|
+
/**
|
|
214
|
+
* Send prompt and stream updates
|
|
215
|
+
*/
|
|
329
216
|
async sendPrompt(prompt) {
|
|
217
|
+
if (!this.client) throw new Error('ACP not connected');
|
|
218
|
+
|
|
330
219
|
const promptContent = Array.isArray(prompt) ? prompt : [{ type: 'text', text: prompt }];
|
|
331
|
-
|
|
220
|
+
|
|
221
|
+
// Setup update handler before sending
|
|
222
|
+
if (this.onUpdate) {
|
|
223
|
+
this.client.on('update', (update) => {
|
|
224
|
+
// Forward updates immediately with no delay
|
|
225
|
+
this.onUpdate({ update });
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// Send prompt and get result
|
|
230
|
+
return this.client.request('session/prompt', {
|
|
231
|
+
sessionId: this.sessionId,
|
|
232
|
+
prompt: promptContent
|
|
233
|
+
}, 300000);
|
|
332
234
|
}
|
|
333
235
|
|
|
236
|
+
/**
|
|
237
|
+
* Check if connection is running
|
|
238
|
+
*/
|
|
334
239
|
isRunning() {
|
|
335
|
-
return this.
|
|
240
|
+
return this.client !== null;
|
|
336
241
|
}
|
|
337
242
|
|
|
243
|
+
/**
|
|
244
|
+
* Terminate connection
|
|
245
|
+
*/
|
|
338
246
|
async terminate() {
|
|
339
|
-
if (!this.
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
247
|
+
if (!this.client) return;
|
|
248
|
+
|
|
249
|
+
try {
|
|
250
|
+
await this.client.close();
|
|
251
|
+
} catch (err) {
|
|
252
|
+
console.error(`[ACP] Error during terminate: ${err.message}`);
|
|
253
|
+
} finally {
|
|
254
|
+
this.client = null;
|
|
255
|
+
this.sessionId = null;
|
|
256
|
+
}
|
|
344
257
|
}
|
|
345
258
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agentgui",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.42",
|
|
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
|