@alfe.ai/openclaw-chat 0.9.9 → 0.9.10

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
@@ -4,19 +4,27 @@ OpenClaw chat plugin for Alfe — web widget and mobile app channels
4
4
 
5
5
  ## What It Does
6
6
 
7
- OpenClaw plugin that registers the `alfe-chat` channel with the OpenClaw agent runtime. Follows the standard OpenClaw plugin pattern:
7
+ OpenClaw plugin that registers the `alfe` channel with the OpenClaw agent runtime. It:
8
8
 
9
- - Exports a plugin object with `id`, `name`, `activate`, `deactivate`
10
- - Connects to the Alfe daemon IPC for capability registration
11
- - Gracefully degrades if the daemon is unavailable
9
+ - connects to Alfe's chat relay over an authenticated WebSocket;
10
+ - dispatches web, mobile, voice, and agent-to-agent turns through OpenClaw;
11
+ - streams tool/thinking activity and persists bounded local history;
12
+ - uploads agent-produced media through the attachment presign flow; and
13
+ - exposes `list_agents`, `message_agent`, `end_conversation`, and
14
+ `chat_present_components` to the model.
12
15
 
13
16
  ## Key Files
14
17
 
15
18
  ```
16
19
  src/
17
- ├── plugin.ts # Plugin entry point (activate/deactivate lifecycle)
18
- ├── index.ts # Public re-exports
19
- └── types.ts # Plugin-specific type definitions
20
+ ├── plugin.ts # Activation, relay RPC, dispatch, inbound attachments
21
+ ├── alfe-channel.ts # Channel config and outbound delivery
22
+ ├── a2a-tools.ts # Cross-agent and interactive-component tools
23
+ ├── session-store.ts # Atomic local conversation persistence
24
+ ├── transcript-activity.ts # Bounded OpenClaw transcript replay
25
+ ├── outbound-media.ts # Bounded local-media upload
26
+ ├── index.ts # Public re-exports
27
+ └── types.ts # Plugin-specific type definitions
20
28
  ```
21
29
 
22
30
  ## Development
@@ -24,6 +32,8 @@ src/
24
32
  ```bash
25
33
  pnpm install
26
34
  pnpm --filter @alfe.ai/openclaw-chat build
35
+ pnpm --filter @alfe.ai/openclaw-chat typecheck
36
+ pnpm --filter @alfe.ai/openclaw-chat lint
27
37
  ```
28
38
 
29
39
  ## Testing
@@ -32,10 +42,9 @@ pnpm --filter @alfe.ai/openclaw-chat build
32
42
  pnpm --filter @alfe.ai/openclaw-chat test
