@alfe.ai/openclaw-chat 0.9.20 → 0.9.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/index.d.cts CHANGED
@@ -1,2 +1,2 @@
1
- import { A as AlfeResolvedAccount, D as createAlfeChannelPlugin, M as SessionData, N as SessionSummary, O as AlfeChannelConfig, d as _default, j as ChatMessage, k as AlfePluginConfig } from "./plugin.cjs";
1
+ import { A as AlfePluginConfig, M as ChatMessage, N as SessionData, O as createAlfeChannelPlugin, P as SessionSummary, d as _default, j as AlfeResolvedAccount, k as AlfeChannelConfig } from "./plugin.cjs";
2
2
  export { type AlfeChannelConfig, type AlfePluginConfig, type AlfeResolvedAccount, type ChatMessage, type SessionData, type SessionSummary, createAlfeChannelPlugin, _default as default };
package/dist/index.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- import { A as AlfeResolvedAccount, D as createAlfeChannelPlugin, M as SessionData, N as SessionSummary, O as AlfeChannelConfig, d as _default, j as ChatMessage, k as AlfePluginConfig } from "./plugin.js";
1
+ import { A as AlfePluginConfig, M as ChatMessage, N as SessionData, O as createAlfeChannelPlugin, P as SessionSummary, d as _default, j as AlfeResolvedAccount, k as AlfeChannelConfig } from "./plugin.js";
2
2
  export { type AlfeChannelConfig, type AlfePluginConfig, type AlfeResolvedAccount, type ChatMessage, type SessionData, type SessionSummary, createAlfeChannelPlugin, _default as default };
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
- import { _ as plugin_default, w as createAlfeChannelPlugin } from "./plugin2.js";
1
+ import { T as createAlfeChannelPlugin, _ as plugin_default } from "./plugin2.js";
2
2
  export { createAlfeChannelPlugin, plugin_default as default };
package/dist/plugin.cjs CHANGED
@@ -26,3 +26,4 @@ exports.resolveRequestedRunId = require_plugin.resolveRequestedRunId;
26
26
  exports.rewriteAssistantText = require_plugin.rewriteAssistantText;
27
27
  exports.stripLeakedToolSummary = require_plugin.stripLeakedToolSummary;
28
28
  exports.withReplyRunId = require_plugin.withReplyRunId;
29
+ exports.withRunIdSeededChannelRuntime = require_plugin.withRunIdSeededChannelRuntime;
package/dist/plugin.d.cts CHANGED
@@ -1,3 +1,24 @@
1
+ //#region src/runtime-config.d.ts
2
+ /**
3
+ * Read the live OpenClaw config through the plugin runtime.
4
+ *
5
+ * OpenClaw 2026.9.x replaced `runtime.config.loadConfig()` with
6
+ * `runtime.config.current()`; 2026.6.11 exposes only `loadConfig()`. On 9.5
7
+ * `current()` returns the process-wide pinned config object BY REFERENCE (not a
8
+ * copy, not frozen): treat the result as read-only, mutating it corrupts config
9
+ * for every plugin in the process. Both verified on the dists (2026-09-21): on 9.5 the
10
+ * old call throws "runtime.config.loadConfig is not a function" inside the
11
+ * chat request handler, which took chat down on the first 2026.9.5 host. A
12
+ * managed fleet is mixed across a runtime upgrade, so support both shapes.
13
+ *
14
+ * Kept local rather than in `@alfe.ai/openclaw-plugin-kit`: a kit release fans
15
+ * out to every plugin package and its manifest pin.
16
+ */
17
+ interface RuntimeConfigApi {
18
+ current?: () => unknown;
19
+ loadConfig?: () => Record<string, unknown>;
20
+ }
21
+ //#endregion
1
22
  //#region src/session-store.d.ts
2
23
  /**
3
24
  * Session Store — persists chat sessions to the local filesystem.
@@ -396,9 +417,7 @@ interface PluginLogger {
396
417
  debug(msg: string, ...args: unknown[]): void;
397
418
  }
398
419
  interface PluginRuntime {
399
- config: {
400
- loadConfig(): Record<string, unknown>;
401
- };
420
+ config: RuntimeConfigApi;
402
421
  events: {
403
422
  onAgentEvent(listener: (evt: AgentEventPayload) => void): () => void;
404
423
  };
@@ -450,6 +469,22 @@ interface AgentEventPayload {
450
469
  interface RunEventGate {
451
470
  runId: string | null;
452
471
  sessionKey: string | null;
472
+ /**
473
+ * Set to `false` when the gate is PRE-ARMED with a service-owned run id, and
474
+ * flipped to `true` by `withRunIdSeededChannelRuntime` once a dispatch seam
475
+ * has actually accepted that id. Absent when no seed was attempted.
476
+ */
477
+ seedApplied?: boolean;
478
+ /**
479
+ * Set to `true` immediately before the turn enters `dispatchInbound`. Only
480
+ * meaningful alongside `seedApplied`: it separates a seed that is still
481
+ * PENDING (dispatch has not run yet, so the listener is only seeing other
482
+ * runs) from one that has FAILED (dispatch is under way and the run is
483
+ * already emitting under an id no seam ever took). Without it a concurrent
484
+ * cron/heartbeat event arriving between listener subscription and dispatch
485
+ * could steal the gate.
486
+ */
487
+ dispatchStarted?: boolean;
453
488
  }
454
489
  /**
455
490
  * Decide whether an agent-event belongs to THIS turn's run and should be
@@ -492,6 +527,33 @@ declare function admitAgentEvent(gate: RunEventGate, evt: Pick<AgentEventPayload
492
527
  declare function resolveRequestedRunId(idempotencyKey: unknown): string | null;
493
528
  /** Preserve OpenClaw-provided reply options while seeding this exact run id. */
494
529
  declare function withReplyRunId(params: Record<string, unknown>, runId: string): Record<string, unknown>;
