@clawchatsai/connector 0.0.50 → 0.0.52
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/dist/index.js +19 -6
- package/package.json +1 -1
- package/server.js +27 -7
package/dist/index.js
CHANGED
|
@@ -325,15 +325,28 @@ async function stopClawChats(ctx) {
|
|
|
325
325
|
* guard checks MediaPath/MediaPaths but not inline base64 attachments).
|
|
326
326
|
* Injecting a minimal placeholder ensures the agent run proceeds.
|
|
327
327
|
*/
|
|
328
|
+
// Track which ClawChats thread sessions have received the file-hint (once per session).
|
|
329
|
+
const _hintedSessions = new Set();
|
|
328
330
|
function normalizeGatewayPayload(raw) {
|
|
329
331
|
try {
|
|
330
332
|
const parsed = JSON.parse(raw);
|
|
331
|
-
if (parsed.method === 'chat.send' &&
|
|
332
|
-
|
|
333
|
-
parsed.params
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
333
|
+
if (parsed.method === 'chat.send' && typeof parsed.params?.message === 'string') {
|
|
334
|
+
// Fix image-only messages: inject placeholder so gateway doesn't reject empty body.
|
|
335
|
+
if (Array.isArray(parsed.params?.attachments) &&
|
|
336
|
+
parsed.params.attachments.length > 0 &&
|
|
337
|
+
!parsed.params.message?.trim()) {
|
|
338
|
+
parsed.params.message = '[Image]';
|
|
339
|
+
return JSON.stringify(parsed);
|
|
340
|
+
}
|
|
341
|
+
// Hint injection: teach the agent to emit MEDIA:/path via exec after creating files.
|
|
342
|
+
// Injected once per ClawChats thread session (~15 tokens, invisible to user).
|
|
343
|
+
const sk = parsed.params.sessionKey || '';
|
|
344
|
+
if (sk.includes(':chat:') && !_hintedSessions.has(sk)) {
|
|
345
|
+
_hintedSessions.add(sk);
|
|
346
|
+
parsed.params.message += '\n[ClawChats: after creating or saving any file, run: exec echo "MEDIA:/absolute/path/to/file" — this delivers it inline to the chat]';
|
|
347
|
+
console.log(`[clawchats] hint-injected for session ${sk}`);
|
|
348
|
+
return JSON.stringify(parsed);
|
|
349
|
+
}
|
|
337
350
|
}
|
|
338
351
|
}
|
|
339
352
|
catch {
|
package/package.json
CHANGED
package/server.js
CHANGED
|
@@ -4064,15 +4064,31 @@ export function createApp(config = {}) {
|
|
|
4064
4064
|
let content = extractContent(message);
|
|
4065
4065
|
if (!content || !content.trim()) { console.log(`Skipping empty assistant response for thread ${parsed.threadId}`); return; }
|
|
4066
4066
|
|
|
4067
|
-
// Attach
|
|
4068
|
-
//
|
|
4067
|
+
// Attach media captured by the after_tool_call hook (MEDIA: lines from exec stdout).
|
|
4068
|
+
// Classify by extension: images → inline markdown, audio/docs → metadata.attachments.
|
|
4069
|
+
// Always clear the stash regardless of whether paths were found (prevents cross-turn leaks).
|
|
4069
4070
|
const _mediaStash = config.mediaStash;
|
|
4070
4071
|
const _pendingPaths = _mediaStash?.get(sessionKey) ?? [];
|
|
4071
4072
|
_mediaStash?.delete(sessionKey);
|
|
4073
|
+
const _IMAGE_EXTS = new Set(['png','jpg','jpeg','gif','webp','bmp','svg','ico','avif','tiff']);
|
|
4074
|
+
const _AUDIO_EXTS = new Set(['mp3','wav','ogg','m4a','flac','aac','opus','wma']);
|
|
4075
|
+
const _pendingAttachments = []; // non-image files → metadata.attachments
|
|
4076
|
+
const imagePaths = []; // images → inline markdown (declared outside if for broadcast scope)
|
|
4072
4077
|
if (_pendingPaths.length > 0) {
|
|
4073
|
-
const
|
|
4074
|
-
|
|
4075
|
-
|
|
4078
|
+
for (const p of _pendingPaths) {
|
|
4079
|
+
const ext = (p.split('.').pop() || '').toLowerCase();
|
|
4080
|
+
if (_IMAGE_EXTS.has(ext)) {
|
|
4081
|
+
imagePaths.push(p);
|
|
4082
|
+
} else {
|
|
4083
|
+
const name = p.split('/').pop();
|
|
4084
|
+
const type = _AUDIO_EXTS.has(ext) ? 'audio' : 'file';
|
|
4085
|
+
_pendingAttachments.push({ path: p, name, type });
|
|
4086
|
+
}
|
|
4087
|
+
}
|
|
4088
|
+
if (imagePaths.length > 0) {
|
|
4089
|
+
content = content.trimEnd() + '\n\n' + imagePaths.map(p => ``).join('\n');
|
|
4090
|
+
}
|
|
4091
|
+
console.log(`[clawchats] media-attach: ${imagePaths.length} image(s) inline, ${_pendingAttachments.length} attachment(s) for ${sessionKey}`);
|
|
4076
4092
|
}
|
|
4077
4093
|
|
|
4078
4094
|
const now = Date.now();
|
|
@@ -4096,13 +4112,17 @@ export function createApp(config = {}) {
|
|
|
4096
4112
|
if (lastAssistantIdx >= 0) metadata.activityLog.splice(lastAssistantIdx, 1);
|
|
4097
4113
|
metadata.activitySummary = this.generateActivitySummary(metadata.activityLog);
|
|
4098
4114
|
}
|
|
4115
|
+
if (_pendingAttachments.length > 0) {
|
|
4116
|
+
metadata.attachments = [...(metadata.attachments || []), ..._pendingAttachments];
|
|
4117
|
+
}
|
|
4099
4118
|
db.prepare('UPDATE messages SET content = ?, metadata = ?, timestamp = ? WHERE id = ?')
|
|
4100
4119
|
.run(content, JSON.stringify(metadata), now, pendingMsg.id);
|
|
4101
4120
|
messageId = pendingMsg.id;
|
|
4102
4121
|
} else {
|
|
4103
4122
|
// No pending activity — normal INSERT (simple responses, no tools)
|
|
4104
4123
|
messageId = seq != null ? `gw-${parsed.threadId}-${seq}` : `gw-${parsed.threadId}-${now}`;
|
|
4105
|
-
|
|
4124
|
+
const newMeta = _pendingAttachments.length > 0 ? JSON.stringify({ attachments: _pendingAttachments }) : null;
|
|
4125
|
+
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, newMeta, now, now);
|
|
4106
4126
|
}
|
|
4107
4127
|
|
|
4108
4128
|
try {
|
|
@@ -4112,7 +4132,7 @@ export function createApp(config = {}) {
|
|
|
4112
4132
|
const threadInfo = db.prepare('SELECT title FROM threads WHERE id = ?').get(parsed.threadId);
|
|
4113
4133
|
const unreadCount = db.prepare('SELECT COUNT(*) as c FROM unread_messages WHERE thread_id = ?').get(parsed.threadId).c;
|
|
4114
4134
|
const preview = content.length > 120 ? content.substring(0, 120) + '...' : content;
|
|
4115
|
-
this.broadcastToBrowsers(JSON.stringify({ type: 'clawchats', event: 'message-saved', threadId: parsed.threadId, workspace: parsed.workspace, messageId, timestamp: now, title: threadInfo?.title || 'Chat', preview, unreadCount }));
|
|
4135
|
+
this.broadcastToBrowsers(JSON.stringify({ type: 'clawchats', event: 'message-saved', threadId: parsed.threadId, workspace: parsed.workspace, messageId, timestamp: now, title: threadInfo?.title || 'Chat', preview, unreadCount, updatedContent: imagePaths.length > 0 ? content : undefined, updatedAttachments: _pendingAttachments.length > 0 ? _pendingAttachments : undefined }));
|
|
4116
4136
|
const workspaceUnreadTotal = db.prepare('SELECT COALESCE(SUM(unread_count), 0) as total FROM threads').get().total;
|
|
4117
4137
|
this.broadcastToBrowsers(JSON.stringify({ type: 'clawchats', event: 'unread-update', workspace: parsed.workspace, threadId: parsed.threadId, messageId, action: 'new', unreadCount, workspaceUnreadTotal, title: threadInfo?.title || 'Chat', preview, timestamp: now }));
|
|
4118
4138
|
console.log(`Saved assistant message to ${parsed.workspace}/${parsed.threadId} (${pendingMsg ? 'merged into pending' : 'seq: ' + seq})`);
|