larkway 0.3.39 → 0.3.40
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 +1 -1
- package/README.zh.md +1 -1
- package/dist/cli/index.js +85 -2
- package/dist/main.js +275 -32
- package/package.json +1 -1
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.
|
|
11
|
+
**Current release: v0.3.40**
|
|
12
12
|
|
|
13
13
|
---
|
|
14
14
|
|
package/README.zh.md
CHANGED
package/dist/cli/index.js
CHANGED
|
@@ -112500,6 +112500,72 @@ var init_channelCotClient = __esm({
|
|
|
112500
112500
|
}
|
|
112501
112501
|
});
|
|
112502
112502
|
|
|
112503
|
+
// src/lark/messageLookupClient.ts
|
|
112504
|
+
async function withTimeout2(p, ms, label) {
|
|
112505
|
+
let timer;
|
|
112506
|
+
const timeout = new Promise((_resolve, reject) => {
|
|
112507
|
+
timer = setTimeout(() => reject(new Error(`[channel.msglookup] ${label} timed out after ${ms}ms`)), ms);
|
|
112508
|
+
timer.unref?.();
|
|
112509
|
+
});
|
|
112510
|
+
try {
|
|
112511
|
+
return await Promise.race([p, timeout]);
|
|
112512
|
+
} finally {
|
|
112513
|
+
if (timer) clearTimeout(timer);
|
|
112514
|
+
}
|
|
112515
|
+
}
|
|
112516
|
+
var LOOKUP_TIMEOUT_MS, CACHE_MAX, ChannelMessageLookupClient;
|
|
112517
|
+
var init_messageLookupClient = __esm({
|
|
112518
|
+
"src/lark/messageLookupClient.ts"() {
|
|
112519
|
+
"use strict";
|
|
112520
|
+
LOOKUP_TIMEOUT_MS = 5e3;
|
|
112521
|
+
CACHE_MAX = 200;
|
|
112522
|
+
ChannelMessageLookupClient = class {
|
|
112523
|
+
#resolveChannel;
|
|
112524
|
+
#cache = /* @__PURE__ */ new Map();
|
|
112525
|
+
constructor(opts) {
|
|
112526
|
+
this.#resolveChannel = opts.resolveChannel;
|
|
112527
|
+
}
|
|
112528
|
+
async get(messageId, opts) {
|
|
112529
|
+
if (!opts?.refresh) {
|
|
112530
|
+
const cached = this.#cache.get(messageId);
|
|
112531
|
+
if (cached) return cached;
|
|
112532
|
+
}
|
|
112533
|
+
try {
|
|
112534
|
+
const channel = this.#resolveChannel();
|
|
112535
|
+
if (!channel) return void 0;
|
|
112536
|
+
const req = {
|
|
112537
|
+
url: `/open-apis/im/v1/messages/${encodeURIComponent(messageId)}`,
|
|
112538
|
+
method: "GET"
|
|
112539
|
+
};
|
|
112540
|
+
const res = await withTimeout2(
|
|
112541
|
+
channel.rawClient.request(req),
|
|
112542
|
+
LOOKUP_TIMEOUT_MS,
|
|
112543
|
+
`GET message ${messageId}`
|
|
112544
|
+
);
|
|
112545
|
+
if (res.code !== void 0 && res.code !== 0) return void 0;
|
|
112546
|
+
const items = res.data?.["items"];
|
|
112547
|
+
if (!Array.isArray(items) || items.length === 0) return void 0;
|
|
112548
|
+
const item = items[0];
|
|
112549
|
+
const body = item["body"];
|
|
112550
|
+
const info = {
|
|
112551
|
+
msgType: typeof item["msg_type"] === "string" ? item["msg_type"] : void 0,
|
|
112552
|
+
content: body && typeof body === "object" && typeof body["content"] === "string" ? body["content"] : void 0,
|
|
112553
|
+
threadId: typeof item["thread_id"] === "string" && item["thread_id"] ? item["thread_id"] : void 0
|
|
112554
|
+
};
|
|
112555
|
+
if (this.#cache.size >= CACHE_MAX) {
|
|
112556
|
+
const oldest = this.#cache.keys().next().value;
|
|
112557
|
+
if (oldest !== void 0) this.#cache.delete(oldest);
|
|
112558
|
+
}
|
|
112559
|
+
this.#cache.set(messageId, info);
|
|
112560
|
+
return info;
|
|
112561
|
+
} catch {
|
|
112562
|
+
return void 0;
|
|
112563
|
+
}
|
|
112564
|
+
}
|
|
112565
|
+
};
|
|
112566
|
+
}
|
|
112567
|
+
});
|
|
112568
|
+
|
|
112503
112569
|
// src/lark/channelClient.ts
|
|
112504
112570
|
var channelClient_exports = {};
|
|
112505
112571
|
__export(channelClient_exports, {
|
|
@@ -112660,6 +112726,7 @@ var init_channelClient = __esm({
|
|
|
112660
112726
|
init_channelCardKitClient();
|
|
112661
112727
|
init_channelPostClient();
|
|
112662
112728
|
init_channelCotClient();
|
|
112729
|
+
init_messageLookupClient();
|
|
112663
112730
|
execFile4 = promisify4(execFileCallback);
|
|
112664
112731
|
LEARNED_CHATS_LIMIT = 100;
|
|
112665
112732
|
SEEN_MESSAGES_LIMIT = 1e3;
|
|
@@ -112820,6 +112887,8 @@ var init_channelClient = __esm({
|
|
|
112820
112887
|
postClient = null;
|
|
112821
112888
|
/** Lazily built COT (思维链) client; only requested when a bot enables cot != off. */
|
|
112822
112889
|
cotClient = null;
|
|
112890
|
+
/** Lazily built single-message lookup client (v4 任务派单 root-type probe). */
|
|
112891
|
+
messageLookupClient = null;
|
|
112823
112892
|
constructor(opts) {
|
|
112824
112893
|
if (!opts.appId || !opts.appSecret) {
|
|
112825
112894
|
throw new Error(
|
|
@@ -113103,6 +113172,20 @@ var init_channelClient = __esm({
|
|
|
113103
113172
|
}
|
|
113104
113173
|
return this.cotClient;
|
|
113105
113174
|
}
|
|
113175
|
+
/**
|
|
113176
|
+
* Return a MessageLookupClient bound to this client's channel handle
|
|
113177
|
+
* (docs/task-handle.md §15.4 — the v4 任务派单 root-type probe). Same lazy
|
|
113178
|
+
* channel resolution and generic rawClient.request() auth path as the COT
|
|
113179
|
+
* client above; strictly best-effort per the client's own contract.
|
|
113180
|
+
*/
|
|
113181
|
+
outboundMessageLookupClient() {
|
|
113182
|
+
if (!this.messageLookupClient) {
|
|
113183
|
+
this.messageLookupClient = new ChannelMessageLookupClient({
|
|
113184
|
+
resolveChannel: () => this.channel
|
|
113185
|
+
});
|
|
113186
|
+
}
|
|
113187
|
+
return this.messageLookupClient;
|
|
113188
|
+
}
|
|
113106
113189
|
/**
|
|
113107
113190
|
* Return an OutboundPostClient bound to this client's channel handle.
|
|
113108
113191
|
*
|
|
@@ -113793,7 +113876,7 @@ function isTaskRequestTimeoutError(err2) {
|
|
|
113793
113876
|
if (err2 instanceof TaskApiError && err2.cause instanceof TaskRequestTimeoutError) return true;
|
|
113794
113877
|
return false;
|
|
113795
113878
|
}
|
|
113796
|
-
function
|
|
113879
|
+
function withTimeout3(promise, ms, label) {
|
|
113797
113880
|
let timer;
|
|
113798
113881
|
const timeout = new Promise((_, reject) => {
|
|
113799
113882
|
timer = setTimeout(() => reject(new TaskRequestTimeoutError(`${label} timed out after ${ms}ms`)), ms);
|
|
@@ -113837,7 +113920,7 @@ var init_client = __esm({
|
|
|
113837
113920
|
}
|
|
113838
113921
|
/** Single choke point for every outbound call — applies the timeout race uniformly. */
|
|
113839
113922
|
#request(config, label) {
|
|
113840
|
-
return
|
|
113923
|
+
return withTimeout3(this.#requester.request(config), this.#timeoutMs, label);
|
|
113841
113924
|
}
|
|
113842
113925
|
async getTask(taskGuid) {
|
|
113843
113926
|
let resp;
|
package/dist/main.js
CHANGED
|
@@ -116650,6 +116650,71 @@ function stringField(data, key) {
|
|
|
116650
116650
|
return typeof value === "string" ? value : void 0;
|
|
116651
116651
|
}
|
|
116652
116652
|
|
|
116653
|
+
// src/lark/messageLookupClient.ts
|
|
116654
|
+
function buildTopicDeepLink(chatId, threadId) {
|
|
116655
|
+
const c = encodeURIComponent(chatId);
|
|
116656
|
+
const t = encodeURIComponent(threadId);
|
|
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
|
+
}
|
|
116659
|
+
var LOOKUP_TIMEOUT_MS = 5e3;
|
|
116660
|
+
var CACHE_MAX = 200;
|
|
116661
|
+
async function withTimeout2(p, ms, label) {
|
|
116662
|
+
let timer;
|
|
116663
|
+
const timeout = new Promise((_resolve, reject) => {
|
|
116664
|
+
timer = setTimeout(() => reject(new Error(`[channel.msglookup] ${label} timed out after ${ms}ms`)), ms);
|
|
116665
|
+
timer.unref?.();
|
|
116666
|
+
});
|
|
116667
|
+
try {
|
|
116668
|
+
return await Promise.race([p, timeout]);
|
|
116669
|
+
} finally {
|
|
116670
|
+
if (timer) clearTimeout(timer);
|
|
116671
|
+
}
|
|
116672
|
+
}
|
|
116673
|
+
var ChannelMessageLookupClient = class {
|
|
116674
|
+
#resolveChannel;
|
|
116675
|
+
#cache = /* @__PURE__ */ new Map();
|
|
116676
|
+
constructor(opts) {
|
|
116677
|
+
this.#resolveChannel = opts.resolveChannel;
|
|
116678
|
+
}
|
|
116679
|
+
async get(messageId, opts) {
|
|
116680
|
+
if (!opts?.refresh) {
|
|
116681
|
+
const cached = this.#cache.get(messageId);
|
|
116682
|
+
if (cached) return cached;
|
|
116683
|
+
}
|
|
116684
|
+
try {
|
|
116685
|
+
const channel = this.#resolveChannel();
|
|
116686
|
+
if (!channel) return void 0;
|
|
116687
|
+
const req = {
|
|
116688
|
+
url: `/open-apis/im/v1/messages/${encodeURIComponent(messageId)}`,
|
|
116689
|
+
method: "GET"
|
|
116690
|
+
};
|
|
116691
|
+
const res = await withTimeout2(
|
|
116692
|
+
channel.rawClient.request(req),
|
|
116693
|
+
LOOKUP_TIMEOUT_MS,
|
|
116694
|
+
`GET message ${messageId}`
|
|
116695
|
+
);
|
|
116696
|
+
if (res.code !== void 0 && res.code !== 0) return void 0;
|
|
116697
|
+
const items = res.data?.["items"];
|
|
116698
|
+
if (!Array.isArray(items) || items.length === 0) return void 0;
|
|
116699
|
+
const item = items[0];
|
|
116700
|
+
const body = item["body"];
|
|
116701
|
+
const info = {
|
|
116702
|
+
msgType: typeof item["msg_type"] === "string" ? item["msg_type"] : void 0,
|
|
116703
|
+
content: body && typeof body === "object" && typeof body["content"] === "string" ? body["content"] : void 0,
|
|
116704
|
+
threadId: typeof item["thread_id"] === "string" && item["thread_id"] ? item["thread_id"] : void 0
|
|
116705
|
+
};
|
|
116706
|
+
if (this.#cache.size >= CACHE_MAX) {
|
|
116707
|
+
const oldest = this.#cache.keys().next().value;
|
|
116708
|
+
if (oldest !== void 0) this.#cache.delete(oldest);
|
|
116709
|
+
}
|
|
116710
|
+
this.#cache.set(messageId, info);
|
|
116711
|
+
return info;
|
|
116712
|
+
} catch {
|
|
116713
|
+
return void 0;
|
|
116714
|
+
}
|
|
116715
|
+
}
|
|
116716
|
+
};
|
|
116717
|
+
|
|
116653
116718
|
// src/lark/channelClient.ts
|
|
116654
116719
|
var execFile = promisify(execFileCallback);
|
|
116655
116720
|
var LEARNED_CHATS_LIMIT = 100;
|
|
@@ -116947,6 +117012,8 @@ var ChannelClient = class {
|
|
|
116947
117012
|
postClient = null;
|
|
116948
117013
|
/** Lazily built COT (思维链) client; only requested when a bot enables cot != off. */
|
|
116949
117014
|
cotClient = null;
|
|
117015
|
+
/** Lazily built single-message lookup client (v4 任务派单 root-type probe). */
|
|
117016
|
+
messageLookupClient = null;
|
|
116950
117017
|
constructor(opts) {
|
|
116951
117018
|
if (!opts.appId || !opts.appSecret) {
|
|
116952
117019
|
throw new Error(
|
|
@@ -117230,6 +117297,20 @@ var ChannelClient = class {
|
|
|
117230
117297
|
}
|
|
117231
117298
|
return this.cotClient;
|
|
117232
117299
|
}
|
|
117300
|
+
/**
|
|
117301
|
+
* Return a MessageLookupClient bound to this client's channel handle
|
|
117302
|
+
* (docs/task-handle.md §15.4 — the v4 任务派单 root-type probe). Same lazy
|
|
117303
|
+
* channel resolution and generic rawClient.request() auth path as the COT
|
|
117304
|
+
* client above; strictly best-effort per the client's own contract.
|
|
117305
|
+
*/
|
|
117306
|
+
outboundMessageLookupClient() {
|
|
117307
|
+
if (!this.messageLookupClient) {
|
|
117308
|
+
this.messageLookupClient = new ChannelMessageLookupClient({
|
|
117309
|
+
resolveChannel: () => this.channel
|
|
117310
|
+
});
|
|
117311
|
+
}
|
|
117312
|
+
return this.messageLookupClient;
|
|
117313
|
+
}
|
|
117233
117314
|
/**
|
|
117234
117315
|
* Return an OutboundPostClient bound to this client's channel handle.
|
|
117235
117316
|
*
|
|
@@ -118656,6 +118737,17 @@ function extractPostText(value) {
|
|
|
118656
118737
|
}
|
|
118657
118738
|
return "";
|
|
118658
118739
|
}
|
|
118740
|
+
function parseTodoShareContent(rawContent) {
|
|
118741
|
+
try {
|
|
118742
|
+
const parsed = JSON.parse(rawContent);
|
|
118743
|
+
const taskGuid = parsed["task_id"];
|
|
118744
|
+
if (typeof taskGuid !== "string" || taskGuid.length === 0) return null;
|
|
118745
|
+
const summaryText = extractPostText(parsed["summary"]).trim();
|
|
118746
|
+
return { taskGuid, summaryText };
|
|
118747
|
+
} catch {
|
|
118748
|
+
return null;
|
|
118749
|
+
}
|
|
118750
|
+
}
|
|
118659
118751
|
function extractText(event) {
|
|
118660
118752
|
let parsed;
|
|
118661
118753
|
try {
|
|
@@ -118667,6 +118759,10 @@ function extractText(event) {
|
|
|
118667
118759
|
return stripAtPlaceholders(event.content);
|
|
118668
118760
|
}
|
|
118669
118761
|
let raw = "";
|
|
118762
|
+
const todo = typeof parsed["task_id"] === "string" ? parseTodoShareContent(event.content) : null;
|
|
118763
|
+
if (todo) {
|
|
118764
|
+
return `[\u98DE\u4E66\u4EFB\u52A1] ${todo.summaryText || "(\u65E0\u6807\u9898)"} (task_guid=${todo.taskGuid})`;
|
|
118765
|
+
}
|
|
118670
118766
|
if (typeof parsed["text"] === "string") {
|
|
118671
118767
|
raw = parsed["text"];
|
|
118672
118768
|
} else if (parsed["content"] !== void 0) {
|
|
@@ -119114,6 +119210,27 @@ function renderTaskHandleBlock(tasklistGuid, claimed, candidates) {
|
|
|
119114
119210
|
"</task-handle>"
|
|
119115
119211
|
];
|
|
119116
119212
|
}
|
|
119213
|
+
function renderTaskRootBlock(taskRoot) {
|
|
119214
|
+
const lines = [
|
|
119215
|
+
"<task-root>",
|
|
119216
|
+
"\u672C\u8BDD\u9898\u7684\u6839\u6D88\u606F\u662F\u4E00\u6761\u98DE\u4E66\u4EFB\u52A1\u5206\u4EAB(\u4EFB\u52A1\u6D3E\u5355\u4E3B\u8DEF\u5F84,docs/task-handle.md \xA715)\u3002",
|
|
119217
|
+
`task_guid: ${taskRoot.guid}`,
|
|
119218
|
+
`task_summary: ${taskRoot.summary || "(\u65E0\u6807\u9898)"}`,
|
|
119219
|
+
...taskRoot.topicLink ? [`topic_link: ${taskRoot.topicLink}`] : [],
|
|
119220
|
+
`task_root_claimed: ${taskRoot.claimed ? "yes" : "no"}`
|
|
119221
|
+
];
|
|
119222
|
+
if (taskRoot.claimed) {
|
|
119223
|
+
lines.push(
|
|
119224
|
+
"\u672C\u8BDD\u9898\u5DF2\u8BA4\u9886\u8FD9\u4E2A\u4EFB\u52A1\u3002\u7EF4\u62A4\u9762\u53EA\u6709\u4EFB\u52A1\u8BC4\u8BBA:\u4EC5\u5728\u4EA4\u4ED8/\u5931\u8D25/\u7B49\u62CD\u677F\u4E09\u7C7B\u91CC\u7A0B\u7891\u8282\u70B9\u53D1\u4EFB\u52A1\u8BC4\u8BBA(lark-cli task comments create);\u771F\u6B63\u4EA4\u4ED8\u7684\u90A3\u4E00\u8F6E\u5728 state.json \u7684 task_handle \u91CC\u58F0\u660E done: true,\u5E76\u53D1\u4E00\u6761\u300C\u5DF2\u4EA4\u4ED8,\u770B\u8FC7\u8BF7\u70B9\u5B8C\u6210\u300D\u8BC4\u8BBA\u3002\u4E0D\u8981\u4FEE\u6539\u4EFB\u52A1\u63CF\u8FF0\u3001\u4E0D\u8981\u52FE\u5B8C\u6210/reopen\u2014\u2014\u5B8C\u6210\u6C38\u8FDC\u7531\u4EBA\u5728\u4EFB\u52A1\u4E2D\u5FC3\u70B9\u3002"
|
|
119225
|
+
);
|
|
119226
|
+
} else {
|
|
119227
|
+
lines.push(
|
|
119228
|
+
`\u672C\u8F6E\u8BF7\u987A\u5E26\u9759\u9ED8\u8BA4\u9886,\u5206\u4E24\u6B65:\u2460 \u5148\u8BFB\u4E00\u904D\u8BE5\u4EFB\u52A1\u7684\u73B0\u6709\u8BC4\u8BBA(lark-cli task comments list)\u2014\u2014\u5982\u679C\u5DF2\u6709**\u5176\u4ED6 agent** \u7684\u8BA4\u9886\u58F0\u660E,\u8BF4\u660E\u540C\u8BDD\u9898\u7684\u53E6\u4E00\u4E2A bot \u5DF2\u63A5\u624B,\u672C\u8F6E\u4E0D\u8BA4\u9886\u3001\u4E0D\u58F0\u5F20\u3001\u6B63\u5E38\u56DE\u7B54\u5373\u53EF(\u53CC @ \u7ADE\u6001\u62A4\u680F:\u91CD\u590D\u8BA4\u9886 = \u53CC\u4EFD\u7EF4\u62A4 + \u53CC\u4EFD\u5DE1\u68C0\u9A9A\u6270);\u2461 \u6CA1\u6709\u522B\u4EBA\u8BA4\u9886 \u2192 \u628A\u4E0A\u9762\u7684 task_guid \u5199\u5165 state.json \u7684 task_handle.guid,\u5E76\u53D1\u4E00\u6761\u8BA4\u9886\u8BC4\u8BBA(lark-cli task comments create),\u5185\u5BB9 = \u4E00\u53E5\u8BA4\u9886\u58F0\u660E${taskRoot.topicLink ? " + \u4E0A\u9762\u7684 topic_link(\u7528\u6237\u70B9\u5B83\u76F4\u8FBE\u672C\u8BDD\u9898)" : ""}\u3002\u4E4B\u540E\u7EF4\u62A4\u9762\u53EA\u6709\u4EFB\u52A1\u8BC4\u8BBA(\u4EA4\u4ED8/\u5931\u8D25/\u7B49\u62CD\u677F\u624D\u53D1,\u8FC7\u7A0B\u788E\u788E\u5FF5\u4E0D\u53D1);\u4E0D\u8981\u4FEE\u6539\u4EFB\u52A1\u63CF\u8FF0\u3001\u4E0D\u8981\u52FE\u5B8C\u6210\u2014\u2014\u5B8C\u6210\u6C38\u8FDC\u7531\u4EBA\u70B9\u3002`
|
|
119229
|
+
);
|
|
119230
|
+
}
|
|
119231
|
+
lines.push("</task-root>");
|
|
119232
|
+
return lines;
|
|
119233
|
+
}
|
|
119117
119234
|
function renderRuntimeWarningsBlock(warnings) {
|
|
119118
119235
|
if (warnings.length === 0) return [];
|
|
119119
119236
|
const hasMissingLarkCli = warnings.some((warning) => warning.command === "lark-cli");
|
|
@@ -119267,6 +119384,7 @@ async function renderPrompt(input) {
|
|
|
119267
119384
|
taskHandleTasklistGuid,
|
|
119268
119385
|
taskHandleClaimed = false,
|
|
119269
119386
|
taskHandleCandidates = [],
|
|
119387
|
+
taskRoot,
|
|
119270
119388
|
mtimeFacts = []
|
|
119271
119389
|
} = input;
|
|
119272
119390
|
const backend = input.backend ?? "claude";
|
|
@@ -119281,7 +119399,8 @@ async function renderPrompt(input) {
|
|
|
119281
119399
|
const stateContract = renderStateContract(conventions.stateFilePath);
|
|
119282
119400
|
const peersBlock = peers && peers.length > 0 ? renderPeersBlock(peers) : [];
|
|
119283
119401
|
const turnTakingBlock = turn_taking_limit && turn_taking_limit > 0 ? renderTurnTakingHint(turn_taking_limit) : [];
|
|
119284
|
-
const taskHandleBlock = taskHandleTasklistGuid ? renderTaskHandleBlock(taskHandleTasklistGuid, taskHandleClaimed, taskHandleCandidates) : [];
|
|
119402
|
+
const taskHandleBlock = taskHandleTasklistGuid && !taskRoot ? renderTaskHandleBlock(taskHandleTasklistGuid, taskHandleClaimed, taskHandleCandidates) : [];
|
|
119403
|
+
const taskRootBlock = taskRoot ? renderTaskRootBlock(taskRoot) : [];
|
|
119285
119404
|
const runtimeWarningsBlock = renderRuntimeWarningsBlock(runtimeWarnings);
|
|
119286
119405
|
const extraRepos = extraRepoPaths ?? conventions.extraRepoPaths ?? [];
|
|
119287
119406
|
const workspaceBlock = isAgentWorkspace ? await renderAgentWorkspaceBlock(conventions, extraRepos, isNewThread, mtimeFacts) : hasRepo ? renderWorkspaceBlock(
|
|
@@ -119398,6 +119517,7 @@ async function renderPrompt(input) {
|
|
|
119398
119517
|
...peersBlock.length > 0 ? ["", ...peersBlock] : [],
|
|
119399
119518
|
...turnTakingBlock.length > 0 ? ["", ...turnTakingBlock] : [],
|
|
119400
119519
|
...taskHandleBlock.length > 0 ? ["", ...taskHandleBlock] : [],
|
|
119520
|
+
...taskRootBlock.length > 0 ? ["", ...taskRootBlock] : [],
|
|
119401
119521
|
"",
|
|
119402
119522
|
"<user-message>",
|
|
119403
119523
|
`${parsed.senderOpenId}: ${parsed.text}`,
|
|
@@ -119454,6 +119574,7 @@ async function renderPrompt(input) {
|
|
|
119454
119574
|
...peersBlock.length > 0 ? ["", ...peersBlock] : [],
|
|
119455
119575
|
...turnTakingBlock.length > 0 ? ["", ...turnTakingBlock] : [],
|
|
119456
119576
|
...taskHandleBlock.length > 0 ? ["", ...taskHandleBlock] : [],
|
|
119577
|
+
...taskRootBlock.length > 0 ? ["", ...taskRootBlock] : [],
|
|
119457
119578
|
"",
|
|
119458
119579
|
"<user-message>",
|
|
119459
119580
|
`${parsed.senderOpenId}: ${parsed.text}`,
|
|
@@ -122481,7 +122602,34 @@ var BridgeHandler = class {
|
|
|
122481
122602
|
const existing = this.deps.sessionStore.get(threadId, botId);
|
|
122482
122603
|
const isNewThread = existing === void 0;
|
|
122483
122604
|
const isTopLevel = !(typeof parsed.raw.root_id === "string" && parsed.raw.root_id);
|
|
122484
|
-
|
|
122605
|
+
let replyInThread = isTopLevel;
|
|
122606
|
+
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
|
+
}
|
|
122632
|
+
}
|
|
122485
122633
|
const prototypeConfig = this.deps.botConfig?.response_surface_prototype;
|
|
122486
122634
|
const cardKitAvailable = isResponseSurfaceCardKitAvailable(
|
|
122487
122635
|
prototypeConfig,
|
|
@@ -122497,7 +122645,7 @@ var BridgeHandler = class {
|
|
|
122497
122645
|
let startFailurePostFallbackSent = false;
|
|
122498
122646
|
if (!cardKitAvailable) {
|
|
122499
122647
|
try {
|
|
122500
|
-
card = await this.deps.cardRenderer.start(
|
|
122648
|
+
card = await this.deps.cardRenderer.start(replyAnchorId, { replyInThread, threadId });
|
|
122501
122649
|
await recordEvent({
|
|
122502
122650
|
status: "running",
|
|
122503
122651
|
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -122596,7 +122744,7 @@ var BridgeHandler = class {
|
|
|
122596
122744
|
try {
|
|
122597
122745
|
cardKitProgress = await createCardKitProgressHandle({
|
|
122598
122746
|
cardKitClient: this.deps.cardKitClient,
|
|
122599
|
-
replyToMessageId:
|
|
122747
|
+
replyToMessageId: replyAnchorId,
|
|
122600
122748
|
replyInThread,
|
|
122601
122749
|
facts: {
|
|
122602
122750
|
botId: this.deps.botConfig?.id ?? "v1-default",
|
|
@@ -122627,7 +122775,7 @@ var BridgeHandler = class {
|
|
|
122627
122775
|
status: "message_sent",
|
|
122628
122776
|
cardId: cardKitProgress.cardId,
|
|
122629
122777
|
messageId: cardKitProgress.messageId,
|
|
122630
|
-
replyToMessageId:
|
|
122778
|
+
replyToMessageId: replyAnchorId,
|
|
122631
122779
|
chatId: parsed.chatId,
|
|
122632
122780
|
threadId,
|
|
122633
122781
|
botId: this.deps.botConfig?.id ?? "",
|
|
@@ -122672,7 +122820,7 @@ var BridgeHandler = class {
|
|
|
122672
122820
|
}
|
|
122673
122821
|
if (!card && cardKitStartFailed) {
|
|
122674
122822
|
try {
|
|
122675
|
-
card = await this.deps.cardRenderer.start(
|
|
122823
|
+
card = await this.deps.cardRenderer.start(replyAnchorId, { replyInThread, threadId });
|
|
122676
122824
|
await this.deps.client.removeProcessingReaction?.(messageId);
|
|
122677
122825
|
await recordEvent({
|
|
122678
122826
|
status: "running",
|
|
@@ -122687,7 +122835,7 @@ var BridgeHandler = class {
|
|
|
122687
122835
|
);
|
|
122688
122836
|
const postFallback = await createOnlyPostFallback({
|
|
122689
122837
|
postClient: this.deps.postClient,
|
|
122690
|
-
replyToMessageId:
|
|
122838
|
+
replyToMessageId: replyAnchorId,
|
|
122691
122839
|
replyInThread,
|
|
122692
122840
|
botId: this.deps.botConfig?.id ?? "v1-default",
|
|
122693
122841
|
threadId,
|
|
@@ -122924,6 +123072,26 @@ var BridgeHandler = class {
|
|
|
122924
123072
|
console.warn("[bridge.handler] live-roster resolve failed (using static peers):", err);
|
|
122925
123073
|
}
|
|
122926
123074
|
}
|
|
123075
|
+
if (deferredTaskRootProbe) {
|
|
123076
|
+
const info = await deferredTaskRootProbe;
|
|
123077
|
+
if (info?.msgType === "todo" && info.content) {
|
|
123078
|
+
const todo = parseTodoShareContent(info.content);
|
|
123079
|
+
if (todo) {
|
|
123080
|
+
const rawThreadId = typeof parsed.raw.thread_id === "string" && parsed.raw.thread_id ? parsed.raw.thread_id : void 0;
|
|
123081
|
+
taskRootInfo = {
|
|
123082
|
+
guid: todo.taskGuid,
|
|
123083
|
+
summary: todo.summaryText,
|
|
123084
|
+
topicLink: rawThreadId ? buildTopicDeepLink(parsed.chatId, rawThreadId) : info.threadId ? buildTopicDeepLink(parsed.chatId, info.threadId) : void 0
|
|
123085
|
+
};
|
|
123086
|
+
}
|
|
123087
|
+
}
|
|
123088
|
+
}
|
|
123089
|
+
if (taskRootInfo && !taskRootInfo.topicLink && replyAnchorId !== messageId && this.deps.messageLookup) {
|
|
123090
|
+
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);
|
|
123093
|
+
}
|
|
123094
|
+
}
|
|
122927
123095
|
const prompt = await renderPrompt({
|
|
122928
123096
|
parsed,
|
|
122929
123097
|
isNewThread: currentIsNewThread,
|
|
@@ -122955,6 +123123,13 @@ var BridgeHandler = class {
|
|
|
122955
123123
|
taskHandleTasklistGuid: this.deps.botConfig?.taskHandle?.tasklistGuid,
|
|
122956
123124
|
taskHandleClaimed: this.deps.taskHandleClaimedLookup?.(threadId) ?? false,
|
|
122957
123125
|
taskHandleCandidates: this.deps.taskHandleCandidatesLookup?.() ?? [],
|
|
123126
|
+
taskRoot: taskRootInfo ? {
|
|
123127
|
+
...taskRootInfo,
|
|
123128
|
+
// Exact-guid comparison (see taskHandleClaimGuidLookup's doc);
|
|
123129
|
+
// falls back to the boolean lookup only when the precise one
|
|
123130
|
+
// isn't wired (older embedding code).
|
|
123131
|
+
claimed: this.deps.taskHandleClaimGuidLookup ? this.deps.taskHandleClaimGuidLookup(threadId) === taskRootInfo.guid : this.deps.taskHandleClaimedLookup?.(threadId) ?? false
|
|
123132
|
+
} : void 0,
|
|
122958
123133
|
mtimeFacts
|
|
122959
123134
|
});
|
|
122960
123135
|
const backend = this.deps.botConfig?.backend ?? "claude";
|
|
@@ -123096,7 +123271,14 @@ var BridgeHandler = class {
|
|
|
123096
123271
|
botId,
|
|
123097
123272
|
threadId,
|
|
123098
123273
|
chatId: parsed.chatId,
|
|
123099
|
-
taskGuid: claimedTaskGuid
|
|
123274
|
+
taskGuid: claimedTaskGuid,
|
|
123275
|
+
// v4 任务派单 (docs/task-handle.md §15.3): a claim on the very
|
|
123276
|
+
// task this thread's ROOT message shares is comment-mode —
|
|
123277
|
+
// maintenance goes through task comments only (share-to-chat
|
|
123278
|
+
// grants read+comment; no tasklist/editor rights needed, and
|
|
123279
|
+
// completion is ALWAYS ticked by the human). Mechanical
|
|
123280
|
+
// equality check, not a judgment call.
|
|
123281
|
+
mode: taskRootInfo?.guid === claimedTaskGuid ? "comment" : void 0
|
|
123100
123282
|
});
|
|
123101
123283
|
} catch (err) {
|
|
123102
123284
|
console.warn("[bridge.handler] taskHandleClaim hook failed (continuing):", err);
|
|
@@ -123241,7 +123423,7 @@ var BridgeHandler = class {
|
|
|
123241
123423
|
console.warn("[bridge.handler] CardKit finalize failed; using card fallback:", err);
|
|
123242
123424
|
cardKitProgress.close();
|
|
123243
123425
|
try {
|
|
123244
|
-
card = await this.deps.cardRenderer.start(
|
|
123426
|
+
card = await this.deps.cardRenderer.start(replyAnchorId, { replyInThread, threadId });
|
|
123245
123427
|
await writeCardFile(worktreePath, {
|
|
123246
123428
|
messageId: card.messageId,
|
|
123247
123429
|
chatId: parsed.chatId,
|
|
@@ -123266,7 +123448,7 @@ var BridgeHandler = class {
|
|
|
123266
123448
|
} catch (legacyErr) {
|
|
123267
123449
|
const postFallback = await createOnlyPostFallback({
|
|
123268
123450
|
postClient: this.deps.postClient,
|
|
123269
|
-
replyToMessageId:
|
|
123451
|
+
replyToMessageId: replyAnchorId,
|
|
123270
123452
|
replyInThread,
|
|
123271
123453
|
botId: this.deps.botConfig?.id ?? "v1-default",
|
|
123272
123454
|
threadId,
|
|
@@ -123290,7 +123472,7 @@ var BridgeHandler = class {
|
|
|
123290
123472
|
} else {
|
|
123291
123473
|
if (!card) {
|
|
123292
123474
|
try {
|
|
123293
|
-
card = await this.deps.cardRenderer.start(
|
|
123475
|
+
card = await this.deps.cardRenderer.start(replyAnchorId, { replyInThread, threadId });
|
|
123294
123476
|
try {
|
|
123295
123477
|
await writeCardFile(worktreePath, {
|
|
123296
123478
|
messageId: card.messageId,
|
|
@@ -123314,7 +123496,7 @@ var BridgeHandler = class {
|
|
|
123314
123496
|
].filter((part) => !!part).join("; ");
|
|
123315
123497
|
const postFallback = await createOnlyPostFallback({
|
|
123316
123498
|
postClient: this.deps.postClient,
|
|
123317
|
-
replyToMessageId:
|
|
123499
|
+
replyToMessageId: replyAnchorId,
|
|
123318
123500
|
replyInThread,
|
|
123319
123501
|
botId: this.deps.botConfig?.id ?? "v1-default",
|
|
123320
123502
|
threadId,
|
|
@@ -123395,7 +123577,7 @@ var BridgeHandler = class {
|
|
|
123395
123577
|
const createHardFailurePostFallback = async (failureReason) => {
|
|
123396
123578
|
const fallback = await createOnlyPostFallback({
|
|
123397
123579
|
postClient: this.deps.postClient,
|
|
123398
|
-
replyToMessageId:
|
|
123580
|
+
replyToMessageId: replyAnchorId,
|
|
123399
123581
|
replyInThread,
|
|
123400
123582
|
botId: this.deps.botConfig?.id ?? "v1-default",
|
|
123401
123583
|
threadId,
|
|
@@ -123424,7 +123606,7 @@ var BridgeHandler = class {
|
|
|
123424
123606
|
cardKitFinalizeErr
|
|
123425
123607
|
);
|
|
123426
123608
|
try {
|
|
123427
|
-
card = await this.deps.cardRenderer.start(
|
|
123609
|
+
card = await this.deps.cardRenderer.start(replyAnchorId, { replyInThread, threadId });
|
|
123428
123610
|
} catch (cardStartErr) {
|
|
123429
123611
|
console.error("[bridge.handler] failure card start also failed:", cardStartErr);
|
|
123430
123612
|
await createHardFailurePostFallback(
|
|
@@ -130014,7 +130196,7 @@ function isStallNudgeState(value) {
|
|
|
130014
130196
|
function isTaskHandleRecord(value) {
|
|
130015
130197
|
if (typeof value !== "object" || value === null) return false;
|
|
130016
130198
|
const v = value;
|
|
130017
|
-
return typeof v["threadId"] === "string" && typeof v["taskGuid"] === "string" && typeof v["chatId"] === "string" && typeof v["claimedTs"] === "number" && (v["lastSeenCommentId"] === void 0 || typeof v["lastSeenCommentId"] === "string") && (v["lastTurnOutcome"] === void 0 || v["lastTurnOutcome"] === "completed" || v["lastTurnOutcome"] === "failed") && (v["stallNudge"] === void 0 || isStallNudgeState(v["stallNudge"])) && (v["stallSuppressUntilActivityAfter"] === void 0 || typeof v["stallSuppressUntilActivityAfter"] === "number") && (v["lastTurnMentions"] === void 0 || Array.isArray(v["lastTurnMentions"]) && v["lastTurnMentions"].every((m) => typeof m === "string")) && (v["lastTurnMentionsAt"] === void 0 || typeof v["lastTurnMentionsAt"] === "number");
|
|
130199
|
+
return typeof v["threadId"] === "string" && typeof v["taskGuid"] === "string" && typeof v["chatId"] === "string" && typeof v["claimedTs"] === "number" && (v["lastSeenCommentId"] === void 0 || typeof v["lastSeenCommentId"] === "string") && (v["lastTurnOutcome"] === void 0 || v["lastTurnOutcome"] === "completed" || v["lastTurnOutcome"] === "failed") && (v["stallNudge"] === void 0 || isStallNudgeState(v["stallNudge"])) && (v["stallSuppressUntilActivityAfter"] === void 0 || typeof v["stallSuppressUntilActivityAfter"] === "number") && (v["lastTurnMentions"] === void 0 || Array.isArray(v["lastTurnMentions"]) && v["lastTurnMentions"].every((m) => typeof m === "string")) && (v["lastTurnMentionsAt"] === void 0 || typeof v["lastTurnMentionsAt"] === "number") && (v["mode"] === void 0 || v["mode"] === "comment") && (v["doneDeclared"] === void 0 || typeof v["doneDeclared"] === "boolean");
|
|
130018
130200
|
}
|
|
130019
130201
|
var TaskHandleStore = class _TaskHandleStore {
|
|
130020
130202
|
#filePath;
|
|
@@ -130209,6 +130391,9 @@ var TaskHandleStore = class _TaskHandleStore {
|
|
|
130209
130391
|
await this.update(input.threadId, (existing) => {
|
|
130210
130392
|
if (existing && existing.taskGuid === input.taskGuid) {
|
|
130211
130393
|
claimed = true;
|
|
130394
|
+
if (input.mode === "comment" && existing.mode === void 0) {
|
|
130395
|
+
return { ...existing, mode: "comment" };
|
|
130396
|
+
}
|
|
130212
130397
|
return existing;
|
|
130213
130398
|
}
|
|
130214
130399
|
if (input.onlyIfThreadUnclaimed && existing !== void 0) {
|
|
@@ -130222,7 +130407,13 @@ var TaskHandleStore = class _TaskHandleStore {
|
|
|
130222
130407
|
}
|
|
130223
130408
|
}
|
|
130224
130409
|
claimed = true;
|
|
130225
|
-
return {
|
|
130410
|
+
return {
|
|
130411
|
+
threadId: input.threadId,
|
|
130412
|
+
taskGuid: input.taskGuid,
|
|
130413
|
+
chatId: input.chatId,
|
|
130414
|
+
claimedTs: Date.now(),
|
|
130415
|
+
...input.mode ? { mode: input.mode } : {}
|
|
130416
|
+
};
|
|
130226
130417
|
});
|
|
130227
130418
|
return { claimed, reason };
|
|
130228
130419
|
}
|
|
@@ -130318,7 +130509,7 @@ function isTaskRequestTimeoutError(err) {
|
|
|
130318
130509
|
if (err instanceof TaskApiError && err.cause instanceof TaskRequestTimeoutError) return true;
|
|
130319
130510
|
return false;
|
|
130320
130511
|
}
|
|
130321
|
-
function
|
|
130512
|
+
function withTimeout3(promise, ms, label) {
|
|
130322
130513
|
let timer;
|
|
130323
130514
|
const timeout = new Promise((_, reject) => {
|
|
130324
130515
|
timer = setTimeout(() => reject(new TaskRequestTimeoutError(`${label} timed out after ${ms}ms`)), ms);
|
|
@@ -130335,7 +130526,7 @@ var TaskListClient = class {
|
|
|
130335
130526
|
}
|
|
130336
130527
|
/** Single choke point for every outbound call — applies the timeout race uniformly. */
|
|
130337
130528
|
#request(config, label) {
|
|
130338
|
-
return
|
|
130529
|
+
return withTimeout3(this.#requester.request(config), this.#timeoutMs, label);
|
|
130339
130530
|
}
|
|
130340
130531
|
async getTask(taskGuid) {
|
|
130341
130532
|
let resp;
|
|
@@ -130677,6 +130868,46 @@ async function applyTaskHandleWriteback(patch, deps) {
|
|
|
130677
130868
|
return;
|
|
130678
130869
|
}
|
|
130679
130870
|
const isCompleted = !!task.completedAt && task.completedAt !== "0";
|
|
130871
|
+
if (record.mode === "comment") {
|
|
130872
|
+
switch (patch.status) {
|
|
130873
|
+
case "received": {
|
|
130874
|
+
if (record.doneDeclared) {
|
|
130875
|
+
await deps.store.update(
|
|
130876
|
+
patch.threadId,
|
|
130877
|
+
(current) => current ? { ...current, doneDeclared: void 0 } : current
|
|
130878
|
+
);
|
|
130879
|
+
}
|
|
130880
|
+
break;
|
|
130881
|
+
}
|
|
130882
|
+
case "completed": {
|
|
130883
|
+
const mentionedPeerBotIds = patch.mentionedPeerBotIds && patch.mentionedPeerBotIds.length > 0 ? patch.mentionedPeerBotIds : void 0;
|
|
130884
|
+
const mentionAnchorMs = patch.turnReceivedAt ?? Date.now();
|
|
130885
|
+
await deps.store.update(
|
|
130886
|
+
patch.threadId,
|
|
130887
|
+
(current) => current ? {
|
|
130888
|
+
...current,
|
|
130889
|
+
lastTurnOutcome: "completed",
|
|
130890
|
+
lastTurnMentions: mentionedPeerBotIds,
|
|
130891
|
+
lastTurnMentionsAt: mentionedPeerBotIds ? mentionAnchorMs : void 0,
|
|
130892
|
+
// Agent declared delivery — its own "已交付" comment is the
|
|
130893
|
+
// user-facing artifact; bridge just stops patrolling the
|
|
130894
|
+
// "delivered, human hasn't ticked complete yet" window.
|
|
130895
|
+
...patch.agentDeclaredDone === true ? { doneDeclared: true } : {}
|
|
130896
|
+
} : current
|
|
130897
|
+
);
|
|
130898
|
+
break;
|
|
130899
|
+
}
|
|
130900
|
+
case "failed": {
|
|
130901
|
+
await deps.store.update(
|
|
130902
|
+
patch.threadId,
|
|
130903
|
+
(current) => current ? { ...current, lastTurnOutcome: "failed", lastTurnMentions: void 0, lastTurnMentionsAt: void 0 } : current
|
|
130904
|
+
);
|
|
130905
|
+
await deps.client.addComment(record.taskGuid, renderFailureComment(patch.failureReason));
|
|
130906
|
+
break;
|
|
130907
|
+
}
|
|
130908
|
+
}
|
|
130909
|
+
return;
|
|
130910
|
+
}
|
|
130680
130911
|
switch (patch.status) {
|
|
130681
130912
|
case "received": {
|
|
130682
130913
|
if (isCompleted) {
|
|
@@ -131438,6 +131669,7 @@ var StallDetector = class {
|
|
|
131438
131669
|
async #pollOne(threadId) {
|
|
131439
131670
|
let record = this.#deps.store.get(threadId);
|
|
131440
131671
|
if (!record) return;
|
|
131672
|
+
if (record.doneDeclared) return;
|
|
131441
131673
|
const lastActiveTs = this.#deps.getLastActiveTs(threadId) ?? record.claimedTs;
|
|
131442
131674
|
const now = Date.now();
|
|
131443
131675
|
if (record.stallSuppressUntilActivityAfter !== void 0 && lastActiveTs <= record.stallSuppressUntilActivityAfter) {
|
|
@@ -131684,6 +131916,7 @@ var StallDetector = class {
|
|
|
131684
131916
|
}
|
|
131685
131917
|
} : r
|
|
131686
131918
|
);
|
|
131919
|
+
if (record.mode === "comment") return;
|
|
131687
131920
|
const logSummary = `\u505C\u6EDE\u5524\u9192 #${prospectiveCount}(${this.#nudgeReasonLabel(reason, dueMs)})`;
|
|
131688
131921
|
const nextDescription = mergeDescriptionSnapshot(task.description, {
|
|
131689
131922
|
status: "in_progress",
|
|
@@ -131942,12 +132175,18 @@ async function runV2Mode({
|
|
|
131942
132175
|
let taskHandleLifecycle;
|
|
131943
132176
|
let taskHandleClaim;
|
|
131944
132177
|
let taskHandleClaimedLookup;
|
|
132178
|
+
let taskHandleClaimGuidLookup;
|
|
131945
132179
|
let taskHandleCandidatesLookup;
|
|
131946
132180
|
let taskCommentPoller;
|
|
131947
132181
|
let stallDetector;
|
|
131948
132182
|
{
|
|
131949
132183
|
const guid = bot.taskHandle?.tasklistGuid ?? await readTeamTasklistGuid(resolveTaskTeamRegistryPath());
|
|
131950
|
-
if (guid) {
|
|
132184
|
+
if (!guid) {
|
|
132185
|
+
console.log(
|
|
132186
|
+
`[larkway] bot "${bot.id}": \u4EFB\u52A1\u6D3E\u5355\u4E3B\u8DEF\u5F84\u5DF2\u5C31\u7EEA(\u5EFA\u4EFB\u52A1\u2192\u53D1\u9001\u5230\u7FA4\u2192@ \u672C bot,\u96F6\u914D\u7F6E)\u3002\u8BDD\u9898\u8F6C\u4EFB\u52A1\u8F85\u8DEF\u5F84\u7684\u5171\u4EAB\u6E05\u5355\u672A\u914D\u7F6E(taskHandle.tasklistGuid \u7F3A\u5931,\u5171\u4EAB\u6CE8\u518C\u6587\u4EF6\u91CC\u4E5F\u6CA1\u6709);\u60F3\u542F\u7528:\u8FD0\u884C larkway tasklist-init(\u89C1 docs/task-handle.md \xA77/\xA715)\u3002`
|
|
132187
|
+
);
|
|
132188
|
+
}
|
|
132189
|
+
{
|
|
131951
132190
|
const taskSdkClient = new import_node_sdk2.Client({
|
|
131952
132191
|
appId: bot.app_id,
|
|
131953
132192
|
appSecret,
|
|
@@ -131957,12 +132196,14 @@ async function runV2Mode({
|
|
|
131957
132196
|
request: (config2) => taskSdkClient.request(config2)
|
|
131958
132197
|
};
|
|
131959
132198
|
const taskListClient = new TaskListClient(taskRequester);
|
|
131960
|
-
|
|
131961
|
-
|
|
131962
|
-
|
|
131963
|
-
|
|
131964
|
-
|
|
131965
|
-
|
|
132199
|
+
if (guid) {
|
|
132200
|
+
await taskListClient.addTasklistMembers(guid, [{ id: bot.app_id, type: "app", role: "editor" }]).catch((err) => {
|
|
132201
|
+
console.warn(
|
|
132202
|
+
`[larkway] bot "${bot.id}": self-join tasklist ${guid} as editor failed (continuing, best-effort): ${compactErrorText(err)}`
|
|
132203
|
+
);
|
|
132204
|
+
});
|
|
132205
|
+
effectiveTaskHandleTasklistGuid = guid;
|
|
132206
|
+
}
|
|
131966
132207
|
const taskHandleStore = await TaskHandleStore.load(resolveTaskHandlesPath(bot.id));
|
|
131967
132208
|
taskHandleLifecycle = (patch) => applyTaskHandleWriteback(patch, { store: taskHandleStore, client: taskListClient });
|
|
131968
132209
|
taskHandleClaim = async (patch) => {
|
|
@@ -131974,12 +132215,13 @@ async function runV2Mode({
|
|
|
131974
132215
|
}
|
|
131975
132216
|
};
|
|
131976
132217
|
taskHandleClaimedLookup = (threadId) => taskHandleStore.get(threadId) !== void 0;
|
|
131977
|
-
|
|
132218
|
+
taskHandleClaimGuidLookup = (threadId) => taskHandleStore.get(threadId)?.taskGuid;
|
|
132219
|
+
if (guid) {
|
|
131978
132220
|
const group = tasklistGuidGroups.get(guid) ?? { client: taskListClient, clientOwnerBotId: bot.id, bots: [] };
|
|
131979
132221
|
group.bots.push({ botId: bot.id, sessionStore, taskHandleStore });
|
|
131980
132222
|
tasklistGuidGroups.set(guid, group);
|
|
132223
|
+
taskHandleCandidatesLookup = () => tasklistPollersByGuid.get(guid)?.getCandidates() ?? [];
|
|
131981
132224
|
}
|
|
131982
|
-
taskHandleCandidatesLookup = () => tasklistPollersByGuid.get(guid)?.getCandidates() ?? [];
|
|
131983
132225
|
taskCommentPoller = new CommentPoller({
|
|
131984
132226
|
store: taskHandleStore,
|
|
131985
132227
|
client: taskListClient,
|
|
@@ -132042,7 +132284,7 @@ async function runV2Mode({
|
|
|
132042
132284
|
// taskHandleCandidatesLookup above (the poller for `guid` is
|
|
132043
132285
|
// constructed AFTER this whole per-bot loop finishes, but this
|
|
132044
132286
|
// closure is only ever INVOKED later, at poll time).
|
|
132045
|
-
getTaskDueMs: (taskGuid) => tasklistPollersByGuid.get(guid)?.getDueTimestamp(taskGuid),
|
|
132287
|
+
getTaskDueMs: (taskGuid) => guid ? tasklistPollersByGuid.get(guid)?.getDueTimestamp(taskGuid) : void 0,
|
|
132046
132288
|
// Round-2 adversarial review fix: THIS bot's own receipt signal
|
|
132047
132289
|
// (not a peer's) — disambiguates a pending-nudge confirmation
|
|
132048
132290
|
// timeout from a nudge turn merely queued behind the semaphore.
|
|
@@ -132091,10 +132333,6 @@ async function runV2Mode({
|
|
|
132091
132333
|
);
|
|
132092
132334
|
stallDetector.start();
|
|
132093
132335
|
}
|
|
132094
|
-
} else {
|
|
132095
|
-
console.log(
|
|
132096
|
-
`[larkway] bot "${bot.id}": \u8BDD\u9898\u2194\u4EFB\u52A1\u53E5\u67C4\u672A\u914D\u7F6E(taskHandle.tasklistGuid \u7F3A\u5931,\u5171\u4EAB\u6CE8\u518C\u6587\u4EF6\u91CC\u4E5F\u6CA1\u6709)\u3002\u60F3\u542F\u7528:\u8FD0\u884C larkway tasklist-init(\u6216\u5BF9\u4F60\u7684 agent \u8BF4"\u5E2E\u6211\u914D\u7F6E Agent Team")(\u89C1 docs/task-handle.md \xA77)\u3002`
|
|
132097
|
-
);
|
|
132098
132336
|
}
|
|
132099
132337
|
}
|
|
132100
132338
|
let codexPool;
|
|
@@ -132165,6 +132403,10 @@ async function runV2Mode({
|
|
|
132165
132403
|
cardKitClient,
|
|
132166
132404
|
cotClient,
|
|
132167
132405
|
postClient,
|
|
132406
|
+
// v4 任务派单 root-type probe (docs/task-handle.md §15.4) — lazy channel
|
|
132407
|
+
// resolution, best-effort by the client's own contract, no config gate
|
|
132408
|
+
// (the probe only ever fires on quote-reply turns).
|
|
132409
|
+
messageLookup: client.outboundMessageLookupClient(),
|
|
132168
132410
|
gitlabToken: effectiveGitlabToken,
|
|
132169
132411
|
agentMemory: bot.agent_memory,
|
|
132170
132412
|
larkCliProfile,
|
|
@@ -132183,6 +132425,7 @@ async function runV2Mode({
|
|
|
132183
132425
|
taskHandleLifecycle,
|
|
132184
132426
|
taskHandleClaim,
|
|
132185
132427
|
taskHandleClaimedLookup,
|
|
132428
|
+
taskHandleClaimGuidLookup,
|
|
132186
132429
|
taskHandleCandidatesLookup
|
|
132187
132430
|
});
|
|
132188
132431
|
handlersByBotId.set(bot.id, handler);
|