openclaw-lark-multi-agent 1.4.3 → 1.4.5

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 CHANGED
@@ -37,7 +37,7 @@ All of them connect to the same OpenClaw Gateway while keeping sessions, queues,
37
37
  - Feishu image download and OpenClaw multimodal attachment forwarding
38
38
  - Bridge attachment marker protocol for generated files/images/documents
39
39
  - Feishu CardKit v2 Markdown rendering, including native table elements for pipe tables
40
- - Live status card: in non-verbose mode each run shows a single self-updating Feishu interactive card (title + recent activity window with per-line timestamps + elapsed/model footer, refreshed once per second), which collapses to a compact one-line summary on a clean finish and retains the recent activity on failure for debugging
40
+ - Live status card: in non-verbose mode each run shows a self-updating Feishu interactive card (title + recent activity window with per-line timestamps + elapsed footer, refreshed once per second), which collapses to tool-call count + elapsed on a clean finish; the final answer is sent separately at the bottom with a model footer
41
41
  - Bridge-level slash commands and escaped OpenClaw slash commands
42
42
  - `/discuss` mode for barrier-style multi-bot group discussion, including per-round markers and no-reply status notices
43
43
  - `/chairman` role: a single per-group chairman that answers plain messages when no bot is in Free mode, and acts as host, challenger, and summarizer inside `/discuss`
