@modelzen/feishu-codex-bridge 0.3.5 → 0.3.7-test.0
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/dist/cli.js +251 -52
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -1111,7 +1111,7 @@ async function registerNewBot(desiredName) {
|
|
|
1111
1111
|
await addBot({ name, appId: app.id, tenant: app.tenant, botName: v.botName, createdAt: Date.now() });
|
|
1112
1112
|
console.log(`\u2713 \u5DF2\u521B\u5EFA\u673A\u5668\u4EBA\u300C${name}\u300D bot: ${v.botName ?? "-"} appId: ${app.id}`);
|
|
1113
1113
|
log.info("onboard", "bot-created", { name, appId: app.id, bot: v.botName ?? null });
|
|
1114
|
-
|
|
1114
|
+
noticeMissingScopes(cfg, v.missingScopes);
|
|
1115
1115
|
const secret = await resolveAppSecret(cfg);
|
|
1116
1116
|
return { cfg, secret, missingScopes: v.missingScopes };
|
|
1117
1117
|
}
|
|
@@ -1125,28 +1125,12 @@ async function validateAndReport(cfg) {
|
|
|
1125
1125
|
}
|
|
1126
1126
|
console.log(`\u2713 \u51ED\u636E\u6821\u9A8C\u901A\u8FC7 bot: ${v.botName ?? "-"} appId: ${cfg.accounts.app.id}`);
|
|
1127
1127
|
log.info("onboard", "credentials-ok", { appId: cfg.accounts.app.id, bot: v.botName ?? null });
|
|
1128
|
-
|
|
1128
|
+
noticeMissingScopes(cfg, v.missingScopes);
|
|
1129
1129
|
return { secret, missingScopes: v.missingScopes };
|
|
1130
1130
|
}
|
|
1131
1131
|
async function confirmReadyForDaemon(result) {
|
|
1132
1132
|
if (!process.stdin.isTTY) return true;
|
|
1133
1133
|
const { app } = result.cfg.accounts;
|
|
1134
|
-
let missing = result.missingScopes;
|
|
1135
|
-
while (missing && missing.length > 0) {
|
|
1136
|
-
const url = buildScopeGrantUrl(app.id, app.tenant);
|
|
1137
|
-
console.log(`
|
|
1138
|
-
\u23F3 \u8FD8\u5DEE ${missing.length} \u9879\u6743\u9650\u672A\u5F00\u901A\uFF0C\u540E\u53F0\u670D\u52A1\u6682\u4E0D\u5B89\u88C5\u3002`);
|
|
1139
|
-
console.log(` \u5F00\u901A\u9875\uFF1A${url}`);
|
|
1140
|
-
await promptEnter(" \u5728\u6D4F\u89C8\u5668\u52FE\u9009\u5168\u90E8\u6743\u9650\u5E76\u786E\u8BA4\u540E\uFF0C\u6309 Enter \u91CD\u65B0\u68C0\u6D4B\uFF08Ctrl+C \u53D6\u6D88\uFF09\u2026 ");
|
|
1141
|
-
const v = await validateAppCredentials(app.id, result.secret, app.tenant);
|
|
1142
|
-
if (!v.ok) {
|
|
1143
|
-
console.error(`\u2717 \u51ED\u636E\u6821\u9A8C\u5931\u8D25\uFF1A${v.reason}`);
|
|
1144
|
-
return false;
|
|
1145
|
-
}
|
|
1146
|
-
missing = v.missingScopes;
|
|
1147
|
-
if (missing && missing.length > 0) console.log(` \u4ECD\u7F3A\uFF1A${missing.join(" ")}`);
|
|
1148
|
-
}
|
|
1149
|
-
console.log("\u2713 \u6743\u9650\u5DF2\u5F00\u901A\u3002");
|
|
1150
1134
|
const eventUrl = buildEventConfigUrl(app.id, app.tenant);
|
|
1151
1135
|
const opened = openUrl(eventUrl);
|
|
1152
1136
|
console.log("\n\u6700\u540E\u8FD9\u51E0\u6B65\u98DE\u4E66\u6CA1\u6709 API/\u6DF1\u94FE\u53EF\u4EE3\u529E\uFF08\u8FDE\u67E5\u8BE2\u8BA2\u9605\u72B6\u6001\u7684\u63A5\u53E3\u90FD\u6CA1\u6709\uFF09\uFF0C\u9700\u4F60\u624B\u52A8\u70B9\uFF1A\n");
|
|
@@ -1178,27 +1162,26 @@ async function promptEnter(message) {
|
|
|
1178
1162
|
rl.close();
|
|
1179
1163
|
}
|
|
1180
1164
|
}
|
|
1181
|
-
function
|
|
1182
|
-
if (missingScopes
|
|
1183
|
-
const url = buildScopeGrantUrl(cfg.accounts.app.id, cfg.accounts.app.tenant);
|
|
1184
|
-
const rule = "\u2500".repeat(64);
|
|
1185
|
-
const opened = openUrl(url);
|
|
1186
|
-
console.log(`
|
|
1187
|
-
${rule}`);
|
|
1188
|
-
console.log(`\u26A0\uFE0F \u8FD8\u5DEE ${missingScopes.length} \u9879\u6743\u9650\u672A\u5F00\u901A \u2014\u2014 \u4E0D\u5F00\u901A\u5219\u6536\u4E0D\u5230\u6D88\u606F\u3001\u53D1\u4E0D\u51FA\u5361\u7247`);
|
|
1189
|
-
console.log(" \u98DE\u4E66\u6CA1\u6709\u300C\u626B\u7801\u5373\u6388\u6743\u300D\u7684\u63A5\u53E3\uFF0C\u53EA\u80FD\u5728\u6D4F\u89C8\u5668\u5F00\u901A\uFF08\u5373\u65F6\u751F\u6548\uFF0C\u65E0\u9700\u91CD\u542F\uFF09\uFF1A");
|
|
1190
|
-
console.log(
|
|
1191
|
-
opened ? "\n \u{1F310} \u5DF2\u81EA\u52A8\u6253\u5F00\u6D4F\u89C8\u5668\u6388\u6743\u9875\u3002\u82E5\u6CA1\u5F39\u51FA\uFF0C\u624B\u52A8\u590D\u5236\u4E0B\u9762\u94FE\u63A5\u6253\u5F00\uFF1A" : "\n \u{1F310} \u5728\u6D4F\u89C8\u5668\u6253\u5F00\u4E0B\u9762\u94FE\u63A5\uFF0C\u52FE\u9009\u5168\u90E8\u6743\u9650 \u2192 \u786E\u8BA4\uFF1A"
|
|
1192
|
-
);
|
|
1193
|
-
console.log(`
|
|
1194
|
-
\u{1F449} ${url}
|
|
1195
|
-
`);
|
|
1196
|
-
console.log(` \uFF08\u672C\u6B21\u7F3A\u5931\uFF1A${missingScopes.join(" ")}\uFF09`);
|
|
1197
|
-
console.log(`${rule}
|
|
1198
|
-
`);
|
|
1199
|
-
} else if (missingScopes === void 0) {
|
|
1165
|
+
function noticeMissingScopes(cfg, missingScopes) {
|
|
1166
|
+
if (missingScopes === void 0) {
|
|
1200
1167
|
log.info("onboard", "scope-check-skipped", { reason: "scope list unavailable" });
|
|
1168
|
+
return;
|
|
1201
1169
|
}
|
|
1170
|
+
if (missingScopes.length === 0) return;
|
|
1171
|
+
const url = buildScopeGrantUrl(cfg.accounts.app.id, cfg.accounts.app.tenant);
|
|
1172
|
+
const opened = openUrl(url);
|
|
1173
|
+
const rule = "-".repeat(64);
|
|
1174
|
+
console.log(`
|
|
1175
|
+
${rule}`);
|
|
1176
|
+
console.log(`\u26A0\uFE0F \u7F3A ${missingScopes.length} \u9879\u6743\u9650\uFF08\u4E0D\u5F71\u54CD\u542F\u52A8\uFF0C\u4F46\u8FD9\u4E9B\u529F\u80FD\u5F00\u901A\u524D\u7528\u4E0D\u4E86\uFF09\uFF1A`);
|
|
1177
|
+
for (const s of missingScopes) console.log(` \xB7 ${labelScope(s)}`);
|
|
1178
|
+
console.log(
|
|
1179
|
+
opened ? "\u{1F310} \u5DF2\u81EA\u52A8\u6253\u5F00\u6D4F\u89C8\u5668\u6388\u6743\u9875\uFF08\u5373\u65F6\u751F\u6548\u3001\u65E0\u9700\u91CD\u542F\uFF09\uFF1A" : " \u53BB\u8FD9\u91CC\u7533\u8BF7\uFF08\u52FE\u9009 \u2192 \u786E\u8BA4\uFF0C\u5373\u65F6\u751F\u6548\u3001\u65E0\u9700\u91CD\u542F\uFF09\uFF1A"
|
|
1180
|
+
);
|
|
1181
|
+
console.log(` \u{1F449} ${url}`);
|
|
1182
|
+
console.log(" \u4E0D\u60F3\u73B0\u5728\u5F04\u4E5F\u884C\uFF1A\u4E4B\u540E\u79C1\u804A\u673A\u5668\u4EBA \u2192\u300C\u{1FA7A} \u8BCA\u65AD\u300D\u53EF\u968F\u65F6\u518D\u7533\u8BF7\u3002");
|
|
1183
|
+
console.log(`${rule}
|
|
1184
|
+
`);
|
|
1202
1185
|
}
|
|
1203
1186
|
|
|
1204
1187
|
// src/bot/bridge.ts
|
|
@@ -5606,6 +5589,199 @@ ${lines}
|
|
|
5606
5589
|
]`;
|
|
5607
5590
|
}
|
|
5608
5591
|
|
|
5592
|
+
// src/bot/context-weave.ts
|
|
5593
|
+
var QUOTE_MAX = 800;
|
|
5594
|
+
var LINE_MAX = 280;
|
|
5595
|
+
var THREAD_WEAVE_MAX = 20;
|
|
5596
|
+
var THREAD_PAGE_SIZE = 50;
|
|
5597
|
+
async function fetchQuotedMessage(channel, messageId) {
|
|
5598
|
+
try {
|
|
5599
|
+
const res = await channel.rawClient.im.v1.message.get({ path: { message_id: messageId } });
|
|
5600
|
+
const items = res.data?.items ?? [];
|
|
5601
|
+
const item = items[0];
|
|
5602
|
+
if (!item || item.deleted) return void 0;
|
|
5603
|
+
const cm = toContextMessage(item);
|
|
5604
|
+
return cm.text.trim() ? cm : void 0;
|
|
5605
|
+
} catch (err) {
|
|
5606
|
+
log.warn("intake", "quote-fetch-failed", { messageId, err: String(err) });
|
|
5607
|
+
return void 0;
|
|
5608
|
+
}
|
|
5609
|
+
}
|
|
5610
|
+
async function fetchThreadContext(channel, threadId, opts = {}) {
|
|
5611
|
+
const limit = opts.limit ?? THREAD_WEAVE_MAX;
|
|
5612
|
+
const since = opts.sinceTime ?? 0;
|
|
5613
|
+
try {
|
|
5614
|
+
const res = await channel.rawClient.im.v1.message.list({
|
|
5615
|
+
params: {
|
|
5616
|
+
container_id_type: "thread",
|
|
5617
|
+
container_id: threadId,
|
|
5618
|
+
sort_type: "ByCreateTimeDesc",
|
|
5619
|
+
page_size: THREAD_PAGE_SIZE
|
|
5620
|
+
}
|
|
5621
|
+
});
|
|
5622
|
+
const items = res.data?.items ?? [];
|
|
5623
|
+
const picked = items.filter((it) => !it.deleted).map(toContextMessage).filter(
|
|
5624
|
+
(m) => m.fromUser && // drop the bot's own replies, other apps, system notices
|
|
5625
|
+
m.messageId !== opts.excludeMessageId && // drop the triggering @ message
|
|
5626
|
+
(since === 0 || m.createTime > since) && // delta only for existing sessions
|
|
5627
|
+
m.text.trim().length > 0
|
|
5628
|
+
);
|
|
5629
|
+
picked.sort((a, b) => a.createTime - b.createTime);
|
|
5630
|
+
const out = picked.slice(-limit);
|
|
5631
|
+
if (picked.length > out.length) {
|
|
5632
|
+
log.info("intake", "thread-context-truncated", { threadId, kept: out.length, total: picked.length });
|
|
5633
|
+
}
|
|
5634
|
+
return out;
|
|
5635
|
+
} catch (err) {
|
|
5636
|
+
log.warn("intake", "thread-context-failed", { threadId, err: String(err) });
|
|
5637
|
+
return [];
|
|
5638
|
+
}
|
|
5639
|
+
}
|
|
5640
|
+
function toContextMessage(item) {
|
|
5641
|
+
const id = item.sender?.id ?? "";
|
|
5642
|
+
const name = item.sender?.sender_name || (id ? `\u7528\u6237${id.slice(-4)}` : "\u67D0\u4EBA");
|
|
5643
|
+
return {
|
|
5644
|
+
messageId: item.message_id ?? "",
|
|
5645
|
+
senderName: name,
|
|
5646
|
+
text: extractMessageText(item.msg_type, item.body?.content, item.mentions),
|
|
5647
|
+
fromUser: item.sender?.sender_type === "user",
|
|
5648
|
+
createTime: Number(item.create_time) || 0
|
|
5649
|
+
};
|
|
5650
|
+
}
|
|
5651
|
+
function extractMessageText(msgType, content, mentions) {
|
|
5652
|
+
if (!content) return placeholderFor(msgType);
|
|
5653
|
+
let parsed;
|
|
5654
|
+
try {
|
|
5655
|
+
parsed = JSON.parse(content);
|
|
5656
|
+
} catch {
|
|
5657
|
+
return placeholderFor(msgType);
|
|
5658
|
+
}
|
|
5659
|
+
switch (msgType) {
|
|
5660
|
+
case "text":
|
|
5661
|
+
return replaceMentions(parsed?.text ?? "", mentions);
|
|
5662
|
+
case "post":
|
|
5663
|
+
return replaceMentions(extractPostText(parsed), mentions);
|
|
5664
|
+
case "image":
|
|
5665
|
+
return "[\u56FE\u7247]";
|
|
5666
|
+
case "audio":
|
|
5667
|
+
return "[\u8BED\u97F3]";
|
|
5668
|
+
case "media":
|
|
5669
|
+
return "[\u89C6\u9891]";
|
|
5670
|
+
case "file": {
|
|
5671
|
+
const name = parsed?.file_name;
|
|
5672
|
+
return name ? `[\u6587\u4EF6\uFF1A${name}]` : "[\u6587\u4EF6]";
|
|
5673
|
+
}
|
|
5674
|
+
case "sticker":
|
|
5675
|
+
return "[\u8868\u60C5]";
|
|
5676
|
+
case "interactive":
|
|
5677
|
+
return "[\u5361\u7247\u6D88\u606F]";
|
|
5678
|
+
case "share_chat":
|
|
5679
|
+
return "[\u5206\u4EAB\u7FA4\u540D\u7247]";
|
|
5680
|
+
case "share_user":
|
|
5681
|
+
return "[\u5206\u4EAB\u4E2A\u4EBA\u540D\u7247]";
|
|
5682
|
+
case "merge_forward":
|
|
5683
|
+
case "forward":
|
|
5684
|
+
return "[\u5408\u5E76\u8F6C\u53D1\u6D88\u606F]";
|
|
5685
|
+
default:
|
|
5686
|
+
return placeholderFor(msgType);
|
|
5687
|
+
}
|
|
5688
|
+
}
|
|
5689
|
+
function placeholderFor(msgType) {
|
|
5690
|
+
return msgType ? `[${msgType} \u6D88\u606F]` : "[\u6D88\u606F]";
|
|
5691
|
+
}
|
|
5692
|
+
function extractPostText(parsed) {
|
|
5693
|
+
if (!parsed || typeof parsed !== "object") return "";
|
|
5694
|
+
const obj = parsed;
|
|
5695
|
+
let title = obj.title;
|
|
5696
|
+
let blocks = obj.content;
|
|
5697
|
+
if (!Array.isArray(blocks)) {
|
|
5698
|
+
for (const v of Object.values(obj)) {
|
|
5699
|
+
if (v && typeof v === "object" && Array.isArray(v.content)) {
|
|
5700
|
+
title = v.title;
|
|
5701
|
+
blocks = v.content;
|
|
5702
|
+
break;
|
|
5703
|
+
}
|
|
5704
|
+
}
|
|
5705
|
+
}
|
|
5706
|
+
const parts = [];
|
|
5707
|
+
if (typeof title === "string" && title.trim()) parts.push(title.trim());
|
|
5708
|
+
if (Array.isArray(blocks)) {
|
|
5709
|
+
for (const line of blocks) {
|
|
5710
|
+
if (!Array.isArray(line)) continue;
|
|
5711
|
+
const lineText = line.map(nodeToText).join("");
|
|
5712
|
+
if (lineText) parts.push(lineText);
|
|
5713
|
+
}
|
|
5714
|
+
}
|
|
5715
|
+
return parts.join("\n");
|
|
5716
|
+
}
|
|
5717
|
+
function nodeToText(node) {
|
|
5718
|
+
if (!node || typeof node !== "object") return "";
|
|
5719
|
+
const n = node;
|
|
5720
|
+
switch (n.tag) {
|
|
5721
|
+
case "text":
|
|
5722
|
+
return typeof n.text === "string" ? n.text : "";
|
|
5723
|
+
case "a":
|
|
5724
|
+
return typeof n.text === "string" ? n.text : typeof n.href === "string" ? n.href : "";
|
|
5725
|
+
case "at": {
|
|
5726
|
+
const name = typeof n.user_name === "string" ? n.user_name : "";
|
|
5727
|
+
return name ? `@${name}` : "@\u67D0\u4EBA";
|
|
5728
|
+
}
|
|
5729
|
+
case "img":
|
|
5730
|
+
return "[\u56FE\u7247]";
|
|
5731
|
+
case "media":
|
|
5732
|
+
return "[\u89C6\u9891]";
|
|
5733
|
+
case "emotion":
|
|
5734
|
+
return "[\u8868\u60C5]";
|
|
5735
|
+
default:
|
|
5736
|
+
return typeof n.text === "string" ? n.text : "";
|
|
5737
|
+
}
|
|
5738
|
+
}
|
|
5739
|
+
function replaceMentions(text, mentions) {
|
|
5740
|
+
if (!text || !mentions?.length) return text;
|
|
5741
|
+
let out = text;
|
|
5742
|
+
for (const m of mentions) {
|
|
5743
|
+
if (!m.key) continue;
|
|
5744
|
+
out = out.split(m.key).join(m.name ? `@${m.name}` : "@\u67D0\u4EBA");
|
|
5745
|
+
}
|
|
5746
|
+
return out;
|
|
5747
|
+
}
|
|
5748
|
+
function sanitizeContext(s, maxLen, oneLine2) {
|
|
5749
|
+
if (!s) return "";
|
|
5750
|
+
let out = s.replace(/[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]/g, "").replace(/\r\n?/g, "\n");
|
|
5751
|
+
out = oneLine2 ? out.replace(/\s+/g, " ") : out.replace(/[ \t]+\n/g, "\n").replace(/\n{3,}/g, "\n\n");
|
|
5752
|
+
out = out.trim();
|
|
5753
|
+
return out.length > maxLen ? `${out.slice(0, maxLen)}\u2026` : out;
|
|
5754
|
+
}
|
|
5755
|
+
function weaveQuote(text, quoted) {
|
|
5756
|
+
if (!quoted) return text;
|
|
5757
|
+
const who = sanitizeContext(quoted.senderName, 40, true) || "\u67D0\u4EBA";
|
|
5758
|
+
const body = sanitizeContext(quoted.text, QUOTE_MAX, true);
|
|
5759
|
+
if (!body) return text;
|
|
5760
|
+
const block = `[\u7528\u6237\u5F15\u7528\u4E86\u4E00\u6761\u6D88\u606F\uFF08\u6765\u81EA ${who}\uFF09\uFF1A
|
|
5761
|
+
${body}
|
|
5762
|
+
]`;
|
|
5763
|
+
const base = text.trim();
|
|
5764
|
+
return base ? `${block}
|
|
5765
|
+
|
|
5766
|
+
${base}` : block;
|
|
5767
|
+
}
|
|
5768
|
+
function weaveThreadHistory(text, msgs) {
|
|
5769
|
+
if (msgs.length === 0) return text;
|
|
5770
|
+
const lines = msgs.map((m) => {
|
|
5771
|
+
const who = sanitizeContext(m.senderName, 40, true) || "\u67D0\u4EBA";
|
|
5772
|
+
const body = sanitizeContext(m.text, LINE_MAX, true);
|
|
5773
|
+
return body ? `${who}\uFF1A${body}` : "";
|
|
5774
|
+
}).filter((l) => l.length > 0);
|
|
5775
|
+
if (lines.length === 0) return text;
|
|
5776
|
+
const block = `[\u8BDD\u9898\u4E2D\u5728\u6B64\u4E4B\u524D\u5DF2\u6709\u7684\u6D88\u606F\uFF08\u6309\u65F6\u95F4\u5148\u540E\u6392\u5217\uFF0C\u4F9B\u4F60\u7406\u89E3\u4E0A\u4E0B\u6587\uFF09\uFF1A
|
|
5777
|
+
${lines.join("\n")}
|
|
5778
|
+
]`;
|
|
5779
|
+
const base = text.trim();
|
|
5780
|
+
return base ? `${block}
|
|
5781
|
+
|
|
5782
|
+
${base}` : block;
|
|
5783
|
+
}
|
|
5784
|
+
|
|
5609
5785
|
// src/bot/comments.ts
|
|
5610
5786
|
var SUPPORTED_FILE_TYPES = /* @__PURE__ */ new Set(["doc", "docx", "sheet", "file"]);
|
|
5611
5787
|
var REPLY_MAX_CHARS = 2e3;
|
|
@@ -6091,20 +6267,26 @@ function createOrchestrator(channel, cfg, fallbackCwd) {
|
|
|
6091
6267
|
const perm = turnPerm(project, senderId);
|
|
6092
6268
|
return { sessionKey: perm.roleSuffix ? `${baseKey}#${perm.roleSuffix}` : baseKey, ...perm };
|
|
6093
6269
|
}
|
|
6094
|
-
async function
|
|
6095
|
-
|
|
6096
|
-
|
|
6097
|
-
|
|
6098
|
-
|
|
6099
|
-
|
|
6270
|
+
async function ingestContext(msg, text) {
|
|
6271
|
+
let body = text;
|
|
6272
|
+
if (messageHasFiles(msg)) {
|
|
6273
|
+
const files = await collectInboundFiles(channel, msg);
|
|
6274
|
+
body = weaveFileManifest(text, files);
|
|
6275
|
+
if (!body.trim()) {
|
|
6276
|
+
body = "\u7528\u6237\u53D1\u6765\u4E00\u4E2A\u9644\u4EF6\uFF0C\u4F46\u6865\u6CA1\u80FD\u4E0B\u8F7D\u5B83\uFF08\u53EF\u80FD\u8D85\u8FC7 50MB \u4E0A\u9650\u6216\u88AB\u98DE\u4E66\u62D2\u7EDD\uFF09\u3002\u8BF7\u544A\u8BC9\u7528\u6237\u9644\u4EF6\u6CA1\u8BFB\u5230\uFF0C\u53EF\u4EE5\u91CD\u53D1\uFF0C\u6216\u6539\u4E3A\u7C98\u8D34\u6587\u672C / \u53D1\u56FE\u7247\u3002";
|
|
6277
|
+
}
|
|
6278
|
+
}
|
|
6279
|
+
if (msg.replyToMessageId) {
|
|
6280
|
+
const quoted = await fetchQuotedMessage(channel, msg.replyToMessageId);
|
|
6281
|
+
body = weaveQuote(body, quoted);
|
|
6100
6282
|
}
|
|
6101
|
-
return
|
|
6283
|
+
return body;
|
|
6102
6284
|
}
|
|
6103
6285
|
async function handleTurn(msg, text, sessionKey, flat, project, perm) {
|
|
6104
6286
|
const existing = active.get(sessionKey);
|
|
6105
6287
|
if (existing) {
|
|
6106
6288
|
const images = messageHasImages(msg) ? await collectInboundImages(channel, msg) : void 0;
|
|
6107
|
-
const woven = await
|
|
6289
|
+
const woven = await ingestContext(msg, text);
|
|
6108
6290
|
const cur = active.get(sessionKey);
|
|
6109
6291
|
if (!cur) {
|
|
6110
6292
|
startReservedRun(msg, woven, sessionKey, flat, project, perm, images, true, text);
|
|
@@ -6141,8 +6323,15 @@ function createOrchestrator(channel, cfg, fallbackCwd) {
|
|
|
6141
6323
|
const reaction = runReaction(msg.messageId, !sema.hasFree());
|
|
6142
6324
|
try {
|
|
6143
6325
|
const images = preloadedImages ?? (messageHasImages(msg) ? await collectInboundImages(channel, msg) : void 0);
|
|
6144
|
-
|
|
6145
|
-
|
|
6326
|
+
let firstText = preIngested ? text : await ingestContext(msg, text);
|
|
6327
|
+
const { thread: resolved, recreated } = await resolveThread(sessionKey, msg.chatId, {
|
|
6328
|
+
mode: perm.mode,
|
|
6329
|
+
network: perm.network
|
|
6330
|
+
});
|
|
6331
|
+
let thread = resolved;
|
|
6332
|
+
const neverSeen = !thread;
|
|
6333
|
+
const codexEmpty = neverSeen || recreated;
|
|
6334
|
+
const prior = neverSeen ? void 0 : await getSession(sessionKey);
|
|
6146
6335
|
if (!thread) {
|
|
6147
6336
|
const cwd = project?.cwd ?? fallbackCwd;
|
|
6148
6337
|
thread = await backend.startThread({ cwd, mode: perm.mode, network: perm.network });
|
|
@@ -6156,10 +6345,19 @@ function createOrchestrator(channel, cfg, fallbackCwd) {
|
|
|
6156
6345
|
// `summaryText` (handleTurn's original) so the session label isn't
|
|
6157
6346
|
// manifest boilerplate + a temp path.
|
|
6158
6347
|
summary: stripFileTokens(summaryText2 ?? text).slice(0, 80),
|
|
6348
|
+
lastSeenAt: msg.createTime,
|
|
6159
6349
|
createdAt: Date.now(),
|
|
6160
6350
|
updatedAt: Date.now()
|
|
6161
6351
|
});
|
|
6162
6352
|
}
|
|
6353
|
+
if (msg.threadId && (codexEmpty || prior?.lastSeenAt !== void 0)) {
|
|
6354
|
+
const history = await fetchThreadContext(channel, msg.threadId, {
|
|
6355
|
+
sinceTime: codexEmpty ? 0 : prior?.lastSeenAt ?? 0,
|
|
6356
|
+
excludeMessageId: msg.messageId
|
|
6357
|
+
});
|
|
6358
|
+
firstText = weaveThreadHistory(firstText, history);
|
|
6359
|
+
}
|
|
6360
|
+
if (!neverSeen) void patchSession(sessionKey, { lastSeenAt: msg.createTime }).catch(() => void 0);
|
|
6163
6361
|
reserved.thread = thread;
|
|
6164
6362
|
await launchRun(
|
|
6165
6363
|
{
|
|
@@ -6185,9 +6383,9 @@ function createOrchestrator(channel, cfg, fallbackCwd) {
|
|
|
6185
6383
|
}
|
|
6186
6384
|
async function resolveThread(threadId, chatId, perm) {
|
|
6187
6385
|
const live = sessions.get(threadId);
|
|
6188
|
-
if (live) return live;
|
|
6386
|
+
if (live) return { thread: live, recreated: false };
|
|
6189
6387
|
const rec = await getSession(threadId);
|
|
6190
|
-
if (!rec) return void 0;
|
|
6388
|
+
if (!rec) return { thread: void 0, recreated: false };
|
|
6191
6389
|
try {
|
|
6192
6390
|
const resumed = await backend.resumeThread({
|
|
6193
6391
|
cwd: rec.cwd,
|
|
@@ -6198,7 +6396,7 @@ function createOrchestrator(channel, cfg, fallbackCwd) {
|
|
|
6198
6396
|
network: perm?.network
|
|
6199
6397
|
});
|
|
6200
6398
|
sessions.set(threadId, resumed);
|
|
6201
|
-
return resumed;
|
|
6399
|
+
return { thread: resumed, recreated: false };
|
|
6202
6400
|
} catch (err) {
|
|
6203
6401
|
log.fail("agent", err, { phase: "resume-on-turn", threadId });
|
|
6204
6402
|
const project = await getProjectByChatId(chatId);
|
|
@@ -6211,7 +6409,8 @@ function createOrchestrator(channel, cfg, fallbackCwd) {
|
|
|
6211
6409
|
network: perm?.network ?? project?.network
|
|
6212
6410
|
});
|
|
6213
6411
|
sessions.set(threadId, fresh);
|
|
6214
|
-
|
|
6412
|
+
await patchSession(threadId, { codexThreadId: fresh.codexThreadId }).catch(() => void 0);
|
|
6413
|
+
return { thread: fresh, recreated: true };
|
|
6215
6414
|
}
|
|
6216
6415
|
}
|
|
6217
6416
|
async function evictLiveSessionsForChat(chatId) {
|
|
@@ -6243,7 +6442,7 @@ function createOrchestrator(channel, cfg, fallbackCwd) {
|
|
|
6243
6442
|
return;
|
|
6244
6443
|
}
|
|
6245
6444
|
const images = messageHasImages(msg) ? await collectInboundImages(channel, msg) : void 0;
|
|
6246
|
-
const firstText = await
|
|
6445
|
+
const firstText = await ingestContext(msg, text) || "\u4F60\u597D\uFF0C\u6211\u4EEC\u5F00\u59CB\u5427\u3002";
|
|
6247
6446
|
log.info("card", "start", { project: project?.name ?? "(unregistered)", model, effort, images: images?.length ?? 0 });
|
|
6248
6447
|
await launchRun(
|
|
6249
6448
|
{
|
package/package.json
CHANGED