@evgenyy/lessinbox-channel 0.1.6 → 0.1.9

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,9 +4,10 @@ Openclaw channel plugin that sends and receives agent conversation state through
4
4
 
5
5
  ## What it does
6
6
 
7
- - Starts Lessinbox sessions (runs) when needed
7
+ - Keeps one continuous conversation per target (`channel:*` or `thread:*`)
8
+ - Starts new Lessinbox sessions (runs) only when needed
8
9
  - Posts assistant messages into Lessinbox threads
9
- - Maintains persistent `conversationId -> { threadId, runId }` mapping (`memory` or `redis`)
10
+ - Maintains persistent `conversationId -> { threadId, runId? }` mapping (`memory` or `redis`)
10
11
  - Opens workspace WebSocket stream and emits realtime inbound events (`message.created`, `interrupt.answered`, `run.state_changed`, etc.)
11
12
  - Exposes a channel plugin manifest + Lessinbox skill bundle
12
13
 
@@ -22,6 +23,7 @@ Openclaw channel plugin that sends and receives agent conversation state through
22
23
  {
23
24
  "channels": {
24
25
  "lessinbox": {
26
+ "enabled": true,
25
27
  "accounts": {
26
28
  "default": {
27
29
  "enabled": true,
@@ -34,7 +36,7 @@ Openclaw channel plugin that sends and receives agent conversation state through
34
36
  "redisPrefix": "lessinbox:openclaw",
35
37
  "workspaceStream": {
36
38
  "enabled": true,
37
- "wsUrl": "wss://lessinbox.example.com/v1/ws",
39
+ "wsUrl": "wss://lessinbox.example.com/v2/ws",
38
40
  "reconnectMs": 1500
39
41
  }
40
42
  }
@@ -50,13 +52,20 @@ The plugin expects Openclaw to call `outbound.sendText(...)` with:
50
52
 
51
53
  - `text` (required)
52
54
  - `config` (runtime config object)
53
- - optional: `accountId`, `channelId`, `title`, `threadId`, `runId`, `conversationId`, `metadata`
55
+ - optional: `accountId`, `target`, `channelId`, `title`, `threadId`, `runId`, `conversationId`, `metadata`
54
56
 
55
57
  If `threadId` and `runId` are missing, the plugin will create a new Lessinbox run and use that thread.
56
58
 
59
+ Target formats supported by the plugin:
60
+
61
+ - `channel:<channelId>` - continue the latest thread for this channel (or create one if missing)
62
+ - `thread:<threadId>` - create/continue conversation in that exact thread
63
+ - raw channel id (`cml...` or `chn_...`) - treated as `channel:<id>`
64
+ - account alias (`default`, etc.) - switches account when it matches configured account id
65
+
57
66
  ## Realtime inbound usage
58
67
 
59
- The package exports `subscribeToLessinboxEvents(...)` and also exposes `plugin.inbound.subscribe(...)`.
68
+ The package exports `subscribeToLessinboxEvents(...)` as a direct helper for custom integrations.
60
69
 
61
70
  Example:
62
71
 
@@ -72,7 +81,15 @@ const unsubscribe = subscribeToLessinboxEvents({
72
81
  })
73
82
  ```
74
83
 
75
- On terminal run states (`completed`, `failed`, `canceled`) the plugin clears stored run mapping automatically.
84
+ For Openclaw-native runtime lifecycle, inbound processing is wired through channel gateway hooks:
85
+
86
+ - `plugin.gateway.startAccount(...)` subscribes to Lessinbox workspace stream.
87
+ - `plugin.gateway.stopAccount(...)` unsubscribes and shuts down account listeners.
88
+ - Incoming `message.created` user messages are routed through Openclaw runtime reply dispatcher.
89
+
90
+ This is the preferred runtime path in v2.
91
+
92
+ On terminal run states (`completed`, `failed`, `canceled`) the plugin keeps thread mapping and clears only the run id, so the next outbound message starts a fresh run in the same thread.
76
93
 
77
94
  ## Build
78
95
 
package/dist/index.d.ts CHANGED
@@ -4,6 +4,7 @@ export interface LessinboxWorkspaceStreamConfig {
4
4
  reconnectMs?: number;
5
5
  }
6
6
  export interface LessinboxAccountConfig {
7
+ accountId?: string;
7
8
  enabled?: boolean;
8
9
  apiUrl: string;
9
10
  apiKey: string;
@@ -41,9 +42,64 @@ export interface InterruptCreateInput {
41
42
  }
42
43
  export interface LessinboxRunRef {
43
44
  threadId: string;
44
- runId: string;
45
+ runId?: string;
45
46
  status?: string;
46
47
  }
48
+ interface V2RunSnapshot {
49
+ id: string;
50
+ thread_id: string | null;
51
+ channel_id: string;
52
+ status: string;
53
+ }
54
+ interface V2RunGetResponse {
55
+ run: V2RunSnapshot;
56
+ }
57
+ interface V2ThreadRunSnapshot {
58
+ id: string;
59
+ status: string;
60
+ }
61
+ interface V2ThreadGetResponse {
62
+ id: string;
63
+ channel_id: string;
64
+ run: V2ThreadRunSnapshot | null;
65
+ session?: V2ThreadRunSnapshot | null;
66
+ }
67
+ interface V2ThreadSummary {
68
+ id: string;
69
+ channel_id: string;
70
+ run_id: string | null;
71
+ run_status: string | null;
72
+ session_id?: string | null;
73
+ session_status?: string | null;
74
+ }
75
+ interface V2ThreadListResponse {
76
+ threads: V2ThreadSummary[];
77
+ next_cursor: string | null;
78
+ }
79
+ interface V2MessageActor {
80
+ type?: string;
81
+ user_id?: string;
82
+ userId?: string;
83
+ agent_id?: string;
84
+ agentId?: string;
85
+ }
86
+ interface V2ThreadFeedMessage {
87
+ id: string;
88
+ run_id?: string | null;
89
+ session_id?: string | null;
90
+ kind?: string | null;
91
+ text?: string | null;
92
+ actor?: V2MessageActor;
93
+ created_at?: string;
94
+ }
95
+ interface V2ThreadFeedEntry {
96
+ type?: string;
97
+ message?: V2ThreadFeedMessage;
98
+ }
99
+ interface V2ThreadFeedResponse {
100
+ entries: V2ThreadFeedEntry[];
101
+ next_cursor: string | null;
102
+ }
47
103
  export type LessinboxWorkspaceEventKind = "event.appended" | "artifact.ready" | "checkpoint.created" | "checkpoint.resolved" | "run.status" | "thread.summary" | "message.created" | "interrupt.opened" | "interrupt.answered" | "run.state_changed";
48
104
  export interface LessinboxWorkspaceEvent {
49
105
  kind: string;
@@ -103,32 +159,64 @@ export declare class LessinboxApi {
103
159
  message: string;
104
160
  details?: Record<string, unknown>;
105
161
  }): Promise<void>;
162
+ getRun(runId: string): Promise<V2RunGetResponse>;
163
+ getThread(threadId: string): Promise<V2ThreadGetResponse>;
106
164
  listThreads(input?: {
107
165
  bucket?: "needs_input" | "in_progress" | "done" | "all";
108
166
  channelId?: string;
109
167
  cursor?: string;
110
168
  limit?: number;
111
- }): Promise<{
112
- threads: unknown[];
113
- next_cursor: string | null;
114
- }>;
169
+ }): Promise<V2ThreadListResponse>;
115
170
  createWorkspaceWsToken(): Promise<{
116
171
  token: string;
117
172
  expires_at: string;
118
173
  }>;
174
+ getThreadFeed(input: {
175
+ threadId: string;
176
+ limit?: number;
177
+ cursor?: string;
178
+ }): Promise<V2ThreadFeedResponse>;
119
179
  private request;
120
180
  }
121
181
  export declare function resolveAccountConfig(config: unknown, accountId?: string): LessinboxAccountConfig;
122
- type ChannelPluginRuntimeAPI = {
182
+ type ChannelLogSink = {
183
+ debug?: (message: string) => void;
184
+ info?: (message: string) => void;
185
+ warn?: (message: string) => void;
186
+ error?: (message: string) => void;
187
+ };
188
+ type ChannelRuntimeState = Record<string, unknown>;
189
+ type ChannelGatewayContext = {
190
+ cfg: unknown;
191
+ accountId: string;
192
+ account: LessinboxAccountConfig;
193
+ runtime: unknown;
194
+ abortSignal: AbortSignal;
195
+ log?: ChannelLogSink;
196
+ getStatus: () => ChannelRuntimeState;
197
+ setStatus: (next: ChannelRuntimeState) => void;
198
+ };
199
+ type ChannelGatewayStartContext = ChannelGatewayContext;
200
+ type ChannelGatewayStopContext = ChannelGatewayContext;
201
+ type OpenclawPluginService = {
202
+ id: string;
203
+ start: (ctx: unknown) => void | Promise<void>;
204
+ stop?: (ctx: unknown) => void | Promise<void>;
205
+ };
206
+ type OpenclawPluginApi = {
207
+ runtime?: unknown;
123
208
  registerChannel: (input: {
124
209
  plugin: unknown;
125
- }) => void;
126
- onShutdown?: (handler: () => Promise<void> | void) => void;
210
+ } | unknown) => void;
211
+ registerService?: (service: OpenclawPluginService) => void;
127
212
  };
128
213
  type SendTextInput = {
129
214
  text: string;
130
215
  accountId?: string;
131
216
  config?: unknown;
217
+ target?: string;
218
+ to?: string;
219
+ recipient?: string;
132
220
  channelId?: string;
133
221
  title?: string;
134
222
  threadId?: string;
@@ -136,18 +224,23 @@ type SendTextInput = {
136
224
  conversationId?: string;
137
225
  metadata?: Record<string, unknown>;
138
226
  };
139
- type SubscribeInput = {
140
- config?: unknown;
141
- accountId?: string;
142
- onEvent: LessinboxInboundEventHandler;
227
+ type SendMediaInput = Omit<SendTextInput, "text"> & {
228
+ text?: string;
229
+ mediaUrl?: string;
143
230
  };
144
231
  declare function sendTextToLessinbox(input: SendTextInput): Promise<{
145
232
  ok: boolean;
146
233
  threadId: string;
147
234
  runId: string;
148
235
  }>;
236
+ declare function sendMediaToLessinbox(input: SendMediaInput): Promise<{
237
+ ok: boolean;
238
+ threadId: string;
239
+ runId: string;
240
+ }>;
241
+ declare function startLessinboxGatewayAccount(ctx: ChannelGatewayStartContext): Promise<void>;
242
+ declare function stopLessinboxGatewayAccount(ctx: ChannelGatewayStopContext): Promise<void>;
149
243
  export declare function subscribeToLessinboxEvents(input: SubscribeToLessinboxEventsInput): () => void;
150
- declare function subscribeFromPlugin(input: SubscribeInput): () => void;
151
244
  export declare function createLessinboxPlugin(): {
152
245
  id: string;
153
246
  meta: {
@@ -160,18 +253,45 @@ export declare function createLessinboxPlugin(): {
160
253
  };
161
254
  capabilities: {
162
255
  chatTypes: string[];
256
+ threads: boolean;
257
+ media: boolean;
258
+ };
259
+ reload: {
260
+ configPrefixes: string[];
163
261
  };
164
262
  config: {
165
263
  listAccountIds: (cfg: unknown) => string[];
166
264
  resolveAccount: (cfg: unknown, accountId?: string) => LessinboxAccountConfig;
265
+ isConfigured: (account: LessinboxAccountConfig) => boolean;
266
+ describeAccount: (account: LessinboxAccountConfig) => {
267
+ accountId: string;
268
+ enabled: boolean;
269
+ configured: boolean;
270
+ workspaceId: string;
271
+ apiUrl: string;
272
+ };
167
273
  };
168
274
  outbound: {
169
275
  deliveryMode: string;
170
276
  sendText: typeof sendTextToLessinbox;
277
+ sendMedia: typeof sendMediaToLessinbox;
278
+ };
279
+ gateway: {
280
+ startAccount: typeof startLessinboxGatewayAccount;
281
+ stopAccount: typeof stopLessinboxGatewayAccount;
171
282
  };
172
- inbound: {
173
- subscribe: typeof subscribeFromPlugin;
283
+ status: {
284
+ defaultRuntime: {
285
+ accountId: string;
286
+ running: boolean;
287
+ connected: boolean;
288
+ lastConnectedAt: null;
289
+ lastDisconnect: null;
290
+ lastStartAt: null;
291
+ lastStopAt: null;
292
+ lastError: null;
293
+ };
174
294
  };
175
295
  };
176
- export default function register(api: ChannelPluginRuntimeAPI): void;
296
+ export default function register(api: OpenclawPluginApi): void;
177
297
  export {};