chatcc-agent 0.5.1 → 0.5.3
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/src/claude-bridge.js +64 -18
- package/src/index.js +25 -3
package/package.json
CHANGED
package/src/claude-bridge.js
CHANGED
|
@@ -7,6 +7,8 @@ const path = require('path');
|
|
|
7
7
|
const { StreamBuffer } = require('./stream-buffer');
|
|
8
8
|
const { ProcessQueue } = require('./process-queue');
|
|
9
9
|
const { PERMISSION_MODES, PERMISSION_ACTIONS } = require('./constants');
|
|
10
|
+
const { startTrace, getTraceId } = require('./trace');
|
|
11
|
+
const logUploader = require('./log-uploader');
|
|
10
12
|
|
|
11
13
|
class ClaudeBridge {
|
|
12
14
|
|
|
@@ -219,6 +221,7 @@ class ClaudeBridge {
|
|
|
219
221
|
});
|
|
220
222
|
this.imClient.sendCustomMessage(replyTo, 'agent_tool', {
|
|
221
223
|
msg_id: msgID, session_id: sessionID,
|
|
224
|
+
request_id: entry.requestID || msgID,
|
|
222
225
|
tool: toolName, input: toolInput,
|
|
223
226
|
tool_use_id: requestId,
|
|
224
227
|
state: 'denied', workspace_violation: true,
|
|
@@ -251,6 +254,7 @@ class ClaudeBridge {
|
|
|
251
254
|
|
|
252
255
|
this.imClient.sendCustomMessage(replyTo, 'agent_tool', {
|
|
253
256
|
msg_id: msgID, session_id: sessionID,
|
|
257
|
+
request_id: entry.requestID || msgID,
|
|
254
258
|
tool: toolName, input: toolInput,
|
|
255
259
|
tool_use_id: clientKey,
|
|
256
260
|
state: 'pending', requires_approval: true,
|
|
@@ -306,13 +310,36 @@ class ClaudeBridge {
|
|
|
306
310
|
const msgID = crypto.randomUUID();
|
|
307
311
|
const replyTo = from;
|
|
308
312
|
const images = imagePaths || [];
|
|
313
|
+
const requestID = options.requestId || msgID;
|
|
314
|
+
const traceID = options.traceId || getTraceId() || requestID;
|
|
315
|
+
logUploader.collect('INFO', '[ChatTrace] queued user_text', {
|
|
316
|
+
tag: 'ChatTrace',
|
|
317
|
+
request_id: requestID,
|
|
318
|
+
session_id: sessionID,
|
|
319
|
+
msg_id: msgID,
|
|
320
|
+
});
|
|
309
321
|
|
|
310
322
|
// Send stream_start when the task actually starts (not on message receipt),
|
|
311
323
|
// so that rapid-fire messages don't have their stream_start ignored
|
|
312
324
|
// by the client's StreamManager which keys on sessionID.
|
|
313
325
|
const taskFn = async () => {
|
|
314
|
-
|
|
315
|
-
|
|
326
|
+
return startTrace(traceID, async () => {
|
|
327
|
+
logUploader.collect('INFO', '[ChatTrace] started claude', {
|
|
328
|
+
tag: 'ChatTrace',
|
|
329
|
+
request_id: requestID,
|
|
330
|
+
session_id: sessionID,
|
|
331
|
+
msg_id: msgID,
|
|
332
|
+
});
|
|
333
|
+
await this.imClient.sendCustomMessage(replyTo, 'stream_start', {
|
|
334
|
+
msg_id: msgID,
|
|
335
|
+
session_id: sessionID,
|
|
336
|
+
request_id: requestID,
|
|
337
|
+
});
|
|
338
|
+
return this._spawnClaude(msgID, replyTo, content, sessionID, {
|
|
339
|
+
cwd, claudeSessionId, images, onClaudeSessionId, onResumeFailed, onSessionTitle,
|
|
340
|
+
workspaceRestricted, permissions, requestId: requestID,
|
|
341
|
+
});
|
|
342
|
+
});
|
|
316
343
|
};
|
|
317
344
|
|
|
318
345
|
try {
|
|
@@ -321,6 +348,7 @@ class ClaudeBridge {
|
|
|
321
348
|
console.error('[Bridge] Queue error:', err.message, err.stack);
|
|
322
349
|
await this.imClient.sendCustomMessage(replyTo, 'stream_end', {
|
|
323
350
|
msg_id: msgID,
|
|
351
|
+
request_id: requestID,
|
|
324
352
|
exit_code: 1,
|
|
325
353
|
session_id: sessionID,
|
|
326
354
|
error: err.message,
|
|
@@ -464,7 +492,7 @@ class ClaudeBridge {
|
|
|
464
492
|
|
|
465
493
|
_spawnClaude(msgID, replyTo, content, sessionID, options = {}) {
|
|
466
494
|
return new Promise((resolve, reject) => {
|
|
467
|
-
const { cwd, claudeSessionId, images, onClaudeSessionId, onResumeFailed, onSessionTitle, workspaceRestricted = false, permissions = null } = options;
|
|
495
|
+
const { cwd, claudeSessionId, images, onClaudeSessionId, onResumeFailed, onSessionTitle, workspaceRestricted = false, permissions = null, requestId = msgID } = options;
|
|
468
496
|
const usePermissionControl = permissions?.defaultMode !== PERMISSION_MODES.AUTO_ALL;
|
|
469
497
|
const useStreamInput = usePermissionControl;
|
|
470
498
|
const args = [
|
|
@@ -510,10 +538,10 @@ class ClaudeBridge {
|
|
|
510
538
|
const sendStreamEnd = async (payload) => {
|
|
511
539
|
if (streamEndSent) return;
|
|
512
540
|
streamEndSent = true;
|
|
513
|
-
await this.imClient.sendCustomMessage(replyTo, 'stream_end', { msg_id: msgID, session_id: sessionID, ...payload });
|
|
541
|
+
await this.imClient.sendCustomMessage(replyTo, 'stream_end', { msg_id: msgID, session_id: sessionID, request_id: requestId, ...payload });
|
|
514
542
|
};
|
|
515
543
|
|
|
516
|
-
this._activeProcesses.set(msgID, { proc, sessionID, cwd: cwd || process.cwd(), workspaceRestricted, usePermissionControl, sendStreamEnd });
|
|
544
|
+
this._activeProcesses.set(msgID, { proc, sessionID, requestID: requestId, cwd: cwd || process.cwd(), workspaceRestricted, usePermissionControl, sendStreamEnd });
|
|
517
545
|
|
|
518
546
|
// In stream-json input mode, send the user message via stdin after spawn.
|
|
519
547
|
// Build content blocks: text first, then images as image media blocks.
|
|
@@ -541,6 +569,7 @@ class ClaudeBridge {
|
|
|
541
569
|
const streamBuffer = new StreamBuffer(async (delta) => {
|
|
542
570
|
await this.imClient.sendCustomMessage(replyTo, 'stream_chunk', {
|
|
543
571
|
msg_id: msgID,
|
|
572
|
+
request_id: requestId,
|
|
544
573
|
delta,
|
|
545
574
|
session_id: sessionID,
|
|
546
575
|
});
|
|
@@ -552,7 +581,7 @@ class ClaudeBridge {
|
|
|
552
581
|
if (!line.trim()) return;
|
|
553
582
|
try {
|
|
554
583
|
const event = JSON.parse(line);
|
|
555
|
-
this._handleEvent(event, replyTo, msgID, streamBuffer, sessionID, onClaudeSessionId, onSessionTitle, cwd, workspaceRestricted, permissions, sendStreamEnd);
|
|
584
|
+
this._handleEvent(event, replyTo, msgID, streamBuffer, sessionID, onClaudeSessionId, onSessionTitle, cwd, workspaceRestricted, permissions, sendStreamEnd, requestId);
|
|
556
585
|
} catch (e) {}
|
|
557
586
|
});
|
|
558
587
|
|
|
@@ -617,7 +646,14 @@ class ClaudeBridge {
|
|
|
617
646
|
} catch (e) {
|
|
618
647
|
console.error('[Bridge] Failed to send stream_end:', e.message, e.stack);
|
|
619
648
|
}
|
|
620
|
-
|
|
649
|
+
logUploader.collect(code === 0 ? 'INFO' : 'WARN', '[ChatTrace] ended claude', {
|
|
650
|
+
tag: 'ChatTrace',
|
|
651
|
+
request_id: requestId,
|
|
652
|
+
session_id: sessionID,
|
|
653
|
+
msg_id: msgID,
|
|
654
|
+
exit_code: code,
|
|
655
|
+
});
|
|
656
|
+
console.log('[Bridge] Claude Code exited:', code, 'request=', requestId);
|
|
621
657
|
|
|
622
658
|
for (const imgPath of images) {
|
|
623
659
|
try { fs.unlinkSync(imgPath); } catch (e) {}
|
|
@@ -627,13 +663,13 @@ class ClaudeBridge {
|
|
|
627
663
|
}); // end Promise wrapper
|
|
628
664
|
}
|
|
629
665
|
|
|
630
|
-
_handleEvent(event, replyTo, msgID, streamBuffer, sessionID, onClaudeSessionId, onSessionTitle, cwd, workspaceRestricted = false, permissions = null, sendStreamEnd = null) {
|
|
666
|
+
_handleEvent(event, replyTo, msgID, streamBuffer, sessionID, onClaudeSessionId, onSessionTitle, cwd, workspaceRestricted = false, permissions = null, sendStreamEnd = null, requestId = msgID) {
|
|
631
667
|
switch (event.type) {
|
|
632
668
|
case 'system':
|
|
633
669
|
this._handleSystemEvent(event, onClaudeSessionId);
|
|
634
670
|
break;
|
|
635
671
|
case 'assistant':
|
|
636
|
-
this._handleAssistantEvent(event, replyTo, msgID, streamBuffer, sessionID, cwd, workspaceRestricted, permissions, sendStreamEnd);
|
|
672
|
+
this._handleAssistantEvent(event, replyTo, msgID, streamBuffer, sessionID, cwd, workspaceRestricted, permissions, sendStreamEnd, requestId);
|
|
637
673
|
break;
|
|
638
674
|
case 'sdk_control_request': {
|
|
639
675
|
const req = event.request;
|
|
@@ -656,13 +692,13 @@ class ClaudeBridge {
|
|
|
656
692
|
break;
|
|
657
693
|
}
|
|
658
694
|
case 'user':
|
|
659
|
-
this._handleUserEvent(event, replyTo, msgID, streamBuffer, sessionID);
|
|
695
|
+
this._handleUserEvent(event, replyTo, msgID, streamBuffer, sessionID, requestId);
|
|
660
696
|
break;
|
|
661
697
|
case 'result':
|
|
662
698
|
this._handleResultEvent(event, streamBuffer, onSessionTitle, msgID);
|
|
663
699
|
break;
|
|
664
700
|
case 'error':
|
|
665
|
-
this._handleErrorEvent(event, replyTo, msgID, sessionID, streamBuffer, sendStreamEnd);
|
|
701
|
+
this._handleErrorEvent(event, replyTo, msgID, sessionID, streamBuffer, sendStreamEnd, requestId);
|
|
666
702
|
break;
|
|
667
703
|
default:
|
|
668
704
|
console.warn(`[Bridge] Unknown Claude event type: ${event.type}`, JSON.stringify(event).slice(0, 200));
|
|
@@ -676,7 +712,7 @@ class ClaudeBridge {
|
|
|
676
712
|
}
|
|
677
713
|
}
|
|
678
714
|
|
|
679
|
-
_handleAssistantEvent(event, replyTo, msgID, streamBuffer, sessionID, cwd, workspaceRestricted, permissions, sendStreamEnd = null) {
|
|
715
|
+
_handleAssistantEvent(event, replyTo, msgID, streamBuffer, sessionID, cwd, workspaceRestricted, permissions, sendStreamEnd = null, requestId = msgID) {
|
|
680
716
|
const msg = event.message;
|
|
681
717
|
if (!msg?.content) return;
|
|
682
718
|
const entry = this._activeProcesses.get(msgID);
|
|
@@ -693,6 +729,7 @@ class ClaudeBridge {
|
|
|
693
729
|
this.imClient.sendCustomMessage(replyTo, 'user_question', {
|
|
694
730
|
msg_id: msgID,
|
|
695
731
|
session_id: sessionID,
|
|
732
|
+
request_id: requestId,
|
|
696
733
|
tool_use_id: block.id,
|
|
697
734
|
questions: block.input?.questions || [],
|
|
698
735
|
}, {
|
|
@@ -702,13 +739,13 @@ class ClaudeBridge {
|
|
|
702
739
|
ignoreBadge: false,
|
|
703
740
|
}).catch(e => console.error('[Bridge] user_question:', e.message, e.stack));
|
|
704
741
|
} else {
|
|
705
|
-
this._handleToolUseBlock(block, replyTo, msgID, streamBuffer, sessionID, cwd, workspaceRestricted, permissions, useControl, sendStreamEnd);
|
|
742
|
+
this._handleToolUseBlock(block, replyTo, msgID, streamBuffer, sessionID, cwd, workspaceRestricted, permissions, useControl, sendStreamEnd, requestId);
|
|
706
743
|
}
|
|
707
744
|
}
|
|
708
745
|
}
|
|
709
746
|
}
|
|
710
747
|
|
|
711
|
-
_handleToolUseBlock(block, replyTo, msgID, streamBuffer, sessionID, cwd, workspaceRestricted, permissions, useControl, sendStreamEnd = null) {
|
|
748
|
+
_handleToolUseBlock(block, replyTo, msgID, streamBuffer, sessionID, cwd, workspaceRestricted, permissions, useControl, sendStreamEnd = null, requestId = msgID) {
|
|
712
749
|
streamBuffer.flush();
|
|
713
750
|
|
|
714
751
|
if (useControl) {
|
|
@@ -738,6 +775,7 @@ class ClaudeBridge {
|
|
|
738
775
|
const payload = {
|
|
739
776
|
msg_id: msgID,
|
|
740
777
|
session_id: sessionID,
|
|
778
|
+
request_id: requestId,
|
|
741
779
|
tool: block.name,
|
|
742
780
|
input: block.input,
|
|
743
781
|
tool_use_id: block.id,
|
|
@@ -800,7 +838,7 @@ class ClaudeBridge {
|
|
|
800
838
|
return diff;
|
|
801
839
|
}
|
|
802
840
|
|
|
803
|
-
_handleUserEvent(event, replyTo, msgID, streamBuffer, sessionID) {
|
|
841
|
+
_handleUserEvent(event, replyTo, msgID, streamBuffer, sessionID, requestId = msgID) {
|
|
804
842
|
const msg = event.message;
|
|
805
843
|
if (!msg?.content) return;
|
|
806
844
|
for (const block of msg.content) {
|
|
@@ -832,6 +870,7 @@ class ClaudeBridge {
|
|
|
832
870
|
const payload = {
|
|
833
871
|
msg_id: msgID,
|
|
834
872
|
session_id: sessionID,
|
|
873
|
+
request_id: requestId,
|
|
835
874
|
tool_use_id: block.tool_use_id,
|
|
836
875
|
state: block.is_error ? 'failed' : 'completed',
|
|
837
876
|
output: output.slice(0, 4000),
|
|
@@ -878,15 +917,22 @@ class ClaudeBridge {
|
|
|
878
917
|
}
|
|
879
918
|
}
|
|
880
919
|
|
|
881
|
-
_handleErrorEvent(event, replyTo, msgID, sessionID, streamBuffer, sendStreamEnd = null) {
|
|
920
|
+
_handleErrorEvent(event, replyTo, msgID, sessionID, streamBuffer, sendStreamEnd = null, requestId = msgID) {
|
|
882
921
|
streamBuffer.flush();
|
|
883
922
|
streamBuffer.end();
|
|
884
923
|
const payload = { exit_code: 1, error: event.error || event.message || 'Unknown error' };
|
|
885
924
|
if (sendStreamEnd) {
|
|
886
925
|
sendStreamEnd(payload).catch(() => {});
|
|
887
926
|
} else {
|
|
888
|
-
this.imClient.sendCustomMessage(replyTo, 'stream_end', { msg_id: msgID, session_id: sessionID, ...payload }).catch(() => {});
|
|
889
|
-
}
|
|
927
|
+
this.imClient.sendCustomMessage(replyTo, 'stream_end', { msg_id: msgID, session_id: sessionID, request_id: requestId, ...payload }).catch(() => {});
|
|
928
|
+
}
|
|
929
|
+
logUploader.collect('ERROR', '[ChatTrace] claude error event', {
|
|
930
|
+
tag: 'ChatTrace',
|
|
931
|
+
request_id: requestId,
|
|
932
|
+
session_id: sessionID,
|
|
933
|
+
msg_id: msgID,
|
|
934
|
+
error: event.error || event.message || 'Unknown error',
|
|
935
|
+
});
|
|
890
936
|
console.error('[Bridge] Claude error:', event.error || event.message);
|
|
891
937
|
}
|
|
892
938
|
}
|
package/src/index.js
CHANGED
|
@@ -16,6 +16,7 @@ const path = require('path');
|
|
|
16
16
|
const os = require('os');
|
|
17
17
|
const crypto = require('crypto');
|
|
18
18
|
const logUploader = require('./log-uploader');
|
|
19
|
+
const trace = require('./trace');
|
|
19
20
|
|
|
20
21
|
const isDaemon = process.argv.includes('--daemon');
|
|
21
22
|
const CHATCC_DIR = path.join(os.homedir(), '.chatcc');
|
|
@@ -295,9 +296,20 @@ async function main() {
|
|
|
295
296
|
imClient.onMessage(async (from, data) => {
|
|
296
297
|
// 建立请求级 traceId 上下文:沿用入站 trace_id(跨 hop 续链),没有则生成。
|
|
297
298
|
// 后续所有出站 IM/HTTP 调用、日志都会自动带上。
|
|
298
|
-
|
|
299
|
+
trace.enterWithTrace(data && (data.trace_id || data.request_id));
|
|
299
300
|
const qs = bridge.getQueueStatus();
|
|
300
|
-
|
|
301
|
+
const requestID = data.request_id || '';
|
|
302
|
+
const traceID = trace.getTraceId() || '';
|
|
303
|
+
console.log(`[Message] from=${from} cc_type=${data.cc_type} request=${requestID} trace=${traceID} queue_active=${qs.activeCount} queue_waiting=${qs.queuedCount}`);
|
|
304
|
+
logUploader.collect('INFO', `[ChatTrace] received cc_type=${data.cc_type}`, {
|
|
305
|
+
tag: 'ChatTrace',
|
|
306
|
+
from,
|
|
307
|
+
cc_type: data.cc_type,
|
|
308
|
+
request_id: requestID,
|
|
309
|
+
session_id: data.session_id || '',
|
|
310
|
+
queue_active: qs.activeCount,
|
|
311
|
+
queue_waiting: qs.queuedCount,
|
|
312
|
+
});
|
|
301
313
|
|
|
302
314
|
// All messages require authenticated sender
|
|
303
315
|
if (pairedClientIDs.size === 0 || !pairedClientIDs.has(from)) {
|
|
@@ -380,7 +392,17 @@ async function main() {
|
|
|
380
392
|
}).catch(e => console.error('[Session] Failed to send session_update:', e.message, e.stack));
|
|
381
393
|
};
|
|
382
394
|
|
|
383
|
-
const bridgeOpts = {
|
|
395
|
+
const bridgeOpts = {
|
|
396
|
+
cwd,
|
|
397
|
+
claudeSessionId: claudeSid,
|
|
398
|
+
onClaudeSessionId,
|
|
399
|
+
onResumeFailed,
|
|
400
|
+
onSessionTitle,
|
|
401
|
+
workspaceRestricted: isRestricted,
|
|
402
|
+
permissions: sessionPermissions,
|
|
403
|
+
requestId: data.request_id,
|
|
404
|
+
traceId: traceID,
|
|
405
|
+
};
|
|
384
406
|
if (imageUrls.length > 0) {
|
|
385
407
|
Promise.all(imageUrls.map(u => downloadImage(u, tempFiles).catch(e => {
|
|
386
408
|
console.error('[Image] Download failed for', u, ':', e.message, e.stack);
|