openclaw-lark-multi-agent 1.4.5 → 1.4.7

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.
@@ -1825,9 +1825,10 @@ export class FeishuBot {
1825
1825
  }
1826
1826
  completionSummary(meta, emoji = "✅") {
1827
1827
  const en = meta.locale === "en";
1828
- return en
1828
+ const core = en
1829
1829
  ? `${emoji} ${meta.toolCalls} tool call${meta.toolCalls === 1 ? "" : "s"} · ⏱ ${meta.elapsed}`
1830
1830
  : `${emoji} 累计${meta.toolCalls} 次工具调用 · ⏱ 耗时${meta.elapsed}`;
1831
+ return meta.model?.trim() ? `${core} · 🧠 ${this.escapeCardText(meta.model.trim())}` : core;
1831
1832
  }
1832
1833
  /** Best-effort terminal cleanup for status-only rows and legacy v1.4.3
1833
1834
  * outbox entries that once targeted the live-status card. */
@@ -2654,6 +2655,7 @@ export class FeishuBot {
2654
2655
  const summary = this.completionSummary({
2655
2656
  toolCalls: view.toolCalls,
2656
2657
  elapsed: view.elapsed,
2658
+ model: view.model,
2657
2659
  locale: en ? "en" : "zh",
2658
2660
  }, statusEmoji);
2659
2661
  return {
@@ -2686,10 +2688,12 @@ export class FeishuBot {
2686
2688
  elements.push({ tag: "markdown", content: en ? "_working…_" : "_正在启动…_" });
2687
2689
  }
2688
2690
  elements.push({ tag: "hr" });
2689
- // Running/failed status owns timing; model attribution belongs to the final
2690
- // reply card so the two messages have clear, non-duplicated responsibilities.
2691
- const footer = en ? `⏱ ${view.elapsed}` : `⏱ 已用 ${view.elapsed}`;
2692
- elements.push({ tag: "markdown", content: `<font color='grey'>${footer}</font>` });
2691
+ // Keep the active model visible while the run is in progress or failed.
2692
+ // The final reply also keeps model attribution so it remains self-contained.
2693
+ const footerBits = [en ? `⏱ ${view.elapsed}` : `⏱ 已用 ${view.elapsed}`];
2694
+ if (view.model?.trim())
2695
+ footerBits.push(`🧠 ${this.escapeCardText(view.model.trim())}`);
2696
+ elements.push({ tag: "markdown", content: `<font color='grey'>${footerBits.join(" · ")}</font>` });
2693
2697
  const template = view.state === "failed" ? "orange" : "blue";
2694
2698
  return {
2695
2699
  schema: "2.0",
@@ -88,6 +88,9 @@ export declare class OpenClawClient {
88
88
  private extractVisibleAssistantText;
89
89
  private isVerboseTranscriptEnabled;
90
90
  private clearVerboseAssistantState;
91
+ /** Render protocol-4 structured tool args/results without `[object Object]`.
92
+ * Prefer useful, non-secret fields and keep the fallback bounded. */
93
+ private formatToolValue;
91
94
  private claimToolEvent;
92
95
  private pruneVerboseCaches;
93
96
  private flushVerboseAssistantState;
@@ -312,11 +312,13 @@ export class OpenClawClient {
312
312
  if (this.claimToolEvent(rawKey, eventId, phase)) {
313
313
  const meta = data.meta || data.input || data.args || "";
314
314
  const output = data.output || data.result || data.error || "";
315
+ const inputText = this.formatToolValue(meta, String(data.name), "start");
316
+ const outputText = this.formatToolValue(output, String(data.name), phase);
315
317
  if (toolCb)
316
- toolCb(`${data.name} ${phase}`.trim(), String(meta || ""), String(output || ""));
318
+ toolCb(`${data.name} ${phase}`.trim(), inputText, outputText);
317
319
  const progressCb = this.progressCallbacks.get(rawKey) || this.progressCallbacks.get(shortKey);
318
320
  if (progressCb) {
319
- const detail = phase === "start" ? String(meta || "") : String(output || meta || "");
321
+ const detail = phase === "start" ? inputText : (outputText || inputText);
320
322
  void progressCb({ kind: "tool", phase, name: String(data.name), text: `${data.name} ${phase}${detail ? `: ${detail}` : ""}` });
321
323
  }
322
324
  }
@@ -343,12 +345,14 @@ export class OpenClawClient {
343
345
  }
344
346
  const input = data.input || data.args || data.meta || "";
345
347
  const output = data.output || data.result || data.error || "";
348
+ const inputText = this.formatToolValue(input, name, "start");
349
+ const outputText = this.formatToolValue(output, name, phase);
346
350
  const toolCb = this.toolEventCallbacks.get(rawKey) || this.toolEventCallbacks.get(shortKey);
347
351
  if (toolCb)
348
- toolCb(`${name} ${phase}`, String(input || ""), String(output || ""));
352
+ toolCb(`${name} ${phase}`, inputText, outputText);
349
353
  const progressCb = this.progressCallbacks.get(rawKey) || this.progressCallbacks.get(shortKey);
350
354
  if (progressCb) {
351
- const detail = phase === "start" ? String(input || "") : String(output || input || "");
355
+ const detail = phase === "start" ? inputText : (outputText || inputText);
352
356
  void progressCb({ kind: "tool", phase, name, text: `${name} ${phase}${detail ? `: ${detail}` : ""}` });
353
357
  }
354
358
  }
@@ -451,6 +455,75 @@ export class OpenClawClient {
451
455
  this.verboseAssistantLastTouched.delete(key);
452
456
  }
453
457
  }
458
+ /** Render protocol-4 structured tool args/results without `[object Object]`.
459
+ * Prefer useful, non-secret fields and keep the fallback bounded. */
460
+ formatToolValue(value, toolName, phase) {
461
+ if (value === null || value === undefined || value === "")
462
+ return "";
463
+ if (typeof value === "string")
464
+ return value.trim();
465
+ if (typeof value === "number" || typeof value === "boolean" || typeof value === "bigint")
466
+ return String(value);
467
+ if (Array.isArray(value)) {
468
+ const items = value.slice(0, 4).map((item) => this.formatToolValue(item, toolName, phase)).filter(Boolean);
469
+ return items.join("; ");
470
+ }
471
+ if (typeof value !== "object")
472
+ return String(value);
473
+ const record = value;
474
+ const read = (key) => this.formatToolValue(record[key], toolName, phase);
475
+ const first = (...keys) => {
476
+ for (const key of keys) {
477
+ if (!(key in record))
478
+ continue;
479
+ const text = read(key);
480
+ if (text)
481
+ return text;
482
+ }
483
+ return "";
484
+ };
485
+ // Tool-specific start summaries put the primary operation first.
486
+ if (phase === "start") {
487
+ if (toolName === "exec") {
488
+ const command = first("command", "cmd", "script");
489
+ const cwd = first("workdir", "cwd");
490
+ if (command)
491
+ return cwd ? `${command} (cwd: ${cwd})` : command;
492
+ }
493
+ if (toolName === "read" || toolName === "write" || toolName === "edit" || toolName === "apply_patch") {
494
+ const path = first("path", "file", "filename");
495
+ if (path)
496
+ return path;
497
+ }
498
+ if (toolName === "web_fetch") {
499
+ const url = first("url");
500
+ if (url)
501
+ return url;
502
+ }
503
+ if (toolName === "web_search") {
504
+ const query = first("query");
505
+ if (query)
506
+ return query;
507
+ }
508
+ }
509
+ const primary = phase === "start"
510
+ ? first("command", "path", "url", "query", "action", "target", "sessionKey", "key", "name", "description", "text")
511
+ : first("error", "message", "status", "exitCode", "output", "stdout", "stderr", "text", "result");
512
+ if (primary)
513
+ return primary;
514
+ // Safe generic fallback: omit fields that commonly carry credentials or
515
+ // large payloads, then serialize a few scalar fields only.
516
+ const blocked = /token|secret|password|auth|credential|cookie|buffer|content|data|env/i;
517
+ const parts = [];
518
+ for (const [key, raw] of Object.entries(record)) {
519
+ if (blocked.test(key) || raw === null || raw === undefined || typeof raw === "object")
520
+ continue;
521
+ parts.push(`${key}=${String(raw)}`);
522
+ if (parts.length >= 4)
523
+ break;
524
+ }
525
+ return parts.join(", ");
526
+ }
454
527
  claimToolEvent(sessionKey, eventId, phase, now = Date.now()) {
455
528
  const normalizedPhase = phase === "error" ? "end" : phase;
456
529
  const normalizedId = eventId.replace(/^(?:tool|command):/, "");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openclaw-lark-multi-agent",
3
- "version": "1.4.5",
3
+ "version": "1.4.7",
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": {