@@ -99,22 +99,20 @@ On Windows, install [NSSM](https://nssm.cc/download), make sure `nssm.exe` is in
99
99
  lma install-windows-service
100
100
  ```
101
101
 
102
- ### Optional: real-time steering plugin
102
+ ### Real-time steering
103
103
 
104
- To let users nudge/correct a long-running agent turn in real time (instead of
105
- waiting for it to finish), install the bundled `lma-steer` OpenClaw plugin:
104
+ When the agent is mid-run and a new message arrives, the bridge submits it via
105
+ OpenClaw's public `chat.send` API with `queueMode: "steer"`. The RPC
106
+ acknowledgment is provisional: LMA retains its durable pending trigger until a
107
+ matching `session.message` proves transcript commitment/consumption, and only
108
+ then switches Feishu from Typing to Get. Async `error`/`aborted` terminals are
109
+ routed by run id, while unconsumed input remains available for normal queue
110
+ fallback. This replaces the deprecated synchronous plugin primitive whose
111
+ boolean could not represent later runtime rejection.
106
112
 
107
- ```bash
108
- lma install-steer-plugin
109
- # then restart the OpenClaw gateway so it loads the plugin:
110
- systemctl --user restart openclaw-gateway.service
111
- ```
112
-
113
- When the agent is mid-run and a new message arrives, the bridge injects it into
114
- the active run at the next tool-call boundary (Feishu shows the "Get" reaction).
115
- Without the plugin, the bridge falls back to queuing the message until the run
116
- finishes — everything still works, just without real-time steering. See
117
- `plugins/lma-steer/README.md` for details.
113
+ OpenClaw 2026.8+ no longer requires the bundled `lma-steer` plugin. The
114
+ `install-steer-plugin` command remains temporarily for older installations but
115
+ current LMA releases do not call its private Gateway method.
118
116
 
119
117
  Useful CLI commands:
120
118
 
@@ -124,7 +122,7 @@ lma doctor
124
122
  lma start [config]
125
123
  lma init [--state-dir DIR] [--force]
126
124
  lma install-systemd [--user|--system] [--state-dir DIR]
127
- lma install-steer-plugin [--no-force]
125
+ lma install-steer-plugin [--no-force] # legacy; not needed on OpenClaw 2026.8+
128
126
  ```
129
127
 
130
128
  ## Quick start from source
@@ -450,6 +448,26 @@ Assistant replies are sent as Feishu CardKit v2 cards. Markdown is preprocessed
450
448
 
451
449
  For generated files/images/documents, agents should use the bridge attachment marker protocol instead of calling Feishu messaging tools directly. The bridge strips the marker from the visible reply, validates the file path under the configured attachment directory, uploads/sends the attachment, and records it in local context. Markdown documents can be converted into Feishu cloud documents through this path.
452
450
 
451
+ ## OpenClaw 2026.8 compatibility
452
+
453
+ LMA targets Gateway protocol 4 and is built/tested against `openclaw@2026.8.2`.
454
+ OpenClaw 2026.8 moves active session rows and transcripts into the per-agent
455
+ SQLite database. LMA never opens that database or rewrites archived JSONL:
456
+ `/compact` first requests semantic `sessions.compact`, then uses the same
457
+ Gateway RPC with a bounded `maxLines` tail only if semantic compaction cannot
458
+ run. Set `OPENCLAW_LARK_MULTI_AGENT_COMPACT_FALLBACK_MAX_LINES` to change the
459
+ default tail size (`200`, minimum `20`).
460
+
461
+ OpenClaw 2026.8 also separates model metadata from the override allowlist. Every
462
+ model configured in LMA must be permitted by `agents.defaults.modelPolicy.allow`
463
+ when that list is non-empty. Exact refs and `provider/*` wildcards are accepted.
464
+ LMA now surfaces a model-policy rejection instead of logging a false “model
465
+ ensured” success.
466
+
467
+ Mid-run messages use the public `chat.send { queueMode: "steer" }` path. LMA
468
+ keeps the durable trigger until `session.message` confirms consumption; this is
469
+ also the signal for the visible Typing-to-Get transition.
470
+
453
471
  ## Live status
454
472
 
455
473
  In non-verbose mode, each run shows a single self-updating Feishu interactive
@@ -463,7 +481,7 @@ While running, the card shows:
463
481
  - a rolling window of the most recent activity lines (tool start `▸`, tool end
464
482
  `✓`, and intermediate assistant text `•`), each prefixed with the relative
465
483
  time `mm:ss` since the run started;
466
- - a footer with elapsed time and the bound model name.
484
+ - a footer with elapsed time.
467
485
 
468
486
  The card is created lazily (a fast reply that finishes within the create delay
469
487
  never spawns a card, to avoid flicker) and is updated with `im.message.patch`,
@@ -479,8 +497,9 @@ When the run ends:
479
497
  header, so the steps leading up to the failure stay visible for debugging.
480
498
 
481
499
  The final answer is always delivered separately through the normal interactive-card
482
- delivery path, so Markdown renders correctly; the live status card is a distinct
483
- message and never replaces or blocks the final reply.
500
+ delivery path, so it is created at completion time and remains below any user messages
501
+ inserted during the run. Its footer shows only the bound model name. The live-status
502
+ card remains a distinct earlier message and owns tool-call count + elapsed time.
484
503
 
485
504
  Live status is on by default and can be toggled per bot/chat with
486
505
  `/livestatus [on|off]`. It can be disabled globally by setting
package/README.zh-CN.md CHANGED
@@ -408,6 +408,20 @@ v1 行为边界:如果消息已经进入 OpenClaw 正在处理,暂不 abort
408
408
 
409
409
  如果 Agent 需要发送生成的文件、图片或文档,应使用 bridge attachment marker 协议,而不是直接调用飞书消息工具。桥接层会从可见回复中剥离 marker,校验文件路径位于配置的附件目录下,然后上传/发送附件,并写入本地上下文。Markdown 文档也可以通过这个路径转换成飞书云文档。
410
410
 
411
+ ## OpenClaw 2026.8 兼容性
412
+
413
+ LMA 以 Gateway protocol 4 为基线,并使用 `openclaw@2026.8.2` 构建和测试。OpenClaw 2026.8 把活动 session row 与 transcript 迁入每个 Agent 的 SQLite 数据库。LMA 不再直接查找或改写 JSONL:`/compact` 先调用语义化 `sessions.compact`,如果语义压缩无法执行,再通过同一个 Gateway RPC 的 `maxLines` 安全保留最近一段转录。可用 `OPENCLAW_LARK_MULTI_AGENT_COMPACT_FALLBACK_MAX_LINES` 调整默认保留数量(默认 `200`,最小 `20`)。
414
+
415
+ 新版把模型元数据与模型覆盖允许列表分开。如果 `agents.defaults.modelPolicy.allow` 非空,LMA 配置中的每个模型都必须被该列表的精确模型引用或 `provider/*` 通配符允许。遇到拒绝时,LMA 会直接报告 model policy 错误,不再错误记录“模型已确保”。
416
+
417
+ 执行中插入的消息改用公开的 `chat.send { queueMode: "steer" }`。RPC 成功返回只是暂时接受;LMA 会继续保留本地 pending trigger,只有匹配的 `session.message` 确认消息已写入转录并被消费后才清除,同时把飞书标记从 Typing 切换为 Get。异步 `error`/`aborted` 按 runId 投递,未消费的输入仍由普通队列兜底。这样不再依赖无法表达后续异步拒绝的旧同步插件原语。
418
+
419
+ ## Live Status 与最终回复
420
+
421
+ 非 verbose 模式下,执行过程显示一张可更新的 live status 卡片。运行中显示最近活动和耗时;完成后收缩为“累计工具调用次数 + 总耗时”。
422
+
423
+ 最终回复不会覆盖旧的 live status,而是在任务完成时新发一张卡片,因此用户执行途中插入的新消息不会排在最终答案之后。最终回复卡片底部只显示本次使用的模型名;工具调用次数和耗时只保留在 live status 中。
424
+
411
425
  ## 数据模型
412
426
 
413
427
  SQLite 状态位于配置的数据目录。主要表:
package/dist/cli.js CHANGED
@@ -206,12 +206,12 @@ function runCapture(cmd, args) {
206
206
  }
207
207
  }