530
+ /**
531
+ * Seed this turn's run id on EVERY direct-DM dispatch seam across the
532
+ * supported OpenClaw fleet, returning a shallow per-call clone of
533
+ * `runtime.channel` (never mutating the process-global runtime).
534
+ *
535
+ * `dispatchInboundDirectDmWithRuntime` has no `replyOptions` parameter, so the
536
+ * run id has to be merged into whichever lower seam that helper drives — and
537
+ * which one it drives CHANGED between the versions running on the fleet:
538
+ *
539
+ * - **2026.6.x** calls `channel.reply.dispatchReplyWithBufferedBlockDispatcher`
540
+ * directly with a top-level `replyOptions`
541
+ * (`dist/direct-dm-*.js`: `replyOptions: { onModelSelected }`).
542
+ * - **2026.9.x** builds a turn PLAN (`buildDirectDmTurnPlan`) whose
543
+ * `replyOptions` is `{ onModelSelected, ...turnAdoptionLifecycle? }` and
544
+ * hands it to `channel.inbound.run` via `adapter.resolveTurn`
545
+ * (`dist/channel-inbound-*.mjs`). The reply dispatcher is NEVER called, so
546
+ * wrapping it alone is dead code on 9.5. The plan's `replyOptions.runId`
547
+ * survives `runChannelTurn` → `dispatchRoutedChannelTurn` →
548
+ * `dispatchInboundMessage` and becomes the embedded run's id
549
+ * (`embedded-backend-*.mjs`: `const runId = opts.runId ?? randomUUID()`).
550
+ *
551
+ * Both seams are wrapped when present, so a mixed fleet keeps working and the
552
+ * wrapper for the version that is NOT running is simply never invoked.
553
+ * `onSeedApplied` fires the moment a seam actually takes the id, which is what
554
+ * lets `RunEventGate` tell "seeded" from "silently not seeded".
555
+ */
556
+ declare function withRunIdSeededChannelRuntime<T extends Record<string, unknown>>(channel: T, runId: string, onSeedApplied?: () => void): T;
495
557
  interface PluginServiceContext {
496
558
  config: Record<string, unknown>;
497
559
  workspaceDir?: string;
@@ -770,4 +832,4 @@ declare const _default: {
770
832
  deactivate(api: PluginApi): Promise<void>;
771
833
  };
772
834
  //#endregion
773
- export { AlfeResolvedAccount as A, resolveRequestedRunId as C, createAlfeChannelPlugin as D, withReplyRunId as E, SessionData as M, SessionSummary as N, AlfeChannelConfig as O, resolveAbortTargetKeys as S, stripLeakedToolSummary as T, buildTurnEnvelopeContext as _, MAX_INBOUND_ATTACHMENT_SIZE as a, joinAssistantText as b, __agentTurnQueueForTest as c, _default as d, admitAgentEvent as f, buildToolActivity as g, buildOriginEnvelopeContext as h, CROSS_AGENT_TOOLS_SYSTEM_PROMPT as i, ChatMessage as j, AlfePluginConfig as k, __resetAgentTurnQueueForTest as l, buildAttachmentLocalFilename as m, ActiveRunRef as n, RunEventGate as o, buildA2ACompletePayload as p, AnchorEnv as r, VOICE_REPLY_SYSTEM_PROMPT as s, A2A_REPLY_SYSTEM_PROMPT as t, __setActiveRunForTest as u, computeOpenClawSdkAnchors as v, rewriteAssistantText as w, readAttachmentBodyBounded as x, decideChatServiceStart as y };
835
+ export { AlfePluginConfig as A, resolveRequestedRunId as C, withRunIdSeededChannelRuntime as D, withReplyRunId as E, ChatMessage as M, SessionData as N, createAlfeChannelPlugin as O, SessionSummary as P, resolveAbortTargetKeys as S, stripLeakedToolSummary as T, buildTurnEnvelopeContext as _, MAX_INBOUND_ATTACHMENT_SIZE as a, joinAssistantText as b, __agentTurnQueueForTest as c, _default as d, admitAgentEvent as f, buildToolActivity as g, buildOriginEnvelopeContext as h, CROSS_AGENT_TOOLS_SYSTEM_PROMPT as i, AlfeResolvedAccount as j, AlfeChannelConfig as k, __resetAgentTurnQueueForTest as l, buildAttachmentLocalFilename as m, ActiveRunRef as n, RunEventGate as o, buildA2ACompletePayload as p, AnchorEnv as r, VOICE_REPLY_SYSTEM_PROMPT as s, A2A_REPLY_SYSTEM_PROMPT as t, __setActiveRunForTest as u, computeOpenClawSdkAnchors as v, rewriteAssistantText as w, readAttachmentBodyBounded as x, decideChatServiceStart as y };
package/dist/plugin.d.ts CHANGED
@@ -1,3 +1,24 @@
1
+ //#region src/runtime-config.d.ts
2
+ /**
3
+ * Read the live OpenClaw config through the plugin runtime.
4
+ *
5
+ * OpenClaw 2026.9.x replaced `runtime.config.loadConfig()` with
6
+ * `runtime.config.current()`; 2026.6.11 exposes only `loadConfig()`. On 9.5
7
+ * `current()` returns the process-wide pinned config object BY REFERENCE (not a
8
+ * copy, not frozen): treat the result as read-only, mutating it corrupts config
9
+ * for every plugin in the process. Both verified on the dists (2026-09-21): on 9.5 the
10
+ * old call throws "runtime.config.loadConfig is not a function" inside the
11
+ * chat request handler, which took chat down on the first 2026.9.5 host. A
12
+ * managed fleet is mixed across a runtime upgrade, so support both shapes.
13
+ *
14
+ * Kept local rather than in `@alfe.ai/openclaw-plugin-kit`: a kit release fans
15
+ * out to every plugin package and its manifest pin.
16
+ */
17
+ interface RuntimeConfigApi {
18
+ current?: () => unknown;
19
+ loadConfig?: () => Record<string, unknown>;
20
+ }
21
+ //#endregion
1
22
  //#region src/session-store.d.ts
2
23
  /**
3
24
  * Session Store — persists chat sessions to the local filesystem.
@@ -396,9 +417,7 @@ interface PluginLogger {
396
417
  debug(msg: string, ...args: unknown[]): void;
397
418
  }
398
419
  interface PluginRuntime {
399
- config: {
400
- loadConfig(): Record<string, unknown>;
401
- };
420
+ config: RuntimeConfigApi;
402
421
  events: {
403
422
  onAgentEvent(listener: (evt: AgentEventPayload) => void): () => void;
404
423
  };
@@ -450,6 +469,22 @@ interface AgentEventPayload {
450
469
  interface RunEventGate {
451
470
  runId: string | null;
452
471
  sessionKey: string | null;
472
+ /**
473
+ * Set to `false` when the gate is PRE-ARMED with a service-owned run id, and
474
+ * flipped to `true` by `withRunIdSeededChannelRuntime` once a dispatch seam
475
+ * has actually accepted that id. Absent when no seed was attempted.
476
+ */
477
+ seedApplied?: boolean;
478
+ /**
479
+ * Set to `true` immediately before the turn enters `dispatchInbound`. Only
480
+ * meaningful alongside `seedApplied`: it separates a seed that is still
481
+ * PENDING (dispatch has not run yet, so the listener is only seeing other
482
+ * runs) from one that has FAILED (dispatch is under way and the run is
483
+ * already emitting under an id no seam ever took). Without it a concurrent
484
+ * cron/heartbeat event arriving between listener subscription and dispatch
485
+ * could steal the gate.
486
+ */
487
+ dispatchStarted?: boolean;
453
488
  }
