openclaw-lark-multi-agent 1.2.3 → 1.2.4
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/feishu-bot.d.ts +0 -40
- package/dist/feishu-bot.js +7 -102
- package/package.json +1 -1
package/dist/feishu-bot.d.ts
CHANGED
|
@@ -155,46 +155,6 @@ export declare class FeishuBot {
|
|
|
155
155
|
* A plain “no sentence-ending punctuation” is deliberately NOT sufficient.
|
|
156
156
|
*/
|
|
157
157
|
private looksTruncated;
|
|
158
|
-
/**
|
|
159
|
-
* Did the model type a tool call as plain text instead of emitting a real
|
|
160
|
-
* function call? When that happens the visible reply ends with a dangling
|
|
161
|
-
* invoke / parameter / function_calls tag (with or without the `antml:`
|
|
162
|
-
* prefix), because the tool-call markup leaked into the text channel and the
|
|
163
|
-
* tool never actually ran. We detect such a tag anchored at the END of the
|
|
164
|
-
* message — that dangling tag is the tell-tale Stephen flagged ("如果监测到
|
|
165
|
-
* 这个结尾,说明 tool call 格式出了问题"). On detection the auto-retry loop
|
|
166
|
-
* sends an explicit "invoke 格式错了,请重试" probe so the agent re-issues the
|
|
167
|
-
* tool call correctly rather than the generic done-check.
|
|
168
|
-
*/
|
|
169
|
-
/**
|
|
170
|
-
* Did the model serialize a tool call as plain text instead of emitting a real
|
|
171
|
-
* function call? When that happens, the structured tool-call XML leaks into the
|
|
172
|
-
* visible text channel and the tool never runs. We must distinguish this from a
|
|
173
|
-
* reply that merely *mentions* a tag (teaching, quoting, showing an example),
|
|
174
|
-
* which must NOT trigger a retry. So we require strong STRUCTURAL evidence of a
|
|
175
|
-
* leaked call, not just any tag at the end:
|
|
176
|
-
*
|
|
177
|
-
* 1. The reply must END with a closing `</invoke>` (optionally `antml:`).
|
|
178
|
-
* A dangling OPEN tag or a bare `</parameter>`/`<function_calls>` alone is
|
|
179
|
-
* not enough — too easy to hit in normal prose.
|
|
180
|
-
* 2. Somewhere before it there must be a matching `<invoke name="...">` OPEN
|
|
181
|
-
* tag — i.e. a whole call block (`<invoke…>…</invoke>`) materialized as
|
|
182
|
-
* text. A lone `</invoke>` (e.g. “闭合标签是 </invoke>”) is a mention,
|
|
183
|
-
* not a leaked call.
|
|
184
|
-
* 3. The trailing `</invoke>` must NOT be wrapped in inline code/backticks or
|
|
185
|
-
* a fenced code block — those are deliberate examples, not a leak.
|
|
186
|
-
*
|
|
187
|
-
* Anchored at the very end because a genuinely leaked call is the LAST thing in
|
|
188
|
-
* the message (the model stopped right after “emitting” it). This keeps the
|
|
189
|
-
* trigger tight so we do not retry on normal replies that talk about tags.
|
|
190
|
-
*/
|
|
191
|
-
private looksLikeBrokenToolCall;
|
|
192
|
-
/**
|
|
193
|
-
* Is the tag at `tagIndex` inside an inline-code span (`` `…` ``) or a fenced
|
|
194
|
-
* code block (```` ``` ````)? Such tags are deliberate examples, not a leaked
|
|
195
|
-
* tool call, so the broken-tool-call detector must ignore them.
|
|
196
|
-
*/
|
|
197
|
-
private isTrailingTagInCode;
|
|
198
158
|
/** Current context usage percent for a session (totalTokens / contextTokens),
|
|
199
159
|
* or 0 if it cannot be determined. Same basis as /status. */
|
|
200
160
|
private getContextUsagePercent;
|
package/dist/feishu-bot.js
CHANGED
|
@@ -29,14 +29,6 @@ function autoRetryMax() { return Number(process.env.OPENCLAW_LARK_MULTI_AGENT_AU
|
|
|
29
29
|
// for the locale-appropriate phrase; detection accepts BOTH so either works.
|
|
30
30
|
const AUTO_RETRY_DONE_PHRASE = process.env.OPENCLAW_LARK_MULTI_AGENT_AUTO_RETRY_DONE_PHRASE || "\u7ed3\u675f\u4e86";
|
|
31
31
|
const AUTO_RETRY_DONE_PHRASE_EN = process.env.OPENCLAW_LARK_MULTI_AGENT_AUTO_RETRY_DONE_PHRASE_EN || "DONE";
|
|
32
|
-
// When the previous reply ended with a dangling tool-call tag (the model typed
|
|
33
|
-
// an invoke/tool call as plain text and it never ran), the auto-retry probe
|
|
34
|
-
// explicitly tells the agent its invoke format was broken and to retry, instead
|
|
35
|
-
// of the generic done-check. It still accepts the done phrase if actually done.
|
|
36
|
-
const AUTO_RETRY_BROKEN_TOOL_PROBE = process.env.OPENCLAW_LARK_MULTI_AGENT_AUTO_RETRY_BROKEN_TOOL_PROBE
|
|
37
|
-
|| `刚才的任务是不是没结束,invoke 格式错了,invoke 格式错了,请重试。如果结束了,回复“${AUTO_RETRY_DONE_PHRASE}”。`;
|
|
38
|
-
const AUTO_RETRY_BROKEN_TOOL_PROBE_EN = process.env.OPENCLAW_LARK_MULTI_AGENT_AUTO_RETRY_BROKEN_TOOL_PROBE_EN
|
|
39
|
-
|| `Your last task did not finish — the invoke format was broken, the invoke format was broken, please retry. If it is actually done, reply "${AUTO_RETRY_DONE_PHRASE_EN}".`;
|
|
40
32
|
// When an auto-retry probe errors AND context usage is at/above this percent,
|
|
41
33
|
// auto-compact the session once and keep retrying (instead of giving up). This
|
|
42
34
|
// rescues the “session too heavy → run times out / lock contention” failure mode.
|
|
@@ -1733,76 +1725,6 @@ export class FeishuBot {
|
|
|
1733
1725
|
return true;
|
|
1734
1726
|
return false;
|
|
1735
1727
|
}
|
|
1736
|
-
/**
|
|
1737
|
-
* Did the model type a tool call as plain text instead of emitting a real
|
|
1738
|
-
* function call? When that happens the visible reply ends with a dangling
|
|
1739
|
-
* invoke / parameter / function_calls tag (with or without the `antml:`
|
|
1740
|
-
* prefix), because the tool-call markup leaked into the text channel and the
|
|
1741
|
-
* tool never actually ran. We detect such a tag anchored at the END of the
|
|
1742
|
-
* message — that dangling tag is the tell-tale Stephen flagged ("如果监测到
|
|
1743
|
-
* 这个结尾,说明 tool call 格式出了问题"). On detection the auto-retry loop
|
|
1744
|
-
* sends an explicit "invoke 格式错了,请重试" probe so the agent re-issues the
|
|
1745
|
-
* tool call correctly rather than the generic done-check.
|
|
1746
|
-
*/
|
|
1747
|
-
/**
|
|
1748
|
-
* Did the model serialize a tool call as plain text instead of emitting a real
|
|
1749
|
-
* function call? When that happens, the structured tool-call XML leaks into the
|
|
1750
|
-
* visible text channel and the tool never runs. We must distinguish this from a
|
|
1751
|
-
* reply that merely *mentions* a tag (teaching, quoting, showing an example),
|
|
1752
|
-
* which must NOT trigger a retry. So we require strong STRUCTURAL evidence of a
|
|
1753
|
-
* leaked call, not just any tag at the end:
|
|
1754
|
-
*
|
|
1755
|
-
* 1. The reply must END with a closing `</invoke>` (optionally `antml:`).
|
|
1756
|
-
* A dangling OPEN tag or a bare `</parameter>`/`<function_calls>` alone is
|
|
1757
|
-
* not enough — too easy to hit in normal prose.
|
|
1758
|
-
* 2. Somewhere before it there must be a matching `<invoke name="...">` OPEN
|
|
1759
|
-
* tag — i.e. a whole call block (`<invoke…>…</invoke>`) materialized as
|
|
1760
|
-
* text. A lone `</invoke>` (e.g. “闭合标签是 </invoke>”) is a mention,
|
|
1761
|
-
* not a leaked call.
|
|
1762
|
-
* 3. The trailing `</invoke>` must NOT be wrapped in inline code/backticks or
|
|
1763
|
-
* a fenced code block — those are deliberate examples, not a leak.
|
|
1764
|
-
*
|
|
1765
|
-
* Anchored at the very end because a genuinely leaked call is the LAST thing in
|
|
1766
|
-
* the message (the model stopped right after “emitting” it). This keeps the
|
|
1767
|
-
* trigger tight so we do not retry on normal replies that talk about tags.
|
|
1768
|
-
*/
|
|
1769
|
-
looksLikeBrokenToolCall(text) {
|
|
1770
|
-
const t = (text || "").trim();
|
|
1771
|
-
if (!t)
|
|
1772
|
-
return false;
|
|
1773
|
-
// (1) Must end with a closing invoke tag (the serialized call's terminator).
|
|
1774
|
-
const closeMatch = /<\/\s*(?:antml:)?invoke\s*>\s*$/i.exec(t);
|
|
1775
|
-
if (!closeMatch)
|
|
1776
|
-
return false;
|
|
1777
|
-
// (3) Reject when that closing tag is fenced or inline-code wrapped (example).
|
|
1778
|
-
if (this.isTrailingTagInCode(t, closeMatch.index))
|
|
1779
|
-
return false;
|
|
1780
|
-
// (2) Require a matching opening `<invoke ...>` before the closer — proof that
|
|
1781
|
-
// a whole call block leaked, not just a mention of the closing tag.
|
|
1782
|
-
const head = t.slice(0, closeMatch.index);
|
|
1783
|
-
if (!/<\s*(?:antml:)?invoke\b[^>]*>/i.test(head))
|
|
1784
|
-
return false;
|
|
1785
|
-
return true;
|
|
1786
|
-
}
|
|
1787
|
-
/**
|
|
1788
|
-
* Is the tag at `tagIndex` inside an inline-code span (`` `…` ``) or a fenced
|
|
1789
|
-
* code block (```` ``` ````)? Such tags are deliberate examples, not a leaked
|
|
1790
|
-
* tool call, so the broken-tool-call detector must ignore them.
|
|
1791
|
-
*/
|
|
1792
|
-
isTrailingTagInCode(text, tagIndex) {
|
|
1793
|
-
const before = text.slice(0, tagIndex);
|
|
1794
|
-
// Fenced block: an odd number of ``` before the tag means we are inside one.
|
|
1795
|
-
const fences = (before.match(/```/g) || []).length;
|
|
1796
|
-
if (fences % 2 === 1)
|
|
1797
|
-
return true;
|
|
1798
|
-
// Inline code: an odd number of unescaped backticks on the tag's own line.
|
|
1799
|
-
const lineStart = before.lastIndexOf("\n") + 1;
|
|
1800
|
-
const lineBefore = before.slice(lineStart);
|
|
1801
|
-
const ticks = (lineBefore.match(/`/g) || []).length;
|
|
1802
|
-
if (ticks % 2 === 1)
|
|
1803
|
-
return true;
|
|
1804
|
-
return false;
|
|
1805
|
-
}
|
|
1806
1728
|
/** Current context usage percent for a session (totalTokens / contextTokens),
|
|
1807
1729
|
* or 0 if it cannot be determined. Same basis as /status. */
|
|
1808
1730
|
async getContextUsagePercent(sessionKey) {
|
|
@@ -1906,10 +1828,6 @@ export class FeishuBot {
|
|
|
1906
1828
|
// and stop retrying immediately instead of issuing more probes.
|
|
1907
1829
|
const startStopEpoch = this.stopEpoch.get(chatId) || 0;
|
|
1908
1830
|
const wasStopped = () => (this.stopEpoch.get(chatId) || 0) !== startStopEpoch;
|
|
1909
|
-
// Set per-iteration when the reply under inspection ended with a dangling
|
|
1910
|
-
// tool-call tag (model typed an invoke as plain text); selects the explicit
|
|
1911
|
-
// "invoke format was broken, retry" probe instead of the generic done-check.
|
|
1912
|
-
let brokenToolCall = false;
|
|
1913
1831
|
for (let attempt = 1; attempt <= maxRetry; attempt++) {
|
|
1914
1832
|
// /stop while we were looping: abandon retries and deliver what we have.
|
|
1915
1833
|
if (wasStopped()) {
|
|
@@ -1918,7 +1836,6 @@ export class FeishuBot {
|
|
|
1918
1836
|
throw new Error(`Agent error: ${params.initialError || "stopped by user"}`);
|
|
1919
1837
|
return reply;
|
|
1920
1838
|
}
|
|
1921
|
-
brokenToolCall = false;
|
|
1922
1839
|
if (!forceProbe) {
|
|
1923
1840
|
const parsed = this.extractBridgeAttachments(reply);
|
|
1924
1841
|
const trimmed = parsed.text.trim();
|
|
@@ -1931,32 +1848,20 @@ export class FeishuBot {
|
|
|
1931
1848
|
// and runtime failures have their own handling upstream.
|
|
1932
1849
|
if (!trimmed || trimmed.toUpperCase() === "NO_REPLY" || this.isRuntimeFailureText(trimmed))
|
|
1933
1850
|
return reply;
|
|
1934
|
-
|
|
1935
|
-
// signal — engage even if the generic truncation heuristic would miss it.
|
|
1936
|
-
brokenToolCall = this.looksLikeBrokenToolCall(trimmed);
|
|
1937
|
-
if (!this.looksTruncated(trimmed) && !brokenToolCall)
|
|
1851
|
+
if (!this.looksTruncated(trimmed))
|
|
1938
1852
|
return reply;
|
|
1939
1853
|
}
|
|
1940
1854
|
forceProbe = false; // consumed; subsequent rounds inspect the probe reply
|
|
1941
1855
|
const en = this.isEn(chatId);
|
|
1942
1856
|
await liveStatus?.progress(en
|
|
1943
|
-
? (
|
|
1944
|
-
|
|
1945
|
-
: `⚠️ Reply looks incomplete — auto-checking (${attempt}/${maxRetry})…`)
|
|
1946
|
-
: (brokenToolCall
|
|
1947
|
-
? `⚠️ 工具调用格式疑似出错,正在让它重试(${attempt}/${maxRetry})…`
|
|
1948
|
-
: `⚠️ 疑似任务未完成,正在自动重试(${attempt}/${maxRetry})…`)).catch(() => { });
|
|
1857
|
+
? `⚠️ Reply looks incomplete — auto-checking (${attempt}/${maxRetry})…`
|
|
1858
|
+
: `⚠️ 疑似任务未完成,正在自动重试(${attempt}/${maxRetry})…`).catch(() => { });
|
|
1949
1859
|
// One short line, in the user's own voice (we intentionally do not change
|
|
1950
1860
|
// senderName). Each locale uses its own done-phrase so the English probe
|
|
1951
|
-
// stays fully English; detection accepts both phrases.
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
const probe = brokenToolCall
|
|
1956
|
-
? (en ? AUTO_RETRY_BROKEN_TOOL_PROBE_EN : AUTO_RETRY_BROKEN_TOOL_PROBE)
|
|
1957
|
-
: (en
|
|
1958
|
-
? `Is that done? If so just reply "${AUTO_RETRY_DONE_PHRASE_EN}", otherwise keep going.`
|
|
1959
|
-
: `刚才那个任务结束了吗?结束了就回我“${AUTO_RETRY_DONE_PHRASE}”,没结束就接着做。`);
|
|
1861
|
+
// stays fully English; detection accepts both phrases.
|
|
1862
|
+
const probe = en
|
|
1863
|
+
? `Is that done? If so just reply "${AUTO_RETRY_DONE_PHRASE_EN}", otherwise keep going.`
|
|
1864
|
+
: `刚才那个任务结束了吗?结束了就回我“${AUTO_RETRY_DONE_PHRASE}”,没结束就接着做。`;
|
|
1960
1865
|
let probeReply;
|
|
1961
1866
|
try {
|
|
1962
1867
|
probeReply = await this.withSessionHealthMonitor(chatId, lastHumanMessageId, triggerId, this.openclawClient.chatSendWithContext({
|