larkway 0.3.25 → 0.3.26

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.25**
11
+ **Current release: v0.3.26**
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.25**
11
+ **当前版本:v0.3.26**
12
12
 
13
13
  ---
14
14
 
package/dist/main.js CHANGED
@@ -117684,12 +117684,14 @@ var AnswerChannelExtractor = class {
117684
117684
  mode = "waiting";
117685
117685
  buffer = "";
117686
117686
  visibleText = "";
117687
+ lastSnapshotText = "";
117687
117688
  ingestDelta(text, raw) {
117688
117689
  if (!text || this.mode === "closed") return [];
117689
117690
  this.buffer += text;
117690
117691
  return this.drain(raw);
117691
117692
  }
117692
117693
  ingestSnapshot(text, raw) {
117694
+ this.lastSnapshotText = text;
117693
117695
  const events = splitAnswerChannelText(text, raw);
117694
117696
  const out = [];
117695
117697
  for (const event of events) {
@@ -117697,13 +117699,30 @@ var AnswerChannelExtractor = class {
117697
117699
  out.push(event);
117698
117700
  continue;
117699
117701
  }
117700
- if (event.text === this.visibleText) continue;
117702
+ if (event.text === this.visibleText) {
117703
+ if (text.includes(ANSWER_END_MARKER)) this.mode = "closed";
117704
+ continue;
117705
+ }
117701
117706
  this.visibleText = event.text;
117702
117707
  out.push(event);
117703
117708
  if (text.includes(ANSWER_END_MARKER)) this.mode = "closed";
117704
117709
  }
117705
117710
  return out;
117706
117711
  }
117712
+ ingestGrowingSnapshot(text, raw) {
117713
+ if (!text || this.mode === "closed") return [];
117714
+ if (this.lastSnapshotText && text.startsWith(this.lastSnapshotText)) {
117715
+ const delta = text.slice(this.lastSnapshotText.length);
117716
+ this.lastSnapshotText = text;
117717
+ return delta ? this.ingestDelta(delta, raw) : [];
117718
+ }
117719
+ if (this.lastSnapshotText === "") {
117720
+ this.lastSnapshotText = text;
117721
+ return this.ingestDelta(text, raw);
117722
+ }
117723
+ this.lastSnapshotText = text;
117724
+ return this.ingestSnapshot(text, raw);
117725
+ }
117707
117726
  drain(raw) {
117708
117727
  const events = [];
117709
117728
  if (this.mode === "waiting") {
@@ -125748,7 +125767,7 @@ function* parseLinesMulti(line, answerExtractor = new AnswerChannelExtractor())
125748
125767
  if (typeof item !== "object" || item === null) continue;
125749
125768
  const block = item;
125750
125769
  if (block["type"] === "text" && typeof block["text"] === "string") {
125751
- yield* answerExtractor.ingestSnapshot(block["text"], obj);
125770
+ yield* answerExtractor.ingestGrowingSnapshot(block["text"], obj);
125752
125771
  emitted = true;
125753
125772
  } else if (block["type"] === "tool_use") {
125754
125773
  yield {
@@ -126060,64 +126079,80 @@ function buildCodexCommand(opts, codexBinPath = "codex") {
126060
126079
  const freshFlags = opts.cwd != null ? ["-C", opts.cwd, ...commonFlags] : commonFlags;
126061
126080
  return [codexBinPath, ["exec", ...freshFlags]];
126062
126081
  }
126063
- function* parseCodexLine(line) {
126064
- const trimmed = line.trim();
126065
- if (trimmed === "") return;
126066
- let obj;
126067
- try {
126068
- obj = JSON.parse(trimmed);
126069
- } catch {
126070
- yield { type: "raw", raw: trimmed };
126071
- return;
126072
- }
126073
- if (typeof obj !== "object" || obj === null) {
126074
- yield { type: "raw", raw: obj };
126075
- return;
126076
- }
126077
- const record = obj;
126078
- const topType = record["type"];
126079
- if (topType === "thread.started" && typeof record["thread_id"] === "string") {
126080
- yield { type: "system_init", sessionId: record["thread_id"], raw: obj };
126081
- return;
126082
- }
126083
- if (topType === "turn.completed") {
126084
- yield { type: "result", stopReason: "end_turn", raw: obj };
126085
- return;
126086
- }
126087
- if (topType === "item.started") {
126088
- const item = record["item"];
126089
- if (typeof item === "object" && item !== null) {
126090
- const itemRecord = item;
126091
- if (itemRecord["type"] === "command_execution" && typeof itemRecord["command"] === "string") {
126092
- yield {
126093
- type: "tool_use",
126094
- toolName: "shell",
126095
- toolInput: { command: itemRecord["command"] },
126096
- raw: obj
126097
- };
126082
+ var CodexLineParser = class {
126083
+ answerExtractor = new AnswerChannelExtractor();
126084
+ *parseLine(line) {
126085
+ const trimmed = line.trim();
126086
+ if (trimmed === "") return;
126087
+ let obj;
126088
+ try {
126089
+ obj = JSON.parse(trimmed);
126090
+ } catch {
126091
+ yield { type: "raw", raw: trimmed };
126092
+ return;
126093
+ }
126094
+ if (typeof obj !== "object" || obj === null) {
126095
+ yield { type: "raw", raw: obj };
126096
+ return;
126097
+ }
126098
+ const record = obj;
126099
+ const topType = record["type"];
126100
+ if (topType === "thread.started" && typeof record["thread_id"] === "string") {
126101
+ yield { type: "system_init", sessionId: record["thread_id"], raw: obj };
126102
+ return;
126103
+ }
126104
+ if (topType === "turn.completed") {
126105
+ yield { type: "result", stopReason: "end_turn", raw: obj };
126106
+ return;
126107
+ }
126108
+ const agentMessageText = agentMessageTextFrom(record);
126109
+ if (agentMessageText !== void 0) {
126110
+ if (topType === "item.completed") {
126111
+ yield* this.answerExtractor.ingestSnapshot(agentMessageText, obj);
126098
126112
  return;
126099
126113
  }
126114
+ yield* this.answerExtractor.ingestGrowingSnapshot(agentMessageText, obj);
126115
+ return;
126100
126116
  }
126101
- yield { type: "raw", raw: obj };
126102
- return;
126103
- }
126104
- if (topType === "item.completed") {
126105
- const item = record["item"];
126106
- if (typeof item === "object" && item !== null) {
126107
- const itemRecord = item;
126108
- if (itemRecord["type"] === "command_execution") {
126109
- yield { type: "tool_result", raw: obj };
126110
- return;
126117
+ if (topType === "item.started") {
126118
+ const item = record["item"];
126119
+ if (typeof item === "object" && item !== null) {
126120
+ const itemRecord = item;
126121
+ if (itemRecord["type"] === "command_execution" && typeof itemRecord["command"] === "string") {
126122
+ yield {
126123
+ type: "tool_use",
126124
+ toolName: "shell",
126125
+ toolInput: { command: itemRecord["command"] },
126126
+ raw: obj
126127
+ };
126128
+ return;
126129
+ }
126111
126130
  }
126112
- if (itemRecord["type"] === "agent_message" && typeof itemRecord["text"] === "string") {
126113
- yield* splitAnswerChannelText(itemRecord["text"], obj);
126114
- return;
126131
+ yield { type: "raw", raw: obj };
126132
+ return;
126133
+ }
126134
+ if (topType === "item.completed") {
126135
+ const item = record["item"];
126136
+ if (typeof item === "object" && item !== null) {
126137
+ const itemRecord = item;
126138
+ if (itemRecord["type"] === "command_execution") {
126139
+ yield { type: "tool_result", raw: obj };
126140
+ return;
126141
+ }
126115
126142
  }
126143
+ yield { type: "raw", raw: obj };
126144
+ return;
126116
126145
  }
126117
126146
  yield { type: "raw", raw: obj };
126118
- return;
126119
126147
  }
126120
- yield { type: "raw", raw: obj };
126148
+ };
126149
+ function agentMessageTextFrom(record) {
126150
+ const item = record["item"];
126151
+ if (typeof item !== "object" || item === null) return void 0;
126152
+ const itemRecord = item;
126153
+ if (itemRecord["type"] !== "agent_message") return void 0;
126154
+ const text = itemRecord["text"];
126155
+ return typeof text === "string" ? text : void 0;
126121
126156
  }
126122
126157
  function runCodex(opts, codexBinPath = "codex") {
126123
126158
  const timeoutMs = opts.timeoutMs ?? 15 * 60 * 1e3;
@@ -126242,9 +126277,10 @@ stderr: ${stderr}` : "")
126242
126277
  crlfDelay: Infinity,
126243
126278
  signal: rlAbortController.signal
126244
126279
  });
126280
+ const parser = new CodexLineParser();
126245
126281
  try {
126246
126282
  for await (const line of rl) {
126247
- for (const event of parseCodexLine(line)) {
126283
+ for (const event of parser.parseLine(line)) {
126248
126284
  if (event.type === "system_init") {
126249
126285
  discoveredSessionId = event.sessionId;
126250
126286
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "larkway",
3
- "version": "0.3.25",
3
+ "version": "0.3.26",
4
4
  "description": "Thin bridge: Feishu thread to local Claude Code CLI",
5
5
  "license": "MIT",
6
6
  "author": "Chuck Wu (chuckwu0)",