@robota-sdk/agent-transport-ws 3.0.0-beta.53 → 3.0.0-beta.55
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/dist/node/index.cjs +215 -59
- package/dist/node/index.d.cts +77 -16
- package/dist/node/index.d.ts +77 -16
- package/dist/node/index.js +215 -59
- package/package.json +4 -2
package/dist/node/index.cjs
CHANGED
|
@@ -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
|
|
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
|
-
|
|
46
|
-
|
|
47
|
-
|
|
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
|
-
|
|
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
|
package/dist/node/index.d.cts
CHANGED
|
@@ -1,16 +1,7 @@
|
|
|
1
|
-
import { InteractiveSession,
|
|
1
|
+
import { IToolState, IExecutionResult, ICommandResult, InteractiveSession, TBackgroundTaskEvent, TBackgroundJobGroupEvent, IBackgroundTaskState, IBackgroundJobGroupState, IBackgroundTaskLogPage, IBackgroundTaskListFilter, IBackgroundTaskInput, IBackgroundTaskLogCursor, ITransportAdapter } from '@robota-sdk/agent-sdk';
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
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
|
|
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?:
|
|
81
|
+
data?: ICommandResult['data'];
|
|
62
82
|
} | {
|
|
63
83
|
type: 'messages';
|
|
64
|
-
messages:
|
|
84
|
+
messages: ReturnType<InteractiveSession['getMessages']>;
|
|
65
85
|
} | {
|
|
66
86
|
type: 'context';
|
|
67
|
-
state:
|
|
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;
|
package/dist/node/index.d.ts
CHANGED
|
@@ -1,16 +1,7 @@
|
|
|
1
|
-
import { InteractiveSession,
|
|
1
|
+
import { IToolState, IExecutionResult, ICommandResult, InteractiveSession, TBackgroundTaskEvent, TBackgroundJobGroupEvent, IBackgroundTaskState, IBackgroundJobGroupState, IBackgroundTaskLogPage, IBackgroundTaskListFilter, IBackgroundTaskInput, IBackgroundTaskLogCursor, ITransportAdapter } from '@robota-sdk/agent-sdk';
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
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
|
|
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?:
|
|
81
|
+
data?: ICommandResult['data'];
|
|
62
82
|
} | {
|
|
63
83
|
type: 'messages';
|
|
64
|
-
messages:
|
|
84
|
+
messages: ReturnType<InteractiveSession['getMessages']>;
|
|
65
85
|
} | {
|
|
66
86
|
type: 'context';
|
|
67
|
-
state:
|
|
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;
|
package/dist/node/index.js
CHANGED
|
@@ -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
|
|
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
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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
|
-
|
|
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.
|
|
3
|
+
"version": "3.0.0-beta.55",
|
|
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.
|
|
33
|
+
"@robota-sdk/agent-sdk": "3.0.0-beta.55"
|
|
34
34
|
},
|
|
35
35
|
"devDependencies": {
|
|
36
36
|
"rimraf": "^5.0.5",
|
|
@@ -44,6 +44,8 @@
|
|
|
44
44
|
},
|
|
45
45
|
"scripts": {
|
|
46
46
|
"build": "tsup src/index.ts --format esm,cjs --dts --out-dir dist/node --clean",
|
|
47
|
+
"build:js": "tsup src/index.ts --format esm,cjs --out-dir dist/node --clean",
|
|
48
|
+
"build:types": "tsup src/index.ts --format esm,cjs --dts-only --out-dir dist/node",
|
|
47
49
|
"test": "vitest run --passWithNoTests",
|
|
48
50
|
"typecheck": "tsc --noEmit",
|
|
49
51
|
"lint": "eslint src/ --ext .ts",
|