larkway 0.3.39 → 0.3.41
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 +285 -33
- 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.41**
|
|
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,74 @@ 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
|
+
function realTopicThreadId(threadId) {
|
|
116660
|
+
return typeof threadId === "string" && threadId.startsWith("omt_") ? threadId : void 0;
|
|
116661
|
+
}
|
|
116662
|
+
var LOOKUP_TIMEOUT_MS = 5e3;
|
|
116663
|
+
var CACHE_MAX = 200;
|
|
116664
|
+
async function withTimeout2(p, ms, label) {
|
|
116665
|
+
let timer;
|
|
116666
|
+
const timeout = new Promise((_resolve, reject) => {
|
|
116667
|
+
timer = setTimeout(() => reject(new Error(`[channel.msglookup] ${label} timed out after ${ms}ms`)), ms);
|
|
116668
|
+
timer.unref?.();
|
|
116669
|
+
});
|
|
116670
|
+
try {
|
|
116671
|
+
return await Promise.race([p, timeout]);
|
|
116672
|
+
} finally {
|
|
116673
|
+
if (timer) clearTimeout(timer);
|
|
116674
|
+
}
|
|
116675
|
+
}
|
|
116676
|
+
var ChannelMessageLookupClient = class {
|
|
116677
|
+
#resolveChannel;
|
|
116678
|
+
#cache = /* @__PURE__ */ new Map();
|
|
116679
|
+
constructor(opts) {
|
|
116680
|
+
this.#resolveChannel = opts.resolveChannel;
|
|
116681
|
+
}
|
|
116682
|
+
async get(messageId, opts) {
|
|
116683
|
+
if (!opts?.refresh) {
|
|
116684
|
+
const cached = this.#cache.get(messageId);
|
|
116685
|
+
if (cached) return cached;
|
|
116686
|
+
}
|
|
116687
|
+
try {
|
|
116688
|
+
const channel = this.#resolveChannel();
|
|
116689
|
+
if (!channel) return void 0;
|
|
116690
|
+
const req = {
|
|
116691
|
+
url: `/open-apis/im/v1/messages/${encodeURIComponent(messageId)}`,
|
|
116692
|
+
method: "GET"
|
|
116693
|
+
};
|
|
116694
|
+
const res = await withTimeout2(
|
|
116695
|
+
channel.rawClient.request(req),
|
|
116696
|
+
LOOKUP_TIMEOUT_MS,
|
|
116697
|
+
`GET message ${messageId}`
|
|
116698
|
+
);
|
|
116699
|
+
if (res.code !== void 0 && res.code !== 0) return void 0;
|
|
116700
|
+
const items = res.data?.["items"];
|
|
116701
|
+
if (!Array.isArray(items) || items.length === 0) return void 0;
|
|
116702
|
+
const item = items[0];
|
|
116703
|
+
const body = item["body"];
|
|
116704
|
+
const info = {
|
|
116705
|
+
msgType: typeof item["msg_type"] === "string" ? item["msg_type"] : void 0,
|
|
116706
|
+
content: body && typeof body === "object" && typeof body["content"] === "string" ? body["content"] : void 0,
|
|
116707
|
+
threadId: typeof item["thread_id"] === "string" && item["thread_id"] ? item["thread_id"] : void 0
|
|
116708
|
+
};
|
|
116709
|
+
if (this.#cache.size >= CACHE_MAX) {
|
|
116710
|
+
const oldest = this.#cache.keys().next().value;
|
|
116711
|
+
if (oldest !== void 0) this.#cache.delete(oldest);
|
|
116712
|
+
}
|
|
116713
|
+
this.#cache.set(messageId, info);
|
|
116714
|
+
return info;
|
|
116715
|
+
} catch {
|
|
116716
|
+
return void 0;
|
|
116717
|
+
}
|
|
116718
|
+
}
|
|
116719
|
+
};
|
|
116720
|
+
|
|
116653
116721
|
// src/lark/channelClient.ts
|
|
116654
116722
|
var execFile = promisify(execFileCallback);
|
|
116655
116723
|
var LEARNED_CHATS_LIMIT = 100;
|
|
@@ -116947,6 +117015,8 @@ var ChannelClient = class {
|
|
|
116947
117015
|
postClient = null;
|
|
116948
117016
|
/** Lazily built COT (思维链) client; only requested when a bot enables cot != off. */
|
|
116949
117017
|
cotClient = null;
|
|
117018
|
+
/** Lazily built single-message lookup client (v4 任务派单 root-type probe). */
|
|
117019
|
+
messageLookupClient = null;
|
|
116950
117020
|
constructor(opts) {
|
|
116951
117021
|
if (!opts.appId || !opts.appSecret) {
|
|
116952
117022
|
throw new Error(
|
|
@@ -117230,6 +117300,20 @@ var ChannelClient = class {
|
|
|
117230
117300
|
}
|
|
117231
117301
|
return this.cotClient;
|
|
117232
117302
|
}
|
|
117303
|
+
/**
|
|
117304
|
+
* Return a MessageLookupClient bound to this client's channel handle
|
|
117305
|
+
* (docs/task-handle.md §15.4 — the v4 任务派单 root-type probe). Same lazy
|
|
117306
|
+
* channel resolution and generic rawClient.request() auth path as the COT
|
|
117307
|
+
* client above; strictly best-effort per the client's own contract.
|
|
117308
|
+
*/
|
|
117309
|
+
outboundMessageLookupClient() {
|
|
117310
|
+
if (!this.messageLookupClient) {
|
|
117311
|
+
this.messageLookupClient = new ChannelMessageLookupClient({
|
|
117312
|
+
resolveChannel: () => this.channel
|
|
117313
|
+
});
|
|
117314
|
+
}
|
|
117315
|
+
return this.messageLookupClient;
|
|
117316
|
+
}
|
|
117233
117317
|
/**
|
|
117234
117318
|
* Return an OutboundPostClient bound to this client's channel handle.
|
|
117235
117319
|
*
|
|
@@ -118656,6 +118740,17 @@ function extractPostText(value) {
|
|
|
118656
118740
|
}
|
|
118657
118741
|
return "";
|
|
118658
118742
|
}
|
|
118743
|
+
function parseTodoShareContent(rawContent) {
|
|
118744
|
+
try {
|
|
118745
|
+
const parsed = JSON.parse(rawContent);
|
|
118746
|
+
const taskGuid = parsed["task_id"];
|
|
118747
|
+
if (typeof taskGuid !== "string" || taskGuid.length === 0) return null;
|
|
118748
|
+
const summaryText = extractPostText(parsed["summary"]).trim();
|
|
118749
|
+
return { taskGuid, summaryText };
|
|
118750
|
+
} catch {
|
|
118751
|
+
return null;
|
|
118752
|
+
}
|
|
118753
|
+
}
|
|
118659
118754
|
function extractText(event) {
|
|
118660
118755
|
let parsed;
|
|
118661
118756
|
try {
|
|
@@ -118667,6 +118762,10 @@ function extractText(event) {
|
|
|
118667
118762
|
return stripAtPlaceholders(event.content);
|
|
118668
118763
|
}
|
|
118669
118764
|
let raw = "";
|
|
118765
|
+
const todo = typeof parsed["task_id"] === "string" ? parseTodoShareContent(event.content) : null;
|
|
118766
|
+
if (todo) {
|
|
118767
|
+
return `[\u98DE\u4E66\u4EFB\u52A1] ${todo.summaryText || "(\u65E0\u6807\u9898)"} (task_guid=${todo.taskGuid})`;
|
|
118768
|
+
}
|
|
118670
118769
|
if (typeof parsed["text"] === "string") {
|
|
118671
118770
|
raw = parsed["text"];
|
|
118672
118771
|
} else if (parsed["content"] !== void 0) {
|
|
@@ -119114,6 +119213,27 @@ function renderTaskHandleBlock(tasklistGuid, claimed, candidates) {
|
|
|
119114
119213
|
"</task-handle>"
|
|
119115
119214
|
];
|
|
119116
119215
|
}
|
|
119216
|
+
function renderTaskRootBlock(taskRoot) {
|
|
119217
|
+
const lines = [
|
|
119218
|
+
"<task-root>",
|
|
119219
|
+
"\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",
|
|
119220
|
+
`task_guid: ${taskRoot.guid}`,
|
|
119221
|
+
`task_summary: ${taskRoot.summary || "(\u65E0\u6807\u9898)"}`,
|
|
119222
|
+
...taskRoot.topicLink ? [`topic_link: ${taskRoot.topicLink}`] : [],
|
|
119223
|
+
`task_root_claimed: ${taskRoot.claimed ? "yes" : "no"}`
|
|
119224
|
+
];
|
|
119225
|
+
if (taskRoot.claimed) {
|
|
119226
|
+
lines.push(
|
|
119227
|
+
"\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"
|
|
119228
|
+
);
|
|
119229
|
+
} else {
|
|
119230
|
+
lines.push(
|
|
119231
|
+
`\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`
|
|
119232
|
+
);
|
|
119233
|
+
}
|
|
119234
|
+
lines.push("</task-root>");
|
|
119235
|
+
return lines;
|
|
119236
|
+
}
|
|
119117
119237
|
function renderRuntimeWarningsBlock(warnings) {
|
|
119118
119238
|
if (warnings.length === 0) return [];
|
|
119119
119239
|
const hasMissingLarkCli = warnings.some((warning) => warning.command === "lark-cli");
|
|
@@ -119267,6 +119387,7 @@ async function renderPrompt(input) {
|
|
|
119267
119387
|
taskHandleTasklistGuid,
|
|
119268
119388
|
taskHandleClaimed = false,
|
|
119269
119389
|
taskHandleCandidates = [],
|
|
119390
|
+
taskRoot,
|
|
119270
119391
|
mtimeFacts = []
|
|
119271
119392
|
} = input;
|
|
119272
119393
|
const backend = input.backend ?? "claude";
|
|
@@ -119281,7 +119402,8 @@ async function renderPrompt(input) {
|
|
|
119281
119402
|
const stateContract = renderStateContract(conventions.stateFilePath);
|
|
119282
119403
|
const peersBlock = peers && peers.length > 0 ? renderPeersBlock(peers) : [];
|
|
119283
119404
|
const turnTakingBlock = turn_taking_limit && turn_taking_limit > 0 ? renderTurnTakingHint(turn_taking_limit) : [];
|
|
119284
|
-
const taskHandleBlock = taskHandleTasklistGuid ? renderTaskHandleBlock(taskHandleTasklistGuid, taskHandleClaimed, taskHandleCandidates) : [];
|
|
119405
|
+
const taskHandleBlock = taskHandleTasklistGuid && !taskRoot ? renderTaskHandleBlock(taskHandleTasklistGuid, taskHandleClaimed, taskHandleCandidates) : [];
|
|
119406
|
+
const taskRootBlock = taskRoot ? renderTaskRootBlock(taskRoot) : [];
|
|
119285
119407
|
const runtimeWarningsBlock = renderRuntimeWarningsBlock(runtimeWarnings);
|
|
119286
119408
|
const extraRepos = extraRepoPaths ?? conventions.extraRepoPaths ?? [];
|
|
119287
119409
|
const workspaceBlock = isAgentWorkspace ? await renderAgentWorkspaceBlock(conventions, extraRepos, isNewThread, mtimeFacts) : hasRepo ? renderWorkspaceBlock(
|
|
@@ -119398,6 +119520,7 @@ async function renderPrompt(input) {
|
|
|
119398
119520
|
...peersBlock.length > 0 ? ["", ...peersBlock] : [],
|
|
119399
119521
|
...turnTakingBlock.length > 0 ? ["", ...turnTakingBlock] : [],
|
|
119400
119522
|
...taskHandleBlock.length > 0 ? ["", ...taskHandleBlock] : [],
|
|
119523
|
+
...taskRootBlock.length > 0 ? ["", ...taskRootBlock] : [],
|
|
119401
119524
|
"",
|
|
119402
119525
|
"<user-message>",
|
|
119403
119526
|
`${parsed.senderOpenId}: ${parsed.text}`,
|
|
@@ -119454,6 +119577,7 @@ async function renderPrompt(input) {
|
|
|
119454
119577
|
...peersBlock.length > 0 ? ["", ...peersBlock] : [],
|
|
119455
119578
|
...turnTakingBlock.length > 0 ? ["", ...turnTakingBlock] : [],
|
|
119456
119579
|
...taskHandleBlock.length > 0 ? ["", ...taskHandleBlock] : [],
|
|
119580
|
+
...taskRootBlock.length > 0 ? ["", ...taskRootBlock] : [],
|
|
119457
119581
|
"",
|
|
119458
119582
|
"<user-message>",
|
|
119459
119583
|
`${parsed.senderOpenId}: ${parsed.text}`,
|
|
@@ -122481,7 +122605,35 @@ var BridgeHandler = class {
|
|
|
122481
122605
|
const existing = this.deps.sessionStore.get(threadId, botId);
|
|
122482
122606
|
const isNewThread = existing === void 0;
|
|
122483
122607
|
const isTopLevel = !(typeof parsed.raw.root_id === "string" && parsed.raw.root_id);
|
|
122484
|
-
|
|
122608
|
+
let replyInThread = isTopLevel;
|
|
122609
|
+
let replyAnchorId = messageId;
|
|
122610
|
+
let taskRootInfo;
|
|
122611
|
+
let deferredTaskRootProbe;
|
|
122612
|
+
{
|
|
122613
|
+
const rootId = typeof parsed.raw.root_id === "string" && parsed.raw.root_id ? parsed.raw.root_id : void 0;
|
|
122614
|
+
const rawThreadId = realTopicThreadId(parsed.raw.thread_id);
|
|
122615
|
+
if (rootId && this.deps.messageLookup) {
|
|
122616
|
+
const probe = this.deps.messageLookup.get(rootId).catch(() => void 0);
|
|
122617
|
+
if (rawThreadId) {
|
|
122618
|
+
deferredTaskRootProbe = probe;
|
|
122619
|
+
} else {
|
|
122620
|
+
const info = await probe;
|
|
122621
|
+
if (info?.msgType === "todo" && info.content) {
|
|
122622
|
+
const todo = parseTodoShareContent(info.content);
|
|
122623
|
+
if (todo) {
|
|
122624
|
+
const probeThreadId = realTopicThreadId(info.threadId);
|
|
122625
|
+
taskRootInfo = {
|
|
122626
|
+
guid: todo.taskGuid,
|
|
122627
|
+
summary: todo.summaryText,
|
|
122628
|
+
topicLink: probeThreadId ? buildTopicDeepLink(parsed.chatId, probeThreadId) : void 0
|
|
122629
|
+
};
|
|
122630
|
+
replyInThread = true;
|
|
122631
|
+
replyAnchorId = rootId;
|
|
122632
|
+
}
|
|
122633
|
+
}
|
|
122634
|
+
}
|
|
122635
|
+
}
|
|
122636
|
+
}
|
|
122485
122637
|
const prototypeConfig = this.deps.botConfig?.response_surface_prototype;
|
|
122486
122638
|
const cardKitAvailable = isResponseSurfaceCardKitAvailable(
|
|
122487
122639
|
prototypeConfig,
|
|
@@ -122497,7 +122649,7 @@ var BridgeHandler = class {
|
|
|
122497
122649
|
let startFailurePostFallbackSent = false;
|
|
122498
122650
|
if (!cardKitAvailable) {
|
|
122499
122651
|
try {
|
|
122500
|
-
card = await this.deps.cardRenderer.start(
|
|
122652
|
+
card = await this.deps.cardRenderer.start(replyAnchorId, { replyInThread, threadId });
|
|
122501
122653
|
await recordEvent({
|
|
122502
122654
|
status: "running",
|
|
122503
122655
|
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -122568,7 +122720,10 @@ var BridgeHandler = class {
|
|
|
122568
122720
|
inputPreview: parsed.text,
|
|
122569
122721
|
target: {
|
|
122570
122722
|
chatId: parsed.chatId,
|
|
122571
|
-
|
|
122723
|
+
// omt_* only (see realTopicThreadId): passing a reply-chain om_*
|
|
122724
|
+
// id as receive_id_type=thread_id anchored the COT bubble onto a
|
|
122725
|
+
// stray thread on the TRIGGER message (2026-07-08 dogfood).
|
|
122726
|
+
threadId: realTopicThreadId(parsed.raw.thread_id),
|
|
122572
122727
|
originMessageId
|
|
122573
122728
|
}
|
|
122574
122729
|
});
|
|
@@ -122596,7 +122751,7 @@ var BridgeHandler = class {
|
|
|
122596
122751
|
try {
|
|
122597
122752
|
cardKitProgress = await createCardKitProgressHandle({
|
|
122598
122753
|
cardKitClient: this.deps.cardKitClient,
|
|
122599
|
-
replyToMessageId:
|
|
122754
|
+
replyToMessageId: replyAnchorId,
|
|
122600
122755
|
replyInThread,
|
|
122601
122756
|
facts: {
|
|
122602
122757
|
botId: this.deps.botConfig?.id ?? "v1-default",
|
|
@@ -122627,7 +122782,7 @@ var BridgeHandler = class {
|
|
|
122627
122782
|
status: "message_sent",
|
|
122628
122783
|
cardId: cardKitProgress.cardId,
|
|
122629
122784
|
messageId: cardKitProgress.messageId,
|
|
122630
|
-
replyToMessageId:
|
|
122785
|
+
replyToMessageId: replyAnchorId,
|
|
122631
122786
|
chatId: parsed.chatId,
|
|
122632
122787
|
threadId,
|
|
122633
122788
|
botId: this.deps.botConfig?.id ?? "",
|
|
@@ -122672,7 +122827,7 @@ var BridgeHandler = class {
|
|
|
122672
122827
|
}
|
|
122673
122828
|
if (!card && cardKitStartFailed) {
|
|
122674
122829
|
try {
|
|
122675
|
-
card = await this.deps.cardRenderer.start(
|
|
122830
|
+
card = await this.deps.cardRenderer.start(replyAnchorId, { replyInThread, threadId });
|
|
122676
122831
|
await this.deps.client.removeProcessingReaction?.(messageId);
|
|
122677
122832
|
await recordEvent({
|
|
122678
122833
|
status: "running",
|
|
@@ -122687,7 +122842,7 @@ var BridgeHandler = class {
|
|
|
122687
122842
|
);
|
|
122688
122843
|
const postFallback = await createOnlyPostFallback({
|
|
122689
122844
|
postClient: this.deps.postClient,
|
|
122690
|
-
replyToMessageId:
|
|
122845
|
+
replyToMessageId: replyAnchorId,
|
|
122691
122846
|
replyInThread,
|
|
122692
122847
|
botId: this.deps.botConfig?.id ?? "v1-default",
|
|
122693
122848
|
threadId,
|
|
@@ -122924,6 +123079,28 @@ var BridgeHandler = class {
|
|
|
122924
123079
|
console.warn("[bridge.handler] live-roster resolve failed (using static peers):", err);
|
|
122925
123080
|
}
|
|
122926
123081
|
}
|
|
123082
|
+
if (deferredTaskRootProbe) {
|
|
123083
|
+
const info = await deferredTaskRootProbe;
|
|
123084
|
+
if (info?.msgType === "todo" && info.content) {
|
|
123085
|
+
const todo = parseTodoShareContent(info.content);
|
|
123086
|
+
if (todo) {
|
|
123087
|
+
const rawThreadId = realTopicThreadId(parsed.raw.thread_id);
|
|
123088
|
+
const probeThreadId = realTopicThreadId(info.threadId);
|
|
123089
|
+
taskRootInfo = {
|
|
123090
|
+
guid: todo.taskGuid,
|
|
123091
|
+
summary: todo.summaryText,
|
|
123092
|
+
topicLink: rawThreadId ? buildTopicDeepLink(parsed.chatId, rawThreadId) : probeThreadId ? buildTopicDeepLink(parsed.chatId, probeThreadId) : void 0
|
|
123093
|
+
};
|
|
123094
|
+
}
|
|
123095
|
+
}
|
|
123096
|
+
}
|
|
123097
|
+
if (taskRootInfo && !taskRootInfo.topicLink && replyAnchorId !== messageId && this.deps.messageLookup) {
|
|
123098
|
+
const refreshed = await this.deps.messageLookup.get(replyAnchorId, { refresh: true }).catch(() => void 0);
|
|
123099
|
+
const refreshedThreadId = realTopicThreadId(refreshed?.threadId);
|
|
123100
|
+
if (refreshedThreadId) {
|
|
123101
|
+
taskRootInfo.topicLink = buildTopicDeepLink(parsed.chatId, refreshedThreadId);
|
|
123102
|
+
}
|
|
123103
|
+
}
|
|
122927
123104
|
const prompt = await renderPrompt({
|
|
122928
123105
|
parsed,
|
|
122929
123106
|
isNewThread: currentIsNewThread,
|
|
@@ -122955,6 +123132,13 @@ var BridgeHandler = class {
|
|
|
122955
123132
|
taskHandleTasklistGuid: this.deps.botConfig?.taskHandle?.tasklistGuid,
|
|
122956
123133
|
taskHandleClaimed: this.deps.taskHandleClaimedLookup?.(threadId) ?? false,
|
|
122957
123134
|
taskHandleCandidates: this.deps.taskHandleCandidatesLookup?.() ?? [],
|
|
123135
|
+
taskRoot: taskRootInfo ? {
|
|
123136
|
+
...taskRootInfo,
|
|
123137
|
+
// Exact-guid comparison (see taskHandleClaimGuidLookup's doc);
|
|
123138
|
+
// falls back to the boolean lookup only when the precise one
|
|
123139
|
+
// isn't wired (older embedding code).
|
|
123140
|
+
claimed: this.deps.taskHandleClaimGuidLookup ? this.deps.taskHandleClaimGuidLookup(threadId) === taskRootInfo.guid : this.deps.taskHandleClaimedLookup?.(threadId) ?? false
|
|
123141
|
+
} : void 0,
|
|
122958
123142
|
mtimeFacts
|
|
122959
123143
|
});
|
|
122960
123144
|
const backend = this.deps.botConfig?.backend ?? "claude";
|
|
@@ -123096,7 +123280,14 @@ var BridgeHandler = class {
|
|
|
123096
123280
|
botId,
|
|
123097
123281
|
threadId,
|
|
123098
123282
|
chatId: parsed.chatId,
|
|
123099
|
-
taskGuid: claimedTaskGuid
|
|
123283
|
+
taskGuid: claimedTaskGuid,
|
|
123284
|
+
// v4 任务派单 (docs/task-handle.md §15.3): a claim on the very
|
|
123285
|
+
// task this thread's ROOT message shares is comment-mode —
|
|
123286
|
+
// maintenance goes through task comments only (share-to-chat
|
|
123287
|
+
// grants read+comment; no tasklist/editor rights needed, and
|
|
123288
|
+
// completion is ALWAYS ticked by the human). Mechanical
|
|
123289
|
+
// equality check, not a judgment call.
|
|
123290
|
+
mode: taskRootInfo?.guid === claimedTaskGuid ? "comment" : void 0
|
|
123100
123291
|
});
|
|
123101
123292
|
} catch (err) {
|
|
123102
123293
|
console.warn("[bridge.handler] taskHandleClaim hook failed (continuing):", err);
|
|
@@ -123241,7 +123432,7 @@ var BridgeHandler = class {
|
|
|
123241
123432
|
console.warn("[bridge.handler] CardKit finalize failed; using card fallback:", err);
|
|
123242
123433
|
cardKitProgress.close();
|
|
123243
123434
|
try {
|
|
123244
|
-
card = await this.deps.cardRenderer.start(
|
|
123435
|
+
card = await this.deps.cardRenderer.start(replyAnchorId, { replyInThread, threadId });
|
|
123245
123436
|
await writeCardFile(worktreePath, {
|
|
123246
123437
|
messageId: card.messageId,
|
|
123247
123438
|
chatId: parsed.chatId,
|
|
@@ -123266,7 +123457,7 @@ var BridgeHandler = class {
|
|
|
123266
123457
|
} catch (legacyErr) {
|
|
123267
123458
|
const postFallback = await createOnlyPostFallback({
|
|
123268
123459
|
postClient: this.deps.postClient,
|
|
123269
|
-
replyToMessageId:
|
|
123460
|
+
replyToMessageId: replyAnchorId,
|
|
123270
123461
|
replyInThread,
|
|
123271
123462
|
botId: this.deps.botConfig?.id ?? "v1-default",
|
|
123272
123463
|
threadId,
|
|
@@ -123290,7 +123481,7 @@ var BridgeHandler = class {
|
|
|
123290
123481
|
} else {
|
|
123291
123482
|
if (!card) {
|
|
123292
123483
|
try {
|
|
123293
|
-
card = await this.deps.cardRenderer.start(
|
|
123484
|
+
card = await this.deps.cardRenderer.start(replyAnchorId, { replyInThread, threadId });
|
|
123294
123485
|
try {
|
|
123295
123486
|
await writeCardFile(worktreePath, {
|
|
123296
123487
|
messageId: card.messageId,
|
|
@@ -123314,7 +123505,7 @@ var BridgeHandler = class {
|
|
|
123314
123505
|
].filter((part) => !!part).join("; ");
|
|
123315
123506
|
const postFallback = await createOnlyPostFallback({
|
|
123316
123507
|
postClient: this.deps.postClient,
|
|
123317
|
-
replyToMessageId:
|
|
123508
|
+
replyToMessageId: replyAnchorId,
|
|
123318
123509
|
replyInThread,
|
|
123319
123510
|
botId: this.deps.botConfig?.id ?? "v1-default",
|
|
123320
123511
|
threadId,
|
|
@@ -123395,7 +123586,7 @@ var BridgeHandler = class {
|
|
|
123395
123586
|
const createHardFailurePostFallback = async (failureReason) => {
|
|
123396
123587
|
const fallback = await createOnlyPostFallback({
|
|
123397
123588
|
postClient: this.deps.postClient,
|
|
123398
|
-
replyToMessageId:
|
|
123589
|
+
replyToMessageId: replyAnchorId,
|
|
123399
123590
|
replyInThread,
|
|
123400
123591
|
botId: this.deps.botConfig?.id ?? "v1-default",
|
|
123401
123592
|
threadId,
|
|
@@ -123424,7 +123615,7 @@ var BridgeHandler = class {
|
|
|
123424
123615
|
cardKitFinalizeErr
|
|
123425
123616
|
);
|
|
123426
123617
|
try {
|
|
123427
|
-
card = await this.deps.cardRenderer.start(
|
|
123618
|
+
card = await this.deps.cardRenderer.start(replyAnchorId, { replyInThread, threadId });
|
|
123428
123619
|
} catch (cardStartErr) {
|
|
123429
123620
|
console.error("[bridge.handler] failure card start also failed:", cardStartErr);
|
|
123430
123621
|
await createHardFailurePostFallback(
|
|
@@ -130014,7 +130205,7 @@ function isStallNudgeState(value) {
|
|
|
130014
130205
|
function isTaskHandleRecord(value) {
|
|
130015
130206
|
if (typeof value !== "object" || value === null) return false;
|
|
130016
130207
|
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");
|
|
130208
|
+
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
130209
|
}
|
|
130019
130210
|
var TaskHandleStore = class _TaskHandleStore {
|
|
130020
130211
|
#filePath;
|
|
@@ -130209,6 +130400,9 @@ var TaskHandleStore = class _TaskHandleStore {
|
|
|
130209
130400
|
await this.update(input.threadId, (existing) => {
|
|
130210
130401
|
if (existing && existing.taskGuid === input.taskGuid) {
|
|
130211
130402
|
claimed = true;
|
|
130403
|
+
if (input.mode === "comment" && existing.mode === void 0) {
|
|
130404
|
+
return { ...existing, mode: "comment" };
|
|
130405
|
+
}
|
|
130212
130406
|
return existing;
|
|
130213
130407
|
}
|
|
130214
130408
|
if (input.onlyIfThreadUnclaimed && existing !== void 0) {
|
|
@@ -130222,7 +130416,13 @@ var TaskHandleStore = class _TaskHandleStore {
|
|
|
130222
130416
|
}
|
|
130223
130417
|
}
|
|
130224
130418
|
claimed = true;
|
|
130225
|
-
return {
|
|
130419
|
+
return {
|
|
130420
|
+
threadId: input.threadId,
|
|
130421
|
+
taskGuid: input.taskGuid,
|
|
130422
|
+
chatId: input.chatId,
|
|
130423
|
+
claimedTs: Date.now(),
|
|
130424
|
+
...input.mode ? { mode: input.mode } : {}
|
|
130425
|
+
};
|
|
130226
130426
|
});
|
|
130227
130427
|
return { claimed, reason };
|
|
130228
130428
|
}
|
|
@@ -130318,7 +130518,7 @@ function isTaskRequestTimeoutError(err) {
|
|
|
130318
130518
|
if (err instanceof TaskApiError && err.cause instanceof TaskRequestTimeoutError) return true;
|
|
130319
130519
|
return false;
|
|
130320
130520
|
}
|
|
130321
|
-
function
|
|
130521
|
+
function withTimeout3(promise, ms, label) {
|
|
130322
130522
|
let timer;
|
|
130323
130523
|
const timeout = new Promise((_, reject) => {
|
|
130324
130524
|
timer = setTimeout(() => reject(new TaskRequestTimeoutError(`${label} timed out after ${ms}ms`)), ms);
|
|
@@ -130335,7 +130535,7 @@ var TaskListClient = class {
|
|
|
130335
130535
|
}
|
|
130336
130536
|
/** Single choke point for every outbound call — applies the timeout race uniformly. */
|
|
130337
130537
|
#request(config, label) {
|
|
130338
|
-
return
|
|
130538
|
+
return withTimeout3(this.#requester.request(config), this.#timeoutMs, label);
|
|
130339
130539
|
}
|
|
130340
130540
|
async getTask(taskGuid) {
|
|
130341
130541
|
let resp;
|
|
@@ -130677,6 +130877,46 @@ async function applyTaskHandleWriteback(patch, deps) {
|
|
|
130677
130877
|
return;
|
|
130678
130878
|
}
|
|
130679
130879
|
const isCompleted = !!task.completedAt && task.completedAt !== "0";
|
|
130880
|
+
if (record.mode === "comment") {
|
|
130881
|
+
switch (patch.status) {
|
|
130882
|
+
case "received": {
|
|
130883
|
+
if (record.doneDeclared) {
|
|
130884
|
+
await deps.store.update(
|
|
130885
|
+
patch.threadId,
|
|
130886
|
+
(current) => current ? { ...current, doneDeclared: void 0 } : current
|
|
130887
|
+
);
|
|
130888
|
+
}
|
|
130889
|
+
break;
|
|
130890
|
+
}
|
|
130891
|
+
case "completed": {
|
|
130892
|
+
const mentionedPeerBotIds = patch.mentionedPeerBotIds && patch.mentionedPeerBotIds.length > 0 ? patch.mentionedPeerBotIds : void 0;
|
|
130893
|
+
const mentionAnchorMs = patch.turnReceivedAt ?? Date.now();
|
|
130894
|
+
await deps.store.update(
|
|
130895
|
+
patch.threadId,
|
|
130896
|
+
(current) => current ? {
|
|
130897
|
+
...current,
|
|
130898
|
+
lastTurnOutcome: "completed",
|
|
130899
|
+
lastTurnMentions: mentionedPeerBotIds,
|
|
130900
|
+
lastTurnMentionsAt: mentionedPeerBotIds ? mentionAnchorMs : void 0,
|
|
130901
|
+
// Agent declared delivery — its own "已交付" comment is the
|
|
130902
|
+
// user-facing artifact; bridge just stops patrolling the
|
|
130903
|
+
// "delivered, human hasn't ticked complete yet" window.
|
|
130904
|
+
...patch.agentDeclaredDone === true ? { doneDeclared: true } : {}
|
|
130905
|
+
} : current
|
|
130906
|
+
);
|
|
130907
|
+
break;
|
|
130908
|
+
}
|
|
130909
|
+
case "failed": {
|
|
130910
|
+
await deps.store.update(
|
|
130911
|
+
patch.threadId,
|
|
130912
|
+
(current) => current ? { ...current, lastTurnOutcome: "failed", lastTurnMentions: void 0, lastTurnMentionsAt: void 0 } : current
|
|
130913
|
+
);
|
|
130914
|
+
await deps.client.addComment(record.taskGuid, renderFailureComment(patch.failureReason));
|
|
130915
|
+
break;
|
|
130916
|
+
}
|
|
130917
|
+
}
|
|
130918
|
+
return;
|
|
130919
|
+
}
|
|
130680
130920
|
switch (patch.status) {
|
|
130681
130921
|
case "received": {
|
|
130682
130922
|
if (isCompleted) {
|
|
@@ -131438,6 +131678,7 @@ var StallDetector = class {
|
|
|
131438
131678
|
async #pollOne(threadId) {
|
|
131439
131679
|
let record = this.#deps.store.get(threadId);
|
|
131440
131680
|
if (!record) return;
|
|
131681
|
+
if (record.doneDeclared) return;
|
|
131441
131682
|
const lastActiveTs = this.#deps.getLastActiveTs(threadId) ?? record.claimedTs;
|
|
131442
131683
|
const now = Date.now();
|
|
131443
131684
|
if (record.stallSuppressUntilActivityAfter !== void 0 && lastActiveTs <= record.stallSuppressUntilActivityAfter) {
|
|
@@ -131684,6 +131925,7 @@ var StallDetector = class {
|
|
|
131684
131925
|
}
|
|
131685
131926
|
} : r
|
|
131686
131927
|
);
|
|
131928
|
+
if (record.mode === "comment") return;
|
|
131687
131929
|
const logSummary = `\u505C\u6EDE\u5524\u9192 #${prospectiveCount}(${this.#nudgeReasonLabel(reason, dueMs)})`;
|
|
131688
131930
|
const nextDescription = mergeDescriptionSnapshot(task.description, {
|
|
131689
131931
|
status: "in_progress",
|
|
@@ -131942,12 +132184,18 @@ async function runV2Mode({
|
|
|
131942
132184
|
let taskHandleLifecycle;
|
|
131943
132185
|
let taskHandleClaim;
|
|
131944
132186
|
let taskHandleClaimedLookup;
|
|
132187
|
+
let taskHandleClaimGuidLookup;
|
|
131945
132188
|
let taskHandleCandidatesLookup;
|
|
131946
132189
|
let taskCommentPoller;
|
|
131947
132190
|
let stallDetector;
|
|
131948
132191
|
{
|
|
131949
132192
|
const guid = bot.taskHandle?.tasklistGuid ?? await readTeamTasklistGuid(resolveTaskTeamRegistryPath());
|
|
131950
|
-
if (guid) {
|
|
132193
|
+
if (!guid) {
|
|
132194
|
+
console.log(
|
|
132195
|
+
`[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`
|
|
132196
|
+
);
|
|
132197
|
+
}
|
|
132198
|
+
{
|
|
131951
132199
|
const taskSdkClient = new import_node_sdk2.Client({
|
|
131952
132200
|
appId: bot.app_id,
|
|
131953
132201
|
appSecret,
|
|
@@ -131957,12 +132205,14 @@ async function runV2Mode({
|
|
|
131957
132205
|
request: (config2) => taskSdkClient.request(config2)
|
|
131958
132206
|
};
|
|
131959
132207
|
const taskListClient = new TaskListClient(taskRequester);
|
|
131960
|
-
|
|
131961
|
-
|
|
131962
|
-
|
|
131963
|
-
|
|
131964
|
-
|
|
131965
|
-
|
|
132208
|
+
if (guid) {
|
|
132209
|
+
await taskListClient.addTasklistMembers(guid, [{ id: bot.app_id, type: "app", role: "editor" }]).catch((err) => {
|
|
132210
|
+
console.warn(
|
|
132211
|
+
`[larkway] bot "${bot.id}": self-join tasklist ${guid} as editor failed (continuing, best-effort): ${compactErrorText(err)}`
|
|
132212
|
+
);
|
|
132213
|
+
});
|
|
132214
|
+
effectiveTaskHandleTasklistGuid = guid;
|
|
132215
|
+
}
|
|
131966
132216
|
const taskHandleStore = await TaskHandleStore.load(resolveTaskHandlesPath(bot.id));
|
|
131967
132217
|
taskHandleLifecycle = (patch) => applyTaskHandleWriteback(patch, { store: taskHandleStore, client: taskListClient });
|
|
131968
132218
|
taskHandleClaim = async (patch) => {
|
|
@@ -131974,12 +132224,13 @@ async function runV2Mode({
|
|
|
131974
132224
|
}
|
|
131975
132225
|
};
|
|
131976
132226
|
taskHandleClaimedLookup = (threadId) => taskHandleStore.get(threadId) !== void 0;
|
|
131977
|
-
|
|
132227
|
+
taskHandleClaimGuidLookup = (threadId) => taskHandleStore.get(threadId)?.taskGuid;
|
|
132228
|
+
if (guid) {
|
|
131978
132229
|
const group = tasklistGuidGroups.get(guid) ?? { client: taskListClient, clientOwnerBotId: bot.id, bots: [] };
|
|
131979
132230
|
group.bots.push({ botId: bot.id, sessionStore, taskHandleStore });
|
|
131980
132231
|
tasklistGuidGroups.set(guid, group);
|
|
132232
|
+
taskHandleCandidatesLookup = () => tasklistPollersByGuid.get(guid)?.getCandidates() ?? [];
|
|
131981
132233
|
}
|
|
131982
|
-
taskHandleCandidatesLookup = () => tasklistPollersByGuid.get(guid)?.getCandidates() ?? [];
|
|
131983
132234
|
taskCommentPoller = new CommentPoller({
|
|
131984
132235
|
store: taskHandleStore,
|
|
131985
132236
|
client: taskListClient,
|
|
@@ -132042,7 +132293,7 @@ async function runV2Mode({
|
|
|
132042
132293
|
// taskHandleCandidatesLookup above (the poller for `guid` is
|
|
132043
132294
|
// constructed AFTER this whole per-bot loop finishes, but this
|
|
132044
132295
|
// closure is only ever INVOKED later, at poll time).
|
|
132045
|
-
getTaskDueMs: (taskGuid) => tasklistPollersByGuid.get(guid)?.getDueTimestamp(taskGuid),
|
|
132296
|
+
getTaskDueMs: (taskGuid) => guid ? tasklistPollersByGuid.get(guid)?.getDueTimestamp(taskGuid) : void 0,
|
|
132046
132297
|
// Round-2 adversarial review fix: THIS bot's own receipt signal
|
|
132047
132298
|
// (not a peer's) — disambiguates a pending-nudge confirmation
|
|
132048
132299
|
// timeout from a nudge turn merely queued behind the semaphore.
|
|
@@ -132091,10 +132342,6 @@ async function runV2Mode({
|
|
|
132091
132342
|
);
|
|
132092
132343
|
stallDetector.start();
|
|
132093
132344
|
}
|
|
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
132345
|
}
|
|
132099
132346
|
}
|
|
132100
132347
|
let codexPool;
|
|
@@ -132165,6 +132412,10 @@ async function runV2Mode({
|
|
|
132165
132412
|
cardKitClient,
|
|
132166
132413
|
cotClient,
|
|
132167
132414
|
postClient,
|
|
132415
|
+
// v4 任务派单 root-type probe (docs/task-handle.md §15.4) — lazy channel
|
|
132416
|
+
// resolution, best-effort by the client's own contract, no config gate
|
|
132417
|
+
// (the probe only ever fires on quote-reply turns).
|
|
132418
|
+
messageLookup: client.outboundMessageLookupClient(),
|
|
132168
132419
|
gitlabToken: effectiveGitlabToken,
|
|
132169
132420
|
agentMemory: bot.agent_memory,
|
|
132170
132421
|
larkCliProfile,
|
|
@@ -132183,6 +132434,7 @@ async function runV2Mode({
|
|
|
132183
132434
|
taskHandleLifecycle,
|
|
132184
132435
|
taskHandleClaim,
|
|
132185
132436
|
taskHandleClaimedLookup,
|
|
132437
|
+
taskHandleClaimGuidLookup,
|
|
132186
132438
|
taskHandleCandidatesLookup
|
|
132187
132439
|
});
|
|
132188
132440
|
handlersByBotId.set(bot.id, handler);
|