@clawchatsai/connector 0.1.8 → 0.1.10
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/package.json +1 -1
- package/server/gateway.js +36 -2
package/package.json
CHANGED
package/server/gateway.js
CHANGED
|
@@ -11,9 +11,13 @@ export class GatewayClient {
|
|
|
11
11
|
this.debugLogger = debugLogger;
|
|
12
12
|
this.gatewayWsUrl = gatewayWsUrl;
|
|
13
13
|
this.authToken = authToken;
|
|
14
|
-
// Per-session buffer of
|
|
14
|
+
// Per-session buffer of attachment paths captured during a run. Drained by saveAssistantMessage.
|
|
15
|
+
// Sources: (1) MEDIA: paths from exec tool's echo args, (2) Write/Edit tool args.path.
|
|
15
16
|
// Lives on the stable singleton — safe from plugin re-registration that broke the old closure-based stash.
|
|
16
17
|
this._runMediaPaths = new Map();
|
|
18
|
+
// Per-toolCallId staging for Write/Edit: path captured at phase:start, promoted to _runMediaPaths
|
|
19
|
+
// at phase:result if the call succeeded. Keyed by toolCallId; value: { sessionKey, path }.
|
|
20
|
+
this._runWriteBuffers = new Map();
|
|
17
21
|
|
|
18
22
|
this.ws = null;
|
|
19
23
|
this.connected = false;
|
|
@@ -292,7 +296,7 @@ export class GatewayClient {
|
|
|
292
296
|
if (!db.prepare('SELECT id FROM threads WHERE id = ?').get(parsed.threadId)) return;
|
|
293
297
|
const now = Date.now();
|
|
294
298
|
try {
|
|
295
|
-
db.prepare('INSERT INTO messages (id, thread_id, role, content, status, metadata, timestamp, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?)').run(`gw-error-${parsed.threadId}-${now}`, parsed.threadId, 'system', `[error] ${
|
|
299
|
+
db.prepare('INSERT INTO messages (id, thread_id, role, content, status, metadata, timestamp, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?)').run(`gw-error-${parsed.threadId}-${now}`, parsed.threadId, 'system', `[error] ${message?.error || message?.content || 'Unknown error'}`, 'sent', '{"transient":true}', now, now);
|
|
296
300
|
// Clear stale pending flag so browsers reloading the chat don't re-derive "thinking..." state.
|
|
297
301
|
db.prepare("UPDATE messages SET metadata = json_remove(metadata, '$.pending') WHERE thread_id = ? AND role = 'assistant' AND json_extract(metadata, '$.pending') = 1").run(parsed.threadId);
|
|
298
302
|
} catch (e) { console.error('Failed to save error marker:', e.message); }
|
|
@@ -379,6 +383,31 @@ export class GatewayClient {
|
|
|
379
383
|
this._runMediaPaths.set(sessionKey, [...new Set([...existing, ...paths])]);
|
|
380
384
|
}
|
|
381
385
|
}
|
|
386
|
+
// Capture Write/Edit tool paths. The split pane (and existing attachment chip render) handles
|
|
387
|
+
// any text-ish or PDF file, so allow that broad set; image/audio extensions also flow through
|
|
388
|
+
// — saveAssistantMessage routes them by ext (image → markdown, audio → audio chip).
|
|
389
|
+
// Buffer at phase:start when args.path is present; promote to _runMediaPaths at phase:result
|
|
390
|
+
// only if the call succeeded — failed writes shouldn't surface a chip for a file that may not exist.
|
|
391
|
+
const VIEWABLE_EXTS = new Set([
|
|
392
|
+
'pdf','md','mdx','rst','txt','log',
|
|
393
|
+
'csv','tsv','json','xml','yaml','yml','toml','ini','env','conf',
|
|
394
|
+
'js','jsx','mjs','cjs','ts','tsx','py','rb','go','rs','java','c','cpp','h','hpp','cs','php',
|
|
395
|
+
'html','htm','css','scss','sass','less','sh','bash','zsh','sql','pl','lua','r','swift','kt','dart',
|
|
396
|
+
'png','jpg','jpeg','gif','webp','bmp','svg','ico','avif','tiff',
|
|
397
|
+
'mp3','wav','ogg','m4a','flac','aac','opus','wma'
|
|
398
|
+
]);
|
|
399
|
+
if (sessionKey && data?.toolCallId && /^(write|edit|multiedit|notebookedit)$/i.test(data?.name || '') && data?.phase === 'start' && typeof data?.args?.path === 'string' && data.args.path.length > 0) {
|
|
400
|
+
const ext = (data.args.path.split('.').pop() || '').toLowerCase();
|
|
401
|
+
if (VIEWABLE_EXTS.has(ext)) this._runWriteBuffers.set(data.toolCallId, { sessionKey, path: data.args.path });
|
|
402
|
+
}
|
|
403
|
+
if (data?.toolCallId && data?.phase === 'result' && this._runWriteBuffers.has(data.toolCallId)) {
|
|
404
|
+
const buf = this._runWriteBuffers.get(data.toolCallId);
|
|
405
|
+
this._runWriteBuffers.delete(data.toolCallId);
|
|
406
|
+
if (!data.isError && buf.sessionKey === sessionKey) {
|
|
407
|
+
const existing = this._runMediaPaths.get(buf.sessionKey) ?? [];
|
|
408
|
+
this._runMediaPaths.set(buf.sessionKey, [...new Set([...existing, buf.path])]);
|
|
409
|
+
}
|
|
410
|
+
}
|
|
382
411
|
const step = { type: 'tool', timestamp: Date.now(), name: data?.name || 'unknown', phase: data?.phase || 'start', toolCallId: data?.toolCallId, meta: data?.meta || (argsMeta ? String(argsMeta) : undefined), isError: data?.isError || false };
|
|
383
412
|
if (data?.phase === 'result') {
|
|
384
413
|
const existing = log.steps.findLast(s => s.toolCallId === data.toolCallId && (s.phase === 'start' || s.phase === 'running'));
|
|
@@ -428,6 +457,11 @@ export class GatewayClient {
|
|
|
428
457
|
this._syntheticErrorRuns.add(runId);
|
|
429
458
|
const parsed = parseSessionKey(sessionKey);
|
|
430
459
|
if (parsed) {
|
|
460
|
+
// Mirror the state:'error' path — writeActivityToDb just set metadata.pending=true,
|
|
461
|
+
// and without this the flag survives: loadHistory re-derives has_pending on reconnect,
|
|
462
|
+
// "thinking…" sticks, and sendMessage's isStreaming() guard queues future sends into
|
|
463
|
+
// _pendingSend forever.
|
|
464
|
+
this.saveErrorMarker(sessionKey, { error: data?.error || 'Agent failed before reply' });
|
|
431
465
|
this.broadcastToBrowsers(JSON.stringify({
|
|
432
466
|
type: 'clawchats',
|
|
433
467
|
event: 'streaming-end',
|