openclaw-lark-multi-agent 1.8.19 → 1.8.21
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/config.d.ts +15 -1
- package/dist/config.js +14 -2
- package/dist/feishu-bot.d.ts +47 -7
- package/dist/feishu-bot.js +423 -163
- package/dist/go-controller.d.ts +2 -0
- package/dist/go-controller.js +24 -8
- package/dist/input-status-display.d.ts +45 -0
- package/dist/input-status-display.js +67 -0
- package/dist/live-status.d.ts +22 -3
- package/dist/live-status.js +93 -25
- package/dist/message-store.d.ts +68 -2
- package/dist/message-store.js +249 -13
- package/dist/openclaw-client.d.ts +0 -6
- package/dist/openclaw-client.js +178 -67
- package/dist/queued-presentation.d.ts +8 -1
- package/dist/queued-presentation.js +109 -4
- package/dist/terminal-errors.d.ts +12 -0
- package/dist/terminal-errors.js +23 -3
- package/docs/releases/1.8.20.md +30 -0
- package/package.json +4 -3
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { inputPreview, visibleInputPreview } from "./input-status-display.js";
|
|
1
2
|
import { randomUUID } from "node:crypto";
|
|
2
3
|
import { LiveStatusController, LiveStatusEffectRejectedError, LiveStatusEffectUnknownError, LiveStatusEffectSuppressedError } from "./live-status.js";
|
|
3
4
|
/** One unbound cohort per chat, or reuse the exact active-root presentation.
|
|
@@ -173,6 +174,44 @@ export class QueuedPresentations {
|
|
|
173
174
|
const row = this.latest(record);
|
|
174
175
|
return !!row && this.usable(row) && !row.linkedReply && row.lease === record.lease && this.enabled(record.chatId);
|
|
175
176
|
}
|
|
177
|
+
inputContext(record, change) {
|
|
178
|
+
const row = this.latest(record);
|
|
179
|
+
if (!row || row.lease !== record.lease || row.retired || row.stopEpoch !== this.store.getStopGeneration(this.bot, row.chatId))
|
|
180
|
+
return undefined;
|
|
181
|
+
const messages = { ...row.inputMessageIds }, states = { ...row.inputStates };
|
|
182
|
+
for (const id of row.triggerIds) {
|
|
183
|
+
if (messages[id] !== undefined)
|
|
184
|
+
continue;
|
|
185
|
+
const source = this.store.getMessageByRowId(id);
|
|
186
|
+
messages[id] = source?.chatId === row.chatId && source.senderType === 'human' ? source.messageId : '';
|
|
187
|
+
}
|
|
188
|
+
const phaseKind = () => row.view?.state === 'replied' ? 'replied' : row.view?.state === 'failed' ? 'failed' :
|
|
189
|
+
row.view?.state === 'done' ? (row.view.noReply ? 'no_reply' : 'completed') : row.view?.phase === 'waiting' ? 'waiting' :
|
|
190
|
+
row.view?.phase === 'working' ? 'processing' : row.view?.phase === 'admitted' ? 'accepted' : row.submissionId ? 'submitting' : 'queued';
|
|
191
|
+
if (change) {
|
|
192
|
+
const ids = change.ids || (change.kind === 'replied' ? row.triggerIds : row.ordinaryTriggerIds || row.triggerIds);
|
|
193
|
+
if (change.kind === 'partial' && !ids.length)
|
|
194
|
+
row.inputNote = { kind: 'partial', detail: change.detail, at: change.at };
|
|
195
|
+
else {
|
|
196
|
+
if (['queued', 'accepted', 'submitting', 'replied'].includes(change.kind))
|
|
197
|
+
delete row.inputNote;
|
|
198
|
+
for (const id of ids)
|
|
199
|
+
if (row.triggerIds.includes(id) && states[id]?.kind !== change.kind) {
|
|
200
|
+
const known = states[id]?.kind || (row.enqueueAcceptedIds?.includes(id) ? 'append_queued' : row.runId && row.ordinaryTriggerIds?.includes(id) ? phaseKind() : undefined);
|
|
201
|
+
if (change.kind === 'received' && known && !['received', 'queued'].includes(known))
|
|
202
|
+
continue;
|
|
203
|
+
states[id] = { kind: change.kind, detail: change.detail, at: change.at };
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
row.inputStates = states;
|
|
207
|
+
row.inputMessageIds = messages;
|
|
208
|
+
this.save(row);
|
|
209
|
+
}
|
|
210
|
+
return { botName: this.bot, appId: this.app, chatId: row.chatId, stopEpoch: row.stopEpoch, note: row.inputNote,
|
|
211
|
+
items: row.triggerIds.map(id => ({ ...states[id], rowId: id, messageId: messages[id], preview: row.inputPreviews?.[id], previewSource: row.inputPreviewSources?.[id], kind: states[id]?.kind ||
|
|
212
|
+
(row.linkedReply?.rendered ? 'replied' : row.ordinaryTriggerIds?.includes(id) ? phaseKind() : row.enqueueAcceptedIds?.includes(id) ? 'append_queued' : 'received'),
|
|
213
|
+
at: states[id]?.at || 0 })) };
|
|
214
|
+
}
|
|
176
215
|
hydrate(record) {
|
|
177
216
|
const cached = this.entries.get(record.id);
|
|
178
217
|
if (cached && cached.record.lease === record.lease)
|
|
@@ -204,6 +243,7 @@ export class QueuedPresentations {
|
|
|
204
243
|
};
|
|
205
244
|
const live = new LiveStatusController({
|
|
206
245
|
warn: base.warn,
|
|
246
|
+
inputStatus: change => this.inputContext(record, change),
|
|
207
247
|
desired,
|
|
208
248
|
unconfirmedInputs: outcome => {
|
|
209
249
|
try {
|
|
@@ -221,7 +261,15 @@ export class QueuedPresentations {
|
|
|
221
261
|
if (!pending.length)
|
|
222
262
|
return undefined;
|
|
223
263
|
const en = this.english(row.chatId);
|
|
264
|
+
const ordinaryIds = ordinary.filter(id => !pending.includes(id));
|
|
265
|
+
if (!ordinaryIds.length)
|
|
266
|
+
return {
|
|
267
|
+
title: en ? `${this.bot} input confirmation pending` : `${this.bot} 输入接收待确认`,
|
|
268
|
+
detail: en ? `${pending.length} input(s) retain local/submission receipts; no ordinary run acceptance confirmed; no replay.` :
|
|
269
|
+
`${pending.length} 条输入保留本地/提交回执;尚未确认普通运行接收,不重放。`, ordinaryIds,
|
|
270
|
+
};
|
|
224
271
|
return {
|
|
272
|
+
ordinaryIds,
|
|
225
273
|
title: en ? `${this.bot} ${ordinary.length ? outcome === "failed" ? "run ended · " : "partial result · " : ""}input confirmation pending`
|
|
226
274
|
: `${this.bot} ${ordinary.length ? outcome === "failed" ? "本轮结束 · " : "部分完成 · " : ""}追加输入回执待确认`,
|
|
227
275
|
detail: en ? `${pending.length} additional input(s) have only local/enqueue receipts; actual receiver, commitment and result are unconfirmed. Root completion covers only ordinary inputs; no automatic replay.`
|
|
@@ -346,9 +394,12 @@ export class QueuedPresentations {
|
|
|
346
394
|
this.entries.set(record.id, entry);
|
|
347
395
|
return entry;
|
|
348
396
|
}
|
|
349
|
-
receive(chat, rowId, messageId, native = false, activeRunId) {
|
|
397
|
+
receive(chat, rowId, messageId, native = false, activeRunId, displayText) {
|
|
350
398
|
if (native || !rowId || !this.enabled(chat))
|
|
351
399
|
return undefined;
|
|
400
|
+
const input = this.store.getMessageByRowId(rowId);
|
|
401
|
+
if (!input || input.chatId !== chat || input.messageId !== messageId || input.senderType !== 'human')
|
|
402
|
+
return undefined;
|
|
352
403
|
const all = this.records(chat);
|
|
353
404
|
const replied = all.find(row => !row.retired && row.stopEpoch === this.store.getStopGeneration(this.bot, chat)
|
|
354
405
|
&& (row.view?.state === "replied" || !!row.linkedReply?.rendered) && row.triggerIds.includes(rowId));
|
|
@@ -366,9 +417,17 @@ export class QueuedPresentations {
|
|
|
366
417
|
}
|
|
367
418
|
if (!record.triggerIds.includes(rowId))
|
|
368
419
|
record.triggerIds.push(rowId);
|
|
420
|
+
record.inputMessageIds = { ...record.inputMessageIds, [rowId]: messageId };
|
|
421
|
+
const visible = typeof displayText === 'object' && displayText.source === 'visible';
|
|
422
|
+
const text = typeof displayText === 'string' ? displayText : displayText?.text;
|
|
423
|
+
if (text !== undefined && (record.inputPreviews?.[rowId] === undefined || visible && record.inputPreviewSources?.[rowId] !== 'visible')) {
|
|
424
|
+
record.inputPreviews = { ...record.inputPreviews, [rowId]: text.trim() ? (visible ? visibleInputPreview(text, 512, true) : inputPreview(text, 512, true)) : '' };
|
|
425
|
+
if (visible)
|
|
426
|
+
record.inputPreviewSources = { ...record.inputPreviewSources, [rowId]: 'visible' };
|
|
427
|
+
}
|
|
369
428
|
this.save(record); // local receipt already exists; UI record is separate
|
|
370
429
|
const { live } = this.hydrate(record);
|
|
371
|
-
live.received(this.english(chat) ? "Input durably received locally; acceptance not yet confirmed" : "消息已在本地可靠收件;尚未确认 Gateway 接收");
|
|
430
|
+
live.received(this.english(chat) ? "Input durably received locally; acceptance not yet confirmed" : "消息已在本地可靠收件;尚未确认 Gateway 接收", [rowId]);
|
|
372
431
|
live.start();
|
|
373
432
|
return live;
|
|
374
433
|
}
|
|
@@ -398,6 +457,7 @@ export class QueuedPresentations {
|
|
|
398
457
|
// The actual public submission key seals this cohort. It is NOT a run ID.
|
|
399
458
|
row.submissionId = submissionId;
|
|
400
459
|
this.save(row);
|
|
460
|
+
live?.submitting();
|
|
401
461
|
}
|
|
402
462
|
async insertionAccepted(chat, rowId, expectedRunId) {
|
|
403
463
|
const row = this.records(chat).find(row => this.usable(row) && !row.linkedReply && row.triggerIds.includes(rowId)
|
|
@@ -414,9 +474,9 @@ export class QueuedPresentations {
|
|
|
414
474
|
const live = this.hydrate(row).live;
|
|
415
475
|
const detail = this.english(chat) ? "Input enqueued; actual receiver, commitment and result unconfirmed" : "追加输入已进入队列;实际接收方、提交及处理结果未确认";
|
|
416
476
|
if (row.runId)
|
|
417
|
-
live.received(detail); // ordinary A
|
|
477
|
+
live.received(detail, [rowId], 'append_queued'); // ordinary A keeps its own activity phase
|
|
418
478
|
else
|
|
419
|
-
await live.showInputWaiting(detail, this.english(chat) ? `${this.bot} enqueued · input confirmation pending` : `${this.bot} 已排队 ·
|
|
479
|
+
await live.showInputWaiting(detail, this.english(chat) ? `${this.bot} enqueued · input confirmation pending` : `${this.bot} 已排队 · 追加输入回执待确认`, [rowId], 'append_queued');
|
|
420
480
|
}
|
|
421
481
|
/** A legacy insertion owns no actual run. Its card may nevertheless report
|
|
422
482
|
* the exact associated run's platform-delivered reply, without changing any
|
|
@@ -552,6 +612,51 @@ export class QueuedPresentations {
|
|
|
552
612
|
const row = this.records(chat).find(row => this.usable(row) && row.runId === runId);
|
|
553
613
|
return row ? this.hydrate(row).live : undefined;
|
|
554
614
|
}
|
|
615
|
+
/** Recall invalidates copied UI previews, including frozen/terminal cards.
|
|
616
|
+
* It never changes input custody or recreates/retargets an uncertain effect. */
|
|
617
|
+
async refreshRecalledInput(chat, rowId, messageId) {
|
|
618
|
+
const source = this.store.getMessageByRowId(rowId);
|
|
619
|
+
if (!source || source.chatId !== chat || source.messageId !== messageId || source.senderType !== 'human' || !this.store.isMessageRecalled(messageId))
|
|
620
|
+
return;
|
|
621
|
+
const effects = [];
|
|
622
|
+
for (const snapshot of this.records(chat)) {
|
|
623
|
+
if (!snapshot.triggerIds.includes(rowId) || snapshot.inputMessageIds?.[rowId] && snapshot.inputMessageIds[rowId] !== messageId)
|
|
624
|
+
continue;
|
|
625
|
+
const row = this.latest(snapshot);
|
|
626
|
+
if (!row)
|
|
627
|
+
continue;
|
|
628
|
+
const previews = { ...row.inputPreviews }, sources = { ...row.inputPreviewSources };
|
|
629
|
+
delete previews[rowId];
|
|
630
|
+
delete sources[rowId];
|
|
631
|
+
const view = row.view ? { ...row.view, lines: row.view.lines.filter(line => line.kind !== 'steer'), inputStatus: row.view.inputStatus ? {
|
|
632
|
+
...row.view.inputStatus, items: row.view.inputStatus.items.map(item => {
|
|
633
|
+
if (item.rowId !== rowId && item.messageId !== messageId)
|
|
634
|
+
return item;
|
|
635
|
+
const { preview, previewSource, ...reference } = item;
|
|
636
|
+
return reference;
|
|
637
|
+
}),
|
|
638
|
+
} : undefined } : undefined;
|
|
639
|
+
this.save({ ...row, inputPreviews: previews, inputPreviewSources: sources, view });
|
|
640
|
+
if (row.retired || row.stopEpoch !== this.store.getStopGeneration(this.bot, chat) || row.linkedReply?.rendered === 'reply_payload')
|
|
641
|
+
continue;
|
|
642
|
+
let entry = this.entries.get(row.id);
|
|
643
|
+
if (entry?.record.lease !== row.lease)
|
|
644
|
+
entry = undefined;
|
|
645
|
+
if (!entry && row.create === 'accepted' && row.cardId && !Object.keys(row.editEffects || {}).length)
|
|
646
|
+
entry = this.hydrate(this.latest(row));
|
|
647
|
+
if (!entry)
|
|
648
|
+
continue; // unknown/no-ID restoration: persist redaction only
|
|
649
|
+
const owned = entry;
|
|
650
|
+
const effectCurrent = () => {
|
|
651
|
+
const fresh = this.latest(owned.record);
|
|
652
|
+
return !this.disposed && !!fresh && !fresh.retired && fresh.lease === owned.record.lease && this.enabled(chat) &&
|
|
653
|
+
fresh.stopEpoch === this.store.getStopGeneration(this.bot, chat) && fresh.create === 'accepted' && !!fresh.cardId &&
|
|
654
|
+
fresh.cardId === owned.live.id && !Object.keys(fresh.editEffects || {}).length && fresh.linkedReply?.rendered !== 'reply_payload';
|
|
655
|
+
};
|
|
656
|
+
effects.push(owned.live.redactRecalledInput(effectCurrent));
|
|
657
|
+
}
|
|
658
|
+
await Promise.allSettled(effects);
|
|
659
|
+
}
|
|
555
660
|
retire(chat) {
|
|
556
661
|
// Stop old callbacks even when the UI journal is temporarily unavailable.
|
|
557
662
|
for (const entry of this.entries.values())
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
/** Only gateway envelopes / committed assistant metadata populate this record. */
|
|
2
2
|
export interface ErrorProvenance {
|
|
3
3
|
sessionKey: string;
|
|
4
|
+
sessionId?: string;
|
|
5
|
+
botIdentity?: string;
|
|
6
|
+
stopEpoch?: number;
|
|
4
7
|
runId: string;
|
|
5
8
|
source: "chat" | "session.message";
|
|
6
9
|
state: string;
|
|
@@ -8,6 +11,13 @@ export interface ErrorProvenance {
|
|
|
8
11
|
detail?: string;
|
|
9
12
|
userStop?: boolean;
|
|
10
13
|
}
|
|
14
|
+
export type ClosedStopReason = 'timeout' | 'restart' | 'superseded' | 'aborted' | 'error' | 'stop' | 'rpc';
|
|
15
|
+
export declare const closedStopReason: (value: unknown) => ClosedStopReason | undefined;
|
|
16
|
+
/** Carries original public terminal metadata, never model/prompt-derived cause. */
|
|
17
|
+
export declare class RunTerminalError extends Error {
|
|
18
|
+
readonly provenance: ErrorProvenance;
|
|
19
|
+
constructor(provenance: ErrorProvenance);
|
|
20
|
+
}
|
|
11
21
|
export interface ProactiveMessageMeta {
|
|
12
22
|
/** Bridge-local Go result owner; never derived from an untrusted model body. */
|
|
13
23
|
goJobId?: number;
|
|
@@ -39,10 +49,12 @@ export interface ErrorVerdict {
|
|
|
39
49
|
reason: string;
|
|
40
50
|
task?: TaskEvidence;
|
|
41
51
|
terminalKey?: string;
|
|
52
|
+
stopReason?: ClosedStopReason;
|
|
42
53
|
text?: string;
|
|
43
54
|
}
|
|
44
55
|
export type ReadRPC = (method: string, params: Record<string, unknown>, timeoutMs: number) => Promise<any>;
|
|
45
56
|
export declare const errorIdentity: (sessionKey: string, runId: string) => string;
|
|
57
|
+
export declare const errorNoticeIdentity: (p: ErrorProvenance) => string;
|
|
46
58
|
export declare const genericFailure: (text: string) => boolean;
|
|
47
59
|
export declare function announceCandidate(p: ErrorProvenance): {
|
|
48
60
|
runId: string;
|
package/dist/terminal-errors.js
CHANGED
|
@@ -1,6 +1,19 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
import { safeErrorSummary } from "./provider-errors.js";
|
|
3
|
+
export const closedStopReason = (value) => typeof value === 'string' && ['timeout', 'restart', 'superseded', 'aborted', 'error', 'stop', 'rpc'].includes(value) ? value : undefined;
|
|
4
|
+
/** Carries original public terminal metadata, never model/prompt-derived cause. */
|
|
5
|
+
export class RunTerminalError extends Error {
|
|
6
|
+
provenance;
|
|
7
|
+
constructor(provenance) {
|
|
8
|
+
super(`Agent error: ${provenance.detail || provenance.stopReason || 'aborted'}`);
|
|
9
|
+
this.provenance = provenance;
|
|
10
|
+
this.name = 'RunTerminalError';
|
|
11
|
+
}
|
|
12
|
+
}
|
|
3
13
|
export const errorIdentity = (sessionKey, runId) => createHash("sha256").update(JSON.stringify([sessionKey, runId])).digest("hex");
|
|
14
|
+
export const errorNoticeIdentity = (p) => p.sessionId && p.botIdentity && Number.isSafeInteger(p.stopEpoch)
|
|
15
|
+
? errorIdentity(JSON.stringify([p.sessionKey, p.sessionId, p.botIdentity, p.stopEpoch]), p.runId)
|
|
16
|
+
: errorIdentity(p.sessionKey, p.runId);
|
|
4
17
|
export const genericFailure = (text) => text.trim() === "The agent run failed before producing a reply.";
|
|
5
18
|
// These are candidate locators, NOT business identities. Verify every component
|
|
6
19
|
// against tasks.list + a fresh tasks.get. Unsupported/batch forms stay unknown.
|
|
@@ -39,11 +52,16 @@ export async function inspectError(p, rpc) {
|
|
|
39
52
|
if (!p.runId)
|
|
40
53
|
return pending("missing_run_identity");
|
|
41
54
|
if (!announce) {
|
|
55
|
+
if (p.source === 'chat' && p.state === 'aborted' && closedStopReason(p.stopReason))
|
|
56
|
+
return {
|
|
57
|
+
state: 'terminal', reason: 'closed_gateway_terminal', terminalKey: `run-error:${errorNoticeIdentity(p)}`,
|
|
58
|
+
text: '⚠️ 该次运行已终止,未能完成回复。',
|
|
59
|
+
};
|
|
42
60
|
// Ordinary chat.error is a gateway terminal envelope (after gateway retry
|
|
43
61
|
// grace). Do not rewrite normal collector/auth/parameter/tool-error handling.
|
|
44
62
|
if (p.source === "chat" && (p.state === "error" || (p.state === "final" && p.stopReason === "error")))
|
|
45
63
|
return {
|
|
46
|
-
state: "terminal", reason: "ordinary_gateway_error", terminalKey: `run-error:${
|
|
64
|
+
state: "terminal", reason: "ordinary_gateway_error", terminalKey: `run-error:${errorNoticeIdentity(p)}`,
|
|
47
65
|
text: `⚠️ Agent run failed${p.detail ? `: ${safeErrorSummary(p.detail)}` : ""}`,
|
|
48
66
|
};
|
|
49
67
|
try {
|
|
@@ -52,10 +70,12 @@ export async function inspectError(p, rpc) {
|
|
|
52
70
|
const started = wait?.startedAt;
|
|
53
71
|
const terminalTime = typeof ended === "number" && Number.isFinite(ended) && ended > 0
|
|
54
72
|
&& (started === undefined || (typeof started === "number" && Number.isFinite(started) && started >= 0 && ended >= started));
|
|
55
|
-
if (wait?.runId === p.runId && wait?.status === "error" && wait?.pendingError !== true
|
|
73
|
+
if (wait?.runId === p.runId && (wait?.status === "error" || wait?.status === 'timeout') && wait?.pendingError !== true
|
|
74
|
+
&& !['queue', 'gateway_draining'].includes(wait?.timeoutPhase) && (!p.sessionId || !wait.sessionId || wait.sessionId === p.sessionId)
|
|
56
75
|
&& wait?.yielded !== true && terminalTime)
|
|
57
76
|
return {
|
|
58
|
-
state: "terminal", reason: "exact_run_terminal_error", terminalKey: `run-error:${
|
|
77
|
+
state: "terminal", reason: "exact_run_terminal_error", terminalKey: `run-error:${errorNoticeIdentity(p)}`,
|
|
78
|
+
stopReason: closedStopReason(wait.stopReason) || (wait.status === 'timeout' ? 'timeout' : undefined),
|
|
59
79
|
text: "⚠️ 该次运行已终止,未能完成回复。",
|
|
60
80
|
};
|
|
61
81
|
return pending("wait_not_terminal_failure");
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# LMA 1.8.20
|
|
2
|
+
|
|
3
|
+
## Live cards identify the actual input
|
|
4
|
+
|
|
5
|
+
All live input-delivery hints now use a brief localized `【emoji status】` followed by the corresponding original message body or an explicitly bounded excerpt. Local receipt, queueing, submission pending, append queueing, acceptance, processing, waiting/paused, unconfirmed/partial, delivery pending, round ended, no-content, linked reply, interrupted and not-submitted states retain their distinct meaning.
|
|
6
|
+
|
|
7
|
+
- Concurrent, merged and appended inputs retain independent source references and phases across card updates and recovery. One root's result does not certify another appended input's commitment.
|
|
8
|
+
- Necessary unknown/receipt qualifications remain separate from the message preview. Display records do not grant admission, Get/DONE, retry or business-completion authority.
|
|
9
|
+
- Literal human text is preserved instead of being mistaken for legacy media/routing markers. Structured media uses visible captions/names/type markers, not server paths, private keys or hydrated document bodies.
|
|
10
|
+
- Source identity and recall are checked at the card boundary. Existing known UI cards redact recalled previews without waiting for unrelated reaction network operations; unknown UI effects remain fenced and protected business-answer payloads are not overwritten.
|
|
11
|
+
- Complete outer request budgeting preserves newest inputs and marks omissions; user markup cannot create active mentions or card actions.
|
|
12
|
+
|
|
13
|
+
## Private-chat ownership survives bot renaming
|
|
14
|
+
|
|
15
|
+
Private ownership is pinned to the receiving application's stable identity. Same-app display-name changes preserve intake/startup access and proven previous-name deduplication. App/chat/message ingress tombstones, including owner-rejected inputs, prevent historical commands from executing again after a rename.
|
|
16
|
+
|
|
17
|
+
Foreign/malformed pins and unmatched unpinned legacy owners remain fail-closed. Only a separately verified, targeted operator migration can establish a lost legacy alias. No broad takeover or historical-message replay is performed.
|
|
18
|
+
|
|
19
|
+
## Terminal errors retain their real cause and one delivery owner
|
|
20
|
+
|
|
21
|
+
Closed run/session abort metadata now survives the foreground collector. Proven total-deadline termination is no longer presented as merely paused waiting; bare `aborted` remains unknown instead of being guessed as a human stop or timeout.
|
|
22
|
+
|
|
23
|
+
Foreground and late proactive failures coordinate through the existing exact-run error journal/outbox, including compatible legacy owners. Original app/session/physical target/stop epoch/recall checks apply immediately before ordinary admitted-error SDK sends too. New same-run progress during an awaited cause lookup fences stale provisional abort decisions. Finals and explicit stop receipts retain priority.
|
|
24
|
+
|
|
25
|
+
## Compatibility and operational boundaries
|
|
26
|
+
|
|
27
|
+
- Runtime dependency constraints and the consolidated plugin contract are unchanged. Preserve compatible target-native dependencies; build and test locally rather than installing/building on deployment hosts.
|
|
28
|
+
- Existing history, business namespaces, sync labels and old delivered/unknown-attempt custody are not bulk renamed or replayed.
|
|
29
|
+
- This LMA package does not itself upgrade or patch OpenClaw Core, change model selection, increase configured execution deadlines, or alter model permissions.
|
|
30
|
+
- Automated acceptance uses offline actual receiver/controller/outbox/Card2 SDK boundaries and synthetic HTTP transport. Real target process activation, original-message delivery and Feishu client appearance require separate deployment/acceptance receipts.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "openclaw-lark-multi-agent",
|
|
3
|
-
"version": "1.8.
|
|
3
|
+
"version": "1.8.21",
|
|
4
4
|
"description": "Multi-bot Lark/Feishu bridge for OpenClaw, with per-bot model routing and isolated sessions",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
@@ -80,7 +80,8 @@
|
|
|
80
80
|
"docs/releases/1.8.16.md",
|
|
81
81
|
"docs/releases/1.8.17.md",
|
|
82
82
|
"docs/releases/1.8.18.md",
|
|
83
|
-
"docs/releases/1.8.19.md"
|
|
83
|
+
"docs/releases/1.8.19.md",
|
|
84
|
+
"docs/releases/1.8.20.md"
|
|
84
85
|
],
|
|
85
86
|
"engines": {
|
|
86
87
|
"node": ">=22"
|
|
@@ -88,4 +89,4 @@
|
|
|
88
89
|
"publishConfig": {
|
|
89
90
|
"access": "public"
|
|
90
91
|
}
|
|
91
|
-
}
|
|
92
|
+
}
|