larkway 0.3.57 → 0.3.58

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.57**
11
+ **Current release: v0.3.58**
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.57**
11
+ **当前版本:v0.3.58**
12
12
 
13
13
  ---
14
14
 
package/dist/cli/index.js CHANGED
@@ -109971,6 +109971,70 @@ var init_client = __esm({
109971
109971
  wrapErr(label, err2);
109972
109972
  }
109973
109973
  }
109974
+ /**
109975
+ * task_handle v5 (BL-48, 声明式建卡): create a task under this bot's app
109976
+ * identity. `due.timestamp` is ms-epoch string per task v2; `members` lets
109977
+ * the create carry the originator as follower in one call; `tasklists`
109978
+ * attaches the task to the bot's configured list when present. Response
109979
+ * shape mirrors createTasklist's envelope (`data.task.guid`).
109980
+ */
109981
+ async createTask(input) {
109982
+ const label = `createTask(${input.summary.slice(0, 30)})`;
109983
+ let resp;
109984
+ try {
109985
+ resp = await this.#request(
109986
+ {
109987
+ method: "POST",
109988
+ url: `${TASK_V2_BASE}/tasks`,
109989
+ params: { user_id_type: "open_id" },
109990
+ data: {
109991
+ summary: input.summary,
109992
+ ...input.description ? { description: input.description } : {},
109993
+ ...input.due ? { due: input.due } : {},
109994
+ ...input.members && input.members.length > 0 ? { members: input.members } : {},
109995
+ ...input.tasklists && input.tasklists.length > 0 ? { tasklists: input.tasklists } : {}
109996
+ }
109997
+ },
109998
+ label
109999
+ );
110000
+ } catch (err2) {
110001
+ wrapErr(label, err2);
110002
+ }
110003
+ const guid = resp.data?.task?.["guid"];
110004
+ if (typeof guid !== "string" || guid.length === 0) {
110005
+ throw new TaskApiError(`[tasklist.client] ${label} returned no guid`, {});
110006
+ }
110007
+ return { guid };
110008
+ }
110009
+ /**
110010
+ * task_handle v5: add members (e.g. a late follower) to an existing TASK
110011
+ * (`POST /tasks/:guid/add_members` — task-level counterpart of
110012
+ * addTasklistMembers). Best-effort semantics at the caller, same as every
110013
+ * other write here.
110014
+ */
110015
+ async addTaskMembers(taskGuid, members) {
110016
+ const label = `addTaskMembers(${taskGuid})`;
110017
+ try {
110018
+ await this.#request(
110019
+ {
110020
+ method: "POST",
110021
+ url: `${TASK_V2_BASE}/tasks/${encodeURIComponent(taskGuid)}/add_members`,
110022
+ params: { user_id_type: "open_id" },
110023
+ data: { members }
110024
+ },
110025
+ label
110026
+ );
110027
+ } catch (err2) {
110028
+ wrapErr(label, err2);
110029
+ }
110030
+ }
110031
+ /**
110032
+ * task_handle v5: reschedule (or set) the task due. ms-epoch string per
110033
+ * task v2 `due.timestamp`; same patch semantics as complete()/reopen().
110034
+ */
110035
+ async patchDue(taskGuid, due) {
110036
+ await this.#patchTask(taskGuid, { due }, ["due"]);
110037
+ }
109974
110038
  };
109975
110039
  }
109976
110040
  });
