chatccc 0.2.48 → 0.2.49

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "chatccc",
3
- "version": "0.2.48",
3
+ "version": "0.2.49",
4
4
  "description": "Feishu bot bridge for Claude Code",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -311,7 +311,7 @@ describe("buildSessionsCard", () => {
311
311
 
312
312
  it("returns valid JSON with session listing", () => {
313
313
  const card = buildSessionsCard([
314
- { sessionId: "abc123", chatName: "test-group", active: true, turnCount: 5, elapsedSeconds: 120, model: "Claude Opus 4.7", tool: "claude" },
314
+ { sessionId: "abc123", chatName: "test-group", chatId: "oc_test123", active: true, turnCount: 5, elapsedSeconds: 120, model: "Claude Opus 4.7", tool: "claude" },
315
315
  ]);
316
316
  const parsed = JSON.parse(card);
317
317
  expect(parsed.elements[0].text.content).toContain("共 **1** 个会话");
@@ -322,7 +322,7 @@ describe("buildSessionsCard", () => {
322
322
 
323
323
  it("shows idle status for inactive sessions", () => {
324
324
  const card = buildSessionsCard([
325
- { sessionId: "xyz", chatName: "", active: false, turnCount: 0, elapsedSeconds: null, model: "Claude Sonnet 4.6", tool: "claude" },
325
+ { sessionId: "xyz", chatName: "", chatId: "oc_xyz", active: false, turnCount: 0, elapsedSeconds: null, model: "Claude Sonnet 4.6", tool: "claude" },
326
326
  ]);
327
327
  const parsed = JSON.parse(card);
328
328
  expect(parsed.elements[0].text.content).toContain("⚪ 空闲");
@@ -330,7 +330,7 @@ describe("buildSessionsCard", () => {
330
330
 
331
331
  it("shows elapsed time for active sessions", () => {
332
332
  const card = buildSessionsCard([
333
- { sessionId: "active123", chatName: "", active: true, turnCount: 3, elapsedSeconds: 95, model: "Claude Opus 4.7", tool: "claude" },
333
+ { sessionId: "active123", chatName: "", chatId: "oc_active123", active: true, turnCount: 3, elapsedSeconds: 95, model: "Claude Opus 4.7", tool: "claude" },
334
334
  ]);
335
335
  const parsed = JSON.parse(card);
336
336
  expect(parsed.elements[0].text.content).toContain("1分35秒");
@@ -338,8 +338,8 @@ describe("buildSessionsCard", () => {
338
338
 
339
339
  it("separates Claude Code and Cursor sessions", () => {
340
340
  const card = buildSessionsCard([
341
- { sessionId: "c1", chatName: "", active: false, turnCount: 1, elapsedSeconds: null, model: "(留空)", tool: "claude" },
342
- { sessionId: "c2", chatName: "", active: false, turnCount: 2, elapsedSeconds: null, model: "claude-opus-4-7-max", tool: "cursor" },
341
+ { sessionId: "c1", chatName: "", chatId: "oc_c1", active: false, turnCount: 1, elapsedSeconds: null, model: "(留空)", tool: "claude" },
342
+ { sessionId: "c2", chatName: "", chatId: "oc_c2", active: false, turnCount: 2, elapsedSeconds: null, model: "claude-opus-4-7-max", tool: "cursor" },
343
343
  ]);
344
344
  const parsed = JSON.parse(card);
345
345
  const content: string = parsed.elements[0].text.content;
@@ -349,7 +349,7 @@ describe("buildSessionsCard", () => {
349
349
 
350
350
  it("omits Cursor section when no Cursor sessions", () => {
351
351
  const card = buildSessionsCard([
352
- { sessionId: "c1", chatName: "", active: false, turnCount: 1, elapsedSeconds: null, model: "(留空)", tool: "claude" },
352
+ { sessionId: "c1", chatName: "", chatId: "oc_c1", active: false, turnCount: 1, elapsedSeconds: null, model: "(留空)", tool: "claude" },
353
353
  ]);
354
354
  const parsed = JSON.parse(card);
355
355
  const content: string = parsed.elements[0].text.content;
@@ -359,15 +359,30 @@ describe("buildSessionsCard", () => {
359
359
 
360
360
  it("displays chatName when provided", () => {
361
361
  const card = buildSessionsCard([
362
- { sessionId: "abc123", chatName: "帮我写代码-src", active: false, turnCount: 2, elapsedSeconds: null, model: "Claude Opus 4.7", tool: "claude" },
362
+ { sessionId: "abc123", chatName: "帮我写代码-src", chatId: "oc_abc123", active: false, turnCount: 2, elapsedSeconds: null, model: "Claude Opus 4.7", tool: "claude" },
363
363
  ]);
364
364
  const parsed = JSON.parse(card);
365
365
  expect(parsed.elements[0].text.content).toContain("帮我写代码-src");
366
366
  });
367
367
 
368
+ it("shows (群聊) tag for group chat sessions and not for private chats", () => {
369
+ const card = buildSessionsCard([
370
+ { sessionId: "g1", chatName: "group-chat", chatId: "oc_group1", active: false, turnCount: 1, elapsedSeconds: null, model: "Claude Opus 4.7", tool: "claude" },
371
+ { sessionId: "p1", chatName: "private-chat", chatId: "ou_private1", active: false, turnCount: 1, elapsedSeconds: null, model: "Claude Opus 4.7", tool: "claude" },
372
+ ]);
373
+ const content: string = JSON.parse(card).elements[0].text.content;
374
+ expect(content).toContain("(群聊)");
375
+ // 群聊会话包含 (群聊)
376
+ expect(content).toMatch(/group-chat.*\(群聊\)/);
377
+ // 私聊会话不包含 (群聊)
378
+ expect(content).toMatch(/private-chat/);
379
+ const afterPrivateChat = content.split("private-chat")[1];
380
+ expect(afterPrivateChat).not.toContain("(群聊)");
381
+ });
382
+
368
383
  it("includes /session help text in non-empty card", () => {
369
384
  const card = buildSessionsCard([
370
- { sessionId: "abc123", chatName: "", active: false, turnCount: 2, elapsedSeconds: null, model: "Claude Opus 4.7", tool: "claude" },
385
+ { sessionId: "abc123", chatName: "", chatId: "oc_abc123", active: false, turnCount: 2, elapsedSeconds: null, model: "Claude Opus 4.7", tool: "claude" },
371
386
  ]);
372
387
  const parsed = JSON.parse(card);
373
388
  expect(parsed.elements[2].text.content).toContain("/session 数字");
package/src/session.ts CHANGED
@@ -525,7 +525,7 @@ export async function runAgentSession(
525
525
  ensureDisplayLoop(sessionId);
526
526
 
527
527
  // 设置最后活跃群头像为 busy
528
- const activeCid = getLastActiveChat(sessionId);
528
+ const activeCid = getLastActiveChat(sessionId) ?? getChatsForSession(sessionId)[0];
529
529
  if (activeCid) {
530
530
  setChatAvatar(token, activeCid, tool, "busy").catch(() => {});
531
531
  }
@@ -617,7 +617,7 @@ export async function runAgentSession(
617
617
  running: false,
618
618
  });
619
619
  }
620
- const active1 = getLastActiveChat(sessionId);
620
+ const active1 = getLastActiveChat(sessionId) ?? getChatsForSession(sessionId)[0];
621
621
  if (active1) setChatAvatar(token, active1, tool, "idle").catch(() => {});
622
622
  console.log(`[${ts()}] Session ${sessionId} stopped (content chunks: ${state.chunkCount})`);
623
623
  if (tid) logTrace(tid, "SESSION_END", { sessionId, outcome: "stopped", chunks: state.chunkCount });
@@ -634,7 +634,7 @@ export async function runAgentSession(
634
634
  running: false,
635
635
  });
636
636
  }
637
- const active2 = getLastActiveChat(sessionId);
637
+ const active2 = getLastActiveChat(sessionId) ?? getChatsForSession(sessionId)[0];
638
638
  if (active2) setChatAvatar(token, active2, tool, "idle").catch(() => {});
639
639
  console.log(`[${ts()}] Session ${sessionId} stream complete (content chunks: ${state.chunkCount})`);
640
640
  if (tid) logTrace(tid, "SESSION_END", { sessionId, chunks: state.chunkCount, finalTextLen: finalReply.length });
@@ -664,6 +664,12 @@ export function ensureDisplayLoop(sessionId: string): void {
664
664
  if (state.status !== "running") {
665
665
  clearInterval(interval);
666
666
  displayLoops.delete(sessionId);
667
+ // 兜底:lastActiveChatMap 可能因进程重启丢失,从 registry 映射恢复头像
668
+ const fallbackChat = getChatsForSession(sessionId)[0];
669
+ if (fallbackChat) {
670
+ const t = await import("./feishu-platform.ts").then(m => m.getTenantAccessToken());
671
+ setChatAvatar(t, fallbackChat, state.tool, "idle").catch(() => {});
672
+ }
667
673
  }
668
674
  return;
669
675
  }