@robota-sdk/agent-transport-ws 3.0.0-beta.54 → 3.0.0-beta.56

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.
@@ -25,9 +25,129 @@ __export(index_exports, {
25
25
  });
26
26
  module.exports = __toCommonJS(index_exports);
27
27
 
28
+ // src/ws-background-messages.ts
29
+ function handleBackgroundQueryMessage(session, send, msg) {
30
+ if (msg.type === "get-background-tasks") {
31
+ send({ type: "background_tasks", tasks: session.listBackgroundTasks(msg.filter) });
32
+ return;
33
+ }
34
+ if (msg.type === "get-background-task") {
35
+ sendBackgroundTaskSnapshot(session, send, msg);
36
+ return;
37
+ }
38
+ if (msg.type === "get-background-job-groups") {
39
+ send({ type: "background_job_groups", groups: session.listBackgroundJobGroups() });
40
+ return;
41
+ }
42
+ if (msg.type === "get-background-job-group") {
43
+ sendBackgroundJobGroupSnapshot(session, send, msg);
44
+ return;
45
+ }
46
+ if (msg.type === "wait-background-job-group") {
47
+ sendBackgroundJobGroupWaitResult(session, send, msg);
48
+ return;
49
+ }
50
+ sendBackgroundTaskLogPage(session, send, msg);
51
+ }
52
+ function handleBackgroundControlMessage(session, send, msg) {
53
+ if (!msg.taskId) {
54
+ send({ type: "protocol_error", message: "taskId is required" });
55
+ return;
56
+ }
57
+ if (msg.type === "cancel-background-task") {
58
+ sendBackgroundTaskControlResult(
59
+ send,
60
+ "cancel",
61
+ msg.taskId,
62
+ session.cancelBackgroundTask(msg.taskId, msg.reason)
63
+ );
64
+ return;
65
+ }
66
+ if (msg.type === "close-background-task") {
67
+ sendBackgroundTaskControlResult(
68
+ send,
69
+ "close",
70
+ msg.taskId,
71
+ session.closeBackgroundTask(msg.taskId)
72
+ );
73
+ return;
74
+ }
75
+ sendBackgroundTaskInput(session, send, msg);
76
+ }
77
+ function sendBackgroundTaskSnapshot(session, send, msg) {
78
+ if (!msg.taskId) {
79
+ send({ type: "protocol_error", message: "taskId is required" });
80
+ return;
81
+ }
82
+ send({
83
+ type: "background_task",
84
+ taskId: msg.taskId,
85
+ task: session.getBackgroundTask(msg.taskId) ?? null
86
+ });
87
+ }
88
+ function sendBackgroundTaskLogPage(session, send, msg) {
89
+ if (!msg.taskId) {
90
+ send({ type: "protocol_error", message: "taskId is required" });
91
+ return;
92
+ }
93
+ session.readBackgroundTaskLog(msg.taskId, msg.cursor).then(
94
+ (page) => send({ type: "background_task_log", taskId: msg.taskId, page }),
95
+ (error) => send({ type: "protocol_error", message: error.message })
96
+ );
97
+ }
98
+ function sendBackgroundJobGroupSnapshot(session, send, msg) {
99
+ if (!msg.groupId) {
100
+ send({ type: "protocol_error", message: "groupId is required" });
101
+ return;
102
+ }
103
+ send({
104
+ type: "background_job_group",
105
+ groupId: msg.groupId,
106
+ group: session.getBackgroundJobGroup(msg.groupId) ?? null
107
+ });
108
+ }
109
+ function sendBackgroundJobGroupWaitResult(session, send, msg) {
110
+ if (!msg.groupId) {
111
+ send({ type: "protocol_error", message: "groupId is required" });
112
+ return;
113
+ }
114
+ session.waitBackgroundJobGroup(msg.groupId).then(
115
+ (group) => send({ type: "background_job_group", groupId: msg.groupId, group }),
116
+ (error) => send({ type: "protocol_error", message: error.message })
117
+ );
118
+ }
119
+ function sendBackgroundTaskInput(session, send, msg) {
120
+ if (!msg.input) {
121
+ send({ type: "protocol_error", message: "input is required" });
122
+ return;
123
+ }
124
+ sendBackgroundTaskControlResult(
125
+ send,
126
+ "send",
127
+ msg.taskId,
128
+ session.sendBackgroundTask(msg.taskId, msg.input)
129
+ );
130
+ }
131
+ function sendBackgroundTaskControlResult(send, action, taskId, operation) {
132
+ operation.then(
133
+ () => send({ type: "background_task_control_result", action, taskId, success: true }),
134
+ (error) => send({
135
+ type: "background_task_control_result",
136
+ action,
137
+ taskId,
138
+ success: false,
139
+ message: error.message
140
+ })
141
+ );
142
+ }
143
+
28
144
  // src/ws-handler.ts
