linkshell-cli 0.2.122 → 0.2.123

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.
@@ -529,6 +529,49 @@ function textFromBlocks(blocks: AgentContentBlock[]): string {
529
529
  .join("\n");
530
530
  }
531
531
 
532
+ function contentBlocksFromValue(value: unknown): AgentContentBlock[] {
533
+ if (typeof value === "string") {
534
+ return value.trim() ? [{ type: "text", text: value }] : [];
535
+ }
536
+ if (Array.isArray(value)) {
537
+ return value.flatMap((entry) => contentBlocksFromValue(entry));
538
+ }
539
+ const raw = asRecord(value);
540
+ if (!raw) return [];
541
+ const rawType = firstString(raw, ["type", "kind"]);
542
+ const normalizedType = normalizedIdentifier(rawType);
543
+ if (normalizedType === "image" || normalizedType === "inputimage" || normalizedType === "outputimage") {
544
+ const data = firstString(raw, [
545
+ "data",
546
+ "url",
547
+ "uri",
548
+ "imageUrl",
549
+ "image_url",
550
+ "base64",
551
+ ]);
552
+ const mimeType = firstString(raw, ["mimeType", "mime_type", "mediaType", "media_type"]);
553
+ const text = firstString(raw, ["text", "alt", "caption", "name"]);
554
+ return [{ type: "image", data, mimeType, text }];
555
+ }
556
+ if (normalizedType === "text" || normalizedType === "outputtext" || normalizedType === "inputtext") {
557
+ const text = firstString(raw, ["text", "content", "message"]);
558
+ return text ? [{ type: "text", text }] : [];
559
+ }
560
+ const nested = raw.content ?? raw.contentItems ?? raw.parts;
561
+ if (Array.isArray(nested)) return contentBlocksFromValue(nested);
562
+ const text = firstString(raw, ["text", "message", "content"]);
563
+ return text ? [{ type: "text", text }] : [];
564
+ }
565
+
566
+ function contentBlocksFromItem(item: Record<string, unknown>): AgentContentBlock[] {
567
+ for (const key of ["content", "contentItems", "parts", "message"]) {
568
+ const blocks = contentBlocksFromValue(item[key]);
569
+ if (blocks.length > 0) return blocks;
570
+ }
571
+ const text = firstString(item, ["text", "message"]);
572
+ return text ? [{ type: "text", text }] : [];
573
+ }
574
+
532
575
  function protocolSupportsImages(protocol: AgentProtocol | undefined): boolean {
533
576
  return protocol === "codex-app-server" ||
534
577
  protocol === "claude-agent-sdk" ||
@@ -2391,20 +2434,24 @@ export class AgentWorkspaceProxy {
2391
2434
  if (!conversationId) return;
2392
2435
  const itemId = firstString(item, ["id"]) ?? id("msg");
2393
2436
  const existing = this.findItem(conversationId, itemId);
2394
- const content = firstString(item, ["text", "content", "message"]) ?? existing?.text;
2395
- if (!content) return;
2437
+ const content = contentBlocksFromItem(item);
2438
+ const nextContent = content.length > 0
2439
+ ? content
2440
+ : existing?.content ?? (existing?.text ? [{ type: "text", text: existing.text }] : []);
2441
+ const text = textFromBlocks(nextContent);
2442
+ if (!nextContent.length && !text) return;
2396
2443
  this.upsertItem(conversationId, {
2397
2444
  id: itemId,
2398
2445
  conversationId,
2399
2446
  type: "message",
2400
2447
  role: "assistant",
2401
- content: [{ type: "text", text: content }],
2402
- text: content,
2448
+ content: nextContent,
2449
+ text,
2403
2450
  createdAt: existing?.createdAt ?? Date.now(),
2404
2451
  updatedAt: Date.now(),
2405
2452
  isStreaming: streaming,
2406
2453
  });
2407
- this.updateConversationPreview(conversationId, content, streaming ? "running" : "idle");
2454
+ this.updateConversationPreview(conversationId, text || "图片附件", streaming ? "running" : "idle");
2408
2455
  }
2409
2456
 
2410
2457
  private handleSessionUpdate(params: unknown): void {
@@ -2413,7 +2460,8 @@ export class AgentWorkspaceProxy {
2413
2460
  const text =
2414
2461
  firstString(raw, ["delta", "text", "content", "message"]) ??
2415
2462
  firstString(nested, ["delta", "text", "content", "message"]);
2416
- if (!text) return;
2463
+ const content = contentBlocksFromItem(raw);
2464
+ if (!text && content.length === 0) return;
2417
2465
  const conversationId = this.conversationIdFromParams(raw) ?? this.fallbackConversationId();
2418
2466
  if (!conversationId) return;
2419
2467
  if (firstString(raw, ["toolName", "tool", "name"])) {
@@ -2430,18 +2478,20 @@ export class AgentWorkspaceProxy {
2430
2478
  return;
2431
2479
  }
2432
2480
  const role = raw.role === "user" || raw.role === "system" ? raw.role : "assistant";
2481
+ const blocks = content.length > 0 ? content : [{ type: "text" as const, text }];
2482
+ const preview = textFromBlocks(blocks);
2433
2483
  this.upsertItem(conversationId, {
2434
2484
  id: firstString(raw, ["messageId", "id"]) ?? id("msg"),
2435
2485
  conversationId,
2436
2486
  type: "message",
2437
2487
  role,
2438
- content: [{ type: "text", text }],
2439
- text,
2488
+ content: blocks,
2489
+ text: preview,
2440
2490
  createdAt: Date.now(),
2441
2491
  updatedAt: Date.now(),
2442
2492
  isStreaming: raw.done === false || raw.isStreaming === true,
2443
2493
  });
2444
- this.updateConversationPreview(conversationId, text, raw.done === true ? "idle" : "running");
2494
+ this.updateConversationPreview(conversationId, preview || "图片附件", raw.done === true ? "idle" : "running");
2445
2495
  }
2446
2496
 
2447
2497
  private handleSemanticSystemItem(