package/dist/main.js CHANGED
@@ -114958,6 +114958,13 @@ function renderStateContract(stateFilePath) {
114958
114958
  "- \u9700\u8981\u8986\u76D6\u5361\u7247\u6B63\u6587:status=ready + last_message(\u4E0D\u5199\u65F6\u6B63\u6587=\u7B54\u6848\u901A\u9053\u5185\u5BB9);dev_url/mr_url \u7B49\u4E1A\u52A1\u5B57\u6BB5\u53EF\u81EA\u7531\u5199\u5165,\u4F46 bridge \u4E0D\u611F\u77E5\u5176\u542B\u4E49,\u8981\u8BA9\u7528\u6237\u770B\u5230\u5C31\u5199\u8FDB\u6B63\u6587\u3002",
114959
114959
  "- \u9700\u8981\u5728\u7EC8\u6001\u5361\u4E0A @ \u4EBA:response_surface \u5199 `{post:{mentions:[{user_id}]}}`;\u8FD9\u53EA\u662F\u89C6\u89C9\u63D0\u793A,\u8981 peer bot \u6D88\u8D39\u6B63\u6587\u5FC5\u987B\u8D70 handoffs \u6216\u53E6\u53D1\u771F\u5B9E post(\u5361\u7247\u5BF9 agent \u4E0D\u53EF\u8BFB)\u3002",
114960
114960
  "- \u4EA4\u63A5\u7ED9 peer bot:handoffs(\u6700\u591A 3 \u6761 `{to, text}`,to \u5199 peer-bots \u540D\u518C\u91CC\u7684\u540D\u5B57)\u3002bridge \u66FF\u4F60\u53D1\u4E00\u6761\u5E26\u771F\u5B9E at \u6807\u7B7E\u7684 post \u7559\u75D5,\u540C\u6865\u8FDB\u7A0B\u5185\u7684 peer \u8FD8\u4F1A\u88AB**\u672C\u5730\u76F4\u9012**\u7ACB\u5373\u5524\u9192\u2014\u2014\u6BD4\u81EA\u5DF1\u7528 lark-cli \u53D1 post \u66F4\u5FEB\u66F4\u53EF\u9760\u3002text \u5FC5\u987B\u81EA\u5305\u542B:\u5BF9\u65B9\u53EA\u770B\u5F97\u5230\u8FD9\u6761\u6587\u672C,\u80CC\u666F\u3001\u8981\u505A\u4EC0\u4E48\u3001\u671F\u671B\u4EA7\u51FA\u90FD\u5199\u6E05\u695A\u3002",
114961
+ "- **\u4EFB\u52A1\u5361(\u4E94\u4FE1\u53F7\u59D4\u6258\u5951\u7EA6,\u503C\u5F97\u8FFD\u8E2A\u7684\u6D3B\u5FC5\u7528)**:\u5224\u65AD\u6807\u51C6\u2014\u2014\u4E09\u4E2A\u6708\u540E\u590D\u76D8\u4F1A\u627E\u5B83\u3001\u6216\u6267\u884C\u8DE8\u8D8A\u672C\u8F6E\u5BF9\u8BDD(\u8981\u8DD1\u5F88\u4E45/\u7B49\u5916\u90E8/\u5206\u9636\u6BB5)\u2192 \u5199 task_handle\u3002\u4E00\u95EE\u4E00\u7B54\u804A\u5B8C\u5C31\u5B8C\u7684**\u4E0D\u5EFA\u5361**\u3002",
114962
+ " - \u63A5\u4E86:`task_handle: {create: {summary, due?}}`\u2014\u2014bridge \u81EA\u52A8\u5EFA\u5361\u3001\u628A\u8BDD\u9898\u56DE\u94FE\u5199\u8FDB\u4EFB\u52A1\u63CF\u8FF0\u3001\u628A\u53D1\u8D77\u4EBA\u52A0\u8FDB\u5173\u6CE8\u5217\u8868,\u5E76\u7ED1\u5B9A\u672C\u8BDD\u9898\u3002\u5DF2\u6709\u4EFB\u52A1\u5219\u7528 `{guid}` \u8BA4\u9886\u3002",
114963
+ ' - \u8FDB\u5C55:`{note: "\u4E00\u53E5\u8BDD\u91CC\u7A0B\u7891"}` \u53EA\u5728\u5B9E\u8D28\u8FDB\u5C55\u65F6\u5199(\u9636\u6BB5\u7ED3\u8BBA/\u5173\u952E\u8F6C\u6298),bridge \u843D\u5230\u4EFB\u52A1\u4FA7;\u4E0D\u8981\u6BCF\u8F6E\u90FD\u5199\u3002',
114964
+ ' - \u6539\u671F:`{due: "<ISO \u6216 YYYY-MM-DD>", due_reason: "\u4E00\u53E5\u539F\u56E0"}`\u2014\u2014\u5EF6\u671F\u5FC5\u987B\u5E26\u539F\u56E0,bridge \u4F1A\u6539\u5361\u4E0A\u622A\u6B62\u5E76\u8BC4\u8BBA\u8BF4\u660E\u3002',
114965
+ " - \u5B8C\u6210:`{done: true, note: ...}` \u4EA4\u4ED8\u8F6E\u624D\u5199;\u5931\u8D25\u8BA9\u4EFB\u52A1\u4FDD\u6301 open\u3002",
114966
+ ' - \u963B\u585E:`{blocked: "\u5361\u5728\u54EA\u3001\u9700\u8981\u4EBA\u505A\u4EC0\u4E48"}`\u2014\u2014bridge \u843D\u4EFB\u52A1\u8BC4\u8BBA,\u5173\u6CE8\u4EBA\u4F1A\u6536\u5230\u901A\u77E5;\u540C\u65F6\u5728\u6B63\u6587\u7528 choices \u628A\u51B3\u5B9A\u6536\u655B\u6210\u9009\u9879\u3002',
114967
+ " - \u4EA4\u4ED8\u53CC\u6307\u9488:\u7ED3\u8BBA\u62A5\u544A\u5148\u7528 lark-cli \u5BFC\u6210**\u98DE\u4E66\u6587\u6863**,\u56DE\u590D\u548C\u4EFB\u52A1\u8BC4\u8BBA\u91CC doc \u94FE\u63A5\u5728\u524D\u3001\u672C\u5730\u7EDD\u5BF9\u8DEF\u5F84\u5728\u540E(\u672C\u5730\u6587\u4EF6\u662F\u53D6\u8BC1\u771F\u76F8,doc \u662F\u7ED9\u4EBA\u770B\u7684\u5F62\u6001)\u3002",
114961
114968
  "",
114962
114969
  "\u8FB9\u754C\u4E0E\u8D23\u4EFB:",
114963
114970
  "- **\u7EDD\u4E0D\u81EA\u5DF1 PATCH/PUT bridge \u7BA1\u7406\u7684\u5361\u7247/post** \u2014\u2014 \u7F51\u7EDC\u66F4\u65B0\u662F bridge \u7684\u6D3B,\u81EA\u5DF1\u6539\u4F1A\u548C\u5361\u7247 finalize\u3001\u6309\u94AE\u56DE\u4F20\u3001\u5D29\u6E83\u6062\u590D\u51B2\u7A81\u3002",
@@ -114970,7 +114977,7 @@ function renderContractAnchor(stateFilePath) {
114970
114977
  const stateTarget = stateFilePath ? `\`${stateFilePath}\`` : "\u5DE5\u4F5C\u76EE\u5F55\u91CC\u7684 `.larkway/state.json`";
114971
114978
  return [
114972
114979
  "<contract-anchor>",
114973
- `\u5951\u7EA6\u540C\u672C\u8BDD\u9898\u9996\u8F6E\u6CE8\u5165,\u672A\u53D8\u5316:\u7ED9\u7528\u6237\u770B\u7684\u7B54\u6848\u6B63\u6587\u5305\u5728\u72EC\u7ACB\u884C marker \`${ANSWER_BEGIN_MARKER}\` / \`${ANSWER_END_MARKER}\` \u4E4B\u95F4\u8F93\u51FA;\u7ED3\u8BBA\u4E00\u6210\u5F62\u5C31\u5148\u6D41\u51FA\u7B54\u6848\u3002\u7EAF\u6587\u5B57\u56DE\u7B54\u4E0D\u7528\u5199 state.json;\u5931\u8D25/\u7B49\u8865\u5145/choices/\u56FE\u6587\u6DF7\u6392/handoffs \u4EA4\u63A5\u65F6\u624D\u5199 ${stateTarget}(\u539F\u5B50\u5199 + \u5237\u65B0 updated_at)\u3002\u505A\u5B8C\u5E72\u51C0\u9000\u51FA\u8FDB\u7A0B\u3002`,
114980
+ `\u5951\u7EA6\u540C\u672C\u8BDD\u9898\u9996\u8F6E\u6CE8\u5165,\u672A\u53D8\u5316:\u7ED9\u7528\u6237\u770B\u7684\u7B54\u6848\u6B63\u6587\u5305\u5728\u72EC\u7ACB\u884C marker \`${ANSWER_BEGIN_MARKER}\` / \`${ANSWER_END_MARKER}\` \u4E4B\u95F4\u8F93\u51FA;\u7ED3\u8BBA\u4E00\u6210\u5F62\u5C31\u5148\u6D41\u51FA\u7B54\u6848\u3002\u7EAF\u6587\u5B57\u56DE\u7B54\u4E0D\u7528\u5199 state.json;\u5931\u8D25/\u7B49\u8865\u5145/choices/\u56FE\u6587\u6DF7\u6392/handoffs \u4EA4\u63A5/task_handle \u4EFB\u52A1\u4FE1\u53F7(\u5EFA\u5361/\u91CC\u7A0B\u7891/\u6539\u671F/\u963B\u585E/\u4EA4\u4ED8)\u65F6\u624D\u5199 ${stateTarget}(\u539F\u5B50\u5199 + \u5237\u65B0 updated_at)\u3002\u505A\u5B8C\u5E72\u51C0\u9000\u51FA\u8FDB\u7A0B\u3002`,
114974
114981
  "</contract-anchor>"
114975
114982
  ];
114976
114983
  }
@@ -116921,7 +116928,26 @@ var StateFileSchema = external_exports.object({
116921
116928
  * 不要粘贴完整回复;bridge 侧仍有 sanitizeSummary 的机械截断兜底(见
116922
116929
  * src/tasklist/writeback.ts),但那只防"过长",不防"文不对题"。
116923
116930
  */
116924
- task_handle: external_exports.object({ guid: external_exports.string().min(1), done: external_exports.boolean().optional(), note: external_exports.string().optional() }).optional(),
116931
+ /**
116932
+ * v5 (BL-48, 五信号委托契约): `guid` becomes optional because `create` is the
116933
+ * new way a task comes into existence — agent 自判「这个活值得追踪」时声明
116934
+ * create,bridge 机械建卡(话题回链 + 发起人进关注列表)并自动认领。其余
116935
+ * 新字段同样是声明式信号:`due`(+`due_reason`)= 改期/设期,`blocked` =
116936
+ * 阻塞需要人(bridge 落任务评论,关注人收到推送)。全部按需、互不依赖;
116937
+ * bridge 不判断该不该建卡/该不该改期 —— 判断永远在 agent 侧(薄通道)。
116938
+ */
116939
+ task_handle: external_exports.object({
116940
+ guid: external_exports.string().min(1).optional(),
116941
+ create: external_exports.object({
116942
+ summary: external_exports.string().trim().min(1).max(500),
116943
+ due: external_exports.string().trim().min(1).optional()
116944
+ }).optional(),
116945
+ due: external_exports.string().trim().min(1).optional(),
116946
+ due_reason: external_exports.string().optional(),
116947
+ blocked: external_exports.string().optional(),
116948
+ done: external_exports.boolean().optional(),
116949
+ note: external_exports.string().optional()
116950
+ }).optional(),
116925
116951
  /**
116926
116952
  * Freshness signal for the handler's stale-guard (it compares this against a
116927
116953
  * pre-run snapshot to decide whether the bot rewrote state THIS turn).
@@ -120150,7 +120176,45 @@ var BridgeHandler = class {
120150
120176
  reason: rawReportedState === null ? "\u672C\u8F6E\u7ED3\u675F\u65F6\u672A\u8BFB\u53D6\u5230 state.json\u3002" : "\u672C\u8F6E state.json \u6CA1\u6709 fresh updated_at\uFF0C\u5DF2\u5FFD\u7565\u65E7\u72B6\u6001\u3002"
120151
120177
  });
120152
120178
  }
120153
- const claimedTaskGuid = reportedState?.task_handle?.guid;
120179
+ const declaredTaskHandle = reportedState?.task_handle;
120180
+ let bridgeCreatedTaskGuid;
120181
+ if (declaredTaskHandle && (declaredTaskHandle.create || declaredTaskHandle.due || declaredTaskHandle.blocked) && this.deps.taskHandleDeclare && botId) {
120182
+ try {
120183
+ let topicLink;
120184
+ if (declaredTaskHandle.create) {
120185
+ const direct = realTopicThreadId(parsed.raw.thread_id);
120186
+ if (direct) {
120187
+ topicLink = buildTopicDeepLink(parsed.chatId, direct);
120188
+ } else if (this.deps.messageLookup) {
120189
+ const refreshed = await this.deps.messageLookup.get(replyAnchorId, { refresh: true }).catch(() => void 0);
120190
+ const refreshedThreadId = realTopicThreadId(refreshed?.threadId);
120191
+ if (refreshedThreadId) {
120192
+ topicLink = buildTopicDeepLink(parsed.chatId, refreshedThreadId);
120193
+ }
120194
+ }
120195
+ }
120196
+ const result2 = await this.deps.taskHandleDeclare({
120197
+ botId,
120198
+ threadId,
120199
+ chatId: parsed.chatId,
120200
+ senderOpenId: parsed.senderOpenId || void 0,
120201
+ create: declaredTaskHandle.create,
120202
+ declaredGuid: declaredTaskHandle.guid,
120203
+ due: declaredTaskHandle.due,
120204
+ dueReason: declaredTaskHandle.due_reason,
120205
+ blocked: declaredTaskHandle.blocked,
120206
+ topicLink,
120207
+ chatLink: `https://applink.feishu.cn/client/chat/open?openChatId=${parsed.chatId}`
120208
+ });
120209
+ bridgeCreatedTaskGuid = result2?.createdGuid;
120210
+ for (const line of result2?.outcomes ?? []) {
120211
+ await recordEvent({ status: "running", appendPath: "\u4EFB\u52A1\u4FE1\u53F7", reason: line });
120212
+ }
120213
+ } catch (err) {
120214
+ console.warn("[bridge.handler] taskHandleDeclare hook failed (continuing):", err);
120215
+ }
120216
+ }
120217
+ const claimedTaskGuid = bridgeCreatedTaskGuid ?? reportedState?.task_handle?.guid;
120154
120218
  if (claimedTaskGuid && this.deps.taskHandleClaim && botId) {
120155
120219
  try {
120156
120220
  await this.deps.taskHandleClaim({
@@ -128362,6 +128426,70 @@ var TaskListClient = class {
128362
128426
  wrapErr(label, err);
128363
128427
  }
128364
128428
  }
128429
+ /**
128430
+ * task_handle v5 (BL-48, 声明式建卡): create a task under this bot's app
128431
+ * identity. `due.timestamp` is ms-epoch string per task v2; `members` lets
128432
+ * the create carry the originator as follower in one call; `tasklists`
128433
+ * attaches the task to the bot's configured list when present. Response
128434
+ * shape mirrors createTasklist's envelope (`data.task.guid`).
128435
+ */
128436
+ async createTask(input) {
128437
+ const label = `createTask(${input.summary.slice(0, 30)})`;
128438
+ let resp;
128439
+ try {
128440
+ resp = await this.#request(
128441
+ {
128442
+ method: "POST",
128443
+ url: `${TASK_V2_BASE}/tasks`,
128444
+ params: { user_id_type: "open_id" },
128445
+ data: {
128446
+ summary: input.summary,
128447
+ ...input.description ? { description: input.description } : {},
128448
+ ...input.due ? { due: input.due } : {},
128449
+ ...input.members && input.members.length > 0 ? { members: input.members } : {},
128450
+ ...input.tasklists && input.tasklists.length > 0 ? { tasklists: input.tasklists } : {}
128451
+ }
128452
+ },
128453
+ label
128454
+ );
128455
+ } catch (err) {
128456
+ wrapErr(label, err);
128457
+ }
128458
+ const guid = resp.data?.task?.["guid"];
128459
+ if (typeof guid !== "string" || guid.length === 0) {
128460
+ throw new TaskApiError(`[tasklist.client] ${label} returned no guid`, {});
128461
+ }
128462
+ return { guid };
128463
+ }
128464
+ /**
128465
+ * task_handle v5: add members (e.g. a late follower) to an existing TASK
128466
+ * (`POST /tasks/:guid/add_members` — task-level counterpart of
128467
+ * addTasklistMembers). Best-effort semantics at the caller, same as every
128468
+ * other write here.
128469
+ */
128470
+ async addTaskMembers(taskGuid, members) {
128471
+ const label = `addTaskMembers(${taskGuid})`;
128472
+ try {
128473
+ await this.#request(
128474
+ {
128475
+ method: "POST",
128476
+ url: `${TASK_V2_BASE}/tasks/${encodeURIComponent(taskGuid)}/add_members`,
128477
+ params: { user_id_type: "open_id" },
128478
+ data: { members }
128479
+ },
128480
+ label
128481
+ );
128482
+ } catch (err) {
128483
+ wrapErr(label, err);
128484
+ }
128485
+ }
128486
+ /**
128487
+ * task_handle v5: reschedule (or set) the task due. ms-epoch string per
128488
+ * task v2 `due.timestamp`; same patch semantics as complete()/reopen().
128489
+ */
128490
+ async patchDue(taskGuid, due) {
128491
+ await this.#patchTask(taskGuid, { due }, ["due"]);
128492
+ }
128365
128493
  };
