@robota-sdk/agent-transport-ws 3.0.0-beta.61 → 3.0.0-beta.63

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.
@@ -20,6 +20,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // src/index.ts
21
21
  var index_exports = {};
22
22
  __export(index_exports, {
23
+ WsTransport: () => WsTransport,
23
24
  createWsHandler: () => createWsHandler,
24
25
  createWsTransport: () => createWsTransport
25
26
  });
@@ -148,6 +149,7 @@ function createWsHandler(options) {
148
149
  return { onMessage, cleanup };
149
150
  }
150
151
  function subscribeSessionEvents(session, send) {
152
+ const onUserMessage = (content) => send({ type: "user_message", content });
151
153
  const onTextDelta = (delta) => send({ type: "text_delta", delta });
152
154
  const onToolStart = (state) => send({ type: "tool_start", state });
153
155
  const onToolEnd = (state) => send({ type: "tool_end", state });
@@ -157,6 +159,8 @@ function subscribeSessionEvents(session, send) {
157
159
  const onError = (error) => send({ type: "error", message: error.message });
158
160
  const onBackgroundTaskEvent = (event) => send({ type: "background_task_event", event });
159
161
  const onBackgroundJobGroupEvent = (event) => send({ type: "background_job_group_event", event });
162
+ const onExecutionWorkspace = (event) => send({ type: "execution_workspace_event", snapshot: event.snapshot });
163
+ session.on("user_message", onUserMessage);
160
164
  session.on("text_delta", onTextDelta);
161
165
  session.on("tool_start", onToolStart);
162
166
  session.on("tool_end", onToolEnd);
@@ -166,7 +170,9 @@ function subscribeSessionEvents(session, send) {
166
170
  session.on("error", onError);
167
171
  session.on("background_task_event", onBackgroundTaskEvent);
168
172
  session.on("background_job_group_event", onBackgroundJobGroupEvent);
173
+ session.on("execution_workspace_event", onExecutionWorkspace);
169
174
  return () => {
175
+ session.off("user_message", onUserMessage);
170
176
  session.off("text_delta", onTextDelta);
171
177
  session.off("tool_start", onToolStart);
172
178
  session.off("tool_end", onToolEnd);
@@ -176,6 +182,7 @@ function subscribeSessionEvents(session, send) {
176
182
  session.off("error", onError);
177
183
  session.off("background_task_event", onBackgroundTaskEvent);
178
184
  session.off("background_job_group_event", onBackgroundJobGroupEvent);
185
+ session.off("execution_workspace_event", onExecutionWorkspace);
179
186
  };
180
187
  }
181
188
  function createWsMessageHandler(session, send) {
@@ -219,7 +226,7 @@ function isSessionControlMessage(msg) {
219
226
  return msg.type === "submit" || msg.type === "command" || msg.type === "abort" || msg.type === "cancel-queue";
220
227
  }
221
228
  function isSessionQueryMessage(msg) {
222
- return msg.type === "get-messages" || msg.type === "get-context" || msg.type === "get-executing" || msg.type === "get-pending";
229
+ return msg.type === "get-messages" || msg.type === "get-context" || msg.type === "get-executing" || msg.type === "get-pending" || msg.type === "get-execution-workspace";
223
230
  }
224
231
  function isBackgroundQueryMessage(msg) {
225
232
  return msg.type === "get-background-tasks" || msg.type === "get-background-task" || msg.type === "read-background-task-log" || msg.type === "get-background-job-groups" || msg.type === "get-background-job-group" || msg.type === "wait-background-job-group";
@@ -261,6 +268,11 @@ function handleSessionQueryMessage(session, send, msg) {
261
268
  send({ type: "context", state: session.getContextState() });
262
269
  } else if (msg.type === "get-executing") {
263
270
  send({ type: "executing", executing: session.isExecuting() });
271
+ } else if (msg.type === "get-execution-workspace") {
272
+ send({
273
+ type: "execution_workspace_event",
274
+ snapshot: session.getExecutionWorkspaceSnapshot()
275
+ });
264
276
  } else {
265
277
  send({ type: "pending", pending: session.getPendingPrompt() });
266
278
  }
@@ -289,8 +301,94 @@ function createWsTransport(options) {
289
301
  }
290
302
  };
291
303
  }
304
+
305
+ // src/ws-transport-configurable.ts
306
+ var import_node_http = require("http");
307
+ var import_ws = require("ws");
308
+ var DEFAULT_PORT = 7070;
309
+ var DEFAULT_MAX_RETRIES = 20;
310
+ var WsTransport = class {
311
+ name = "ws";
312
+ defaultEnabled = true;
313
+ optionsSchema = {
314
+ port: { type: "number", description: "WebSocket server port", default: DEFAULT_PORT },
315
+ maxRetries: {
316
+ type: "number",
317
+ description: "Port retry attempts when port is occupied",
318
+ default: DEFAULT_MAX_RETRIES
319
+ }
320
+ };
321
+ session = null;
322
+ stopFn = null;
323
+ port;
324
+ maxRetries;
325
+ constructor(config = {}) {
326
+ this.port = config.port ?? DEFAULT_PORT;
327
+ this.maxRetries = config.maxRetries ?? DEFAULT_MAX_RETRIES;
328
+ }
329
+ attach(session) {
330
+ this.session = session;
331
+ }
332
+ async start() {
333
+ if (!this.session) throw new Error("WsTransport: attach() must be called before start()");
334
+ const handle = await this.bindWithRetry(this.session, this.port, this.maxRetries);
335
+ this.stopFn = handle.stop;
336
+ }
337
+ async stop() {
338
+ await this.stopFn?.();
339
+ this.stopFn = null;
340
+ }
341
+ validateOptions(options) {
342
+ const { port, maxRetries } = options;
343
+ if (port !== void 0 && (typeof port !== "number" || port < 1 || port > 65535)) return false;
344
+ if (maxRetries !== void 0 && (typeof maxRetries !== "number" || maxRetries < 0))
345
+ return false;
346
+ return true;
347
+ }
348
+ bindWithRetry(session, port, retriesLeft) {
349
+ return this.tryBind(session, port).catch((err) => {
350
+ if (err.code === "EADDRINUSE" && retriesLeft > 0)
351
+ return this.bindWithRetry(session, port + 1, retriesLeft - 1);
352
+ throw err;
353
+ });
354
+ }
355
+ tryBind(session, port) {
356
+ return new Promise((resolve, reject) => {
357
+ const httpServer = (0, import_node_http.createServer)((_, res) => {
358
+ res.writeHead(400).end("WebSocket endpoint");
359
+ });
360
+ httpServer.on("error", (err) => {
361
+ httpServer.close();
362
+ reject(err);
363
+ });
364
+ httpServer.listen(port, "127.0.0.1", () => {
365
+ const wss = new import_ws.WebSocketServer({ server: httpServer });
366
+ wss.on("connection", (ws) => {
367
+ const send = (message) => {
368
+ if (ws.readyState === import_ws.WebSocket.OPEN) ws.send(JSON.stringify(message));
369
+ };
370
+ const { onMessage, cleanup } = createWsHandler({ session, send });
371
+ ws.on("message", (data) => onMessage(String(data)));
372
+ ws.on("close", cleanup);
373
+ ws.on("error", cleanup);
374
+ send({ type: "messages", messages: session.getMessages() });
375
+ send({
376
+ type: "execution_workspace_event",
377
+ snapshot: session.getExecutionWorkspaceSnapshot()
378
+ });
379
+ });
380
+ resolve({
381
+ stop: () => new Promise((res) => {
382
+ wss.close(() => httpServer.close(() => res()));
383
+ })
384
+ });
385
+ });
386
+ });
387
+ }
388
+ };
292
389
  // Annotate the CommonJS export names for ESM import in node:
293
390
  0 && (module.exports = {
391
+ WsTransport,
294
392
  createWsHandler,
295
393
  createWsTransport
296
394
  });
@@ -1,4 +1,7 @@
1
- import { IToolState, IExecutionResult, ICommandResult, InteractiveSession, TBackgroundTaskEvent, TBackgroundJobGroupEvent, IBackgroundTaskState, IBackgroundJobGroupState, IBackgroundTaskLogPage, IBackgroundTaskListFilter, IBackgroundTaskInput, IBackgroundTaskLogCursor, ITransportAdapter } from '@robota-sdk/agent-sdk';
1
+ import { IToolState, IExecutionResult, ICommandResult, InteractiveSession, IExecutionWorkspaceSnapshot, TBackgroundTaskEvent, TBackgroundJobGroupEvent, IBackgroundTaskState, IBackgroundJobGroupState, IBackgroundTaskLogPage, IBackgroundTaskListFilter, IBackgroundTaskInput, IBackgroundTaskLogCursor } from '@robota-sdk/agent-sdk';
2
+ export { IExecutionWorkspaceEntry, IExecutionWorkspaceSnapshot, TExecutionAttention, TExecutionWorkspaceStatus } from '@robota-sdk/agent-sdk';
3
+ import { ITransportAdapter, IConfigurableTransport } from '@robota-sdk/agent-interface-transport';
4
+ import { ISession, TUniversalValue } from '@robota-sdk/agent-core';
2
5
 
3
6
  type TBackgroundControlAction = 'cancel' | 'close' | 'send';
4
7
  /** Inbound message from client to server. */
@@ -21,6 +24,8 @@ type TClientMessage = {
21
24
  type: 'get-executing';
22
25
  } | {
23
26
  type: 'get-pending';
27
+ } | {
28
+ type: 'get-execution-workspace';
24
29
  } | {
25
30
  type: 'get-background-tasks';
26
31
  filter?: IBackgroundTaskListFilter;
@@ -55,6 +60,9 @@ type TClientMessage = {
55
60
  type TServerMessage = {
56
61
  type: 'text_delta';
57
62
  delta: string;
63
+ } | {
64
+ type: 'user_message';
65
+ content: string;
58
66
  } | {
59
67
  type: 'tool_start';
60
68
  state: IToolState;
@@ -91,6 +99,9 @@ type TServerMessage = {
91
99
  } | {
92
100
  type: 'pending';
93
101
  pending: string | null;
102
+ } | {
103
+ type: 'execution_workspace_event';
104
+ snapshot: IExecutionWorkspaceSnapshot;
94
105
  } | {
95
106
  type: 'background_task_event';
96
107
  event: TBackgroundTaskEvent;
@@ -180,4 +191,41 @@ declare function createWsTransport(options: IWsTransportOptions): ITransportAdap
180
191
  onMessage: ((data: string) => void) | null;
181
192
  };
182
193
 
183
- export { type IWsHandlerOptions, type IWsTransportOptions, type TClientMessage, type TServerMessage, createWsHandler, createWsTransport };
194
+ /**
195
+ * Self-contained WS transport implementing IConfigurableTransport.
196
+ * Owns the WebSocket server lifecycle (ws package), started/stopped via the transport registry.
197
+ */
198
+
199
+ interface IWsTransportConfig {
200
+ port?: number;
201
+ maxRetries?: number;
202
+ }
203
+ declare class WsTransport implements IConfigurableTransport {
204
+ readonly name = "ws";
205
+ readonly defaultEnabled = true;
206
+ readonly optionsSchema: {
207
+ port: {
208
+ type: string;
209
+ description: string;
210
+ default: number;
211
+ };
212
+ maxRetries: {
213
+ type: string;
214
+ description: string;
215
+ default: number;
216
+ };
217
+ };
218
+ private session;
219
+ private stopFn;
220
+ private readonly port;
221
+ private readonly maxRetries;
222
+ constructor(config?: IWsTransportConfig);
223
+ attach(session: ISession): void;
224
+ start(): Promise<void>;
225
+ stop(): Promise<void>;
226
+ validateOptions(options: Record<string, TUniversalValue>): boolean;
227
+ private bindWithRetry;
228
+ private tryBind;
229
+ }
230
+
231
+ export { type IWsHandlerOptions, type IWsTransportConfig, type IWsTransportOptions, type TClientMessage, type TServerMessage, WsTransport, createWsHandler, createWsTransport };
@@ -1,4 +1,7 @@
1
- import { IToolState, IExecutionResult, ICommandResult, InteractiveSession, TBackgroundTaskEvent, TBackgroundJobGroupEvent, IBackgroundTaskState, IBackgroundJobGroupState, IBackgroundTaskLogPage, IBackgroundTaskListFilter, IBackgroundTaskInput, IBackgroundTaskLogCursor, ITransportAdapter } from '@robota-sdk/agent-sdk';
1
+ import { IToolState, IExecutionResult, ICommandResult, InteractiveSession, IExecutionWorkspaceSnapshot, TBackgroundTaskEvent, TBackgroundJobGroupEvent, IBackgroundTaskState, IBackgroundJobGroupState, IBackgroundTaskLogPage, IBackgroundTaskListFilter, IBackgroundTaskInput, IBackgroundTaskLogCursor } from '@robota-sdk/agent-sdk';
2
+ export { IExecutionWorkspaceEntry, IExecutionWorkspaceSnapshot, TExecutionAttention, TExecutionWorkspaceStatus } from '@robota-sdk/agent-sdk';
3
+ import { ITransportAdapter, IConfigurableTransport } from '@robota-sdk/agent-interface-transport';
4
+ import { ISession, TUniversalValue } from '@robota-sdk/agent-core';
2
5
 
3
6
  type TBackgroundControlAction = 'cancel' | 'close' | 'send';
4
7
  /** Inbound message from client to server. */
@@ -21,6 +24,8 @@ type TClientMessage = {
21
24
  type: 'get-executing';
22
25
  } | {
23
26
  type: 'get-pending';
27
+ } | {
28
+ type: 'get-execution-workspace';
24
29
  } | {
25
30
  type: 'get-background-tasks';
26
31
  filter?: IBackgroundTaskListFilter;
@@ -55,6 +60,9 @@ type TClientMessage = {
55
60
  type TServerMessage = {
56
61
  type: 'text_delta';
57
62
  delta: string;
63
+ } | {
64
+ type: 'user_message';
65
+ content: string;
58
66
  } | {
59
67
  type: 'tool_start';
60
68
  state: IToolState;
@@ -91,6 +99,9 @@ type TServerMessage = {
91
99
  } | {
92
100
  type: 'pending';
93
101
  pending: string | null;
102
+ } | {
103
+ type: 'execution_workspace_event';
104
+ snapshot: IExecutionWorkspaceSnapshot;
94
105
  } | {
95
106
  type: 'background_task_event';
96
107
  event: TBackgroundTaskEvent;
@@ -180,4 +191,41 @@ declare function createWsTransport(options: IWsTransportOptions): ITransportAdap
180
191
  onMessage: ((data: string) => void) | null;
181
192
  };
182
193
 
183
- export { type IWsHandlerOptions, type IWsTransportOptions, type TClientMessage, type TServerMessage, createWsHandler, createWsTransport };
194
+ /**
195
+ * Self-contained WS transport implementing IConfigurableTransport.
196
+ * Owns the WebSocket server lifecycle (ws package), started/stopped via the transport registry.
197
+ */
198
+
199
+ interface IWsTransportConfig {
200
+ port?: number;
201
+ maxRetries?: number;
202
+ }
203
+ declare class WsTransport implements IConfigurableTransport {
204
+ readonly name = "ws";
205
+ readonly defaultEnabled = true;
206
+ readonly optionsSchema: {
207
+ port: {
208
+ type: string;
209
+ description: string;
210
+ default: number;
211
+ };
212
+ maxRetries: {
213
+ type: string;
214
+ description: string;
215
+ default: number;
216
+ };
217
+ };
218
+ private session;
219
+ private stopFn;
220
+ private readonly port;
221
+ private readonly maxRetries;
222
+ constructor(config?: IWsTransportConfig);
223
+ attach(session: ISession): void;
224
+ start(): Promise<void>;
225
+ stop(): Promise<void>;
226
+ validateOptions(options: Record<string, TUniversalValue>): boolean;
227
+ private bindWithRetry;
228
+ private tryBind;
229
+ }
230
+
231
+ export { type IWsHandlerOptions, type IWsTransportConfig, type IWsTransportOptions, type TClientMessage, type TServerMessage, WsTransport, createWsHandler, createWsTransport };
@@ -121,6 +121,7 @@ function createWsHandler(options) {
121
121
  return { onMessage, cleanup };
122
122
  }
123
123
  function subscribeSessionEvents(session, send) {
124
+ const onUserMessage = (content) => send({ type: "user_message", content });
124
125
  const onTextDelta = (delta) => send({ type: "text_delta", delta });
125
126
  const onToolStart = (state) => send({ type: "tool_start", state });
126
127
  const onToolEnd = (state) => send({ type: "tool_end", state });
@@ -130,6 +131,8 @@ function subscribeSessionEvents(session, send) {
130
131
  const onError = (error) => send({ type: "error", message: error.message });
131
132
  const onBackgroundTaskEvent = (event) => send({ type: "background_task_event", event });
132
133
  const onBackgroundJobGroupEvent = (event) => send({ type: "background_job_group_event", event });
134
+ const onExecutionWorkspace = (event) => send({ type: "execution_workspace_event", snapshot: event.snapshot });
135
+ session.on("user_message", onUserMessage);
133
136
  session.on("text_delta", onTextDelta);
134
137
  session.on("tool_start", onToolStart);
135
138
  session.on("tool_end", onToolEnd);
@@ -139,7 +142,9 @@ function subscribeSessionEvents(session, send) {
139
142
  session.on("error", onError);
140
143
  session.on("background_task_event", onBackgroundTaskEvent);
141
144
  session.on("background_job_group_event", onBackgroundJobGroupEvent);
145
+ session.on("execution_workspace_event", onExecutionWorkspace);
142
146
  return () => {
147
+ session.off("user_message", onUserMessage);
143
148
  session.off("text_delta", onTextDelta);
144
149
  session.off("tool_start", onToolStart);
145
150
  session.off("tool_end", onToolEnd);
@@ -149,6 +154,7 @@ function subscribeSessionEvents(session, send) {
149
154
  session.off("error", onError);
150
155
  session.off("background_task_event", onBackgroundTaskEvent);
151
156
  session.off("background_job_group_event", onBackgroundJobGroupEvent);
157
+ session.off("execution_workspace_event", onExecutionWorkspace);
152
158
  };
153
159
  }
154
160
  function createWsMessageHandler(session, send) {
@@ -192,7 +198,7 @@ function isSessionControlMessage(msg) {
192
198
  return msg.type === "submit" || msg.type === "command" || msg.type === "abort" || msg.type === "cancel-queue";
193
199
  }
194
200
  function isSessionQueryMessage(msg) {
195
- return msg.type === "get-messages" || msg.type === "get-context" || msg.type === "get-executing" || msg.type === "get-pending";
201
+ return msg.type === "get-messages" || msg.type === "get-context" || msg.type === "get-executing" || msg.type === "get-pending" || msg.type === "get-execution-workspace";
196
202
  }
197
203
  function isBackgroundQueryMessage(msg) {
198
204
  return msg.type === "get-background-tasks" || msg.type === "get-background-task" || msg.type === "read-background-task-log" || msg.type === "get-background-job-groups" || msg.type === "get-background-job-group" || msg.type === "wait-background-job-group";
@@ -234,6 +240,11 @@ function handleSessionQueryMessage(session, send, msg) {
234
240
  send({ type: "context", state: session.getContextState() });
235
241
  } else if (msg.type === "get-executing") {
236
242
  send({ type: "executing", executing: session.isExecuting() });
243
+ } else if (msg.type === "get-execution-workspace") {
244
+ send({
245
+ type: "execution_workspace_event",
246
+ snapshot: session.getExecutionWorkspaceSnapshot()
247
+ });
237
248
  } else {
238
249
  send({ type: "pending", pending: session.getPendingPrompt() });
239
250
  }
@@ -262,7 +273,93 @@ function createWsTransport(options) {
262
273
  }
263
274
  };
264
275
  }
276
+
277
+ // src/ws-transport-configurable.ts
278
+ import { createServer } from "http";
279
+ import { WebSocketServer, WebSocket } from "ws";
280
+ var DEFAULT_PORT = 7070;
281
+ var DEFAULT_MAX_RETRIES = 20;
282
+ var WsTransport = class {
283
+ name = "ws";
284
+ defaultEnabled = true;
285
+ optionsSchema = {
286
+ port: { type: "number", description: "WebSocket server port", default: DEFAULT_PORT },
287
+ maxRetries: {
288
+ type: "number",
289
+ description: "Port retry attempts when port is occupied",
290
+ default: DEFAULT_MAX_RETRIES
291
+ }
292
+ };
293
+ session = null;
294
+ stopFn = null;
295
+ port;
296
+ maxRetries;
297
+ constructor(config = {}) {
298
+ this.port = config.port ?? DEFAULT_PORT;
299
+ this.maxRetries = config.maxRetries ?? DEFAULT_MAX_RETRIES;
300
+ }
301
+ attach(session) {
302
+ this.session = session;
303
+ }
304
+ async start() {
305
+ if (!this.session) throw new Error("WsTransport: attach() must be called before start()");
306
+ const handle = await this.bindWithRetry(this.session, this.port, this.maxRetries);
307
+ this.stopFn = handle.stop;
308
+ }
309
+ async stop() {
310
+ await this.stopFn?.();
311
+ this.stopFn = null;
312
+ }
313
+ validateOptions(options) {
314
+ const { port, maxRetries } = options;
315
+ if (port !== void 0 && (typeof port !== "number" || port < 1 || port > 65535)) return false;
316
+ if (maxRetries !== void 0 && (typeof maxRetries !== "number" || maxRetries < 0))
317
+ return false;
318
+ return true;
319
+ }
320
+ bindWithRetry(session, port, retriesLeft) {
321
+ return this.tryBind(session, port).catch((err) => {
322
+ if (err.code === "EADDRINUSE" && retriesLeft > 0)
323
+ return this.bindWithRetry(session, port + 1, retriesLeft - 1);
324
+ throw err;
325
+ });
326
+ }
327
+ tryBind(session, port) {
328
+ return new Promise((resolve, reject) => {
329
+ const httpServer = createServer((_, res) => {
330
+ res.writeHead(400).end("WebSocket endpoint");
331
+ });
332
+ httpServer.on("error", (err) => {
333
+ httpServer.close();
334
+ reject(err);
335
+ });
336
+ httpServer.listen(port, "127.0.0.1", () => {
337
+ const wss = new WebSocketServer({ server: httpServer });
338
+ wss.on("connection", (ws) => {
339
+ const send = (message) => {
340
+ if (ws.readyState === WebSocket.OPEN) ws.send(JSON.stringify(message));
341
+ };
342
+ const { onMessage, cleanup } = createWsHandler({ session, send });
343
+ ws.on("message", (data) => onMessage(String(data)));
344
+ ws.on("close", cleanup);
345
+ ws.on("error", cleanup);
346
+ send({ type: "messages", messages: session.getMessages() });
347
+ send({
348
+ type: "execution_workspace_event",
349
+ snapshot: session.getExecutionWorkspaceSnapshot()
350
+ });
351
+ });
352
+ resolve({
353
+ stop: () => new Promise((res) => {
354
+ wss.close(() => httpServer.close(() => res()));
355
+ })
356
+ });
357
+ });
358
+ });
359
+ }
360
+ };
265
361
  export {
362
+ WsTransport,
266
363
  createWsHandler,
267
364
  createWsTransport
268
365
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@robota-sdk/agent-transport-ws",
3
- "version": "3.0.0-beta.61",
3
+ "version": "3.0.0-beta.63",
4
4
  "description": "WebSocket transport adapter for exposing InteractiveSession over real-time connections",
5
5
  "type": "module",
6
6
  "main": "dist/node/index.js",
@@ -30,9 +30,13 @@
30
30
  "dist"
31
31
  ],
32
32
  "dependencies": {
33
- "@robota-sdk/agent-sdk": "3.0.0-beta.61"
33
+ "ws": "^8.18.3",
34
+ "@robota-sdk/agent-core": "3.0.0-beta.63",
35
+ "@robota-sdk/agent-interface-transport": "3.0.0-beta.63",
36
+ "@robota-sdk/agent-sdk": "3.0.0-beta.63"
34
37
  },
35
38
  "devDependencies": {
39
+ "@types/ws": "^8.5.10",
36
40
  "rimraf": "^5.0.5",
37
41
  "tsup": "^8.0.1",
38
42
  "typescript": "^5.3.3",