@clawchatsai/connector 0.0.44 → 0.0.46
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.js +65 -14
package/package.json
CHANGED
package/server.js
CHANGED
|
@@ -3969,6 +3969,7 @@ export function createApp(config = {}) {
|
|
|
3969
3969
|
this._externalBroadcastTargets = [];
|
|
3970
3970
|
this.streamState = new Map();
|
|
3971
3971
|
this.activityLogs = new Map();
|
|
3972
|
+
this._pendingMediaUrls = new Map(); // sessionKey → string[] of MEDIA: paths captured during streaming
|
|
3972
3973
|
setInterval(() => {
|
|
3973
3974
|
const cutoff = Date.now() - 10 * 60 * 1000;
|
|
3974
3975
|
for (const [runId, log] of this.activityLogs) {
|
|
@@ -4065,6 +4066,43 @@ export function createApp(config = {}) {
|
|
|
4065
4066
|
if (!content || !content.trim()) { console.log(`Skipping empty assistant response for thread ${parsed.threadId}`); return; }
|
|
4066
4067
|
const now = Date.now();
|
|
4067
4068
|
|
|
4069
|
+
// Check if the final assembled message content contains MEDIA: lines
|
|
4070
|
+
console.log('[ClawChats] saveAssistantMessage rawContent:', JSON.stringify(content.slice(0, 300)), 'rawMsgContent:', JSON.stringify(JSON.stringify(message?.content)?.slice(0, 300)));
|
|
4071
|
+
console.log('[ClawChats] saveAssistantMessage msgKeys:', Object.keys(message||{}), 'msgMediaUrls:', JSON.stringify(message?.mediaUrls), 'msgMediaUrl:', message?.mediaUrl);
|
|
4072
|
+
|
|
4073
|
+
// Extract MEDIA: paths from the final complete message content (if gateway preserves them here)
|
|
4074
|
+
let attachments = null;
|
|
4075
|
+
const mediaMatches = [...content.matchAll(/^MEDIA:\s*(\S+)/gm)].map(m => m[1].trim()).filter(p => /\.\w{1,10}$/.test(p));
|
|
4076
|
+
if (mediaMatches.length > 0) {
|
|
4077
|
+
console.log('[ClawChats] MEDIA paths from final content:', JSON.stringify(mediaMatches));
|
|
4078
|
+
attachments = mediaMatches.map(filePath => {
|
|
4079
|
+
const name = path.basename(filePath);
|
|
4080
|
+
const ext = (name.split('.').pop() || '').toLowerCase();
|
|
4081
|
+
const type = ['jpg','jpeg','png','gif','webp','svg','bmp','ico'].includes(ext) ? 'image'
|
|
4082
|
+
: ['mp3','wav','ogg','aac','m4a','flac','opus'].includes(ext) ? 'audio'
|
|
4083
|
+
: ['mp4','webm','mov','avi','mkv'].includes(ext) ? 'video' : 'file';
|
|
4084
|
+
return { path: filePath, name, type };
|
|
4085
|
+
});
|
|
4086
|
+
}
|
|
4087
|
+
|
|
4088
|
+
// Fallback: use MEDIA: paths captured from delta stream
|
|
4089
|
+
if (!attachments) {
|
|
4090
|
+
const paths = this._pendingMediaUrls.get(sessionKey);
|
|
4091
|
+
this._pendingMediaUrls.delete(sessionKey);
|
|
4092
|
+
if (paths?.length) {
|
|
4093
|
+
attachments = paths.map(filePath => {
|
|
4094
|
+
const name = path.basename(filePath);
|
|
4095
|
+
const ext = (name.split('.').pop() || '').toLowerCase();
|
|
4096
|
+
const type = ['jpg','jpeg','png','gif','webp','svg','bmp','ico'].includes(ext) ? 'image'
|
|
4097
|
+
: ['mp3','wav','ogg','aac','m4a','flac','opus'].includes(ext) ? 'audio'
|
|
4098
|
+
: ['mp4','webm','mov','avi','mkv'].includes(ext) ? 'video' : 'file';
|
|
4099
|
+
return { path: filePath, name, type };
|
|
4100
|
+
});
|
|
4101
|
+
}
|
|
4102
|
+
} else {
|
|
4103
|
+
this._pendingMediaUrls.delete(sessionKey); // clean up either way
|
|
4104
|
+
}
|
|
4105
|
+
|
|
4068
4106
|
// Check for pending activity message
|
|
4069
4107
|
const pendingMsg = db.prepare(`
|
|
4070
4108
|
SELECT id, metadata FROM messages
|
|
@@ -4084,13 +4122,15 @@ export function createApp(config = {}) {
|
|
|
4084
4122
|
if (lastAssistantIdx >= 0) metadata.activityLog.splice(lastAssistantIdx, 1);
|
|
4085
4123
|
metadata.activitySummary = this.generateActivitySummary(metadata.activityLog);
|
|
4086
4124
|
}
|
|
4125
|
+
if (attachments) metadata.attachments = attachments;
|
|
4087
4126
|
db.prepare('UPDATE messages SET content = ?, metadata = ?, timestamp = ? WHERE id = ?')
|
|
4088
4127
|
.run(content, JSON.stringify(metadata), now, pendingMsg.id);
|
|
4089
4128
|
messageId = pendingMsg.id;
|
|
4090
4129
|
} else {
|
|
4091
4130
|
// No pending activity — normal INSERT (simple responses, no tools)
|
|
4092
4131
|
messageId = seq != null ? `gw-${parsed.threadId}-${seq}` : `gw-${parsed.threadId}-${now}`;
|
|
4093
|
-
|
|
4132
|
+
const metaStr = attachments ? JSON.stringify({ attachments }) : null;
|
|
4133
|
+
db.prepare(`INSERT INTO messages (id, thread_id, role, content, status, metadata, timestamp, created_at) VALUES (?, ?, 'assistant', ?, 'sent', ?, ?, ?) ON CONFLICT(id) DO UPDATE SET content = excluded.content, metadata = COALESCE(excluded.metadata, metadata), timestamp = excluded.timestamp`).run(messageId, parsed.threadId, content, metaStr, now, now);
|
|
4094
4134
|
}
|
|
4095
4135
|
|
|
4096
4136
|
try {
|
|
@@ -4132,6 +4172,8 @@ export function createApp(config = {}) {
|
|
|
4132
4172
|
} catch (e) { console.error(`Failed to save error marker:`, e.message); }
|
|
4133
4173
|
}
|
|
4134
4174
|
|
|
4175
|
+
// _scanRecentWorkspaceFiles removed — MEDIA: paths captured from delta stream instead
|
|
4176
|
+
|
|
4135
4177
|
generateThreadTitle(db, threadId, workspace, skipHeuristic = false) {
|
|
4136
4178
|
const thread = db.prepare('SELECT title FROM threads WHERE id = ?').get(threadId);
|
|
4137
4179
|
if (!thread) return;
|
|
@@ -4182,7 +4224,25 @@ export function createApp(config = {}) {
|
|
|
4182
4224
|
if (!this.activityLogs.has(runId)) this.activityLogs.set(runId, { sessionKey, steps: [], startTime: Date.now() });
|
|
4183
4225
|
const log = this.activityLogs.get(runId);
|
|
4184
4226
|
if (stream === 'assistant') {
|
|
4227
|
+
// Check data.mediaUrls — gateway populates this via splitMediaFromOutput on each streaming event.
|
|
4228
|
+
// Partial path events (e.g. ["/"]) fail the extension filter; complete-path events pass.
|
|
4229
|
+
const incomingUrls = Array.isArray(data?.mediaUrls) ? data.mediaUrls : (data?.mediaUrl ? [data.mediaUrl] : []);
|
|
4230
|
+
for (const p of incomingUrls) {
|
|
4231
|
+
if (typeof p === 'string' && /\.\w{1,10}$/.test(p)) {
|
|
4232
|
+
if (!log._seenMediaPaths) log._seenMediaPaths = new Set();
|
|
4233
|
+
if (!log._seenMediaPaths.has(p)) {
|
|
4234
|
+
log._seenMediaPaths.add(p);
|
|
4235
|
+
if (!this._pendingMediaUrls.has(sessionKey)) this._pendingMediaUrls.set(sessionKey, []);
|
|
4236
|
+
this._pendingMediaUrls.get(sessionKey).push(p);
|
|
4237
|
+
console.log('[ClawChats] MEDIA path captured from mediaUrls:', p);
|
|
4238
|
+
}
|
|
4239
|
+
}
|
|
4240
|
+
}
|
|
4241
|
+
if (incomingUrls.length > 0) console.log('[ClawChats] mediaUrls on event:', JSON.stringify(incomingUrls));
|
|
4185
4242
|
const text = data?.text || '';
|
|
4243
|
+
const delta = data?.delta || '';
|
|
4244
|
+
// Log if a MEDIA: directive appears in text or delta (must look like an actual path, not just the word)
|
|
4245
|
+
if (/MEDIA:\s*[./~a-zA-Z]/.test(text.slice(-80)) || /MEDIA:\s*[./~a-zA-Z]/.test(delta)) console.log('[ClawChats] MEDIA in stream! text tail:', JSON.stringify(text.slice(-80)), 'delta:', JSON.stringify(delta));
|
|
4186
4246
|
if (text) {
|
|
4187
4247
|
let currentSegment = log._currentAssistantSegment;
|
|
4188
4248
|
if (!currentSegment || currentSegment._sealed) {
|
|
@@ -4209,6 +4269,7 @@ export function createApp(config = {}) {
|
|
|
4209
4269
|
if (log._currentAssistantSegment && !log._currentAssistantSegment._sealed) { log._currentAssistantSegment._sealed = true; }
|
|
4210
4270
|
const step = { type: 'tool', timestamp: Date.now(), name: data?.name || 'unknown', phase: data?.phase || 'start', toolCallId: data?.toolCallId, meta: data?.meta, isError: data?.isError || false };
|
|
4211
4271
|
if (data?.phase === 'result') {
|
|
4272
|
+
if (data?.name === 'exec') console.log('[ClawChats] exec tool result meta:', JSON.stringify(data?.meta));
|
|
4212
4273
|
const existing = log.steps.findLast(s => s.toolCallId === data.toolCallId && (s.phase === 'start' || s.phase === 'running'));
|
|
4213
4274
|
if (existing) { existing.phase = 'done'; existing.resultMeta = data?.meta; existing.isError = data?.isError || false; existing.durationMs = Date.now() - existing.timestamp; }
|
|
4214
4275
|
else { step.phase = 'done'; log.steps.push(step); }
|
|
@@ -4221,6 +4282,7 @@ export function createApp(config = {}) {
|
|
|
4221
4282
|
}
|
|
4222
4283
|
if (stream === 'lifecycle') {
|
|
4223
4284
|
if (data?.phase === 'end' || data?.phase === 'error') {
|
|
4285
|
+
// MEDIA: paths already captured during delta streaming — nothing to do here
|
|
4224
4286
|
if (log._currentAssistantSegment && !log._currentAssistantSegment._sealed) log._currentAssistantSegment._sealed = true;
|
|
4225
4287
|
const lastAssistantIdx = log.steps.findLastIndex(s => s.type === 'assistant');
|
|
4226
4288
|
if (lastAssistantIdx >= 0) log.steps.splice(lastAssistantIdx, 1);
|
|
@@ -4547,10 +4609,7 @@ export function createApp(config = {}) {
|
|
|
4547
4609
|
}
|
|
4548
4610
|
|
|
4549
4611
|
// ── Browser WebSocket setup (shared logic for standalone and plugin) ────────
|
|
4550
|
-
|
|
4551
|
-
const _clawchatsHintedSessions = new Set();
|
|
4552
|
-
|
|
4553
|
-
function _setupBrowserWs(wssInstance) {
|
|
4612
|
+
function _setupBrowserWs(wssInstance) {
|
|
4554
4613
|
wssInstance.on('connection', (ws) => {
|
|
4555
4614
|
console.log('Browser client connected');
|
|
4556
4615
|
_gatewayClient.addBrowserClient(ws);
|
|
@@ -4580,15 +4639,7 @@ export function createApp(config = {}) {
|
|
|
4580
4639
|
if (msg.action === 'debug-start') { const result = _debugLogger.start(msg.ts, ws); if (result.error === 'already-active') ws.send(JSON.stringify({ type: 'clawchats', event: 'debug-error', error: 'Recording already active in another tab', sessionId: result.sessionId })); else ws.send(JSON.stringify({ type: 'clawchats', event: 'debug-started', sessionId: result.sessionId })); return; }
|
|
4581
4640
|
if (msg.action === 'debug-dump') { const { sessionId, files } = _debugLogger.saveDump(msg); ws.send(JSON.stringify({ type: 'clawchats', event: 'debug-saved', sessionId, files })); return; }
|
|
4582
4641
|
}
|
|
4583
|
-
//
|
|
4584
|
-
if (msg.type === 'req' && msg.method === 'chat.send' && typeof msg.params?.message === 'string') {
|
|
4585
|
-
const sk = msg.params.sessionKey;
|
|
4586
|
-
if (sk && !_clawchatsHintedSessions.has(sk)) {
|
|
4587
|
-
_clawchatsHintedSessions.add(sk);
|
|
4588
|
-
msg.params.message += '\n[ClawChats context: MEDIA: tags are stripped by the gateway and will NOT display. To show images/files inline, use markdown syntax:  for workspace files or  for any path. Always use this instead of MEDIA:]';
|
|
4589
|
-
forwardStr = JSON.stringify(msg);
|
|
4590
|
-
}
|
|
4591
|
-
}
|
|
4642
|
+
// (no hint injection — mediaUrls are captured from agent events directly)
|
|
4592
4643
|
} catch { /* Not JSON or not a ClawChats message, forward as-is */ }
|
|
4593
4644
|
_gatewayClient.sendToGateway(forwardStr);
|
|
4594
4645
|
});
|