29
145
  function createWsHandler(options) {
30
- const { session, send } = options;
146
+ const cleanup = subscribeSessionEvents(options.session, options.send);
147
+ const onMessage = createWsMessageHandler(options.session, options.send);
148
+ return { onMessage, cleanup };
149
+ }
150
+ function subscribeSessionEvents(session, send) {
31
151
  const onTextDelta = (delta) => send({ type: "text_delta", delta });
32
152
  const onToolStart = (state) => send({ type: "tool_start", state });
33
153
  const onToolEnd = (state) => send({ type: "tool_end", state });
@@ -35,6 +155,8 @@ function createWsHandler(options) {
35
155
  const onComplete = (result) => send({ type: "complete", result });
36
156
  const onInterrupted = (result) => send({ type: "interrupted", result });
37
157
  const onError = (error) => send({ type: "error", message: error.message });
158
+ const onBackgroundTaskEvent = (event) => send({ type: "background_task_event", event });
159
+ const onBackgroundJobGroupEvent = (event) => send({ type: "background_job_group_event", event });
38
160
  session.on("text_delta", onTextDelta);
39
161
  session.on("tool_start", onToolStart);
40
162
  session.on("tool_end", onToolEnd);
@@ -42,63 +164,9 @@ function createWsHandler(options) {
42
164
  session.on("complete", onComplete);
43
165
  session.on("interrupted", onInterrupted);
44
166
  session.on("error", onError);
45
- const onMessage = (data) => {
46
- let msg;
47
- try {
48
- msg = JSON.parse(data);
49
- } catch {
50
- send({ type: "protocol_error", message: "Invalid JSON" });
51
- return;
52
- }
53
- switch (msg.type) {
54
- case "submit":
55
- if (!msg.prompt) {
56
- send({ type: "protocol_error", message: "prompt is required" });
57
- return;
58
- }
59
- session.submit(msg.prompt);
60
- break;
61
- case "command":
62
- if (!msg.name) {
63
- send({ type: "protocol_error", message: "name is required" });
64
- return;
65
- }
66
- session.executeCommand(msg.name, msg.args ?? "").then((result) => {
67
- send({
68
- type: "command_result",
69
- name: msg.name,
70
- message: result?.message ?? `Unknown command: ${msg.name}`,
71
- success: result?.success ?? false,
72
- data: result?.data
73
- });
74
- });
75
- break;
76
- case "abort":
77
- session.abort();
78
- break;
79
- case "cancel-queue":
80
- session.cancelQueue();
81
- break;
82
- case "get-messages":
83
- send({ type: "messages", messages: session.getMessages() });
84
- break;
85
- case "get-context":
86
- send({ type: "context", state: session.getContextState() });
87
- break;
88
- case "get-executing":
89
- send({ type: "executing", executing: session.isExecuting() });
90
- break;
91
- case "get-pending":
92
- send({ type: "pending", pending: session.getPendingPrompt() });
93
- break;
94
- default:
95
- send({
96
- type: "protocol_error",
97
- message: `Unknown message type: ${msg.type}`
98
- });
99
- }
100
- };
101
- const cleanup = () => {
167
+ session.on("background_task_event", onBackgroundTaskEvent);
168
+ session.on("background_job_group_event", onBackgroundJobGroupEvent);
169
+ return () => {
102
170
  session.off("text_delta", onTextDelta);
103
171
  session.off("tool_start", onToolStart);
104
172
  session.off("tool_end", onToolEnd);
@@ -106,8 +174,96 @@ function createWsHandler(options) {
106
174
  session.off("complete", onComplete);
107
175
  session.off("interrupted", onInterrupted);
108
176
  session.off("error", onError);
177
+ session.off("background_task_event", onBackgroundTaskEvent);
178
+ session.off("background_job_group_event", onBackgroundJobGroupEvent);
109
179
  };
110
- return { onMessage, cleanup };
180
+ }
181
+ function createWsMessageHandler(session, send) {
182
+ return (data) => {
183
+ const msg = parseClientMessage(data, send);
184
+ if (!msg) return;
185
+ handleClientMessage(session, send, msg);
186
+ };
187
+ }
188
+ function parseClientMessage(data, send) {
189
+ try {
190
+ return JSON.parse(data);
191
+ } catch {
192
+ send({ type: "protocol_error", message: "Invalid JSON" });
193
+ return null;
194
+ }
195
+ }
196
+ function handleClientMessage(session, send, msg) {
197
+ if (isSessionControlMessage(msg)) {
198
+ handleSessionControlMessage(session, send, msg);
199
+ return;
200
+ }
201
+ if (isSessionQueryMessage(msg)) {
202
+ handleSessionQueryMessage(session, send, msg);
203
+ return;
204
+ }
205
+ if (isBackgroundQueryMessage(msg)) {
206
+ handleBackgroundQueryMessage(session, send, msg);
207
+ return;
208
+ }
209
+ if (isBackgroundControlMessage(msg)) {
210
+ handleBackgroundControlMessage(session, send, msg);
211
+ return;
212
+ }
213
+ send({ type: "protocol_error", message: `Unknown message type: ${getMessageType(msg)}` });
214
+ }
215
+ function getMessageType(msg) {
216
+ return msg.type;
217
+ }
218
+ function isSessionControlMessage(msg) {
219
+ return msg.type === "submit" || msg.type === "command" || msg.type === "abort" || msg.type === "cancel-queue";
220
+ }
221
+ function isSessionQueryMessage(msg) {
222
+ return msg.type === "get-messages" || msg.type === "get-context" || msg.type === "get-executing" || msg.type === "get-pending";
223
+ }
224
+ function isBackgroundQueryMessage(msg) {
225
+ 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";
226
+ }
227
+ function isBackgroundControlMessage(msg) {
228
+ return msg.type === "cancel-background-task" || msg.type === "close-background-task" || msg.type === "send-background-task";
229
+ }
230
+ function handleSessionControlMessage(session, send, msg) {
231
+ if (msg.type === "submit") {
232
+ if (!msg.prompt) {
233
+ send({ type: "protocol_error", message: "prompt is required" });
234
+ return;
235
+ }
236
+ session.submit(msg.prompt);
237
+ } else if (msg.type === "command") {
238
+ if (!msg.name) {
239
+ send({ type: "protocol_error", message: "name is required" });
240
+ return;
241
+ }
242
+ session.executeCommand(msg.name, msg.args ?? "").then((result) => {
243
+ send({
244
+ type: "command_result",
245
+ name: msg.name,
246
+ message: result?.message ?? `Unknown command: ${msg.name}`,
247
+ success: result?.success ?? false,
248
+ data: result?.data
249
+ });
250
+ });
251
+ } else if (msg.type === "abort") {
252
+ session.abort();
253
+ } else {
254
+ session.cancelQueue();
255
+ }
256
+ }
257
+ function handleSessionQueryMessage(session, send, msg) {
258
+ if (msg.type === "get-messages") {
259
+ send({ type: "messages", messages: session.getMessages() });
260
+ } else if (msg.type === "get-context") {
261
+ send({ type: "context", state: session.getContextState() });
262
+ } else if (msg.type === "get-executing") {
263
+ send({ type: "executing", executing: session.isExecuting() });
264
+ } else {
265
+ send({ type: "pending", pending: session.getPendingPrompt() });
266
+ }
111
267
  }
112
268
 
113
269
  // src/ws-transport.ts
@@ -1,16 +1,7 @@
1
- import { InteractiveSession, IToolState, IExecutionResult, ITransportAdapter } from '@robota-sdk/agent-sdk';
1
+ import { IToolState, IExecutionResult, ICommandResult, InteractiveSession, TBackgroundTaskEvent, TBackgroundJobGroupEvent, IBackgroundTaskState, IBackgroundJobGroupState, IBackgroundTaskLogPage, IBackgroundTaskListFilter, IBackgroundTaskInput, IBackgroundTaskLogCursor, ITransportAdapter } from '@robota-sdk/agent-sdk';
2
2
 
3
- /**
4
- * WebSocket transport adapter — exposes InteractiveSession over WebSocket.
5
- *
6
- * Framework-agnostic: works with any WebSocket implementation via
7
- * send/onMessage callbacks. No dependency on ws, uWebSockets, etc.
8
- *
9
- * Protocol: JSON messages with { type, ...payload } structure.
10
- * Server pushes InteractiveSession events to client in real-time.
11
- */
12
-
13
- /** Inbound message from client → server. */
3
+ type TBackgroundControlAction = 'cancel' | 'close' | 'send';
4
+ /** Inbound message from client to server. */
14
5
  type TClientMessage = {
15
6
  type: 'submit';
16
7
  prompt: string;
@@ -30,8 +21,37 @@ type TClientMessage = {
30
21
  type: 'get-executing';
31
22
  } | {
32
23
  type: 'get-pending';
24
+ } | {
25
+ type: 'get-background-tasks';
26
+ filter?: IBackgroundTaskListFilter;
27
+ } | {
28
+ type: 'get-background-task';
29
+ taskId: string;
30
+ } | {
31
+ type: 'get-background-job-groups';
32
+ } | {
33
+ type: 'get-background-job-group';
34
+ groupId: string;
35
+ } | {
36
+ type: 'wait-background-job-group';
37
+ groupId: string;
38
+ } | {
39
+ type: 'cancel-background-task';
40
+ taskId: string;
41
+ reason?: string;
42
+ } | {
43
+ type: 'close-background-task';
44
+ taskId: string;
45
+ } | {
46
+ type: 'send-background-task';
47
+ taskId: string;
48
+ input: IBackgroundTaskInput;
49
+ } | {
50
+ type: 'read-background-task-log';
51
+ taskId: string;
52
+ cursor?: IBackgroundTaskLogCursor;
33
53
  };
34
- /** Outbound message from server → client. */
54
+ /** Outbound message from server to client. */
35
55
  type TServerMessage = {
36
56
  type: 'text_delta';
37
57
  delta: string;
@@ -58,23 +78,64 @@ type TServerMessage = {
58
78
  name: string;
59
79
  message: string;
60
80
  success: boolean;
61
- data?: Record<string, unknown>;
81
+ data?: ICommandResult['data'];
62
82
  } | {
63
83
  type: 'messages';
64
- messages: unknown[];
84
+ messages: ReturnType<InteractiveSession['getMessages']>;
65
85
  } | {
66
86
  type: 'context';
67
- state: unknown;
87
+ state: ReturnType<InteractiveSession['getContextState']>;
68
88
  } | {
69
89
  type: 'executing';
70
90
  executing: boolean;
71
91
  } | {
72
92
  type: 'pending';
73
93
  pending: string | null;
94
+ } | {
95
+ type: 'background_task_event';
96
+ event: TBackgroundTaskEvent;
97
+ } | {
98
+ type: 'background_job_group_event';
99
+ event: TBackgroundJobGroupEvent;
100
+ } | {
101
+ type: 'background_tasks';
102
+ tasks: IBackgroundTaskState[];
103
+ } | {
104
+ type: 'background_task';
105
+ taskId: string;
106
+ task: IBackgroundTaskState | null;
107
+ } | {
108
+ type: 'background_job_groups';
109
+ groups: IBackgroundJobGroupState[];
110
+ } | {
111
+ type: 'background_job_group';
112
+ groupId: string;
113
+ group: IBackgroundJobGroupState | null;
114
+ } | {
115
+ type: 'background_task_log';
116
+ taskId: string;
117
+ page: IBackgroundTaskLogPage;
118
+ } | {
119
+ type: 'background_task_control_result';
120
+ action: TBackgroundControlAction;
121
+ taskId: string;
122
+ success: boolean;
123
+ message?: string;
74
124
  } | {
75
125
  type: 'protocol_error';
76
126
  message: string;
77
127
  };
128
+
129
+ /**
130
+ * WebSocket transport adapter — exposes InteractiveSession over WebSocket.
131
+ *
132
+ * Framework-agnostic: works with any WebSocket implementation via
133
+ * send/onMessage callbacks. No dependency on ws, uWebSockets, etc.
134
+ *
135
+ * Protocol: JSON messages with { type, ...payload } structure.
136
+ * Server pushes InteractiveSession events to client in real-time.
137
+ */
138
+
78
139
  interface IWsHandlerOptions {
79
140
  /** InteractiveSession to expose. */
80
141
  session: InteractiveSession;
@@ -1,16 +1,7 @@
1
- import { InteractiveSession, IToolState, IExecutionResult, ITransportAdapter } from '@robota-sdk/agent-sdk';
1
+ import { IToolState, IExecutionResult, ICommandResult, InteractiveSession, TBackgroundTaskEvent, TBackgroundJobGroupEvent, IBackgroundTaskState, IBackgroundJobGroupState, IBackgroundTaskLogPage, IBackgroundTaskListFilter, IBackgroundTaskInput, IBackgroundTaskLogCursor, ITransportAdapter } from '@robota-sdk/agent-sdk';
2
2
 
3
- /**
4
- * WebSocket transport adapter — exposes InteractiveSession over WebSocket.
5
- *
6
- * Framework-agnostic: works with any WebSocket implementation via
7
- * send/onMessage callbacks. No dependency on ws, uWebSockets, etc.
8
- *
9
- * Protocol: JSON messages with { type, ...payload } structure.
10
- * Server pushes InteractiveSession events to client in real-time.
11
- */
12
-
13
- /** Inbound message from client → server. */
3
+ type TBackgroundControlAction = 'cancel' | 'close' | 'send';
4
+ /** Inbound message from client to server. */
14
5
  type TClientMessage = {
15
6
  type: 'submit';
16
7
  prompt: string;
@@ -30,8 +21,37 @@ type TClientMessage = {
30
21
  type: 'get-executing';
31
22
  } | {
32
23
  type: 'get-pending';
24
+ } | {
25
+ type: 'get-background-tasks';
26
+ filter?: IBackgroundTaskListFilter;
27
+ } | {
28
+ type: 'get-background-task';
29
+ taskId: string;
30
+ } | {
31
+ type: 'get-background-job-groups';
32
+ } | {
33
+ type: 'get-background-job-group';
34
+ groupId: string;
35
+ } | {
36
+ type: 'wait-background-job-group';
37
+ groupId: string;
38
+ } | {
39
+ type: 'cancel-background-task';
40
+ taskId: string;
41
+ reason?: string;
42
+ } | {
43
+ type: 'close-background-task';
44
+ taskId: string;
45
+ } | {
46
+ type: 'send-background-task';
47
+ taskId: string;
48
+ input: IBackgroundTaskInput;
49
+ } | {
50
+ type: 'read-background-task-log';
51
+ taskId: string;
52
+ cursor?: IBackgroundTaskLogCursor;
33
53
  };
34
- /** Outbound message from server → client. */
54
+ /** Outbound message from server to client. */
35
55
  type TServerMessage = {
36
56
  type: 'text_delta';
37
57
  delta: string;
@@ -58,23 +78,64 @@ type TServerMessage = {
58
78
  name: string;
59
79
  message: string;
60
80
  success: boolean;
61
- data?: Record<string, unknown>;
81
+ data?: ICommandResult['data'];
62
82
  } | {
63
83
  type: 'messages';
64
- messages: unknown[];
84
+ messages: ReturnType<InteractiveSession['getMessages']>;
65
85
  } | {
66
86
  type: 'context';
67
- state: unknown;
87
+ state: ReturnType<InteractiveSession['getContextState']>;
68
88
  } | {
69
89
  type: 'executing';
70
90
  executing: boolean;
71
91
  } | {
72
92
  type: 'pending';
73
93
  pending: string | null;
94
+ } | {
95
+ type: 'background_task_event';
96
+ event: TBackgroundTaskEvent;
97
+ } | {
98
+ type: 'background_job_group_event';
99
+ event: TBackgroundJobGroupEvent;
100
+ } | {
101
+ type: 'background_tasks';
102
+ tasks: IBackgroundTaskState[];
103
+ } | {
104
+ type: 'background_task';
105
+ taskId: string;
106
+ task: IBackgroundTaskState | null;
107
+ } | {
108
+ type: 'background_job_groups';
109
+ groups: IBackgroundJobGroupState[];
110
+ } | {
111
+ type: 'background_job_group';
112
+ groupId: string;
113
+ group: IBackgroundJobGroupState | null;
114
+ } | {
115
+ type: 'background_task_log';
116
+ taskId: string;
117
+ page: IBackgroundTaskLogPage;
118
+ } | {
119
+ type: 'background_task_control_result';
120
+ action: TBackgroundControlAction;
121
+ taskId: string;
122
+ success: boolean;
123
+ message?: string;
74
124
  } | {
75
125
  type: 'protocol_error';
76
126
  message: string;
77
127
  };
128
+
129
+ /**
130
+ * WebSocket transport adapter — exposes InteractiveSession over WebSocket.
131
+ *
132
+ * Framework-agnostic: works with any WebSocket implementation via
133
+ * send/onMessage callbacks. No dependency on ws, uWebSockets, etc.
134
+ *
135
+ * Protocol: JSON messages with { type, ...payload } structure.
136
+ * Server pushes InteractiveSession events to client in real-time.
137
+ */
138
+
78
139
  interface IWsHandlerOptions {
79
140
  /** InteractiveSession to expose. */
80
141
  session: InteractiveSession;
@@ -1,6 +1,126 @@
1
+ // src/ws-background-messages.ts
2
+ function handleBackgroundQueryMessage(session, send, msg) {
3
+ if (msg.type === "get-background-tasks") {
4
+ send({ type: "background_tasks", tasks: session.listBackgroundTasks(msg.filter) });
5
+ return;
6
+ }
7
+ if (msg.type === "get-background-task") {
8
+ sendBackgroundTaskSnapshot(session, send, msg);
9
+ return;
10
+ }
11
+ if (msg.type === "get-background-job-groups") {
12
+ send({ type: "background_job_groups", groups: session.listBackgroundJobGroups() });
13
+ return;
14
+ }
15
+ if (msg.type === "get-background-job-group") {
16
+ sendBackgroundJobGroupSnapshot(session, send, msg);
17
+ return;
18
+ }
19
+ if (msg.type === "wait-background-job-group") {
20
+ sendBackgroundJobGroupWaitResult(session, send, msg);
21
+ return;
22
+ }
23
+ sendBackgroundTaskLogPage(session, send, msg);
24
+ }
25
+ function handleBackgroundControlMessage(session, send, msg) {
26
+ if (!msg.taskId) {
27
+ send({ type: "protocol_error", message: "taskId is required" });
28
+ return;
29
+ }
30
+ if (msg.type === "cancel-background-task") {
31
+ sendBackgroundTaskControlResult(
32
+ send,
33
+ "cancel",
34
+ msg.taskId,
35
+ session.cancelBackgroundTask(msg.taskId, msg.reason)
36
+ );
37
+ return;
38
+ }
39
+ if (msg.type === "close-background-task") {
40
+ sendBackgroundTaskControlResult(
41
+ send,
42
+ "close",
43
+ msg.taskId,
44
+ session.closeBackgroundTask(msg.taskId)
45
+ );
46
+ return;
47
+ }
48
+ sendBackgroundTaskInput(session, send, msg);
49
+ }
50
+ function sendBackgroundTaskSnapshot(session, send, msg) {
51
+ if (!msg.taskId) {
52
+ send({ type: "protocol_error", message: "taskId is required" });
53
+ return;
54
+ }
55
+ send({
56
+ type: "background_task",
57
+ taskId: msg.taskId,
58
+ task: session.getBackgroundTask(msg.taskId) ?? null
59
+ });
60
+ }
61
+ function sendBackgroundTaskLogPage(session, send, msg) {
62
+ if (!msg.taskId) {
63
+ send({ type: "protocol_error", message: "taskId is required" });
64
+ return;
65
+ }
66
+ session.readBackgroundTaskLog(msg.taskId, msg.cursor).then(
67
+ (page) => send({ type: "background_task_log", taskId: msg.taskId, page }),
68
+ (error) => send({ type: "protocol_error", message: error.message })
69
+ );
70
+ }
71
+ function sendBackgroundJobGroupSnapshot(session, send, msg) {
72
+ if (!msg.groupId) {
73
+ send({ type: "protocol_error", message: "groupId is required" });
74
+ return;
75
+ }
76
+ send({
77
+ type: "background_job_group",
78
+ groupId: msg.groupId,
79
+ group: session.getBackgroundJobGroup(msg.groupId) ?? null
80
+ });
81
+ }
82
+ function sendBackgroundJobGroupWaitResult(session, send, msg) {
83
+ if (!msg.groupId) {
84
+ send({ type: "protocol_error", message: "groupId is required" });
85
+ return;
86
+ }
87
+ session.waitBackgroundJobGroup(msg.groupId).then(
88
+ (group) => send({ type: "background_job_group", groupId: msg.groupId, group }),
89
+ (error) => send({ type: "protocol_error", message: error.message })
90
+ );
91
+ }
92
+ function sendBackgroundTaskInput(session, send, msg) {
93
+ if (!msg.input) {
94
+ send({ type: "protocol_error", message: "input is required" });
95
+ return;
96
+ }
97
+ sendBackgroundTaskControlResult(
98
+ send,
99
+ "send",
100
+ msg.taskId,
101
+ session.sendBackgroundTask(msg.taskId, msg.input)
102
+ );
103
+ }
104
+ function sendBackgroundTaskControlResult(send, action, taskId, operation) {
105
+ operation.then(
106
+ () => send({ type: "background_task_control_result", action, taskId, success: true }),
107
+ (error) => send({
108
+ type: "background_task_control_result",
109
+ action,
110
+ taskId,
111
+ success: false,
112
+ message: error.message
113
+ })
114
+ );
115
+ }
116
+
1
117
  // src/ws-handler.ts
2
118
  function createWsHandler(options) {
3
- const { session, send } = options;
119
+ const cleanup = subscribeSessionEvents(options.session, options.send);
120
+ const onMessage = createWsMessageHandler(options.session, options.send);
121
+ return { onMessage, cleanup };
122
+ }
123
+ function subscribeSessionEvents(session, send) {
4
124
  const onTextDelta = (delta) => send({ type: "text_delta", delta });
5
125
  const onToolStart = (state) => send({ type: "tool_start", state });
6
126
  const onToolEnd = (state) => send({ type: "tool_end", state });
@@ -8,6 +128,8 @@ function createWsHandler(options) {
8
128
  const onComplete = (result) => send({ type: "complete", result });
9
129
  const onInterrupted = (result) => send({ type: "interrupted", result });
10
130
  const onError = (error) => send({ type: "error", message: error.message });
131
+ const onBackgroundTaskEvent = (event) => send({ type: "background_task_event", event });
132
+ const onBackgroundJobGroupEvent = (event) => send({ type: "background_job_group_event", event });
11
133
  session.on("text_delta", onTextDelta);
12
134
  session.on("tool_start", onToolStart);
13
135
  session.on("tool_end", onToolEnd);
@@ -15,63 +137,9 @@ function createWsHandler(options) {
15
137
  session.on("complete", onComplete);
16
138
  session.on("interrupted", onInterrupted);
17
139
  session.on("error", onError);
18
- const onMessage = (data) => {
19
- let msg;
20
- try {
21
- msg = JSON.parse(data);
22
- } catch {
23
- send({ type: "protocol_error", message: "Invalid JSON" });
24
- return;
25
- }
26
- switch (msg.type) {
27
- case "submit":
28
- if (!msg.prompt) {
29
- send({ type: "protocol_error", message: "prompt is required" });
30
- return;
31
- }
32
- session.submit(msg.prompt);
33
- break;
34
- case "command":
35
- if (!msg.name) {
36
- send({ type: "protocol_error", message: "name is required" });
37
- return;
38
- }
39
- session.executeCommand(msg.name, msg.args ?? "").then((result) => {
40
- send({
41
- type: "command_result",
42
- name: msg.name,
43
- message: result?.message ?? `Unknown command: ${msg.name}`,
44
- success: result?.success ?? false,
45
- data: result?.data
46
- });
47
- });
48
- break;
49
- case "abort":
50
- session.abort();
51
- break;
52
- case "cancel-queue":
53
- session.cancelQueue();
54
- break;
55
- case "get-messages":
56
- send({ type: "messages", messages: session.getMessages() });
57
- break;
58
- case "get-context":
59
- send({ type: "context", state: session.getContextState() });
60
- break;
61
- case "get-executing":
62
- send({ type: "executing", executing: session.isExecuting() });
63
- break;
64
- case "get-pending":
65
- send({ type: "pending", pending: session.getPendingPrompt() });
66
- break;
67
- default:
68
- send({
69
- type: "protocol_error",
70
- message: `Unknown message type: ${msg.type}`
71
- });
72
- }
73
- };
74
- const cleanup = () => {
140
+ session.on("background_task_event", onBackgroundTaskEvent);
141
+ session.on("background_job_group_event", onBackgroundJobGroupEvent);
142
+ return () => {
75
143
  session.off("text_delta", onTextDelta);
76
144
  session.off("tool_start", onToolStart);
77
145
  session.off("tool_end", onToolEnd);
@@ -79,8 +147,96 @@ function createWsHandler(options) {
79
147
  session.off("complete", onComplete);
80
148
  session.off("interrupted", onInterrupted);
81
149
  session.off("error", onError);
150
+ session.off("background_task_event", onBackgroundTaskEvent);
151
+ session.off("background_job_group_event", onBackgroundJobGroupEvent);
82
152
  };
83
- return { onMessage, cleanup };
153
+ }
154
+ function createWsMessageHandler(session, send) {
155
+ return (data) => {
156
+ const msg = parseClientMessage(data, send);
157
+ if (!msg) return;
158
+ handleClientMessage(session, send, msg);
159
+ };
160
+ }
161
+ function parseClientMessage(data, send) {
162
+ try {
163
+ return JSON.parse(data);
164
+ } catch {
165
+ send({ type: "protocol_error", message: "Invalid JSON" });
166
+ return null;
167
+ }
168
+ }
169
+ function handleClientMessage(session, send, msg) {
170
+ if (isSessionControlMessage(msg)) {
171
+ handleSessionControlMessage(session, send, msg);
172
+ return;
173
+ }
174
+ if (isSessionQueryMessage(msg)) {
175
+ handleSessionQueryMessage(session, send, msg);
176
+ return;
177
+ }
178
+ if (isBackgroundQueryMessage(msg)) {
179
+ handleBackgroundQueryMessage(session, send, msg);
180
+ return;
181
+ }
182
+ if (isBackgroundControlMessage(msg)) {
183
+ handleBackgroundControlMessage(session, send, msg);
184
+ return;
185
+ }
186
+ send({ type: "protocol_error", message: `Unknown message type: ${getMessageType(msg)}` });
187
+ }
188
+ function getMessageType(msg) {
189
+ return msg.type;
190
+ }
191
+ function isSessionControlMessage(msg) {
192
+ return msg.type === "submit" || msg.type === "command" || msg.type === "abort" || msg.type === "cancel-queue";
193
+ }
194
+ function isSessionQueryMessage(msg) {
195
+ return msg.type === "get-messages" || msg.type === "get-context" || msg.type === "get-executing" || msg.type === "get-pending";
196
+ }
197
+ function isBackgroundQueryMessage(msg) {
198
+ 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";
199
+ }
200
+ function isBackgroundControlMessage(msg) {
201
+ return msg.type === "cancel-background-task" || msg.type === "close-background-task" || msg.type === "send-background-task";
202
+ }
203
+ function handleSessionControlMessage(session, send, msg) {
204
+ if (msg.type === "submit") {
205
+ if (!msg.prompt) {
206
+ send({ type: "protocol_error", message: "prompt is required" });
207
+ return;
208
+ }
209
+ session.submit(msg.prompt);
210
+ } else if (msg.type === "command") {
211
+ if (!msg.name) {
212
+ send({ type: "protocol_error", message: "name is required" });
213
+ return;
214
+ }
215
+ session.executeCommand(msg.name, msg.args ?? "").then((result) => {
216
+ send({
217
+ type: "command_result",
218
+ name: msg.name,
219
+ message: result?.message ?? `Unknown command: ${msg.name}`,
220
+ success: result?.success ?? false,
221
+ data: result?.data
222
+ });
223
+ });
224
+ } else if (msg.type === "abort") {
225
+ session.abort();
226
+ } else {
227
+ session.cancelQueue();
228
+ }
229
+ }
230
+ function handleSessionQueryMessage(session, send, msg) {
231
+ if (msg.type === "get-messages") {
232
+ send({ type: "messages", messages: session.getMessages() });
233
+ } else if (msg.type === "get-context") {
234
+ send({ type: "context", state: session.getContextState() });
235
+ } else if (msg.type === "get-executing") {
236
+ send({ type: "executing", executing: session.isExecuting() });
237
+ } else {
238
+ send({ type: "pending", pending: session.getPendingPrompt() });
239
+ }
84
240
  }
85
241
 
86
242
  // src/ws-transport.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@robota-sdk/agent-transport-ws",
3
- "version": "3.0.0-beta.54",
3
+ "version": "3.0.0-beta.56",
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,7 +30,7 @@
30
30
  "dist"
31
31
  ],
32
32
  "dependencies": {
33
- "@robota-sdk/agent-sdk": "3.0.0-beta.54"
33
+ "@robota-sdk/agent-sdk": "3.0.0-beta.56"
34
34
  },
35
35
  "devDependencies": {
36
36
  "rimraf": "^5.0.5",