larkway 0.3.22 → 0.3.23

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/README.md CHANGED
@@ -8,7 +8,7 @@
8
8
 
9
9
  You @ the bot in a Feishu thread. It runs on your machine — reading your real codebase, executing commands, opening MRs — and posts the result back. You define what the agent knows and what it can do. Larkway just carries the messages.
10
10
 
11
- **Current release: v0.3.22**
11
+ **Current release: v0.3.23**
12
12
 
13
13
  ---
14
14
 
package/README.zh.md CHANGED
@@ -8,7 +8,7 @@
8
8
 
9
9
  你在飞书话题里 @ bot,它在你的机器上运行——读真实代码库、执行命令、开 MR——把结果贴回飞书。你定义 agent 知道什么、能做什么。Larkway 只负责传递消息。
10
10
 
11
- **当前版本:v0.3.22**
11
+ **当前版本:v0.3.23**
12
12
 
13
13
  ---
14
14
 
package/dist/cli/index.js CHANGED
@@ -111185,6 +111185,7 @@ var init_channelCardClient = __esm({
111185
111185
  // src/lark/channelCardKitClient.ts
111186
111186
  import { createHash } from "node:crypto";
111187
111187
  function isRetryable2(err2) {
111188
+ if (err2 instanceof CardKitReplyConversionError) return true;
111188
111189
  if (!(err2 instanceof Error)) return false;
111189
111190
  const msg = err2.message.toLowerCase();
111190
111191
  if (msg.includes("200810")) return true;
@@ -111227,10 +111228,21 @@ async function withCardKitRetry(label, fn, opts) {
111227
111228
  }
111228
111229
  throw lastErr;
111229
111230
  }
111230
- var ChannelCardKitClient;
111231
+ var CardKitReplyConversionError, ChannelCardKitClient;
111231
111232
  var init_channelCardKitClient = __esm({
111232
111233
  "src/lark/channelCardKitClient.ts"() {
111233
111234
  "use strict";
111235
+ CardKitReplyConversionError = class extends Error {
111236
+ messageId;
111237
+ constructor(messageId, cause) {
111238
+ super(
111239
+ `[channel.cardkit] card.idConvert returned no card_id (messageId=${messageId})`
111240
+ );
111241
+ this.name = "CardKitReplyConversionError";
111242
+ this.messageId = messageId;
111243
+ this.cause = cause;
111244
+ }
111245
+ };
111234
111246
  ChannelCardKitClient = class {
111235
111247
  resolveChannel;
111236
111248
  cardThreads;
@@ -111283,17 +111295,19 @@ var init_channelCardKitClient = __esm({
111283
111295
  }
111284
111296
  const converted = await withCardKitRetry(
111285
111297
  "idConvert",
111286
- () => this.channel().rawClient.cardkit.v1.card.idConvert({
111287
- data: { message_id: messageId }
111288
- }),
111298
+ async () => {
111299
+ const converted2 = await this.channel().rawClient.cardkit.v1.card.idConvert({
111300
+ data: { message_id: messageId }
111301
+ });
111302
+ if (!converted2.data?.card_id) {
111303
+ throw new CardKitReplyConversionError(messageId);
111304
+ }
111305
+ return converted2;
111306
+ },
111289
111307
  { maxAttempts: this.maxAttempts, baseDelayMs: this.baseDelayMs }
111290
111308
  );
111291
111309
  const cardId = converted.data?.card_id;
111292
- if (!cardId) {
111293
- throw new Error(
111294
- `[channel.cardkit] card.idConvert returned no card_id (messageId=${messageId})`
111295
- );
111296
- }
111310
+ if (!cardId) throw new CardKitReplyConversionError(messageId);
111297
111311
  this.cardThreads.set(messageId, opts.threadId ?? replyToMessageId);
111298
111312
  return { cardId, messageId };
111299
111313
  }
package/dist/main.js CHANGED
@@ -115422,7 +115422,22 @@ var ChannelCardClient = class {
115422
115422
 
115423
115423
  // src/lark/channelCardKitClient.ts
115424
115424
  import { createHash } from "node:crypto";
115425
+ var CardKitReplyConversionError = class extends Error {
115426
+ messageId;
115427
+ constructor(messageId, cause) {
115428
+ super(
115429
+ `[channel.cardkit] card.idConvert returned no card_id (messageId=${messageId})`
115430
+ );
115431
+ this.name = "CardKitReplyConversionError";
115432
+ this.messageId = messageId;
115433
+ this.cause = cause;
115434
+ }
115435
+ };
115436
+ function cardKitReplyConversionMessageId(err) {
115437
+ return err instanceof CardKitReplyConversionError ? err.messageId : void 0;
115438
+ }
115425
115439
  function isRetryable2(err) {
115440
+ if (err instanceof CardKitReplyConversionError) return true;
115426
115441
  if (!(err instanceof Error)) return false;
115427
115442
  const msg = err.message.toLowerCase();
115428
115443
  if (msg.includes("200810")) return true;
@@ -115520,17 +115535,19 @@ var ChannelCardKitClient = class {
115520
115535
  }
115521
115536
  const converted = await withCardKitRetry(
115522
115537
  "idConvert",
115523
- () => this.channel().rawClient.cardkit.v1.card.idConvert({
115524
- data: { message_id: messageId }
115525
- }),
115538
+ async () => {
115539
+ const converted2 = await this.channel().rawClient.cardkit.v1.card.idConvert({
115540
+ data: { message_id: messageId }
115541
+ });
115542
+ if (!converted2.data?.card_id) {
115543
+ throw new CardKitReplyConversionError(messageId);
115544
+ }
115545
+ return converted2;
115546
+ },
115526
115547
  { maxAttempts: this.maxAttempts, baseDelayMs: this.baseDelayMs }
115527
115548
  );
115528
115549
  const cardId = converted.data?.card_id;
115529
- if (!cardId) {
115530
- throw new Error(
115531
- `[channel.cardkit] card.idConvert returned no card_id (messageId=${messageId})`
115532
- );
115533
- }
115550
+ if (!cardId) throw new CardKitReplyConversionError(messageId);
115534
115551
  this.cardThreads.set(messageId, opts.threadId ?? replyToMessageId);
115535
115552
  return { cardId, messageId };
115536
115553
  }
@@ -116732,20 +116749,6 @@ var ChannelClient = class {
116732
116749
  };
116733
116750
 
116734
116751
  // src/lark/card.ts
116735
- function summarizeInput(input) {
116736
- if (input === null || input === void 0) return "";
116737
- let s;
116738
- if (typeof input === "string") {
116739
- s = input;
116740
- } else if (typeof input === "object") {
116741
- const obj = input;
116742
- const snippet2 = obj["command"] ?? obj["path"] ?? obj["file_path"] ?? obj["description"] ?? null;
116743
- s = snippet2 != null ? String(snippet2) : JSON.stringify(input);
116744
- } else {
116745
- s = String(input);
116746
- }
116747
- return s.length > 60 ? s.slice(0, 57) + "\u2026" : s;
116748
- }
116749
116752
  var MAX_CARD_ELEMENTS = 50;
116750
116753
  var CHOICE_MARKERS = ["A", "B", "C", "D", "E"];
116751
116754
  function choiceMarker(i) {
@@ -116896,7 +116899,7 @@ ${recent.join("\n")}` : recent.join("\n");
116896
116899
  } else if (opts.status === "thinking") {
116897
116900
  pushElement(elements, {
116898
116901
  tag: "markdown",
116899
- content: "\u{1F914} \u601D\u8003\u4E2D\u2026"
116902
+ content: "\u52AA\u529B\u56DE\u7B54\u4E2D..."
116900
116903
  });
116901
116904
  }
116902
116905
  if (opts.status === "failure" && opts.failureReason) {
@@ -116923,7 +116926,7 @@ ${recent.join("\n")}` : recent.join("\n");
116923
116926
  pushElement(elements, buildChoiceRow(opts.choices));
116924
116927
  }
116925
116928
  const headerColor = opts.status === "thinking" || opts.status === "streaming" ? "blue" : opts.status === "success" ? "green" : "red";
116926
- const statusText = opts.status === "thinking" ? "\u23F3 \u5904\u7406\u4E2D" : opts.status === "streaming" ? "\u{1F527} \u5904\u7406\u4E2D" : opts.status === "success" ? "\u2705 \u5B8C\u6210" : "\u274C \u51FA\u9519\u4E86";
116929
+ const statusText = opts.status === "thinking" ? "\u5904\u7406\u4E2D" : opts.status === "streaming" ? "\u56DE\u7B54\u4E2D" : opts.status === "success" ? "\u2705 \u5B8C\u6210" : "\u274C \u51FA\u9519\u4E86";
116927
116930
  const headerTitle = opts.titleOverride ?? statusText;
116928
116931
  const card = {
116929
116932
  schema: "2.0",
@@ -116948,7 +116951,6 @@ var CardHandleImpl = class {
116948
116951
  showToolSummary;
116949
116952
  state = {
116950
116953
  textBuffer: "",
116951
- currentRawMsgId: null,
116952
116954
  toolStatusLines: [],
116953
116955
  lastPatchAt: 0,
116954
116956
  pendingPatch: null
@@ -116960,7 +116962,7 @@ var CardHandleImpl = class {
116960
116962
  * before its own PATCH so the finalize is guaranteed to land LAST. Tracking
116961
116963
  * only "the latest" is insufficient: an older retry can still be in flight
116962
116964
  * after a newer live patch overwrote the pointer, then land after finalize and
116963
- * flip the Feishu card back to 🔧 处理中.
116965
+ * flip the Feishu card back to the in-progress render.
116964
116966
  */
116965
116967
  livePatchesInFlight = /* @__PURE__ */ new Set();
116966
116968
  constructor(opts) {
@@ -116972,8 +116974,9 @@ var CardHandleImpl = class {
116972
116974
  // ── Public: handle ────────────────────────────────────────────────────────
116973
116975
  handle(event) {
116974
116976
  if (this.finalized) return;
116975
- this.accumulate(event);
116976
- this.scheduleThrottledPatch();
116977
+ if (this.accumulate(event)) {
116978
+ this.scheduleThrottledPatch();
116979
+ }
116977
116980
  }
116978
116981
  // ── Public: finalize ─────────────────────────────────────────────────────
116979
116982
  async finalize(opts) {
@@ -117014,37 +117017,14 @@ var CardHandleImpl = class {
117014
117017
  }
117015
117018
  // ── Private: accumulate ───────────────────────────────────────────────────
117016
117019
  accumulate(event) {
117017
- if (event.type === "text_delta") {
117018
- const rawMsgId = extractRawMessageId(event.raw);
117019
- if (rawMsgId !== null && rawMsgId !== this.state.currentRawMsgId) {
117020
- if (this.state.textBuffer.length > 0) {
117021
- this.state.textBuffer += "\n\n";
117022
- }
117023
- this.state.currentRawMsgId = rawMsgId;
117024
- this.state.textBuffer += event.text;
117025
- } else {
117026
- if (this.state.currentRawMsgId === null) {
117027
- this.state.currentRawMsgId = rawMsgId;
117028
- }
117029
- const prevTurnEnd = this.findPrevTurnEnd();
117030
- this.state.textBuffer = this.state.textBuffer.slice(0, prevTurnEnd) + event.text;
117031
- }
117032
- } else if (event.type === "tool_use" && this.showToolSummary) {
117033
- const summary = summarizeInput(event.toolInput);
117034
- const line = summary ? `\u{1F527} ${event.toolName} ${summary}` : `\u{1F527} ${event.toolName}`;
117035
- this.state.toolStatusLines.push(line);
117020
+ if (event.type === "answer_delta") {
117021
+ this.state.textBuffer += event.text;
117022
+ return true;
117023
+ } else if (event.type === "answer_snapshot") {
117024
+ this.state.textBuffer = event.text;
117025
+ return true;
117036
117026
  }
117037
- }
117038
- /**
117039
- * Returns the character offset in textBuffer where the CURRENT turn's text
117040
- * starts. If there is only one turn, returns 0.
117041
- */
117042
- findPrevTurnEnd() {
117043
- if (!this.state.textBuffer) return 0;
117044
- const sep = "\n\n";
117045
- const lastSep = this.state.textBuffer.lastIndexOf(sep);
117046
- if (lastSep === -1) return 0;
117047
- return lastSep + sep.length;
117027
+ return false;
117048
117028
  }
117049
117029
  // ── Private: throttle ────────────────────────────────────────────────────
117050
117030
  scheduleThrottledPatch() {
@@ -117117,14 +117097,6 @@ var CardHandleImpl = class {
117117
117097
  await this.livePatchWithRetry(cardJsonStr);
117118
117098
  }
117119
117099
  };
117120
- function extractRawMessageId(raw) {
117121
- if (typeof raw !== "object" || raw === null) return null;
117122
- const rec = raw;
117123
- const msg = rec["message"];
117124
- if (typeof msg !== "object" || msg === null) return null;
117125
- const id = msg["id"];
117126
- return typeof id === "string" ? id : null;
117127
- }
117128
117100
  async function createCardWithRetry(outbound, replyToMessageId, cardJson, opts, maxAttempts = 3) {
117129
117101
  let lastErr;
117130
117102
  for (let attempt = 1; attempt <= maxAttempts; attempt++) {
@@ -117672,6 +117644,39 @@ function deriveTriggerFacts(parsed, isNewThread, larkCliProfile) {
117672
117644
  };
117673
117645
  }
117674
117646
 
117647
+ // src/agent/answerChannel.ts
117648
+ var ANSWER_BEGIN_MARKER = "LARKWAY_ANSWER_BEGIN";
117649
+ var ANSWER_END_MARKER = "LARKWAY_ANSWER_END";
117650
+ function stripLeadingNewline(text) {
117651
+ return text.replace(/^\r?\n/, "");
117652
+ }
117653
+ function stripTrailingNewline(text) {
117654
+ return text.replace(/\r?\n$/, "");
117655
+ }
117656
+ function markerLineIndex(text, marker) {
117657
+ const re = new RegExp(`(^|\\r?\\n)${marker.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}(\\r?\\n|$)`);
117658
+ const match = re.exec(text);
117659
+ if (!match || match.index == null) return null;
117660
+ const lineStart = match.index + (match[1]?.length ?? 0);
117661
+ const lineEnd = lineStart + marker.length;
117662
+ const after = lineEnd + (match[2]?.length ?? 0);
117663
+ return { start: lineStart, end: after };
117664
+ }
117665
+ function splitAnswerChannelText(text, raw) {
117666
+ const begin = markerLineIndex(text, ANSWER_BEGIN_MARKER);
117667
+ if (!begin) return [{ type: "internal_text", text, raw }];
117668
+ const before = stripTrailingNewline(text.slice(0, begin.start));
117669
+ const afterBegin = text.slice(begin.end);
117670
+ const end = markerLineIndex(afterBegin, ANSWER_END_MARKER);
117671
+ const answer = stripLeadingNewline(end ? afterBegin.slice(0, end.start) : afterBegin);
117672
+ const trailing = end ? stripLeadingNewline(afterBegin.slice(end.end)) : "";
117673
+ const events = [];
117674
+ if (before.trim()) events.push({ type: "internal_text", text: before, raw });
117675
+ events.push({ type: "answer_snapshot", text: stripTrailingNewline(answer), raw });
117676
+ if (trailing.trim()) events.push({ type: "internal_text", text: trailing, raw });
117677
+ return events;
117678
+ }
117679
+
117675
117680
  // src/claude/prompt.ts
117676
117681
  var MEMORY_CATEGORY_FILE_NAMES = [
117677
117682
  "preferences.md",
@@ -117698,11 +117703,16 @@ function renderStateContract(stateFilePath) {
117698
117703
  return [
117699
117704
  "<state-contract>",
117700
117705
  "\u4F60\u548C\u8FD0\u8425\u4E4B\u95F4\u7684\u754C\u9762\u662F\u98DE\u4E66\u8BDD\u9898\u91CC\u7684 response surface,\u8FD9\u662F\u4E00\u4E2A thin-channel \u5916\u58F3:",
117701
- "- \u9ED8\u8BA4\u4E3B\u56DE\u590D\u9762\u662F post/RichText:bridge \u8D77\u624B\u53D1\u4E00\u6761\u8F7B\u91CF\u201C\u6B63\u5728\u5904\u7406\u2026\u201Dpost,\u6267\u884C\u4E2D\u7528\u5206\u5757\u7EA7\u539F\u5730\u7F16\u8F91\u6A21\u62DF\u6D41\u5F0F\u8F93\u51FA,\u7ED3\u675F\u65F6\u7F16\u8F91\u6210\u5E72\u51C0\u7EC8\u7A3F\u3002",
117702
- "- \u5361\u7247\u662F\u4F8B\u5916\u8865\u5145\u9762,\u53EA\u5728\u9700\u8981\u79BB\u6563\u9009\u62E9/\u6309\u94AE(`choices`)\u6216 post \u8868\u8FBE\u4E0D\u4E86\u7684\u7ED3\u6784\u5316\u5185\u5BB9(`content_blocks`/`image_blocks`,\u4F8B\u5982\u56FE\u7247\u7EC4\u3001\u9A8C\u6536\u6E05\u5355\u3001dev \u9884\u89C8)\u65F6\u624D\u8865\u4E00\u5F20\u3002",
117703
- "- bridge \u8D1F\u8D23\u521B\u5EFA/\u7F16\u8F91 post\u3001\u5FC5\u8981\u65F6\u521B\u5EFA/\u66F4\u65B0\u98DE\u4E66\u5361\u7247\u3001\u8282\u6D41\u3001\u6E32\u67D3\u6B63\u6587\u3001\u628A choices \u8F6C\u6210\u6309\u94AE\u5E76\u628A\u70B9\u51FB\u503C\u56DE\u4F20\u7ED9\u4F60\u3002",
117706
+ "- \u9ED8\u8BA4\u4E3B\u56DE\u590D\u9762\u662F\u4E00\u5F20 CardKit \u6D41\u5F0F\u5361\u7247:bridge \u8D77\u624B\u53EA\u663E\u793A\u4E00\u884C\u201C\u52AA\u529B\u56DE\u7B54\u4E2D...\u201D,\u7B54\u6848\u901A\u9053\u4E00\u4EA7\u51FA\u5C31\u9010 token \u6D41\u5165\u540C\u4E00\u5F20\u5361,\u5B8C\u6210\u540E\u6536\u655B\u6210\u5E72\u51C0\u603B\u7ED3\u5361\u3002",
117707
+ "- \u8FD0\u884C\u4E2D\u7EDD\u4E0D\u5C55\u793A\u601D\u8003\u3001\u5DE5\u5177\u8BE6\u60C5\u3001\u8FDB\u5EA6 list\u3001\u672C\u5730\u8DEF\u5F84\u6216\u539F\u59CB runner text_delta;\u8FD9\u4E9B\u90FD\u5C5E\u4E8E\u5185\u90E8\u901A\u9053\u3002",
117704
117708
  "- \u4F60\u8D1F\u8D23\u628A\u6700\u7EC8\u7ED9\u8FD0\u8425\u770B\u7684\u6B63\u6587\u3001\u72B6\u6001\u3001\u4E0B\u4E00\u6B65\u95EE\u9898\u3001\u662F\u5426\u9700\u8981 choices/\u7ED3\u6784\u5316\u5361\u7247\u5199\u8FDB state.json\u3002",
117705
117709
  `\u4F60\u4E0D\u76F4\u63A5\u53D1/\u7F16\u8F91 bridge \u7BA1\u7406\u7684 post \u6216\u5361\u7247;\u4F60\u53EA\u5199 ${stateTarget},bridge \u8BFB\u5B83\u6765\u505A\u5B89\u5168\u6E32\u67D3\u3002`,
117710
+ "",
117711
+ "\u7B54\u6848\u6D41\u901A\u9053:",
117712
+ `- \u53EA\u6709\u771F\u6B63\u8981\u7ED9\u7528\u6237\u770B\u7684\u7B54\u6848\u6B63\u6587,\u624D\u5305\u5728\u72EC\u7ACB\u884C marker \u4E4B\u95F4\u8F93\u51FA\u5230 stdout: \`${ANSWER_BEGIN_MARKER}\` \u5230 \`${ANSWER_END_MARKER}\`\u3002`,
117713
+ "- marker \u5916\u7684\u53D9\u8FF0\u3001\u8BA1\u5212\u3001\u5DE5\u5177\u8BF4\u660E\u3001\u5185\u90E8\u5206\u6790\u90FD\u4F1A\u88AB bridge \u5F53\u4F5C internal_text,\u4E0D\u4F1A\u8FDB\u5165\u5361\u7247\u3002",
117714
+ "- \u5F53\u6700\u7EC8\u7B54\u6848\u8FD8\u6CA1\u51C6\u5907\u597D\u65F6\u53EF\u4EE5\u4E0D\u8F93\u51FA\u7B54\u6848 marker;\u5361\u7247\u53EA\u4FDD\u6301\u201C\u52AA\u529B\u56DE\u7B54\u4E2D...\u201D\u3002\u4E00\u65E6\u5F00\u59CB\u8F93\u51FA\u7B54\u6848 marker \u5185\u6B63\u6587,bridge \u4F1A\u7ACB\u5373\u6D41\u5F0F\u5C55\u793A\u3002",
117715
+ "- \u6BCF\u8F6E\u7ED3\u675F\u524D\u4ECD\u5FC5\u987B\u5199 state.json;\u6700\u7EC8\u6570\u636E\u4EE5 state.json \u7684 last_message/content_blocks/choices \u7B49\u4E3A\u51C6,\u7B54\u6848\u6D41\u53EA\u662F\u8FD0\u884C\u4E2D\u53EF\u89C1\u7684\u4F4E\u5EF6\u8FDF\u6B63\u6587\u901A\u9053\u3002",
117706
117716
  "**\u5B8C\u6210\u672C\u6B21\u54CD\u5E94\u524D\u5FC5\u987B**\u6839\u636E\u5F53\u524D\u5B9E\u9645\u72B6\u6001\u66F4\u65B0\u8FD9\u4E2A\u6587\u4EF6(\u539F\u5B50\u5199:\u5199 .tmp \u518D mv)\u3002",
117707
117717
  "",
117708
117718
  "\u4F60\u80FD\u5199\u7684\u5B57\u6BB5:",
@@ -117724,8 +117734,8 @@ function renderStateContract(stateFilePath) {
117724
117734
  "\u6302\u7740\u4E0D\u9000\u51FA = \u8BDD\u9898\u6C38\u8FDC\u5361\u5728\u300C\u6B63\u5728\u5904\u7406\u2026\u300D)\u3002",
117725
117735
  "",
117726
117736
  "\u5361\u7247\u5C55\u793A\u539F\u5219:",
117727
- "- \u8FD0\u884C\u4E2D\u4E3B\u9762\u662F post,bridge \u901A\u8FC7\u6574\u6761\u5BCC\u6587\u672C\u66FF\u6362\u505A\u5206\u5757\u5237\u65B0;\u5B83\u4E0D\u662F\u9010\u5B57 token streaming,\u4E0D\u8981\u4F9D\u8D56\u5DE5\u5177\u6D41/\u65E5\u5FD7\u8868\u8FBE\u4E1A\u52A1\u9636\u6BB5\u3002",
117728
- "- \u6700\u7EC8 post \u4EE5\u4F60\u7684 `last_message` \u4E3A\u4E3B;\u82E5\u5199\u4E86 `choices`\u3001`content_blocks` \u6216 `image_blocks`,bridge \u4F1A\u5728 post \u4E4B\u540E\u8865\u5361\u7247\u627F\u8F7D\u8FD9\u4E9B\u80FD\u529B\u3002",
117737
+ "- \u8FD0\u884C\u4E2D\u4E3B\u9762\u662F CardKit \u771F\u6D41\u5F0F\u5361\u7247;\u4E0D\u8981\u4F9D\u8D56\u5DE5\u5177\u6D41/\u65E5\u5FD7\u8868\u8FBE\u4E1A\u52A1\u9636\u6BB5,\u5B83\u4EEC\u4E0D\u4F1A\u663E\u793A\u3002",
117738
+ "- \u6700\u7EC8\u5361\u7247\u4EE5\u4F60\u7684 `last_message` \u4E3A\u4E3B;\u82E5\u5199\u4E86 `choices`\u3001`content_blocks` \u6216 `image_blocks`,bridge \u4F1A\u5728\u540C\u4E00\u5F20\u6700\u7EC8\u5361\u7247\u627F\u8F7D\u8FD9\u4E9B\u80FD\u529B\u3002",
117729
117739
  "- \u4E0D\u8981\u4F9D\u8D56 bridge \u4ECE\u8F93\u51FA\u91CC\u89E3\u6790\u4E1A\u52A1\u9636\u6BB5\u3001MR\u3001\u9884\u89C8\u5730\u5740\u6216\u4E0B\u4E00\u6B65\u52A8\u4F5C;\u9700\u8981\u5C55\u793A\u7684\u5185\u5BB9\u4F60\u81EA\u5DF1\u5199\u8FDB last_message\u3002",
117730
117740
  "- \u4E0D\u8981\u6C42\u56FA\u5B9A\u683C\u5F0F\u3002\u6839\u636E\u4EFB\u52A1\u9009\u62E9\u6700\u6E05\u695A\u7684\u8868\u8FBE:\u77ED\u7ED3\u8BBA\u3001\u5206\u70B9\u3001\u8868\u683C\u3001\u94FE\u63A5\u3001\u4E0B\u4E00\u6B65\u95EE\u9898\u90FD\u53EF\u4EE5\u3002",
117731
117741
  "- \u5982\u679C\u672C\u8F6E\u6D89\u53CA repo / \u4EE3\u7801 / \u6587\u6863\u4FEE\u6539,last_message \u5E94\u5305\u542B\u8DB3\u591F\u8BA9\u8FD0\u8425\u9A8C\u6536\u7684\u8BC1\u636E,\u4F8B\u5982\u4F60\u5B9E\u9645\u4F7F\u7528\u7684 workspace/repo\u3001\u5173\u952E diff \u6216\u94FE\u63A5\u3001\u8FD0\u884C\u8FC7\u7684\u6D4B\u8BD5/\u68C0\u67E5\u547D\u4EE4\u548C\u7ED3\u679C\u3002\u5177\u4F53\u8BC1\u636E\u7531\u4EFB\u52A1\u51B3\u5B9A;dogfood E2E \u7684\u4E25\u683C\u6E05\u5355\u53EA\u5728 dogfood guide \u4E2D\u8981\u6C42\u3002",
@@ -119047,8 +119057,9 @@ var CardKitFileSchema = external_exports.object({
119047
119057
  idempotencyKey: external_exports.string().min(1),
119048
119058
  sequence: external_exports.number().int().nonnegative().default(0),
119049
119059
  elements: external_exports.object({
119050
- status: external_exports.object({ elementId: external_exports.string().min(1) }),
119051
- thinking: external_exports.object({ elementId: external_exports.string().min(1) }),
119060
+ status: external_exports.object({ elementId: external_exports.string().min(1) }).optional(),
119061
+ thinking: external_exports.object({ elementId: external_exports.string().min(1) }).optional(),
119062
+ footer: external_exports.object({ elementId: external_exports.string().min(1) }).optional(),
119052
119063
  final: external_exports.object({ elementId: external_exports.string().min(1) })
119053
119064
  }).optional(),
119054
119065
  lastVisibleFallbackMessageId: external_exports.string().min(1).nullable().default(null),
@@ -119111,13 +119122,11 @@ async function deleteCardKitFile(worktreePath) {
119111
119122
  }
119112
119123
 
119113
119124
  // src/lark/cardkitSurface.ts
119114
- var CARDKIT_STATUS_ELEMENT_ID = "status_md";
119115
- var CARDKIT_THINKING_ELEMENT_ID = "thinking_md";
119116
119125
  var CARDKIT_FINAL_ELEMENT_ID = "final_md";
119126
+ var CARDKIT_FOOTER_ELEMENT_ID = "footer_md";
119117
119127
  var CARDKIT_CHOICES_ELEMENT_ID = "choices_slot";
119118
119128
  var MAX_CARD_BYTES = 30 * 1024;
119119
119129
  var FINAL_BUDGET_CHARS = 22e3;
119120
- var THINKING_BUDGET_CHARS = 6e3;
119121
119130
  var CHOICE_MARKERS2 = ["A", "B", "C", "D", "E"];
119122
119131
  function plainText2(content) {
119123
119132
  return { tag: "plain_text", content };
@@ -119164,8 +119173,7 @@ function truncateCardToBudget(card) {
119164
119173
  return cloned;
119165
119174
  }
119166
119175
  function buildCardKitInitialCard(opts = {}) {
119167
- const statusText = opts.statusText ?? "\u6B63\u5728\u5904\u7406\u2026";
119168
- const thinkingText = opts.thinkingText ?? "\u51C6\u5907\u542F\u52A8\u672C\u5730 agent\u3002";
119176
+ const footerText = opts.footerText ?? opts.statusText ?? "\u52AA\u529B\u56DE\u7B54\u4E2D...";
119169
119177
  return {
119170
119178
  schema: "2.0",
119171
119179
  config: {
@@ -119178,45 +119186,14 @@ function buildCardKitInitialCard(opts = {}) {
119178
119186
  print_strategy: "fast"
119179
119187
  }
119180
119188
  },
119181
- header: {
119182
- title: plainText2(opts.title ?? "\u5904\u7406\u4E2D"),
119183
- template: "blue"
119184
- },
119185
119189
  body: {
119186
119190
  elements: [
119187
- markdown(statusText, CARDKIT_STATUS_ELEMENT_ID),
119188
- markdown(thinkingText, CARDKIT_THINKING_ELEMENT_ID),
119189
- { tag: "hr" },
119190
- markdown(" ", CARDKIT_FINAL_ELEMENT_ID)
119191
+ markdown(" ", CARDKIT_FINAL_ELEMENT_ID),
119192
+ markdown(footerText, CARDKIT_FOOTER_ELEMENT_ID)
119191
119193
  ]
119192
119194
  }
119193
119195
  };
119194
119196
  }
119195
- function summarizeCardKitToolInput(input) {
119196
- if (input === null || input === void 0) return "";
119197
- let raw;
119198
- if (typeof input === "string") {
119199
- raw = input;
119200
- } else if (typeof input === "object") {
119201
- const obj = input;
119202
- const snippet2 = obj["command"] ?? obj["path"] ?? obj["file_path"] ?? obj["description"] ?? obj["cmd"] ?? null;
119203
- raw = snippet2 != null ? String(snippet2) : JSON.stringify(input);
119204
- } else {
119205
- raw = String(input);
119206
- }
119207
- return raw.length > 80 ? `${raw.slice(0, 77)}...` : raw;
119208
- }
119209
- function buildCardKitProgressMarkdown(snapshot) {
119210
- const status = snapshot.statusLines.slice(-3).map((line) => `- ${line.trim()}`);
119211
- const tools = snapshot.toolLines.slice(-5).map((line) => `- ${line.trim()}`);
119212
- const sections = [];
119213
- if (status.length) sections.push(`**\u8FDB\u5EA6**
119214
- ${status.join("\n")}`);
119215
- if (tools.length) sections.push(`**\u5DE5\u5177\u8C03\u7528**
119216
- ${tools.join("\n")}`);
119217
- const body = sections.join("\n\n") || "\u6B63\u5728\u5904\u7406\u2026";
119218
- return truncateChars(body, THINKING_BUDGET_CHARS, "\n\n_\u8FDB\u5EA6\u8F83\u957F,\u5DF2\u622A\u65AD\u3002_");
119219
- }
119220
119197
  function choiceMarker2(index) {
119221
119198
  return CHOICE_MARKERS2[index] ?? String(index + 1);
119222
119199
  }
@@ -119298,8 +119275,8 @@ function buildCardKitFinalMarkdown(opts) {
119298
119275
  }
119299
119276
 
119300
119277
  // src/bridge/cardkitProgress.ts
119301
- var DEFAULT_PATCH_INTERVAL_MS = 1e3;
119302
- var DEFAULT_MAX_PROGRESS_UPDATES = 20;
119278
+ var DEFAULT_PATCH_INTERVAL_MS = 250;
119279
+ var DEFAULT_MAX_PROGRESS_UPDATES = 240;
119303
119280
  function idempotencyKey(facts) {
119304
119281
  return deriveCardKitUuid(
119305
119282
  ["reply", facts.botId, facts.threadId, facts.triggerMessageId].join("\0")
@@ -119316,8 +119293,7 @@ var LiveCardKitProgressHandle = class {
119316
119293
  patchIntervalMs;
119317
119294
  maxProgressUpdates;
119318
119295
  onSequenceCommitted;
119319
- statusLines = [];
119320
- toolLines = [];
119296
+ answerBuffer = "";
119321
119297
  pendingPatch = null;
119322
119298
  inFlight = Promise.resolve();
119323
119299
  closed = false;
@@ -119331,18 +119307,19 @@ var LiveCardKitProgressHandle = class {
119331
119307
  this.patchIntervalMs = opts.patchIntervalMs;
119332
119308
  this.maxProgressUpdates = opts.maxProgressUpdates;
119333
119309
  this.onSequenceCommitted = opts.onSequenceCommitted;
119334
- this.statusLines.push(opts.initialStatusText);
119310
+ }
119311
+ get answerText() {
119312
+ return this.answerBuffer;
119335
119313
  }
119336
119314
  handle(event) {
119337
119315
  if (this.closed) return;
119338
- if (event.type === "system_init") {
119339
- this.statusLines.push("\u672C\u5730 agent \u5DF2\u542F\u52A8\u3002");
119316
+ if (event.type === "answer_delta") {
119317
+ this.answerBuffer += event.text;
119340
119318
  this.schedulePatch();
119341
119319
  return;
119342
119320
  }
119343
- if (event.type === "tool_use") {
119344
- const summary = summarizeCardKitToolInput(event.toolInput);
119345
- this.toolLines.push(`\u{1F527} ${event.toolName}${summary ? ` ${summary}` : ""}`);
119321
+ if (event.type === "answer_snapshot") {
119322
+ this.answerBuffer = event.text;
119346
119323
  this.schedulePatch();
119347
119324
  }
119348
119325
  }
@@ -119358,17 +119335,20 @@ var LiveCardKitProgressHandle = class {
119358
119335
  await this.drain();
119359
119336
  this.closed = true;
119360
119337
  const finalMarkdown2 = buildCardKitFinalMarkdown(opts);
119361
- await this.next(
119362
- (sequence) => this.cardKitClient.streamElementContent(
119363
- this.cardId,
119364
- CARDKIT_FINAL_ELEMENT_ID,
119365
- finalMarkdown2,
119366
- {
119367
- sequence,
119368
- uuid: sequenceUuid(this.cardId, "final-content", sequence)
119369
- }
119370
- )
119371
- );
119338
+ if (finalMarkdown2 !== this.answerBuffer) {
119339
+ await this.next(
119340
+ (sequence) => this.cardKitClient.streamElementContent(
119341
+ this.cardId,
119342
+ CARDKIT_FINAL_ELEMENT_ID,
119343
+ finalMarkdown2,
119344
+ {
119345
+ sequence,
119346
+ uuid: sequenceUuid(this.cardId, "final-content", sequence)
119347
+ }
119348
+ )
119349
+ );
119350
+ this.answerBuffer = finalMarkdown2;
119351
+ }
119372
119352
  await this.next(
119373
119353
  (sequence) => this.cardKitClient.updateCardEntity(this.cardId, buildCardKitFinalCard(opts), {
119374
119354
  sequence,
@@ -119408,16 +119388,13 @@ var LiveCardKitProgressHandle = class {
119408
119388
  }
119409
119389
  async patchProgress() {
119410
119390
  if (this.closed || this.progressUpdates >= this.maxProgressUpdates) return;
119411
- const content = buildCardKitProgressMarkdown({
119412
- statusLines: this.statusLines,
119413
- toolLines: this.toolLines
119414
- });
119391
+ if (!this.answerBuffer) return;
119415
119392
  this.progressUpdates += 1;
119416
119393
  this.inFlight = this.inFlight.then(
119417
119394
  () => this.next(
119418
- (sequence) => this.cardKitClient.streamElementContent(this.cardId, "thinking_md", content, {
119395
+ (sequence) => this.cardKitClient.streamElementContent(this.cardId, CARDKIT_FINAL_ELEMENT_ID, this.answerBuffer, {
119419
119396
  sequence,
119420
- uuid: sequenceUuid(this.cardId, "thinking", sequence)
119397
+ uuid: sequenceUuid(this.cardId, "answer", sequence)
119421
119398
  })
119422
119399
  )
119423
119400
  ).catch((err) => {
@@ -119433,8 +119410,8 @@ var LiveCardKitProgressHandle = class {
119433
119410
  };
119434
119411
  async function createCardKitProgressHandle(opts) {
119435
119412
  const key = idempotencyKey(opts.facts);
119436
- const initialStatusText = opts.initialStatusText ?? "\u6B63\u5728\u5904\u7406\u2026";
119437
- const initialCard = buildCardKitInitialCard({ statusText: initialStatusText });
119413
+ const initialStatusText = opts.initialStatusText ?? "\u52AA\u529B\u56DE\u7B54\u4E2D...";
119414
+ const initialCard = buildCardKitInitialCard({ footerText: initialStatusText });
119438
119415
  const created = opts.cardKitClient.createCardReply ? await opts.cardKitClient.createCardReply(
119439
119416
  opts.replyToMessageId,
119440
119417
  initialCard,
@@ -119463,7 +119440,6 @@ async function createCardKitProgressHandle(opts) {
119463
119440
  idempotencyKey: key,
119464
119441
  patchIntervalMs: opts.patchIntervalMs ?? DEFAULT_PATCH_INTERVAL_MS,
119465
119442
  maxProgressUpdates: opts.maxProgressUpdates ?? DEFAULT_MAX_PROGRESS_UPDATES,
119466
- initialStatusText,
119467
119443
  onSequenceCommitted: opts.onSequenceCommitted
119468
119444
  });
119469
119445
  }
@@ -120363,7 +120339,7 @@ async function dispatchResponseSurface(input) {
120363
120339
  }
120364
120340
 
120365
120341
  // src/bridge/postProgress.ts
120366
- var DEFAULT_PLACEHOLDER_TEXT = "\u6B63\u5728\u5904\u7406\u2026";
120342
+ var DEFAULT_PLACEHOLDER_TEXT = "\u52AA\u529B\u56DE\u7B54\u4E2D...";
120367
120343
  var DEFAULT_PATCH_INTERVAL_MS2 = 1500;
120368
120344
  var DEFAULT_MAX_PROGRESS_EDITS = 16;
120369
120345
  function normalizeText2(text) {
@@ -120401,8 +120377,13 @@ var LivePostProgressHandle = class {
120401
120377
  }
120402
120378
  handle(event) {
120403
120379
  if (this.closed) return;
120404
- if (event.type !== "text_delta") return;
120405
- this.textBuffer += event.text;
120380
+ if (event.type === "answer_delta") {
120381
+ this.textBuffer += event.text;
120382
+ } else if (event.type === "answer_snapshot") {
120383
+ this.textBuffer = event.text;
120384
+ } else {
120385
+ return;
120386
+ }
120406
120387
  this.schedulePatch();
120407
120388
  }
120408
120389
  async finalize(opts) {
@@ -121088,7 +121069,7 @@ var BridgeHandler = class {
121088
121069
  threadId,
121089
121070
  triggerMessageId: messageId
121090
121071
  },
121091
- initialStatusText: "\u6B63\u5728\u5904\u7406\u2026",
121072
+ initialStatusText: "\u52AA\u529B\u56DE\u7B54\u4E2D...",
121092
121073
  onSequenceCommitted: async (sequence) => {
121093
121074
  await updateCardKitRecord({ status: "streaming", sequence });
121094
121075
  }
@@ -121107,8 +121088,7 @@ var BridgeHandler = class {
121107
121088
  idempotencyKey: cardKitProgress.idempotencyKey,
121108
121089
  sequence: cardKitProgress.sequence,
121109
121090
  elements: {
121110
- status: { elementId: "status_md" },
121111
- thinking: { elementId: "thinking_md" },
121091
+ footer: { elementId: "footer_md" },
121112
121092
  final: { elementId: "final_md" }
121113
121093
  },
121114
121094
  lastVisibleFallbackMessageId: null,
@@ -121125,8 +121105,20 @@ var BridgeHandler = class {
121125
121105
  reason: "response surface \u4F7F\u7528 CardKit \u4F5C\u4E3A\u672C\u8F6E\u4E3B\u56DE\u590D\u9762\u3002"
121126
121106
  });
121127
121107
  } catch (err) {
121128
- cardKitStartFailed = true;
121129
- console.warn("[bridge.handler] create CardKit progress surface failed; using card fallback:", err);
121108
+ const existingMessageId = cardKitReplyConversionMessageId(err);
121109
+ if (existingMessageId) {
121110
+ card = this.deps.cardRenderer.handleFor(existingMessageId);
121111
+ await this.deps.client.removeProcessingReaction?.(messageId);
121112
+ await recordEvent({
121113
+ status: "running",
121114
+ startedAt: (/* @__PURE__ */ new Date()).toISOString(),
121115
+ appendPath: "\u5DF2\u6536\u7F16 CardKit \u5360\u4F4D\u5361",
121116
+ reason: "CardKit idConvert \u5931\u8D25\u4F46\u5360\u4F4D\u5361\u5DF2\u53D1\u51FA\uFF0Cbridge \u590D\u7528\u540C\u4E00\u5F20\u5361\u505A\u53EF\u89C1\u515C\u5E95\u3002"
121117
+ });
121118
+ } else {
121119
+ cardKitStartFailed = true;
121120
+ console.warn("[bridge.handler] create CardKit progress surface failed; using card fallback:", err);
121121
+ }
121130
121122
  }
121131
121123
  }
121132
121124
  if (!card && !cardKitAvailable && postOutboundAvailable && this.deps.postClient) {
@@ -121153,7 +121145,7 @@ var BridgeHandler = class {
121153
121145
  threadId,
121154
121146
  triggerMessageId: messageId
121155
121147
  },
121156
- initialText: "\u6B63\u5728\u5904\u7406\u2026"
121148
+ initialText: "\u52AA\u529B\u56DE\u7B54\u4E2D..."
121157
121149
  });
121158
121150
  await this.deps.client.removeProcessingReaction?.(messageId);
121159
121151
  await recordEvent({
@@ -121274,7 +121266,7 @@ var BridgeHandler = class {
121274
121266
  gitlabToken: this.deps.gitlabToken
121275
121267
  });
121276
121268
  let sessionId;
121277
- let lastText = "";
121269
+ let trustedAnswerText = "";
121278
121270
  try {
121279
121271
  for await (const ev of handle.events) {
121280
121272
  if (cardKitProgress) cardKitProgress.handle(ev);
@@ -121283,8 +121275,10 @@ var BridgeHandler = class {
121283
121275
  if (ev.type === "system_init") {
121284
121276
  sessionId = ev.sessionId;
121285
121277
  }
121286
- if (ev.type === "text_delta") {
121287
- lastText = ev.text;
121278
+ if (ev.type === "answer_delta") {
121279
+ trustedAnswerText += ev.text;
121280
+ } else if (ev.type === "answer_snapshot") {
121281
+ trustedAnswerText = ev.text;
121288
121282
  }
121289
121283
  }
121290
121284
  const result = await handle.done;
@@ -121330,7 +121324,8 @@ var BridgeHandler = class {
121330
121324
  success = false;
121331
121325
  failureReason = `claude exited ${result.exitCode} \u4E14 bot \u672A\u66F4\u65B0 state.json status \u2014 \u53EF\u80FD\u5D29\u6E83`;
121332
121326
  }
121333
- const cardBody = reportedState?.last_message ?? (lastText.trim() ? lastText : "\u26A0\uFE0F \u672C\u8F6E\u6CA1\u6709\u62FF\u5230 agent \u7684\u65B0\u56DE\u590D(\u53EF\u80FD\u88AB\u4E2D\u65AD\u6216\u672A\u66F4\u65B0\u72B6\u6001),\u518D @ \u6211\u4E00\u6B21\u91CD\u8BD5\u3002");
121327
+ const fallbackAnswer = trustedAnswerText.trim() || cardKitProgress?.answerText.trim() || "";
121328
+ const cardBody = reportedState?.last_message ?? (fallbackAnswer ? fallbackAnswer : "\u26A0\uFE0F \u672C\u8F6E\u6CA1\u6709\u62FF\u5230 agent \u7684\u65B0\u56DE\u590D(\u53EF\u80FD\u88AB\u4E2D\u65AD\u6216\u672A\u66F4\u65B0\u72B6\u6001),\u518D @ \u6211\u4E00\u6B21\u91CD\u8BD5\u3002");
121334
121329
  const noReportThisTurn = reportedState === null;
121335
121330
  const neutralTitle = noReportThisTurn && success && !failureReason ? "\u{1F4AC} \u5DF2\u56DE\u590D" : void 0;
121336
121331
  const baseCardPayload = {
@@ -121546,12 +121541,32 @@ var BridgeHandler = class {
121546
121541
  reason: String(err)
121547
121542
  });
121548
121543
  settle(false);
121544
+ const wtPath = this.deps.conventions.runtime === "agent_workspace" && this.deps.conventions.workspaceSessionsDir ? path10.join(this.deps.conventions.workspaceSessionsDir, threadId) : path10.join(this.deps.conventions.worktreesDir, threadId);
121545
+ const hardFailureText = `\u6267\u884C\u5931\u8D25: ${String(err)}`;
121546
+ const createHardFailurePostFallback = async (failureReason) => {
121547
+ const fallback = await createOnlyPostFallback({
121548
+ postClient: this.deps.postClient,
121549
+ replyToMessageId: messageId,
121550
+ replyInThread,
121551
+ botId: this.deps.botConfig?.id ?? "v1-default",
121552
+ threadId,
121553
+ triggerMessageId: messageId,
121554
+ finalText: hardFailureText,
121555
+ failureReason,
121556
+ title: "Larkway failure fallback",
121557
+ logPrefix: "[bridge.handler]"
121558
+ });
121559
+ if (fallback) {
121560
+ await deleteCardFile(wtPath);
121561
+ await deleteCardKitFile(wtPath);
121562
+ }
121563
+ return fallback;
121564
+ };
121549
121565
  if (!card && cardKitProgress) {
121550
121566
  try {
121551
121567
  await cardKitProgress.finalize({
121552
- finalText: `\u6267\u884C\u5931\u8D25: ${String(err)}`
121568
+ finalText: hardFailureText
121553
121569
  });
121554
- const wtPath = this.deps.conventions.runtime === "agent_workspace" && this.deps.conventions.workspaceSessionsDir ? path10.join(this.deps.conventions.workspaceSessionsDir, threadId) : path10.join(this.deps.conventions.worktreesDir, threadId);
121555
121570
  await deleteCardKitFile(wtPath);
121556
121571
  } catch (cardKitFinalizeErr) {
121557
121572
  console.error(
@@ -121562,6 +121577,9 @@ var BridgeHandler = class {
121562
121577
  card = await this.deps.cardRenderer.start(messageId, { replyInThread, threadId });
121563
121578
  } catch (cardStartErr) {
121564
121579
  console.error("[bridge.handler] failure card start also failed:", cardStartErr);
121580
+ await createHardFailurePostFallback(
121581
+ `CardKit failure finalize failed: ${String(cardKitFinalizeErr)}; legacy visible card fallback also failed: ${String(cardStartErr)}`
121582
+ );
121565
121583
  }
121566
121584
  }
121567
121585
  }
@@ -121579,6 +121597,9 @@ var BridgeHandler = class {
121579
121597
  card = await this.deps.cardRenderer.start(messageId, { replyInThread, threadId });
121580
121598
  } catch (cardStartErr) {
121581
121599
  console.error("[bridge.handler] failure card start also failed:", cardStartErr);
121600
+ await createHardFailurePostFallback(
121601
+ `progress post failure update failed: ${String(postFinalizeErr)}; legacy visible card fallback also failed: ${String(cardStartErr)}`
121602
+ );
121582
121603
  }
121583
121604
  }
121584
121605
  }
@@ -121592,8 +121613,10 @@ var BridgeHandler = class {
121592
121613
  });
121593
121614
  } catch (finalizeErr) {
121594
121615
  console.error("[bridge.handler] finalize(failure) also failed:", finalizeErr);
121616
+ await createHardFailurePostFallback(
121617
+ `legacy visible failure card finalize failed: ${String(finalizeErr)}`
121618
+ );
121595
121619
  }
121596
- const wtPath = this.deps.conventions.runtime === "agent_workspace" && this.deps.conventions.workspaceSessionsDir ? path10.join(this.deps.conventions.workspaceSessionsDir, threadId) : path10.join(this.deps.conventions.worktreesDir, threadId);
121597
121620
  await deleteCardFile(wtPath);
121598
121621
  }
121599
121622
  }
@@ -125450,7 +125473,7 @@ function* parseLinesMulti(line) {
125450
125473
  if (typeof item !== "object" || item === null) continue;
125451
125474
  const block = item;
125452
125475
  if (block["type"] === "text" && typeof block["text"] === "string") {
125453
- yield { type: "text_delta", text: block["text"], raw: obj };
125476
+ yield* splitAnswerChannelText(block["text"], obj);
125454
125477
  emitted = true;
125455
125478
  } else if (block["type"] === "tool_use") {
125456
125479
  yield {
@@ -125795,7 +125818,7 @@ function* parseCodexLine(line) {
125795
125818
  return;
125796
125819
  }
125797
125820
  if (itemRecord["type"] === "agent_message" && typeof itemRecord["text"] === "string") {
125798
- yield { type: "text_delta", text: itemRecord["text"], raw: obj };
125821
+ yield* splitAnswerChannelText(itemRecord["text"], obj);
125799
125822
  return;
125800
125823
  }
125801
125824
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "larkway",
3
- "version": "0.3.22",
3
+ "version": "0.3.23",
4
4
  "description": "Thin bridge: Feishu thread to local Claude Code CLI",
5
5
  "license": "MIT",
6
6
  "author": "Chuck Wu (chuckwu0)",