@robota-sdk/agent-transport-ws 3.0.0-beta.62 → 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
  });
@@ -158,6 +159,7 @@ function subscribeSessionEvents(session, send) {
158
159
  const onError = (error) => send({ type: "error", message: error.message });
159
160
  const onBackgroundTaskEvent = (event) => send({ type: "background_task_event", event });
160
161
  const onBackgroundJobGroupEvent = (event) => send({ type: "background_job_group_event", event });
162
+ const onExecutionWorkspace = (event) => send({ type: "execution_workspace_event", snapshot: event.snapshot });
161
163
  session.on("user_message", onUserMessage);
162
164
  session.on("text_delta", onTextDelta);
163
165
  session.on("tool_start", onToolStart);
@@ -168,6 +170,7 @@ function subscribeSessionEvents(session, send) {
168
170
  session.on("error", onError);
169
171
  session.on("background_task_event", onBackgroundTaskEvent);
170
172
  session.on("background_job_group_event", onBackgroundJobGroupEvent);
173
+ session.on("execution_workspace_event", onExecutionWorkspace);
171
174
  return () => {
172
175
  session.off("user_message", onUserMessage);
173
176
  session.off("text_delta", onTextDelta);
@@ -179,6 +182,7 @@ function subscribeSessionEvents(session, send) {
179
182
  session.off("error", onError);
180
183
  session.off("background_task_event", onBackgroundTaskEvent);
181
184
  session.off("background_job_group_event", onBackgroundJobGroupEvent);
185
+ session.off("execution_workspace_event", onExecutionWorkspace);
182
186
  };
183
187
  }
184
188
  function createWsMessageHandler(session, send) {
@@ -222,7 +226,7 @@ function isSessionControlMessage(msg) {
222
226
  return msg.type === "submit" || msg.type === "command" || msg.type === "abort" || msg.type === "cancel-queue";
223
227
  }
224
228
  function isSessionQueryMessage(msg) {
225
- 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";
226
230
  }
227
231
  function isBackgroundQueryMessage(msg) {
228
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";
@@ -264,6 +268,11 @@ function handleSessionQueryMessage(session, send, msg) {
264
268
  send({ type: "context", state: session.getContextState() });
265
269
  } else if (msg.type === "get-executing") {
266
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
+ });
267
276
  } else {
268
277
  send({ type: "pending", pending: session.getPendingPrompt() });
269
278
  }
@@ -292,8 +301,94 @@ function createWsTransport(options) {
292
301
  }
293
302
  };
294
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
+ };
295
389
  // Annotate the CommonJS export names for ESM import in node:
296
390
  0 && (module.exports = {
391
+ WsTransport,
297
392
  createWsHandler,
298
393
  createWsTransport
299
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;
@@ -94,6 +99,9 @@ type TServerMessage = {
94
99
  } | {
95
100
  type: 'pending';
96
101
  pending: string | null;
102
+ } | {
103
+ type: 'execution_workspace_event';
104
+ snapshot: IExecutionWorkspaceSnapshot;
97
105
  } | {
98
106
  type: 'background_task_event';
99
107
  event: TBackgroundTaskEvent;
@@ -183,4 +191,41 @@ declare function createWsTransport(options: IWsTransportOptions): ITransportAdap
183
191
  onMessage: ((data: string) => void) | null;
184
192
  };
185
193
 
186
- 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;
@@ -94,6 +99,9 @@ type TServerMessage = {
94
99
  } | {
95
100
  type: 'pending';
96
101
  pending: string | null;
102
+ } | {
103
+ type: 'execution_workspace_event';
104
+ snapshot: IExecutionWorkspaceSnapshot;
97
105
  } | {
98
106
  type: 'background_task_event';
99
107
  event: TBackgroundTaskEvent;
@@ -183,4 +191,41 @@ declare function createWsTransport(options: IWsTransportOptions): ITransportAdap
183
191
  onMessage: ((data: string) => void) | null;
184
192
  };
185
193
 
186
- 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 };
@@ -131,6 +131,7 @@ function subscribeSessionEvents(session, send) {
131
131
  const onError = (error) => send({ type: "error", message: error.message });
132
132
  const onBackgroundTaskEvent = (event) => send({ type: "background_task_event", event });
133
133
  const onBackgroundJobGroupEvent = (event) => send({ type: "background_job_group_event", event });
134
+ const onExecutionWorkspace = (event) => send({ type: "execution_workspace_event", snapshot: event.snapshot });
134
135
  session.on("user_message", onUserMessage);
135
136
  session.on("text_delta", onTextDelta);
136
137
  session.on("tool_start", onToolStart);
@@ -141,6 +142,7 @@ function subscribeSessionEvents(session, send) {
141
142
  session.on("error", onError);
142
143
  session.on("background_task_event", onBackgroundTaskEvent);
143
144
  session.on("background_job_group_event", onBackgroundJobGroupEvent);
145
+ session.on("execution_workspace_event", onExecutionWorkspace);
144
146
  return () => {
145
147
  session.off("user_message", onUserMessage);
146
148
  session.off("text_delta", onTextDelta);
@@ -152,6 +154,7 @@ function subscribeSessionEvents(session, send) {
152
154
  session.off("error", onError);
153
155
  session.off("background_task_event", onBackgroundTaskEvent);
154
156
  session.off("background_job_group_event", onBackgroundJobGroupEvent);
157
+ session.off("execution_workspace_event", onExecutionWorkspace);
155
158
  };
156
159
  }
157
160
  function createWsMessageHandler(session, send) {
@@ -195,7 +198,7 @@ function isSessionControlMessage(msg) {
195
198
  return msg.type === "submit" || msg.type === "command" || msg.type === "abort" || msg.type === "cancel-queue";
196
199
  }
197
200
  function isSessionQueryMessage(msg) {
198
- 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";
199
202
  }
200
203
  function isBackgroundQueryMessage(msg) {
201
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";
@@ -237,6 +240,11 @@ function handleSessionQueryMessage(session, send, msg) {
237
240
  send({ type: "context", state: session.getContextState() });
238
241
  } else if (msg.type === "get-executing") {
239
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
+ });
240
248
  } else {
241
249
  send({ type: "pending", pending: session.getPendingPrompt() });
242
250
  }
@@ -265,7 +273,93 @@ function createWsTransport(options) {
265
273
  }
266
274
  };
267
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
+ };
268
361
  export {
362
+ WsTransport,
269
363
  createWsHandler,
270
364
  createWsTransport
271
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.62",
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.62"
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",