larkway 0.3.40 → 0.3.42

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.40**
11
+ **Current release: v0.3.42**
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.40**
11
+ **当前版本:v0.3.42**
12
12
 
13
13
  ---
14
14
 
package/dist/cli/index.js CHANGED
@@ -112702,6 +112702,7 @@ function channelMsgToLarkEvent(msg) {
112702
112702
  if (!message_id || !chat_id || !senderOpenId) return null;
112703
112703
  const thread_id = m?.["thread_id"] ?? msg.threadId ?? m?.["root_id"] ?? msg.rootId ?? message_id;
112704
112704
  const root_id = m?.["root_id"] ?? msg.rootId ?? void 0;
112705
+ const parent_id = m?.["parent_id"] ?? msg.replyToMessageId ?? void 0;
112705
112706
  const rawContent = typeof m?.["content"] === "string" ? m["content"] : void 0;
112706
112707
  const content = rawContent ?? JSON.stringify({ text: stripAtMarkup(msg.content ?? "") });
112707
112708
  return {
@@ -112710,6 +112711,7 @@ function channelMsgToLarkEvent(msg) {
112710
112711
  chat_type: m?.["chat_type"] ?? msg.chatType ?? "group",
112711
112712
  thread_id,
112712
112713
  root_id,
112714
+ parent_id,
112713
112715
  sender_id: senderOpenId,
112714
112716
  mentions: m?.["mentions"] ?? void 0,
112715
112717
  content,
package/dist/main.js CHANGED
@@ -116656,6 +116656,9 @@ function buildTopicDeepLink(chatId, threadId) {
116656
116656
  const t = encodeURIComponent(threadId);
116657
116657
  return `https://applink.feishu.cn/client/thread/open?open_chat_id=${c}&open_thread_id=${t}&openchatid=${c}&openthreadid=${t}&thread_position=-1`;
116658
116658
  }
116659
+ function realTopicThreadId(threadId) {
116660
+ return typeof threadId === "string" && threadId.startsWith("omt_") ? threadId : void 0;
116661
+ }
116659
116662
  var LOOKUP_TIMEOUT_MS = 5e3;
116660
116663
  var CACHE_MAX = 200;
116661
116664
  async function withTimeout2(p, ms, label) {
@@ -116853,6 +116856,7 @@ function channelMsgToLarkEvent(msg) {
116853
116856
  if (!message_id || !chat_id || !senderOpenId) return null;
116854
116857
  const thread_id = m?.["thread_id"] ?? msg.threadId ?? m?.["root_id"] ?? msg.rootId ?? message_id;
116855
116858
  const root_id = m?.["root_id"] ?? msg.rootId ?? void 0;
116859
+ const parent_id = m?.["parent_id"] ?? msg.replyToMessageId ?? void 0;
116856
116860
  const rawContent = typeof m?.["content"] === "string" ? m["content"] : void 0;
116857
116861
  const content = rawContent ?? JSON.stringify({ text: stripAtMarkup(msg.content ?? "") });
116858
116862
  return {
@@ -116861,6 +116865,7 @@ function channelMsgToLarkEvent(msg) {
116861
116865
  chat_type: m?.["chat_type"] ?? msg.chatType ?? "group",
116862
116866
  thread_id,
116863
116867
  root_id,
116868
+ parent_id,
116864
116869
  sender_id: senderOpenId,
116865
116870
  mentions: m?.["mentions"] ?? void 0,
116866
116871
  content,
@@ -122514,8 +122519,10 @@ var BridgeHandler = class {
122514
122519
  for await (const event of this.deps.client.events()) {
122515
122520
  if (this.closed) break;
122516
122521
  if (signal?.aborted) break;
122517
- const key = typeof event.root_id === "string" && event.root_id ? event.root_id : event.message_id;
122522
+ const key = typeof event.root_id === "string" && event.root_id ? event.root_id : typeof event.parent_id === "string" && event.parent_id ? event.parent_id : event.message_id;
122518
122523
  this.threadReceivedAt.set(key, Date.now());
122524
+ const legacySessionKey = typeof event.root_id === "string" && event.root_id ? event.root_id : event.message_id;
122525
+ if (legacySessionKey !== key) this.threadReceivedAt.set(legacySessionKey, Date.now());
122519
122526
  const prev = threadQueues.get(key) ?? Promise.resolve();
122520
122527
  const next = prev.then(() => acquire()).then(() => this.handleOne(event)).catch((err) => {
122521
122528
  console.error(`[bridge.handler] unhandled error on thread ${key}:`, err);
@@ -122553,10 +122560,39 @@ var BridgeHandler = class {
122553
122560
  };
122554
122561
  try {
122555
122562
  const parsed = parseMessage(event);
122556
- const { threadId, messageId, senderOpenId } = parsed;
122563
+ const { messageId, senderOpenId } = parsed;
122564
+ let threadId = parsed.threadId;
122557
122565
  const botId = this.deps.botConfig?.id;
122558
122566
  const eventLogId = messageId;
122559
122567
  const eventStartedAt = Date.now();
122568
+ let taskRootInfo;
122569
+ let taskCardAnchorId;
122570
+ let deferredTaskRootProbe;
122571
+ {
122572
+ const realTopic = realTopicThreadId(parsed.raw.thread_id);
122573
+ const rootCandidate = (typeof parsed.raw.root_id === "string" && parsed.raw.root_id ? parsed.raw.root_id : void 0) ?? (typeof parsed.raw.parent_id === "string" && parsed.raw.parent_id ? parsed.raw.parent_id : void 0);
122574
+ if (rootCandidate && this.deps.messageLookup) {
122575
+ const probe = this.deps.messageLookup.get(rootCandidate).catch(() => void 0);
122576
+ if (realTopic) {
122577
+ deferredTaskRootProbe = probe;
122578
+ } else {
122579
+ const info = await probe;
122580
+ if (info?.msgType === "todo" && info.content) {
122581
+ const todo = parseTodoShareContent(info.content);
122582
+ if (todo) {
122583
+ const probeThreadId = realTopicThreadId(info.threadId);
122584
+ taskRootInfo = {
122585
+ guid: todo.taskGuid,
122586
+ summary: todo.summaryText,
122587
+ topicLink: probeThreadId ? buildTopicDeepLink(parsed.chatId, probeThreadId) : void 0
122588
+ };
122589
+ taskCardAnchorId = rootCandidate;
122590
+ threadId = rootCandidate;
122591
+ }
122592
+ }
122593
+ }
122594
+ }
122595
+ }
122560
122596
  const turnReceivedAt = this.threadReceivedAt.get(threadId);
122561
122597
  const recordEvent = async (patch) => {
122562
122598
  if (!this.deps.recordRuntimeEvent) return;
@@ -122604,31 +122640,9 @@ var BridgeHandler = class {
122604
122640
  const isTopLevel = !(typeof parsed.raw.root_id === "string" && parsed.raw.root_id);
122605
122641
  let replyInThread = isTopLevel;
122606
122642
  let replyAnchorId = messageId;
122607
- let taskRootInfo;
122608
- let deferredTaskRootProbe;
122609
- {
122610
- const rootId = typeof parsed.raw.root_id === "string" && parsed.raw.root_id ? parsed.raw.root_id : void 0;
122611
- const rawThreadId = typeof parsed.raw.thread_id === "string" && parsed.raw.thread_id ? parsed.raw.thread_id : void 0;
122612
- if (rootId && this.deps.messageLookup) {
122613
- const probe = this.deps.messageLookup.get(rootId).catch(() => void 0);
122614
- if (rawThreadId) {
122615
- deferredTaskRootProbe = probe;
122616
- } else {
122617
- const info = await probe;
122618
- if (info?.msgType === "todo" && info.content) {
122619
- const todo = parseTodoShareContent(info.content);
122620
- if (todo) {
122621
- taskRootInfo = {
122622
- guid: todo.taskGuid,
122623
- summary: todo.summaryText,
122624
- topicLink: info.threadId ? buildTopicDeepLink(parsed.chatId, info.threadId) : void 0
122625
- };
122626
- replyInThread = true;
122627
- replyAnchorId = rootId;
122628
- }
122629
- }
122630
- }
122631
- }
122643
+ if (taskCardAnchorId) {
122644
+ replyInThread = true;
122645
+ replyAnchorId = taskCardAnchorId;
122632
122646
  }
122633
122647
  const prototypeConfig = this.deps.botConfig?.response_surface_prototype;
122634
122648
  const cardKitAvailable = isResponseSurfaceCardKitAvailable(
@@ -122716,7 +122730,10 @@ var BridgeHandler = class {
122716
122730
  inputPreview: parsed.text,
122717
122731
  target: {
122718
122732
  chatId: parsed.chatId,
122719
- threadId: typeof parsed.raw.thread_id === "string" && parsed.raw.thread_id ? parsed.raw.thread_id : void 0,
122733
+ // omt_* only (see realTopicThreadId): passing a reply-chain om_*
122734
+ // id as receive_id_type=thread_id anchored the COT bubble onto a
122735
+ // stray thread on the TRIGGER message (2026-07-08 dogfood).
122736
+ threadId: realTopicThreadId(parsed.raw.thread_id),
122720
122737
  originMessageId
122721
122738
  }
122722
122739
  });
@@ -123077,19 +123094,21 @@ var BridgeHandler = class {
123077
123094
  if (info?.msgType === "todo" && info.content) {
123078
123095
  const todo = parseTodoShareContent(info.content);
123079
123096
  if (todo) {
123080
- const rawThreadId = typeof parsed.raw.thread_id === "string" && parsed.raw.thread_id ? parsed.raw.thread_id : void 0;
123097
+ const rawThreadId = realTopicThreadId(parsed.raw.thread_id);
123098
+ const probeThreadId = realTopicThreadId(info.threadId);
123081
123099
  taskRootInfo = {
123082
123100
  guid: todo.taskGuid,
123083
123101
  summary: todo.summaryText,
123084
- topicLink: rawThreadId ? buildTopicDeepLink(parsed.chatId, rawThreadId) : info.threadId ? buildTopicDeepLink(parsed.chatId, info.threadId) : void 0
123102
+ topicLink: rawThreadId ? buildTopicDeepLink(parsed.chatId, rawThreadId) : probeThreadId ? buildTopicDeepLink(parsed.chatId, probeThreadId) : void 0
123085
123103
  };
123086
123104
  }
123087
123105
  }
123088
123106
  }
123089
123107
  if (taskRootInfo && !taskRootInfo.topicLink && replyAnchorId !== messageId && this.deps.messageLookup) {
123090
123108
  const refreshed = await this.deps.messageLookup.get(replyAnchorId, { refresh: true }).catch(() => void 0);
123091
- if (refreshed?.threadId) {
123092
- taskRootInfo.topicLink = buildTopicDeepLink(parsed.chatId, refreshed.threadId);
123109
+ const refreshedThreadId = realTopicThreadId(refreshed?.threadId);
123110
+ if (refreshedThreadId) {
123111
+ taskRootInfo.topicLink = buildTopicDeepLink(parsed.chatId, refreshedThreadId);
123093
123112
  }
123094
123113
  }
123095
123114
  const prompt = await renderPrompt({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "larkway",
3
- "version": "0.3.40",
3
+ "version": "0.3.42",
4
4
  "description": "Thin bridge: Feishu thread to local Claude Code CLI",
5
5
  "license": "MIT",
6
6
  "author": "Chuck Wu (chuckwu0)",