33
43
  ```
34
44
 
35
- ## Dependencies
36
-
37
- - **@alfe.ai/openclaw** OpenClaw runtime plugin API
38
- - **openclaw** — OpenClaw runtime plugin API
45
+ `openclaw` is an optional peer dependency supplied by the host runtime. Alfe
46
+ API calls use `@alfe.ai/agent-api-client`; runtime configuration comes from
47
+ `@alfe.ai/config`; the relay transport is `@alfe.ai/chat`.
39
48
 
40
49
  ## Links
41
50
 
package/dist/index.d.cts CHANGED
@@ -1,2 +1,2 @@
1
- import { C as AlfeResolvedAccount, E as SessionSummary, S as AlfePluginConfig, T as SessionData, b as createAlfeChannelPlugin, g as plugin, w as ChatMessage, x as AlfeChannelConfig } from "./plugin.cjs";
1
+ import { A as SessionData, D as AlfePluginConfig, E as AlfeChannelConfig, O as AlfeResolvedAccount, T as createAlfeChannelPlugin, b as plugin, j as SessionSummary, k as ChatMessage } from "./plugin.cjs";
2
2
  export { type AlfeChannelConfig, type AlfePluginConfig, type AlfeResolvedAccount, type ChatMessage, type SessionData, type SessionSummary, createAlfeChannelPlugin, plugin as default };
package/dist/index.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- import { C as AlfeResolvedAccount, E as SessionSummary, S as AlfePluginConfig, T as SessionData, b as createAlfeChannelPlugin, g as plugin, w as ChatMessage, x as AlfeChannelConfig } from "./plugin.js";
1
+ import { A as SessionData, D as AlfePluginConfig, E as AlfeChannelConfig, O as AlfeResolvedAccount, T as createAlfeChannelPlugin, b as plugin, j as SessionSummary, k as ChatMessage } from "./plugin.js";
2
2
  export { type AlfeChannelConfig, type AlfePluginConfig, type AlfeResolvedAccount, type ChatMessage, type SessionData, type SessionSummary, createAlfeChannelPlugin, plugin as default };
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
- import { _ as createAlfeChannelPlugin, p as plugin } from "./plugin2.js";
1
+ import { S as createAlfeChannelPlugin, _ as plugin } from "./plugin2.js";
2
2
  export { createAlfeChannelPlugin, plugin as default };
package/dist/plugin.cjs CHANGED
@@ -3,19 +3,24 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  const require_plugin = require("./plugin2.cjs");
6
+ exports.A2A_REPLY_SYSTEM_PROMPT = require_plugin.A2A_REPLY_SYSTEM_PROMPT;
6
7
  exports.CROSS_AGENT_TOOLS_SYSTEM_PROMPT = require_plugin.CROSS_AGENT_TOOLS_SYSTEM_PROMPT;
8
+ exports.MAX_INBOUND_ATTACHMENT_SIZE = require_plugin.MAX_INBOUND_ATTACHMENT_SIZE;
7
9
  exports.VOICE_REPLY_SYSTEM_PROMPT = require_plugin.VOICE_REPLY_SYSTEM_PROMPT;
8
10
  exports.__agentTurnQueueForTest = require_plugin.__agentTurnQueueForTest;
9
11
  exports.__resetAgentTurnQueueForTest = require_plugin.__resetAgentTurnQueueForTest;
10
12
  exports.__setActiveRunForTest = require_plugin.__setActiveRunForTest;
11
13
  exports.admitAgentEvent = require_plugin.admitAgentEvent;
12
14
  exports.buildA2ACompletePayload = require_plugin.buildA2ACompletePayload;
15
+ exports.buildAttachmentLocalFilename = require_plugin.buildAttachmentLocalFilename;
13
16
  exports.buildOriginEnvelopeContext = require_plugin.buildOriginEnvelopeContext;
14
17
  exports.buildToolActivity = require_plugin.buildToolActivity;
18
+ exports.buildTurnEnvelopeContext = require_plugin.buildTurnEnvelopeContext;
15
19
  exports.computeOpenClawSdkAnchors = require_plugin.computeOpenClawSdkAnchors;
16
20
  exports.decideChatServiceStart = require_plugin.decideChatServiceStart;
17
21
  exports.default = require_plugin.plugin;
18
22
  exports.joinAssistantText = require_plugin.joinAssistantText;
23
+ exports.readAttachmentBodyBounded = require_plugin.readAttachmentBodyBounded;
19
24
  exports.resolveAbortTargetKeys = require_plugin.resolveAbortTargetKeys;
20
25
  exports.rewriteAssistantText = require_plugin.rewriteAssistantText;
21
26
  exports.stripLeakedToolSummary = require_plugin.stripLeakedToolSummary;
package/dist/plugin.d.cts CHANGED
@@ -229,7 +229,7 @@ interface AlfeChannelOutboundDeps {
229
229
  }) => Promise<AlfeOutboundSessionSummary[]>;
230
230
  getSession: (sessionId: string) => Promise<AlfeOutboundSession | null>;
231
231
  createSession: (sessionId: string, agentId: string, channel: string, tenantId?: string, userId?: string) => Promise<unknown>;
232
- addMessage: (sessionId: string, role: 'user' | 'assistant', content: string, senderId?: string, senderName?: string, components?: StoredMessageComponent[]) => Promise<void>;
232
+ addMessage: (sessionId: string, role: 'user' | 'assistant', content: string, senderId?: string, senderName?: string, components?: StoredMessageComponent[], attachments?: StoredAttachment[]) => Promise<void>;
233
233
  }
234
234
  //#endregion
235
235
  //#region src/alfe-channel.d.ts
@@ -247,15 +247,7 @@ interface OutboundDeliveryResult {
247
247
  conversationId: string;
248
248
  timestamp: number;
249
249
  }
250
- /**
251
- * Present interactive components (link buttons, quick replies) into a
252
- * conversation — the outbound path for the `chat_present_components` tool.
253
- * `to` is a `conv:{id}` or `user:{id}` target (same vocabulary as the channel's
254
- * `resolveTarget`). Persists + broadcasts through the same `sendViaChat` path
255
- * as a normal reply, so the components ride the standard `message` frame and
256
- * are replayed on backfill. Components are sanitized (unsafe URLs dropped)
257
- * before delivery.
258
- */
250
+ /** Persist only bucket-backed media; passthrough URLs have no stable id. */
259
251
 
260
252
  /**
261
253
  * Creates the Alfe ChannelPlugin object for registration with OpenClaw.
@@ -655,6 +647,23 @@ declare function decideChatServiceStart(state: {
655
647
  clientPresent: boolean;
656
648
  clientConnected: boolean;
657
649
  }): 'skip' | 'recycle' | 'connect';
650
+ interface AttachmentRef {
651
+ id: string;
652
+ type: string;
653
+ mimeType: string;
654
+ url: string;
655
+ filename?: string;
656
+ size?: number;
657
+ }
658
+ declare const MAX_INBOUND_ATTACHMENT_SIZE: number;
659
+ /**
660
+ * Read a response body without ever retaining more than the attachment cap.
661
+ * `arrayBuffer()` cannot enforce a cap until after the entire response has
662
+ * already been allocated, so both declared and streamed sizes are checked.
663
+ */
664
+ declare function readAttachmentBodyBounded(response: Response, maxBytes?: number): Promise<Uint8Array>;
665
+ /** Build one safe, collision-resistant filename inside the attachment dir. */
666
+ declare function buildAttachmentLocalFilename(att: Pick<AttachmentRef, 'id' | 'filename'>): string;
658
667
  declare function enqueueAgentTurn(task: () => Promise<void>, opts?: {
659
668
  coalesceKey?: string;
660
669
  onSuperseded?: () => void;
@@ -702,6 +711,14 @@ declare const VOICE_REPLY_SYSTEM_PROMPT: string;
702
711
  * disambiguate; this rides the system prompt on every turn (~40 tokens).
703
712
  */
704
713
  declare const CROSS_AGENT_TOOLS_SYSTEM_PROMPT: string;
714
+ /**
715
+ * Rules for an inbound agent-to-agent turn. This must ride the runtime-rendered
716
+ * `GroupSystemPrompt` field; arbitrary extra-context keys are not shown to the
717
+ * model. Keep the instruction structural: `end_conversation` is the plugin's
718
+ * authoritative turn-local completion signal. The relay still accepts the
719
+ * legacy trailing `[RESOLVED]` sentinel for older plugin versions.
720
+ */
721
+ declare const A2A_REPLY_SYSTEM_PROMPT: string;
705
722
  /**
706
723
  * Per-turn envelope context derived from the RPC `origin` flag, returned as a
707
724
  * `GroupSystemPrompt` block — the PROVEN runtime-rendered seam for per-turn
@@ -727,6 +744,8 @@ declare const CROSS_AGENT_TOOLS_SYSTEM_PROMPT: string;
727
744
  * Exported pure for unit testing (mirrors the codebase's pure-helper pattern).
728
745
  */
729
746
  declare function buildOriginEnvelopeContext(origin: string | undefined): Record<string, unknown>;
747
+ /** Build the single live per-turn prompt block without competing object keys. */
748
+ declare function buildTurnEnvelopeContext(origin: string | undefined, isA2A: boolean): Record<string, unknown>;
730
749
  declare const plugin: {
731
750
  id: string;
732
751
  name: string;
@@ -736,4 +755,4 @@ declare const plugin: {
736
755
  deactivate(api: PluginApi): Promise<void>;
737
756
  };
738
757
  //#endregion
739
- export { AlfeResolvedAccount as C, SessionSummary as E, AlfePluginConfig as S, SessionData as T, resolveAbortTargetKeys as _, VOICE_REPLY_SYSTEM_PROMPT as a, createAlfeChannelPlugin as b, __setActiveRunForTest as c, buildOriginEnvelopeContext as d, buildToolActivity as f, plugin as g, joinAssistantText as h, RunEventGate as i, admitAgentEvent as l, decideChatServiceStart as m, AnchorEnv as n, __agentTurnQueueForTest as o, computeOpenClawSdkAnchors as p, CROSS_AGENT_TOOLS_SYSTEM_PROMPT as r, __resetAgentTurnQueueForTest as s, ActiveRunRef as t, buildA2ACompletePayload as u, rewriteAssistantText as v, ChatMessage as w, AlfeChannelConfig as x, stripLeakedToolSummary as y };
758
+ export { SessionData as A, rewriteAssistantText as C, AlfePluginConfig as D, AlfeChannelConfig as E, AlfeResolvedAccount as O, resolveAbortTargetKeys as S, createAlfeChannelPlugin as T, computeOpenClawSdkAnchors as _, MAX_INBOUND_ATTACHMENT_SIZE as a, plugin as b, __agentTurnQueueForTest as c, admitAgentEvent as d, buildA2ACompletePayload as f, buildTurnEnvelopeContext as g, buildToolActivity as h, CROSS_AGENT_TOOLS_SYSTEM_PROMPT as i, SessionSummary as j, ChatMessage as k, __resetAgentTurnQueueForTest as l, buildOriginEnvelopeContext as m, ActiveRunRef as n, RunEventGate as o, buildAttachmentLocalFilename as p, AnchorEnv as r, VOICE_REPLY_SYSTEM_PROMPT as s, A2A_REPLY_SYSTEM_PROMPT as t, __setActiveRunForTest as u, decideChatServiceStart as v, stripLeakedToolSummary as w, readAttachmentBodyBounded as x, joinAssistantText as y };
package/dist/plugin.d.ts CHANGED
@@ -229,7 +229,7 @@ interface AlfeChannelOutboundDeps {
229
229
  }) => Promise<AlfeOutboundSessionSummary[]>;
230
230
  getSession: (sessionId: string) => Promise<AlfeOutboundSession | null>;
231
231
  createSession: (sessionId: string, agentId: string, channel: string, tenantId?: string, userId?: string) => Promise<unknown>;
232
- addMessage: (sessionId: string, role: 'user' | 'assistant', content: string, senderId?: string, senderName?: string, components?: StoredMessageComponent[]) => Promise<void>;
232
+ addMessage: (sessionId: string, role: 'user' | 'assistant', content: string, senderId?: string, senderName?: string, components?: StoredMessageComponent[], attachments?: StoredAttachment[]) => Promise<void>;
233
233
  }
234
234
  //#endregion
235
235
  //#region src/alfe-channel.d.ts
@@ -247,15 +247,7 @@ interface OutboundDeliveryResult {
247
247
  conversationId: string;
248
248
  timestamp: number;
249
249
  }
250
- /**
251
- * Present interactive components (link buttons, quick replies) into a
252
- * conversation — the outbound path for the `chat_present_components` tool.
253
- * `to` is a `conv:{id}` or `user:{id}` target (same vocabulary as the channel's
254
- * `resolveTarget`). Persists + broadcasts through the same `sendViaChat` path
255
- * as a normal reply, so the components ride the standard `message` frame and
256
- * are replayed on backfill. Components are sanitized (unsafe URLs dropped)
257
- * before delivery.
258
- */
250
+ /** Persist only bucket-backed media; passthrough URLs have no stable id. */
259
251
 
260
252
  /**
261
253
  * Creates the Alfe ChannelPlugin object for registration with OpenClaw.
@@ -655,6 +647,23 @@ declare function decideChatServiceStart(state: {
655
647
  clientPresent: boolean;
656
648
  clientConnected: boolean;
657
649
  }): 'skip' | 'recycle' | 'connect';
650
+ interface AttachmentRef {
651
+ id: string;
652
+ type: string;
653
+ mimeType: string;
654
+ url: string;
655
+ filename?: string;
656
+ size?: number;
657
+ }
658
+ declare const MAX_INBOUND_ATTACHMENT_SIZE: number;
659
+ /**
660
+ * Read a response body without ever retaining more than the attachment cap.
661
+ * `arrayBuffer()` cannot enforce a cap until after the entire response has
662
+ * already been allocated, so both declared and streamed sizes are checked.
663
+ */
664
+ declare function readAttachmentBodyBounded(response: Response, maxBytes?: number): Promise<Uint8Array>;
665
+ /** Build one safe, collision-resistant filename inside the attachment dir. */
666
+ declare function buildAttachmentLocalFilename(att: Pick<AttachmentRef, 'id' | 'filename'>): string;
658
667
  declare function enqueueAgentTurn(task: () => Promise<void>, opts?: {
659
668
  coalesceKey?: string;
660
669
  onSuperseded?: () => void;
@@ -702,6 +711,14 @@ declare const VOICE_REPLY_SYSTEM_PROMPT: string;
702
711
  * disambiguate; this rides the system prompt on every turn (~40 tokens).
703
712
  */
704
713
  declare const CROSS_AGENT_TOOLS_SYSTEM_PROMPT: string;
714
+ /**
715
+ * Rules for an inbound agent-to-agent turn. This must ride the runtime-rendered
716
+ * `GroupSystemPrompt` field; arbitrary extra-context keys are not shown to the
717
+ * model. Keep the instruction structural: `end_conversation` is the plugin's
718
+ * authoritative turn-local completion signal. The relay still accepts the
719
+ * legacy trailing `[RESOLVED]` sentinel for older plugin versions.
720
+ */
721
+ declare const A2A_REPLY_SYSTEM_PROMPT: string;
705
722
  /**
706
723
  * Per-turn envelope context derived from the RPC `origin` flag, returned as a
707
724
  * `GroupSystemPrompt` block — the PROVEN runtime-rendered seam for per-turn
@@ -727,6 +744,8 @@ declare const CROSS_AGENT_TOOLS_SYSTEM_PROMPT: string;
727
744
  * Exported pure for unit testing (mirrors the codebase's pure-helper pattern).
728
745
  */
729
746
  declare function buildOriginEnvelopeContext(origin: string | undefined): Record<string, unknown>;
747
+ /** Build the single live per-turn prompt block without competing object keys. */
748
+ declare function buildTurnEnvelopeContext(origin: string | undefined, isA2A: boolean): Record<string, unknown>;
730
749
  declare const plugin: {
731
750
  id: string;
732
751
  name: string;
@@ -736,4 +755,4 @@ declare const plugin: {
736
755
  deactivate(api: PluginApi): Promise<void>;
737
756
  };
738
757
  //#endregion
739
- export { AlfeResolvedAccount as C, SessionSummary as E, AlfePluginConfig as S, SessionData as T, resolveAbortTargetKeys as _, VOICE_REPLY_SYSTEM_PROMPT as a, createAlfeChannelPlugin as b, __setActiveRunForTest as c, buildOriginEnvelopeContext as d, buildToolActivity as f, plugin as g, joinAssistantText as h, RunEventGate as i, admitAgentEvent as l, decideChatServiceStart as m, AnchorEnv as n, __agentTurnQueueForTest as o, computeOpenClawSdkAnchors as p, CROSS_AGENT_TOOLS_SYSTEM_PROMPT as r, __resetAgentTurnQueueForTest as s, ActiveRunRef as t, buildA2ACompletePayload as u, rewriteAssistantText as v, ChatMessage as w, AlfeChannelConfig as x, stripLeakedToolSummary as y };
758
+ export { SessionData as A, rewriteAssistantText as C, AlfePluginConfig as D, AlfeChannelConfig as E, AlfeResolvedAccount as O, resolveAbortTargetKeys as S, createAlfeChannelPlugin as T, computeOpenClawSdkAnchors as _, MAX_INBOUND_ATTACHMENT_SIZE as a, plugin as b, __agentTurnQueueForTest as c, admitAgentEvent as d, buildA2ACompletePayload as f, buildTurnEnvelopeContext as g, buildToolActivity as h, CROSS_AGENT_TOOLS_SYSTEM_PROMPT as i, SessionSummary as j, ChatMessage as k, __resetAgentTurnQueueForTest as l, buildOriginEnvelopeContext as m, ActiveRunRef as n, RunEventGate as o, buildAttachmentLocalFilename as p, AnchorEnv as r, VOICE_REPLY_SYSTEM_PROMPT as s, A2A_REPLY_SYSTEM_PROMPT as t, __setActiveRunForTest as u, decideChatServiceStart as v, stripLeakedToolSummary as w, readAttachmentBodyBounded as x, joinAssistantText as y };
package/dist/plugin.js CHANGED
@@ -1,2 +1,2 @@
1
- import { a as __setActiveRunForTest, c as buildOriginEnvelopeContext, d as decideChatServiceStart, f as joinAssistantText, g as stripLeakedToolSummary, h as rewriteAssistantText, i as __resetAgentTurnQueueForTest, l as buildToolActivity, m as resolveAbortTargetKeys, n as VOICE_REPLY_SYSTEM_PROMPT, o as admitAgentEvent, p as plugin, r as __agentTurnQueueForTest, s as buildA2ACompletePayload, t as CROSS_AGENT_TOOLS_SYSTEM_PROMPT, u as computeOpenClawSdkAnchors } from "./plugin2.js";
2
- export { CROSS_AGENT_TOOLS_SYSTEM_PROMPT, VOICE_REPLY_SYSTEM_PROMPT, __agentTurnQueueForTest, __resetAgentTurnQueueForTest, __setActiveRunForTest, admitAgentEvent, buildA2ACompletePayload, buildOriginEnvelopeContext, buildToolActivity, computeOpenClawSdkAnchors, decideChatServiceStart, plugin as default, joinAssistantText, resolveAbortTargetKeys, rewriteAssistantText, stripLeakedToolSummary };
1
+ import { _ as plugin, a as __agentTurnQueueForTest, b as rewriteAssistantText, 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 stripLeakedToolSummary, 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 as default, joinAssistantText, readAttachmentBodyBounded, resolveAbortTargetKeys, rewriteAssistantText, stripLeakedToolSummary };