linkshell-cli 0.2.121 → 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.
@@ -330,6 +330,50 @@ function textFromBlocks(blocks) {
330
330
  .filter(Boolean)
331
331
  .join("\n");
332
332
  }
333
+ function contentBlocksFromValue(value) {
334
+ if (typeof value === "string") {
335
+ return value.trim() ? [{ type: "text", text: value }] : [];
336
+ }
337
+ if (Array.isArray(value)) {
338
+ return value.flatMap((entry) => contentBlocksFromValue(entry));
339
+ }
340
+ const raw = asRecord(value);
341
+ if (!raw)
342
+ return [];
343
+ const rawType = firstString(raw, ["type", "kind"]);
344
+ const normalizedType = normalizedIdentifier(rawType);
345
+ if (normalizedType === "image" || normalizedType === "inputimage" || normalizedType === "outputimage") {
346
+ const data = firstString(raw, [
347
+ "data",
348
+ "url",
349
+ "uri",
350
+ "imageUrl",
351
+ "image_url",
352
+ "base64",
353
+ ]);
354
+ const mimeType = firstString(raw, ["mimeType", "mime_type", "mediaType", "media_type"]);
355
+ const text = firstString(raw, ["text", "alt", "caption", "name"]);
356
+ return [{ type: "image", data, mimeType, text }];
357
+ }
358
+ if (normalizedType === "text" || normalizedType === "outputtext" || normalizedType === "inputtext") {
359
+ const text = firstString(raw, ["text", "content", "message"]);
360
+ return text ? [{ type: "text", text }] : [];
361
+ }
362
+ const nested = raw.content ?? raw.contentItems ?? raw.parts;
363
+ if (Array.isArray(nested))
364
+ return contentBlocksFromValue(nested);
365
+ const text = firstString(raw, ["text", "message", "content"]);
366
+ return text ? [{ type: "text", text }] : [];
367
+ }
368
+ function contentBlocksFromItem(item) {
369
+ for (const key of ["content", "contentItems", "parts", "message"]) {
370
+ const blocks = contentBlocksFromValue(item[key]);
371
+ if (blocks.length > 0)
372
+ return blocks;
373
+ }
374
+ const text = firstString(item, ["text", "message"]);
375
+ return text ? [{ type: "text", text }] : [];
376
+ }
333
377
  function protocolSupportsImages(protocol) {
334
378
  return protocol === "codex-app-server" ||
335
379
  protocol === "claude-agent-sdk" ||
@@ -1713,8 +1757,11 @@ export class AgentWorkspaceProxy {
1713
1757
  if (agentSessionId && conversationId) {
1714
1758
  this.conversationByAgentSessionId.set(agentSessionId, conversationId);
1715
1759
  const conversation = this.conversations.get(conversationId);
1716
- if (conversation)
1760
+ if (conversation) {
1717
1761
  conversation.agentSessionId = agentSessionId;
1762
+ conversation.lastActivityAt = Date.now();
1763
+ this.emitConversation(conversation);
1764
+ }
1718
1765
  }
1719
1766
  return;
1720
1767
  }
@@ -2058,28 +2105,33 @@ export class AgentWorkspaceProxy {
2058
2105
  return;
2059
2106
  const itemId = firstString(item, ["id"]) ?? id("msg");
2060
2107
  const existing = this.findItem(conversationId, itemId);
2061
- const content = firstString(item, ["text", "content", "message"]) ?? existing?.text;
2062
- if (!content)
2108
+ const content = contentBlocksFromItem(item);
2109
+ const nextContent = content.length > 0
2110
+ ? content
2111
+ : existing?.content ?? (existing?.text ? [{ type: "text", text: existing.text }] : []);
2112
+ const text = textFromBlocks(nextContent);
2113
+ if (!nextContent.length && !text)
2063
2114
  return;
2064
2115
  this.upsertItem(conversationId, {
2065
2116
  id: itemId,
2066
2117
  conversationId,
2067
2118
  type: "message",
2068
2119
  role: "assistant",
2069
- content: [{ type: "text", text: content }],
2070
- text: content,
2120
+ content: nextContent,
2121
+ text,
2071
2122
  createdAt: existing?.createdAt ?? Date.now(),
2072
2123
  updatedAt: Date.now(),
2073
2124
  isStreaming: streaming,
2074
2125
  });
2075
- this.updateConversationPreview(conversationId, content, streaming ? "running" : "idle");
2126
+ this.updateConversationPreview(conversationId, text || "图片附件", streaming ? "running" : "idle");
2076
2127
  }
2077
2128
  handleSessionUpdate(params) {
2078
2129
  const raw = asRecord(params) ?? {};
2079
2130
  const nested = asRecord(raw.params) ?? {};
2080
2131
  const text = firstString(raw, ["delta", "text", "content", "message"]) ??
2081
2132
  firstString(nested, ["delta", "text", "content", "message"]);
2082
- if (!text)
2133
+ const content = contentBlocksFromItem(raw);
2134
+ if (!text && content.length === 0)
2083
2135
  return;
2084
2136
  const conversationId = this.conversationIdFromParams(raw) ?? this.fallbackConversationId();
2085
2137
  if (!conversationId)
@@ -2098,18 +2150,20 @@ export class AgentWorkspaceProxy {
2098
2150
  return;
2099
2151
  }
2100
2152
  const role = raw.role === "user" || raw.role === "system" ? raw.role : "assistant";
2153
+ const blocks = content.length > 0 ? content : [{ type: "text", text }];
2154
+ const preview = textFromBlocks(blocks);
2101
2155
  this.upsertItem(conversationId, {
2102
2156
  id: firstString(raw, ["messageId", "id"]) ?? id("msg"),
2103
2157
  conversationId,
2104
2158
  type: "message",
2105
2159
  role,
2106
- content: [{ type: "text", text }],
2107
- text,
2160
+ content: blocks,
2161
+ text: preview,
2108
2162
  createdAt: Date.now(),
2109
2163
  updatedAt: Date.now(),
2110
2164
  isStreaming: raw.done === false || raw.isStreaming === true,
2111
2165
  });
2112
- this.updateConversationPreview(conversationId, text, raw.done === true ? "idle" : "running");
2166
+ this.updateConversationPreview(conversationId, preview || "图片附件", raw.done === true ? "idle" : "running");
2113
2167
  }
2114
2168
  handleSemanticSystemItem(item, status, streaming) {
2115
2169
  const itemType = firstString(item, ["type"]);