454
489
  /**
455
490
  * Decide whether an agent-event belongs to THIS turn's run and should be
@@ -492,6 +527,33 @@ declare function admitAgentEvent(gate: RunEventGate, evt: Pick<AgentEventPayload
492
527
  declare function resolveRequestedRunId(idempotencyKey: unknown): string | null;
493
528
  /** Preserve OpenClaw-provided reply options while seeding this exact run id. */
494
529
  declare function withReplyRunId(params: Record<string, unknown>, runId: string): Record<string, unknown>;
530
+ /**
531
+ * Seed this turn's run id on EVERY direct-DM dispatch seam across the
532
+ * supported OpenClaw fleet, returning a shallow per-call clone of
533
+ * `runtime.channel` (never mutating the process-global runtime).
534
+ *
535
+ * `dispatchInboundDirectDmWithRuntime` has no `replyOptions` parameter, so the
536
+ * run id has to be merged into whichever lower seam that helper drives — and
537
+ * which one it drives CHANGED between the versions running on the fleet:
538
+ *
539
+ * - **2026.6.x** calls `channel.reply.dispatchReplyWithBufferedBlockDispatcher`
540
+ * directly with a top-level `replyOptions`
541
+ * (`dist/direct-dm-*.js`: `replyOptions: { onModelSelected }`).
542
+ * - **2026.9.x** builds a turn PLAN (`buildDirectDmTurnPlan`) whose
543
+ * `replyOptions` is `{ onModelSelected, ...turnAdoptionLifecycle? }` and
544
+ * hands it to `channel.inbound.run` via `adapter.resolveTurn`
545
+ * (`dist/channel-inbound-*.mjs`). The reply dispatcher is NEVER called, so
546
+ * wrapping it alone is dead code on 9.5. The plan's `replyOptions.runId`
547
+ * survives `runChannelTurn` → `dispatchRoutedChannelTurn` →
548
+ * `dispatchInboundMessage` and becomes the embedded run's id
549
+ * (`embedded-backend-*.mjs`: `const runId = opts.runId ?? randomUUID()`).
550
+ *
551
+ * Both seams are wrapped when present, so a mixed fleet keeps working and the
552
+ * wrapper for the version that is NOT running is simply never invoked.
553
+ * `onSeedApplied` fires the moment a seam actually takes the id, which is what
554
+ * lets `RunEventGate` tell "seeded" from "silently not seeded".
555
+ */
556
+ declare function withRunIdSeededChannelRuntime<T extends Record<string, unknown>>(channel: T, runId: string, onSeedApplied?: () => void): T;
495
557
  interface PluginServiceContext {
496
558
  config: Record<string, unknown>;
497
559
  workspaceDir?: string;
@@ -770,4 +832,4 @@ declare const _default: {
770
832
  deactivate(api: PluginApi): Promise<void>;
771
833
  };
772
834
  //#endregion
773
- export { AlfeResolvedAccount as A, resolveRequestedRunId as C, createAlfeChannelPlugin as D, withReplyRunId as E, SessionData as M, SessionSummary as N, AlfeChannelConfig as O, resolveAbortTargetKeys as S, stripLeakedToolSummary as T, buildTurnEnvelopeContext as _, MAX_INBOUND_ATTACHMENT_SIZE as a, joinAssistantText as b, __agentTurnQueueForTest as c, _default as d, admitAgentEvent as f, buildToolActivity as g, buildOriginEnvelopeContext as h, CROSS_AGENT_TOOLS_SYSTEM_PROMPT as i, ChatMessage as j, AlfePluginConfig as k, __resetAgentTurnQueueForTest as l, buildAttachmentLocalFilename as m, ActiveRunRef as n, RunEventGate as o, buildA2ACompletePayload as p, AnchorEnv as r, VOICE_REPLY_SYSTEM_PROMPT as s, A2A_REPLY_SYSTEM_PROMPT as t, __setActiveRunForTest as u, computeOpenClawSdkAnchors as v, rewriteAssistantText as w, readAttachmentBodyBounded as x, decideChatServiceStart as y };
835
+ export { AlfePluginConfig as A, resolveRequestedRunId as C, withRunIdSeededChannelRuntime as D, withReplyRunId as E, ChatMessage as M, SessionData as N, createAlfeChannelPlugin as O, SessionSummary as P, resolveAbortTargetKeys as S, stripLeakedToolSummary as T, buildTurnEnvelopeContext as _, MAX_INBOUND_ATTACHMENT_SIZE as a, joinAssistantText as b, __agentTurnQueueForTest as c, _default as d, admitAgentEvent as f, buildToolActivity as g, buildOriginEnvelopeContext as h, CROSS_AGENT_TOOLS_SYSTEM_PROMPT as i, AlfeResolvedAccount as j, AlfeChannelConfig as k, __resetAgentTurnQueueForTest as l, buildAttachmentLocalFilename as m, ActiveRunRef as n, RunEventGate as o, buildA2ACompletePayload as p, AnchorEnv as r, VOICE_REPLY_SYSTEM_PROMPT as s, A2A_REPLY_SYSTEM_PROMPT as t, __setActiveRunForTest as u, computeOpenClawSdkAnchors as v, rewriteAssistantText as w, readAttachmentBodyBounded as x, decideChatServiceStart as y };
package/dist/plugin.js CHANGED
@@ -1,2 +1,2 @@
1
- import { C as withReplyRunId, S as stripLeakedToolSummary, _ as plugin_default, a as __agentTurnQueueForTest, b as resolveRequestedRunId, c as admitAgentEvent, d as buildOriginEnvelopeContext, f as buildToolActivity, g as joinAssistantText, h as decideChatServiceStart, i as VOICE_REPLY_SYSTEM_PROMPT, l as buildA2ACompletePayload, m as computeOpenClawSdkAnchors, n as CROSS_AGENT_TOOLS_SYSTEM_PROMPT, o as __resetAgentTurnQueueForTest, p as buildTurnEnvelopeContext, r as MAX_INBOUND_ATTACHMENT_SIZE, s as __setActiveRunForTest, t as A2A_REPLY_SYSTEM_PROMPT, u as buildAttachmentLocalFilename, v as readAttachmentBodyBounded, x as rewriteAssistantText, y as resolveAbortTargetKeys } from "./plugin2.js";
2
- export { A2A_REPLY_SYSTEM_PROMPT, CROSS_AGENT_TOOLS_SYSTEM_PROMPT, MAX_INBOUND_ATTACHMENT_SIZE, VOICE_REPLY_SYSTEM_PROMPT, __agentTurnQueueForTest, __resetAgentTurnQueueForTest, __setActiveRunForTest, admitAgentEvent, buildA2ACompletePayload, buildAttachmentLocalFilename, buildOriginEnvelopeContext, buildToolActivity, buildTurnEnvelopeContext, computeOpenClawSdkAnchors, decideChatServiceStart, plugin_default as default, joinAssistantText, readAttachmentBodyBounded, resolveAbortTargetKeys, resolveRequestedRunId, rewriteAssistantText, stripLeakedToolSummary, withReplyRunId };
1
+ import { C as withReplyRunId, S as stripLeakedToolSummary, _ as plugin_default, a as __agentTurnQueueForTest, b as resolveRequestedRunId, c as admitAgentEvent, d as buildOriginEnvelopeContext, f as buildToolActivity, g as joinAssistantText, h as decideChatServiceStart, i as VOICE_REPLY_SYSTEM_PROMPT, l as buildA2ACompletePayload, m as computeOpenClawSdkAnchors, n as CROSS_AGENT_TOOLS_SYSTEM_PROMPT, o as __resetAgentTurnQueueForTest, p as buildTurnEnvelopeContext, r as MAX_INBOUND_ATTACHMENT_SIZE, s as __setActiveRunForTest, t as A2A_REPLY_SYSTEM_PROMPT, u as buildAttachmentLocalFilename, v as readAttachmentBodyBounded, w as withRunIdSeededChannelRuntime, x as rewriteAssistantText, y as resolveAbortTargetKeys } from "./plugin2.js";
2
+ export { A2A_REPLY_SYSTEM_PROMPT, CROSS_AGENT_TOOLS_SYSTEM_PROMPT, MAX_INBOUND_ATTACHMENT_SIZE, VOICE_REPLY_SYSTEM_PROMPT, __agentTurnQueueForTest, __resetAgentTurnQueueForTest, __setActiveRunForTest, admitAgentEvent, buildA2ACompletePayload, buildAttachmentLocalFilename, buildOriginEnvelopeContext, buildToolActivity, buildTurnEnvelopeContext, computeOpenClawSdkAnchors, decideChatServiceStart, plugin_default as default, joinAssistantText, readAttachmentBodyBounded, resolveAbortTargetKeys, resolveRequestedRunId, rewriteAssistantText, stripLeakedToolSummary, withReplyRunId, withRunIdSeededChannelRuntime };
package/dist/plugin2.cjs CHANGED
@@ -10,6 +10,13 @@ let _alfe_ai_chat = require("@alfe.ai/chat");
10
10
  let _alfe_ai_agent_api_client = require("@alfe.ai/agent-api-client");
11
11
  let _alfe_ai_config = require("@alfe.ai/config");
12
12
  let _alfe_ai_openclaw_plugin_kit = require("@alfe.ai/openclaw-plugin-kit");
13
+ //#region src/runtime-config.ts
14
+ function readRuntimeConfig(config) {
15
+ if (typeof config?.current === "function") return config.current() ?? {};
16
+ if (typeof config?.loadConfig === "function") return config.loadConfig();
17
+ throw new Error("OpenClaw plugin runtime exposes neither config.current() nor config.loadConfig()");
18
+ }
19
+ //#endregion
13
20
  //#region src/outbound-media.ts
14
21
  /**
15
22
  * Outbound media upload — agent → user.
@@ -2298,8 +2305,10 @@ function getAgentApiClient(apiKeyOverride) {
2298
2305
  */
2299
2306
  function admitAgentEvent(gate, evt) {
2300
2307
  if (!(evt.stream === "assistant" || evt.stream === "thinking" || evt.stream === "tool")) return false;
2301
- if (gate.runId === null) {
2308
+ const seedFailed = gate.seedApplied === false && gate.dispatchStarted === true;
2309
+ if (gate.runId === null || seedFailed) {
2302
2310
  gate.runId = evt.runId;
2311
+ gate.seedApplied = true;
2303
2312
  if (evt.sessionKey) gate.sessionKey ??= evt.sessionKey;
2304
2313
  }
2305
2314
  if (evt.runId !== gate.runId) return false;
@@ -2325,6 +2334,88 @@ function withReplyRunId(params, runId) {
2325
2334
  }
2326
2335
  };
2327
2336
  }
2337
+ /**
2338
+ * True for a spreadable record. Arrays and `null` are excluded so a seam that
2339
+ * hands back a list or a nullish plan is passed through untouched instead of
2340
+ * being silently turned into an object.
2341
+ */
2342
+ function isPlainObject(value) {
2343
+ return typeof value === "object" && value !== null && !Array.isArray(value);
2344
+ }
2345
+ /**
2346
+ * Seed this turn's run id on EVERY direct-DM dispatch seam across the
2347
+ * supported OpenClaw fleet, returning a shallow per-call clone of
2348
+ * `runtime.channel` (never mutating the process-global runtime).
2349
+ *
2350
+ * `dispatchInboundDirectDmWithRuntime` has no `replyOptions` parameter, so the
2351
+ * run id has to be merged into whichever lower seam that helper drives — and
2352
+ * which one it drives CHANGED between the versions running on the fleet:
2353
+ *
2354
+ * - **2026.6.x** calls `channel.reply.dispatchReplyWithBufferedBlockDispatcher`
2355
+ * directly with a top-level `replyOptions`
2356
+ * (`dist/direct-dm-*.js`: `replyOptions: { onModelSelected }`).
2357
+ * - **2026.9.x** builds a turn PLAN (`buildDirectDmTurnPlan`) whose
2358
+ * `replyOptions` is `{ onModelSelected, ...turnAdoptionLifecycle? }` and
2359
+ * hands it to `channel.inbound.run` via `adapter.resolveTurn`
2360
+ * (`dist/channel-inbound-*.mjs`). The reply dispatcher is NEVER called, so
2361
+ * wrapping it alone is dead code on 9.5. The plan's `replyOptions.runId`
2362
+ * survives `runChannelTurn` → `dispatchRoutedChannelTurn` →
2363
+ * `dispatchInboundMessage` and becomes the embedded run's id
2364
+ * (`embedded-backend-*.mjs`: `const runId = opts.runId ?? randomUUID()`).
2365
+ *
2366
+ * Both seams are wrapped when present, so a mixed fleet keeps working and the
2367
+ * wrapper for the version that is NOT running is simply never invoked.
2368
+ * `onSeedApplied` fires the moment a seam actually takes the id, which is what
2369
+ * lets `RunEventGate` tell "seeded" from "silently not seeded".
2370
+ */
2371
+ function withRunIdSeededChannelRuntime(channel, runId, onSeedApplied) {
2372
+ const seeded = { ...channel };
2373
+ const seedPlan = (plan) => {
2374
+ if (!isPlainObject(plan)) return plan;
2375
+ onSeedApplied?.();
2376
+ return withReplyRunId(plan, runId);
2377
+ };
2378
+ const reply = isPlainObject(channel.reply) ? channel.reply : void 0;
2379
+ const lowerDispatch = reply?.dispatchReplyWithBufferedBlockDispatcher;
2380
+ if (reply && typeof lowerDispatch === "function") {
2381
+ const call = lowerDispatch;
2382
+ seeded.reply = {
2383
+ ...reply,
2384
+ dispatchReplyWithBufferedBlockDispatcher: (params) => call.call(reply, seedPlan(params))
2385
+ };
2386
+ }
2387
+ const inbound = isPlainObject(channel.inbound) ? channel.inbound : void 0;
2388
+ const inboundRun = inbound?.run;
2389
+ const inboundDispatch = inbound?.dispatch;
2390
+ if (inbound && (typeof inboundRun === "function" || typeof inboundDispatch === "function")) {
2391
+ const wrapped = { ...inbound };
2392
+ if (typeof inboundRun === "function") {
2393
+ const call = inboundRun;
2394
+ wrapped.run = (params) => {
2395
+ const adapter = isPlainObject(params.adapter) ? params.adapter : void 0;
2396
+ const resolveTurn = adapter?.resolveTurn;
2397
+ if (!adapter || typeof resolveTurn !== "function") return call.call(inbound, params);
2398
+ const resolve = resolveTurn;
2399
+ return call.call(inbound, {
2400
+ ...params,
2401
+ adapter: {
2402
+ ...adapter,
2403
+ resolveTurn: (...args) => {
2404
+ const plan = resolve.apply(adapter, args);
2405
+ return plan instanceof Promise ? plan.then(seedPlan) : seedPlan(plan);
2406
+ }
2407
+ }
2408
+ });
2409
+ };
2410
+ }
2411
+ if (typeof inboundDispatch === "function") {
2412
+ const call = inboundDispatch;
2413
+ wrapped.dispatch = (params) => call.call(inbound, seedPlan(params));
2414
+ }
2415
+ seeded.inbound = wrapped;
2416
+ }
2417
+ return seeded;
2418
+ }
2328
2419
  function asString(v) {
2329
2420
  return typeof v === "string" && v.length > 0 ? v : void 0;
2330
2421
  }
@@ -3083,14 +3174,18 @@ async function handleAgentRequest(request, log) {
3083
3174
  return;
3084
3175
  }
3085
3176
  const senderId = userId ?? "anon";
3086
- const cfg = runtime.config.loadConfig();
3177
+ const cfg = readRuntimeConfig(runtime.config);
3087
3178
  const sessionId = conversationId ?? legacySessionKey;
3088
3179
  if (!await getSession(sessionId)) await createSession(sessionId, "", "alfe", tenantId, userId);
3089
3180
  await addMessage(sessionId, "user", message, userId ?? senderId, displayName ?? senderId, void 0, userAttachmentsToStored(rawAttachments));
3090
3181
  const requestedRunId = resolveRequestedRunId(idempotencyKey);
3091
3182
  const runGate = {
3092
3183
  runId: requestedRunId,
3093
- sessionKey: null
3184
+ sessionKey: null,
3185
+ ...requestedRunId ? {
3186
+ seedApplied: false,
3187
+ dispatchStarted: false
3188
+ } : {}
3094
3189
  };
3095
3190
  let lastThinkingLen = 0;
3096
3191
  let sawStructuredThinking = false;
@@ -3277,13 +3372,10 @@ async function handleAgentRequest(request, log) {
3277
3372
  routeSessionKey,
3278
3373
  ...requestedRunId ? { runId: requestedRunId } : {}
3279
3374
  };
3280
- const dispatchChannel = requestedRunId ? {
3281
- ...runtime.channel,
3282
- reply: {
3283
- ...runtime.channel.reply,
3284
- dispatchReplyWithBufferedBlockDispatcher: (params) => runtime.channel.reply.dispatchReplyWithBufferedBlockDispatcher(withReplyRunId(params, requestedRunId))
3285
- }
3286
- } : runtime.channel;
3375
+ const dispatchChannel = requestedRunId ? withRunIdSeededChannelRuntime(runtime.channel, requestedRunId, () => {
3376
+ runGate.seedApplied = true;
3377
+ }) : runtime.channel;
3378
+ runGate.dispatchStarted = true;
3287
3379
  const result = await dispatchInbound({
3288
3380
  cfg,
3289
3381
  runtime: { channel: dispatchChannel },
@@ -3857,3 +3949,9 @@ Object.defineProperty(exports, "withReplyRunId", {
3857
3949
  return withReplyRunId;
3858
3950
  }
3859
3951
  });
3952
+ Object.defineProperty(exports, "withRunIdSeededChannelRuntime", {
3953
+ enumerable: true,
3954
+ get: function() {
3955
+ return withRunIdSeededChannelRuntime;
3956
+ }
3957
+ });
@@ -1,2 +1,2 @@
1
- import { C as resolveRequestedRunId, E as withReplyRunId, S as resolveAbortTargetKeys, T as stripLeakedToolSummary, _ as buildTurnEnvelopeContext, a as MAX_INBOUND_ATTACHMENT_SIZE, b as joinAssistantText, c as __agentTurnQueueForTest, d as _default, f as admitAgentEvent, g as buildToolActivity, h as buildOriginEnvelopeContext, i as CROSS_AGENT_TOOLS_SYSTEM_PROMPT, l as __resetAgentTurnQueueForTest, m as buildAttachmentLocalFilename, n as ActiveRunRef, o as RunEventGate, p as buildA2ACompletePayload, r as AnchorEnv, s as VOICE_REPLY_SYSTEM_PROMPT, t as A2A_REPLY_SYSTEM_PROMPT, u as __setActiveRunForTest, v as computeOpenClawSdkAnchors, w as rewriteAssistantText, x as readAttachmentBodyBounded, y as decideChatServiceStart } from "./plugin.cjs";
2
- export { A2A_REPLY_SYSTEM_PROMPT, ActiveRunRef, AnchorEnv, CROSS_AGENT_TOOLS_SYSTEM_PROMPT, MAX_INBOUND_ATTACHMENT_SIZE, RunEventGate, VOICE_REPLY_SYSTEM_PROMPT, __agentTurnQueueForTest, __resetAgentTurnQueueForTest, __setActiveRunForTest, admitAgentEvent, buildA2ACompletePayload, buildAttachmentLocalFilename, buildOriginEnvelopeContext, buildToolActivity, buildTurnEnvelopeContext, computeOpenClawSdkAnchors, decideChatServiceStart, _default as default, joinAssistantText, readAttachmentBodyBounded, resolveAbortTargetKeys, resolveRequestedRunId, rewriteAssistantText, stripLeakedToolSummary, withReplyRunId };
1
+ import { C as resolveRequestedRunId, D as withRunIdSeededChannelRuntime, E as withReplyRunId, S as resolveAbortTargetKeys, T as stripLeakedToolSummary, _ as buildTurnEnvelopeContext, a as MAX_INBOUND_ATTACHMENT_SIZE, b as joinAssistantText, c as __agentTurnQueueForTest, d as _default, f as admitAgentEvent, g as buildToolActivity, h as buildOriginEnvelopeContext, i as CROSS_AGENT_TOOLS_SYSTEM_PROMPT, l as __resetAgentTurnQueueForTest, m as buildAttachmentLocalFilename, n as ActiveRunRef, o as RunEventGate, p as buildA2ACompletePayload, r as AnchorEnv, s as VOICE_REPLY_SYSTEM_PROMPT, t as A2A_REPLY_SYSTEM_PROMPT, u as __setActiveRunForTest, v as computeOpenClawSdkAnchors, w as rewriteAssistantText, x as readAttachmentBodyBounded, y as decideChatServiceStart } from "./plugin.cjs";
2
+ export { A2A_REPLY_SYSTEM_PROMPT, ActiveRunRef, AnchorEnv, CROSS_AGENT_TOOLS_SYSTEM_PROMPT, MAX_INBOUND_ATTACHMENT_SIZE, RunEventGate, VOICE_REPLY_SYSTEM_PROMPT, __agentTurnQueueForTest, __resetAgentTurnQueueForTest, __setActiveRunForTest, admitAgentEvent, buildA2ACompletePayload, buildAttachmentLocalFilename, buildOriginEnvelopeContext, buildToolActivity, buildTurnEnvelopeContext, computeOpenClawSdkAnchors, decideChatServiceStart, _default as default, joinAssistantText, readAttachmentBodyBounded, resolveAbortTargetKeys, resolveRequestedRunId, rewriteAssistantText, stripLeakedToolSummary, withReplyRunId, withRunIdSeededChannelRuntime };
package/dist/plugin2.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- import { C as resolveRequestedRunId, E as withReplyRunId, S as resolveAbortTargetKeys, T as stripLeakedToolSummary, _ as buildTurnEnvelopeContext, a as MAX_INBOUND_ATTACHMENT_SIZE, b as joinAssistantText, c as __agentTurnQueueForTest, d as _default, f as admitAgentEvent, g as buildToolActivity, h as buildOriginEnvelopeContext, i as CROSS_AGENT_TOOLS_SYSTEM_PROMPT, l as __resetAgentTurnQueueForTest, m as buildAttachmentLocalFilename, n as ActiveRunRef, o as RunEventGate, p as buildA2ACompletePayload, r as AnchorEnv, s as VOICE_REPLY_SYSTEM_PROMPT, t as A2A_REPLY_SYSTEM_PROMPT, u as __setActiveRunForTest, v as computeOpenClawSdkAnchors, w as rewriteAssistantText, x as readAttachmentBodyBounded, y as decideChatServiceStart } from "./plugin.js";
2
- export { A2A_REPLY_SYSTEM_PROMPT, ActiveRunRef, AnchorEnv, CROSS_AGENT_TOOLS_SYSTEM_PROMPT, MAX_INBOUND_ATTACHMENT_SIZE, RunEventGate, VOICE_REPLY_SYSTEM_PROMPT, __agentTurnQueueForTest, __resetAgentTurnQueueForTest, __setActiveRunForTest, admitAgentEvent, buildA2ACompletePayload, buildAttachmentLocalFilename, buildOriginEnvelopeContext, buildToolActivity, buildTurnEnvelopeContext, computeOpenClawSdkAnchors, decideChatServiceStart, _default as default, joinAssistantText, readAttachmentBodyBounded, resolveAbortTargetKeys, resolveRequestedRunId, rewriteAssistantText, stripLeakedToolSummary, withReplyRunId };
1
+ import { C as resolveRequestedRunId, D as withRunIdSeededChannelRuntime, E as withReplyRunId, S as resolveAbortTargetKeys, T as stripLeakedToolSummary, _ as buildTurnEnvelopeContext, a as MAX_INBOUND_ATTACHMENT_SIZE, b as joinAssistantText, c as __agentTurnQueueForTest, d as _default, f as admitAgentEvent, g as buildToolActivity, h as buildOriginEnvelopeContext, i as CROSS_AGENT_TOOLS_SYSTEM_PROMPT, l as __resetAgentTurnQueueForTest, m as buildAttachmentLocalFilename, n as ActiveRunRef, o as RunEventGate, p as buildA2ACompletePayload, r as AnchorEnv, s as VOICE_REPLY_SYSTEM_PROMPT, t as A2A_REPLY_SYSTEM_PROMPT, u as __setActiveRunForTest, v as computeOpenClawSdkAnchors, w as rewriteAssistantText, x as readAttachmentBodyBounded, y as decideChatServiceStart } from "./plugin.js";
2
+ export { A2A_REPLY_SYSTEM_PROMPT, ActiveRunRef, AnchorEnv, CROSS_AGENT_TOOLS_SYSTEM_PROMPT, MAX_INBOUND_ATTACHMENT_SIZE, RunEventGate, VOICE_REPLY_SYSTEM_PROMPT, __agentTurnQueueForTest, __resetAgentTurnQueueForTest, __setActiveRunForTest, admitAgentEvent, buildA2ACompletePayload, buildAttachmentLocalFilename, buildOriginEnvelopeContext, buildToolActivity, buildTurnEnvelopeContext, computeOpenClawSdkAnchors, decideChatServiceStart, _default as default, joinAssistantText, readAttachmentBodyBounded, resolveAbortTargetKeys, resolveRequestedRunId, rewriteAssistantText, stripLeakedToolSummary, withReplyRunId, withRunIdSeededChannelRuntime };
package/dist/plugin2.js CHANGED
@@ -10,6 +10,13 @@ import { ChatServiceClient, resolveAlfeChat } from "@alfe.ai/chat";
10
10
  import { AgentApiClient, installToolErrorCapture } from "@alfe.ai/agent-api-client";
11
11
  import { getEndpointFromToken, resolveConfig } from "@alfe.ai/config";
12
12
  import { defineTool, getActivationKey, publicToolError, resetActivation } from "@alfe.ai/openclaw-plugin-kit";
13
+ //#region src/runtime-config.ts
14
+ function readRuntimeConfig(config) {
15
+ if (typeof config?.current === "function") return config.current() ?? {};
16
+ if (typeof config?.loadConfig === "function") return config.loadConfig();
17
+ throw new Error("OpenClaw plugin runtime exposes neither config.current() nor config.loadConfig()");
18
+ }
19
+ //#endregion
13
20
  //#region src/outbound-media.ts
14
21
  /**
15
22
  * Outbound media upload — agent → user.
@@ -2298,8 +2305,10 @@ function getAgentApiClient(apiKeyOverride) {
2298
2305
  */
2299
2306
  function admitAgentEvent(gate, evt) {
2300
2307
  if (!(evt.stream === "assistant" || evt.stream === "thinking" || evt.stream === "tool")) return false;
2301
- if (gate.runId === null) {
2308
+ const seedFailed = gate.seedApplied === false && gate.dispatchStarted === true;
2309
+ if (gate.runId === null || seedFailed) {
2302
2310
  gate.runId = evt.runId;
2311
+ gate.seedApplied = true;
2303
2312
  if (evt.sessionKey) gate.sessionKey ??= evt.sessionKey;
2304
2313
  }
2305
2314
  if (evt.runId !== gate.runId) return false;
@@ -2325,6 +2334,88 @@ function withReplyRunId(params, runId) {
2325
2334
  }
2326
2335
  };
2327
2336
  }
2337
+ /**
2338
+ * True for a spreadable record. Arrays and `null` are excluded so a seam that
2339
+ * hands back a list or a nullish plan is passed through untouched instead of
2340
+ * being silently turned into an object.
2341
+ */
2342
+ function isPlainObject(value) {
2343
+ return typeof value === "object" && value !== null && !Array.isArray(value);
2344
+ }
2345
+ /**
2346
+ * Seed this turn's run id on EVERY direct-DM dispatch seam across the
2347
+ * supported OpenClaw fleet, returning a shallow per-call clone of
2348
+ * `runtime.channel` (never mutating the process-global runtime).
2349
+ *
2350
+ * `dispatchInboundDirectDmWithRuntime` has no `replyOptions` parameter, so the
2351
+ * run id has to be merged into whichever lower seam that helper drives — and
2352
+ * which one it drives CHANGED between the versions running on the fleet:
2353
+ *
2354
+ * - **2026.6.x** calls `channel.reply.dispatchReplyWithBufferedBlockDispatcher`
2355
+ * directly with a top-level `replyOptions`
2356
+ * (`dist/direct-dm-*.js`: `replyOptions: { onModelSelected }`).
2357
+ * - **2026.9.x** builds a turn PLAN (`buildDirectDmTurnPlan`) whose
2358
+ * `replyOptions` is `{ onModelSelected, ...turnAdoptionLifecycle? }` and
2359
+ * hands it to `channel.inbound.run` via `adapter.resolveTurn`
2360
+ * (`dist/channel-inbound-*.mjs`). The reply dispatcher is NEVER called, so
2361
+ * wrapping it alone is dead code on 9.5. The plan's `replyOptions.runId`
2362
+ * survives `runChannelTurn` → `dispatchRoutedChannelTurn` →
2363
+ * `dispatchInboundMessage` and becomes the embedded run's id
2364
+ * (`embedded-backend-*.mjs`: `const runId = opts.runId ?? randomUUID()`).
2365
+ *
2366
+ * Both seams are wrapped when present, so a mixed fleet keeps working and the
2367
+ * wrapper for the version that is NOT running is simply never invoked.
2368
+ * `onSeedApplied` fires the moment a seam actually takes the id, which is what
2369
+ * lets `RunEventGate` tell "seeded" from "silently not seeded".
2370
+ */
2371
+ function withRunIdSeededChannelRuntime(channel, runId, onSeedApplied) {
2372
+ const seeded = { ...channel };
2373
+ const seedPlan = (plan) => {
2374
+ if (!isPlainObject(plan)) return plan;
2375
+ onSeedApplied?.();
2376
+ return withReplyRunId(plan, runId);
2377
+ };
2378
+ const reply = isPlainObject(channel.reply) ? channel.reply : void 0;
2379
+ const lowerDispatch = reply?.dispatchReplyWithBufferedBlockDispatcher;
2380
+ if (reply && typeof lowerDispatch === "function") {
2381
+ const call = lowerDispatch;
2382
+ seeded.reply = {
2383
+ ...reply,
2384
+ dispatchReplyWithBufferedBlockDispatcher: (params) => call.call(reply, seedPlan(params))
2385
+ };
2386
+ }
2387
+ const inbound = isPlainObject(channel.inbound) ? channel.inbound : void 0;
2388
+ const inboundRun = inbound?.run;
2389
+ const inboundDispatch = inbound?.dispatch;
2390
+ if (inbound && (typeof inboundRun === "function" || typeof inboundDispatch === "function")) {
2391
+ const wrapped = { ...inbound };
2392
+ if (typeof inboundRun === "function") {
2393
+ const call = inboundRun;
2394
+ wrapped.run = (params) => {
2395
+ const adapter = isPlainObject(params.adapter) ? params.adapter : void 0;
2396
+ const resolveTurn = adapter?.resolveTurn;
2397
+ if (!adapter || typeof resolveTurn !== "function") return call.call(inbound, params);
2398
+ const resolve = resolveTurn;
2399
+ return call.call(inbound, {
2400
+ ...params,
2401
+ adapter: {
2402
+ ...adapter,
2403
+ resolveTurn: (...args) => {
2404
+ const plan = resolve.apply(adapter, args);
2405
+ return plan instanceof Promise ? plan.then(seedPlan) : seedPlan(plan);
2406
+ }
2407
+ }
2408
+ });
2409
+ };
2410
+ }
2411
+ if (typeof inboundDispatch === "function") {
2412
+ const call = inboundDispatch;
2413
+ wrapped.dispatch = (params) => call.call(inbound, seedPlan(params));
2414
+ }
2415
+ seeded.inbound = wrapped;
2416
+ }
2417
+ return seeded;
2418
+ }
2328
2419
  function asString(v) {
2329
2420
  return typeof v === "string" && v.length > 0 ? v : void 0;
2330
2421
  }
@@ -3083,14 +3174,18 @@ async function handleAgentRequest(request, log) {
3083
3174
  return;
3084
3175
  }
3085
3176
  const senderId = userId ?? "anon";
3086
- const cfg = runtime.config.loadConfig();
3177
+ const cfg = readRuntimeConfig(runtime.config);
3087
3178
  const sessionId = conversationId ?? legacySessionKey;
3088
3179
  if (!await getSession(sessionId)) await createSession(sessionId, "", "alfe", tenantId, userId);
3089
3180
  await addMessage(sessionId, "user", message, userId ?? senderId, displayName ?? senderId, void 0, userAttachmentsToStored(rawAttachments));
3090
3181
  const requestedRunId = resolveRequestedRunId(idempotencyKey);
3091
3182
  const runGate = {
3092
3183
  runId: requestedRunId,
3093
- sessionKey: null
3184
+ sessionKey: null,
3185
+ ...requestedRunId ? {
3186
+ seedApplied: false,
3187
+ dispatchStarted: false
3188
+ } : {}
3094
3189
  };
3095
3190
  let lastThinkingLen = 0;
3096
3191
  let sawStructuredThinking = false;
@@ -3277,13 +3372,10 @@ async function handleAgentRequest(request, log) {
3277
3372
  routeSessionKey,
3278
3373
  ...requestedRunId ? { runId: requestedRunId } : {}
3279
3374
  };
3280
- const dispatchChannel = requestedRunId ? {
3281
- ...runtime.channel,
3282
- reply: {
3283
- ...runtime.channel.reply,
3284
- dispatchReplyWithBufferedBlockDispatcher: (params) => runtime.channel.reply.dispatchReplyWithBufferedBlockDispatcher(withReplyRunId(params, requestedRunId))
3285
- }
3286
- } : runtime.channel;
3375
+ const dispatchChannel = requestedRunId ? withRunIdSeededChannelRuntime(runtime.channel, requestedRunId, () => {
3376
+ runGate.seedApplied = true;
3377
+ }) : runtime.channel;
3378
+ runGate.dispatchStarted = true;
3287
3379
  const result = await dispatchInbound({
3288
3380
  cfg,
3289
3381
  runtime: { channel: dispatchChannel },
@@ -3713,4 +3805,4 @@ var plugin_default = {
3713
3805
  register: plugin.activate.bind(plugin)
3714
3806
  };
3715
3807
  //#endregion
3716
- export { withReplyRunId as C, stripLeakedToolSummary as S, plugin_default as _, __agentTurnQueueForTest as a, resolveRequestedRunId as b, admitAgentEvent as c, buildOriginEnvelopeContext as d, buildToolActivity as f, joinAssistantText as g, decideChatServiceStart as h, VOICE_REPLY_SYSTEM_PROMPT as i, buildA2ACompletePayload as l, computeOpenClawSdkAnchors as m, CROSS_AGENT_TOOLS_SYSTEM_PROMPT as n, __resetAgentTurnQueueForTest as o, buildTurnEnvelopeContext as p, MAX_INBOUND_ATTACHMENT_SIZE as r, __setActiveRunForTest as s, A2A_REPLY_SYSTEM_PROMPT as t, buildAttachmentLocalFilename as u, readAttachmentBodyBounded as v, createAlfeChannelPlugin as w, rewriteAssistantText as x, resolveAbortTargetKeys as y };
3808
+ export { withReplyRunId as C, stripLeakedToolSummary as S, createAlfeChannelPlugin as T, plugin_default as _, __agentTurnQueueForTest as a, resolveRequestedRunId as b, admitAgentEvent as c, buildOriginEnvelopeContext as d, buildToolActivity as f, joinAssistantText as g, decideChatServiceStart as h, VOICE_REPLY_SYSTEM_PROMPT as i, buildA2ACompletePayload as l, computeOpenClawSdkAnchors as m, CROSS_AGENT_TOOLS_SYSTEM_PROMPT as n, __resetAgentTurnQueueForTest as o, buildTurnEnvelopeContext as p, MAX_INBOUND_ATTACHMENT_SIZE as r, __setActiveRunForTest as s, A2A_REPLY_SYSTEM_PROMPT as t, buildAttachmentLocalFilename as u, readAttachmentBodyBounded as v, withRunIdSeededChannelRuntime as w, rewriteAssistantText as x, resolveAbortTargetKeys as y };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alfe.ai/openclaw-chat",
3
- "version": "0.9.20",
3
+ "version": "0.9.21",
4
4
  "description": "OpenClaw chat plugin for Alfe — web widget and mobile app channels",
5
5
  "type": "module",
6
6
  "main": "./dist/plugin.js",
@@ -27,7 +27,7 @@
27
27
  "openclaw.plugin.json"
28
28
  ],
29
29
  "dependencies": {
30
- "@alfe.ai/agent-api-client": "^0.19.1",
30
+ "@alfe.ai/agent-api-client": "^0.19.2",
31
31
  "@alfe.ai/chat": "^0.0.19",
32
32
  "@alfe.ai/config": "^0.4.1",
33
33
  "@alfe.ai/openclaw-plugin-kit": "0.2.0"