208
208
  /**
209
- * Install the bundled lma-steer OpenClaw plugin into the local OpenClaw gateway.
210
- * The plugin registers the `lma.steer` gateway method that the bridge uses to
211
- * inject a message into an active run at the next tool-call boundary. The plugin
212
- * ships inside this package under plugins/lma-steer (built to dist).
209
+ * Install the legacy bundled lma-steer plugin. Current LMA uses OpenClaw's
210
+ * public `chat.send { queueMode: "steer" }` API and does not call this plugin;
211
+ * the command remains only for operators maintaining older LMA deployments.
213
212
  */
214
213
  function cmdInstallSteerPlugin(args) {
214
+ console.warn("Note: current LMA releases use native chat.send queueMode=steer; this legacy plugin is not required on OpenClaw 2026.8+.");
215
215
  // Default to --force so reinstalling an updated bundled plugin overwrites the
216
216
  // previously installed copy. Pass --no-force to keep an existing install.
217
217
  const noForce = hasFlag(args, "--no-force");
@@ -1,5 +1,5 @@
1
1
  /** Coarse phase of the compaction flow, drives the card's primary line. */
2
- export type CompactPhase = "native" | "tool-trim";
2
+ export type CompactPhase = "native" | "transcript-trim";
3
3
  /** Terminal outcome used by the renderer to pick color/emoji. */
4
4
  export type CompactState = "running" | "done" | "failed" | "noop";
5
5
  /** Structured snapshot the renderer turns into a Feishu interactive card. */
@@ -27,7 +27,7 @@ export type CompactProgressOptions = {
27
27
  tickMs?: number;
28
28
  };
29
29
  /**
30
- * Lifecycle: start() → (optional) toToolTrim() when native gives up → one of
30
+ * Lifecycle: start() → (optional) toTranscriptTrim() when native gives up → one of
31
31
  * done()/noop()/fail(). The card is created lazily after `delayMs` so a fast
32
32
  * compaction that finishes first never shows a card at all. All terminal calls
33
33
  * are idempotent and safe even if the card was never created.
@@ -50,8 +50,8 @@ export declare class CompactProgressController {
50
50
  get id(): string | undefined;
51
51
  /** Begin the flow; schedules lazy card creation after delayMs. */
52
52
  start(): void;
53
- /** Native compaction gave up (timeout/no-op); switch the card to fast-trim. */
54
- toToolTrim(): Promise<void>;
53
+ /** Native compaction gave up; switch to Gateway-owned transcript-tail trim. */
54
+ toTranscriptTrim(): Promise<void>;
55
55
  /** Finished successfully; patch the card to the done state with a detail line. */
56
56
  done(detail?: string): Promise<void>;
57
57
  /** Nothing to compact; patch the card to a neutral "no-op" state. */
@@ -10,7 +10,7 @@
10
10
  const DEFAULT_DELAY_MS = Number(process.env.OPENCLAW_LARK_MULTI_AGENT_COMPACT_STATUS_DELAY_MS || 500);
11
11
  const DEFAULT_TICK_MS = Number(process.env.OPENCLAW_LARK_MULTI_AGENT_COMPACT_STATUS_TICK_MS || 1000);
12
12
  /**
13
- * Lifecycle: start() → (optional) toToolTrim() when native gives up → one of
13
+ * Lifecycle: start() → (optional) toTranscriptTrim() when native gives up → one of
14
14
  * done()/noop()/fail(). The card is created lazily after `delayMs` so a fast
15
15
  * compaction that finishes first never shows a card at all. All terminal calls
16
16
  * are idempotent and safe even if the card was never created.
@@ -45,11 +45,11 @@ export class CompactProgressController {
45
45
  }, this.opts.delayMs ?? DEFAULT_DELAY_MS);
46
46
  this.createTimer.unref?.();
47
47
  }
48
- /** Native compaction gave up (timeout/no-op); switch the card to fast-trim. */
49
- async toToolTrim() {
50
- if (this.finalized || this.phase === "tool-trim")
48
+ /** Native compaction gave up; switch to Gateway-owned transcript-tail trim. */
49
+ async toTranscriptTrim() {
50
+ if (this.finalized || this.phase === "transcript-trim")
51
51
  return;
52
- this.phase = "tool-trim";
52
+ this.phase = "transcript-trim";
53
53
  await this.refresh();
54
54
  }
55
55
  /** Finished successfully; patch the card to the done state with a detail line. */
@@ -35,14 +35,26 @@ export declare class FeishuBot {
35
35
  private sendQueue;
36
36
  /** Per-chat delayed runtime failure notifications, canceled if a real reply arrives. */
37
37
  private delayedFailureTimers;
38
+ /** Ephemeral model footer overrides used by final-message wrappers. Keeping
39
+ * replyMessage/sendMessage two-argument APIs avoids contaminating ordinary
40
+ * command/error/card sends with model attribution. */
41
+ private replyModelFooters;
42
+ private sendModelFooters;
38
43
  /** Per-chat durable outbox retry timer. */
39
44
  private deliveryRetryTimers;
40
45
  /** Last time a real assistant-visible reply was successfully handed to the delivery pipeline. */
41
46
  private lastRealDeliveryAt;
42
47
  /** Per-chat: whether we've already sent the high-context alert this cycle. */
43
48
  private contextAlerted;
44
- /** Active chatSend trigger target so final replies and proactive session.message share one delivery key. */
49
+ /** Active pre-admission target for the current chat operation. */
45
50
  private activeDeliveryTargets;
51
+ /** Stable targets for concurrent runs in one chat. A steer admission can
52
+ * become an ordinary run while another run remains active. */
53
+ private deliveryTargetsByRun;
54
+ /** Provisional native-steer correlations keyed by durable message row. If the
55
+ * message falls back to a normal run, cancel these before submission so a
56
+ * later identical steer cannot consume the stale observer/correlation. */
57
+ private pendingSteerCleanups;
46
58
  private adminOpenId;
47
59
  private locale;
48
60
  private configPath?;
@@ -122,11 +134,8 @@ export declare class FeishuBot {
122
134
  private buildMarkdownCard;
123
135
  private deliveryOwnsVisiblePayload;
124
136
  private completionSummary;
125
- /** Final answer card used to replace the running live-status card in place. */
126
- private buildFinalAnswerCard;
127
- private patchFinalAnswerCard;
128
- /** Best-effort cleanup when a final-card patch was too large/failed and the
129
- * answer had to be delivered as a separate message. */
137
+ /** Best-effort terminal cleanup for status-only rows and legacy v1.4.3
138
+ * outbox entries that once targeted the live-status card. */
130
139
  private patchLiveStatusDoneSummary;
131
140
  private formatUserVisibleError;
132
141
  private isBridgeControlReply;
@@ -179,6 +188,9 @@ export declare class FeishuBot {
179
188
  */
180
189
  private autoRetryUntilComplete;
181
190
  private cancelDelayedFailure;
191
+ private deliveryRunTargetKey;
192
+ private bindRunDeliveryTarget;
193
+ private releaseRunDeliveryTargetAfter;
182
194
  private setActiveDeliveryTarget;
183
195
  private scheduleDelayedFailure;
184
196
  private deliverySessionKey;
@@ -227,6 +239,7 @@ export declare class FeishuBot {
227
239
  private replyTextMessage;
228
240
  private editTextMessage;
229
241
  private deleteMessageById;
242
+ private replyFinalMessage;
230
243
  private replyMessage;
231
244
  private extractBridgeAttachments;
232
245
  /**
@@ -271,6 +284,7 @@ export declare class FeishuBot {
271
284
  /**
272
285
  * Send a proactive message to a chat (not a reply).
273
286
  */
287
+ private sendFinalMessage;
274
288
  private sendMessage;
275
289
  /**
276
290
  * Enqueue a message send to guarantee ordering per chat.
@@ -301,14 +315,14 @@ export declare class FeishuBot {
301
315
  * Handle /compact command: compress session context.
302
316
  */
303
317
  /**
304
- * Compact a session with a fallback chain:
305
- * 1. OpenClaw's native (LLM-summary) compaction — best semantics.
306
- * 2. If that did not compact (or threw), fall back to "tool-trim": rewrite
307
- * the transcript file to drop tool calls/results while keeping the
308
- * conversation. This needs no model, so it works no matter how large the
309
- * session is (the native path itself fails to fit an oversized session
310
- * into the model). A backup is always written before any file change.
311
- * Returns a structured result describing what happened.
318
+ * Compact a session with a Gateway-owned fallback chain:
319
+ * 1. Semantic compaction through `sessions.compact`.
320
+ * 2. If summarization cannot run, ask the same RPC to retain only a bounded
321
+ * transcript tail via `maxLines`. This works with legacy JSONL and the
322
+ * SQLite session store introduced in OpenClaw 2026.8.x, and preserves
323
+ * OpenClaw's write locks, transcript invariants, and migration ownership.
324
+ *
325
+ * LMA deliberately never opens or rewrites OpenClaw transcript storage.
312
326
  */
313
327
  private compactWithFallback;
314
328
  private handleCompactCommand;