128366
128494
  function isNotFoundLikeRaw(err) {
128367
128495
  const rec = asRecord2(err);
@@ -128583,6 +128711,106 @@ async function applyAutoBindConfirmation(taskGuid, client) {
128583
128711
  }
128584
128712
  }
128585
128713
 
128714
+ // src/tasklist/declare.ts
128715
+ function parseDueInput(raw) {
128716
+ const s = raw.trim();
128717
+ if (/^\d{13}$/.test(s)) return { timestamp: s };
128718
+ if (/^\d{10}$/.test(s)) return { timestamp: `${Number(s) * 1e3}` };
128719
+ if (/^\d{4}-\d{2}-\d{2}$/.test(s)) {
128720
+ const ms2 = Date.parse(`${s}T00:00:00`);
128721
+ if (Number.isNaN(ms2)) return null;
128722
+ return { timestamp: `${ms2}`, is_all_day: true };
128723
+ }
128724
+ const ms = Date.parse(s);
128725
+ if (Number.isNaN(ms)) return null;
128726
+ return { timestamp: `${ms}` };
128727
+ }
128728
+ function formatDueForComment(due) {
128729
+ const d = new Date(Number(due.timestamp));
128730
+ if (Number.isNaN(d.getTime())) return due.timestamp;
128731
+ const pad = (n) => String(n).padStart(2, "0");
128732
+ const date = `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}`;
128733
+ return due.is_all_day ? date : `${date} ${pad(d.getHours())}:${pad(d.getMinutes())}`;
128734
+ }
128735
+ function renderCreateDescription(patch, botName) {
128736
+ const lines = [];
128737
+ if (patch.topicLink) {
128738
+ lines.push(`\u8BDD\u9898\uFF1A${patch.topicLink}`);
128739
+ } else if (patch.chatLink) {
128740
+ lines.push(`\u8BDD\u9898\u6DF1\u94FE\u4E0D\u53EF\u7528\uFF08\u975E\u8BDD\u9898\u6D88\u606F\uFF09\uFF0C\u6240\u5728\u7FA4\uFF1A${patch.chatLink}`);
128741
+ } else {
128742
+ lines.push("\u8BDD\u9898\u6DF1\u94FE\u4E0D\u53EF\u7528\uFF08\u672A\u89E3\u6790\u5230\u8BDD\u9898/\u7FA4\u94FE\u63A5\uFF09");
128743
+ }
128744
+ lines.push(`\u7531 ${botName ?? patch.botId} \u521B\u5EFA \xB7 ${(/* @__PURE__ */ new Date()).toISOString()}`);
128745
+ return lines.join("\n");
128746
+ }
128747
+ async function applyTaskHandleDeclarations(patch, deps) {
128748
+ const outcomes = [];
128749
+ let createdGuid;
128750
+ if (patch.create) {
128751
+ const existing = deps.store.get(patch.threadId);
128752
+ if (existing) {
128753
+ outcomes.push(`create \u8DF3\u8FC7\uFF1A\u8BDD\u9898\u5DF2\u8BA4\u9886\u4EFB\u52A1 ${existing.taskGuid}`);
128754
+ } else {
128755
+ try {
128756
+ const due = patch.create.due ? parseDueInput(patch.create.due) : null;
128757
+ if (patch.create.due && !due) {
128758
+ outcomes.push(`create.due "${patch.create.due}" \u65E0\u6CD5\u89E3\u6790\uFF0C\u4EFB\u52A1\u5C06\u65E0\u622A\u6B62\u65F6\u95F4`);
128759
+ }
128760
+ const members = patch.senderOpenId ? [{ id: patch.senderOpenId, type: "user", role: "follower" }] : [];
128761
+ const { guid } = await deps.client.createTask({
128762
+ summary: patch.create.summary,
128763
+ description: renderCreateDescription(patch, deps.botName),
128764
+ ...due ? { due } : {},
128765
+ ...members.length > 0 ? { members } : {},
128766
+ ...deps.tasklistGuid ? { tasklists: [{ tasklist_guid: deps.tasklistGuid }] } : {}
128767
+ });
128768
+ createdGuid = guid;
128769
+ outcomes.push(
128770
+ `\u5DF2\u5EFA\u4EFB\u52A1 ${guid}\uFF08${patch.create.summary.slice(0, 40)}${due ? ` \xB7 \u622A\u6B62 ${formatDueForComment(due)}` : ""}${patch.senderOpenId ? " \xB7 \u53D1\u8D77\u4EBA\u5DF2\u52A0\u5173\u6CE8" : ""}${patch.topicLink ? "" : "\uFF0C\u8BDD\u9898\u6DF1\u94FE\u7F3A\u5931\u5DF2\u964D\u7EA7\u4E3A\u7FA4\u94FE\u63A5"}\uFF09`
128771
+ );
128772
+ } catch (err) {
128773
+ outcomes.push(`create \u5931\u8D25\uFF08\u672C\u8F6E\u8DF3\u8FC7\uFF0C\u4E0D\u5F71\u54CD\u4EA4\u4ED8\uFF09\uFF1A${String(err.message ?? err)}`);
128774
+ }
128775
+ }
128776
+ }
128777
+ const targetGuid = patch.due || patch.blocked ? createdGuid ?? deps.store.get(patch.threadId)?.taskGuid ?? patch.declaredGuid : void 0;
128778
+ if (patch.due) {
128779
+ if (!targetGuid) {
128780
+ outcomes.push("due \u58F0\u660E\u88AB\u5FFD\u7565\uFF1A\u672C\u8BDD\u9898\u6CA1\u6709\u5DF2\u8BA4\u9886/\u65B0\u5EFA\u7684\u4EFB\u52A1");
128781
+ } else {
128782
+ const due = parseDueInput(patch.due);
128783
+ if (!due) {
128784
+ outcomes.push(`due "${patch.due}" \u65E0\u6CD5\u89E3\u6790\uFF0C\u672A\u6539\u671F`);
128785
+ } else {
128786
+ try {
128787
+ await deps.client.patchDue(targetGuid, due);
128788
+ const line = `\u23F0 \u622A\u6B62\u6539\u4E3A ${formatDueForComment(due)}${patch.dueReason ? `\uFF1A${patch.dueReason}` : ""}`;
128789
+ await deps.client.addComment(targetGuid, line).catch((err) => {
128790
+ console.warn(`[tasklist.declare] due comment failed (due itself applied):`, err);
128791
+ });
128792
+ outcomes.push(`\u5DF2\u6539\u671F ${formatDueForComment(due)}${patch.dueReason ? `\uFF08${patch.dueReason}\uFF09` : ""}`);
128793
+ } catch (err) {
128794
+ outcomes.push(`\u6539\u671F\u5931\u8D25\uFF1A${String(err.message ?? err)}`);
128795
+ }
128796
+ }
128797
+ }
128798
+ }
128799
+ if (patch.blocked) {
128800
+ if (!targetGuid) {
128801
+ outcomes.push("blocked \u58F0\u660E\u88AB\u5FFD\u7565\uFF1A\u672C\u8BDD\u9898\u6CA1\u6709\u5DF2\u8BA4\u9886/\u65B0\u5EFA\u7684\u4EFB\u52A1");
128802
+ } else {
128803
+ try {
128804
+ await deps.client.addComment(targetGuid, `\u{1F6A7} \u963B\u585E\uFF0C\u9700\u8981\u4EBA\u5904\u7406\uFF1A${patch.blocked}`);
128805
+ outcomes.push("\u963B\u585E\u5DF2\u843D\u4EFB\u52A1\u8BC4\u8BBA\uFF08\u5173\u6CE8\u4EBA\u5C06\u6536\u5230\u901A\u77E5\uFF09");
128806
+ } catch (err) {
128807
+ outcomes.push(`\u963B\u585E\u8BC4\u8BBA\u5931\u8D25\uFF1A${String(err.message ?? err)}`);
128808
+ }
128809
+ }
128810
+ }
128811
+ return { createdGuid, outcomes };
128812
+ }
128813
+
128586
128814
  // src/tasklist/commentPoller.ts
128587
128815
  var DEFAULT_INTERVAL_MS = 6e4;
128588
128816
  var DEFAULT_JITTER_MS = 1e4;
@@ -130159,6 +130387,7 @@ async function runV2Mode({
130159
130387
  let effectiveTaskHandleTasklistGuid;
130160
130388
  let taskHandleLifecycle;
130161
130389
  let taskHandleClaim;
130390
+ let taskHandleDeclare;
130162
130391
  let taskHandleClaimedLookup;
130163
130392
  let taskHandleClaimGuidLookup;
130164
130393
  let taskGuidClaimedByOtherBot;
@@ -130194,6 +130423,12 @@ async function runV2Mode({
130194
130423
  const taskHandleStore = await TaskHandleStore.load(resolveTaskHandlesPath(bot.id));
130195
130424
  allTaskHandleStores.push({ botId: bot.id, store: taskHandleStore });
130196
130425
  taskHandleLifecycle = (patch) => applyTaskHandleWriteback(patch, { store: taskHandleStore, client: taskListClient });
130426
+ taskHandleDeclare = (patch) => applyTaskHandleDeclarations(patch, {
130427
+ store: taskHandleStore,
130428
+ client: taskListClient,
130429
+ tasklistGuid: effectiveTaskHandleTasklistGuid,
130430
+ botName: bot.name
130431
+ });
130197
130432
  taskHandleClaim = async (patch) => {
130198
130433
  const result = await taskHandleStore.claim(patch);
130199
130434
  if (!result.claimed) {
@@ -130457,6 +130692,7 @@ async function runV2Mode({
130457
130692
  },
130458
130693
  taskHandleLifecycle,
130459
130694
  taskHandleClaim,
130695
+ taskHandleDeclare,
130460
130696
  taskHandleClaimedLookup,
130461
130697
  taskHandleClaimGuidLookup,
130462
130698
  taskGuidClaimedByOtherBot,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "larkway",
3
- "version": "0.3.57",
3
+ "version": "0.3.58",
4
4
  "description": "Thin bridge: Feishu thread to local Claude Code CLI",
5
5
  "license": "MIT",
6
6
  "author": "Chuck Wu (chuckwu0)",