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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,94 +1,11 @@
1
1
  # @robota-sdk/agent-transport-ws
2
2
 
3
- Framework-agnostic WebSocket transport adapter for exposing `InteractiveSession` over real-time bidirectional connections. Works with any WebSocket implementation (Node.js `ws`, browser `WebSocket`, uWebSockets, etc.) via send/onMessage callbacks — no WebSocket library dependency.
4
-
5
- ## Installation
6
-
7
- ```bash
8
- pnpm add @robota-sdk/agent-transport-ws
9
- ```
10
-
11
- ## Usage
12
-
13
- ```typescript
14
- import { createWsHandler } from '@robota-sdk/agent-transport-ws';
15
- import type { InteractiveSession } from '@robota-sdk/agent-sdk';
16
- import { WebSocketServer } from 'ws';
17
-
18
- const wss = new WebSocketServer({ port: 8080 });
19
-
20
- wss.on('connection', (ws) => {
21
- const { onMessage, cleanup } = createWsHandler({
22
- session: interactiveSession,
23
- send: (msg) => ws.send(JSON.stringify(msg)),
24
- });
25
-
26
- ws.on('message', (data) => onMessage(String(data)));
27
- ws.on('close', cleanup);
28
- });
29
- ```
30
-
31
- ## Message Protocol
32
-
33
- All messages are JSON-encoded objects with a `type` field.
34
-
35
- ### Client to Server
36
-
37
- | type | payload | maps to |
38
- | --------------- | --------------------------------- | ---------------------------- |
39
- | `submit` | `{ prompt: string }` | `session.submit(prompt)` |
40
- | `command` | `{ name: string, args?: string }` | `session.executeCommand()` |
41
- | `abort` | — | `session.abort()` |
42
- | `cancel-queue` | — | `session.cancelQueue()` |
43
- | `get-messages` | — | `session.getMessages()` |
44
- | `get-context` | — | `session.getContextState()` |
45
- | `get-executing` | — | `session.isExecuting()` |
46
- | `get-pending` | — | `session.getPendingPrompt()` |
47
-
48
- ### Server to Client (pushed events)
49
-
50
- | type | payload | source |
51
- | ----------------------- | ----------------------------------- | ---------------------------------------- |
52
- | `text_delta` | `{ delta: string }` | InteractiveSession event |
53
- | `tool_start` | `{ state: IToolState }` | InteractiveSession event |
54
- | `tool_end` | `{ state: IToolState }` | InteractiveSession event |
55
- | `background_task_event` | `{ event: TBackgroundTaskEvent }` | InteractiveSession background task event |
56
- | `thinking` | `{ isThinking: boolean }` | InteractiveSession event |
57
- | `complete` | `{ result: IExecutionResult }` | InteractiveSession event |
58
- | `interrupted` | `{ result: IExecutionResult }` | InteractiveSession event |
59
- | `error` | `{ message: string }` | InteractiveSession event |
60
- | `command_result` | `{ name, message, success, data? }` | command response |
61
- | `messages` | `{ messages: TUniversalMessage[] }` | get-messages response |
62
- | `context` | `{ state: IContextWindowState }` | get-context response |
63
- | `executing` | `{ executing: boolean }` | get-executing response |
64
- | `pending` | `{ pending: string\|null }` | get-pending response |
65
- | `protocol_error` | `{ message: string }` | invalid client message |
66
-
67
- ## ITransportAdapter
68
-
69
- The WebSocket transport implements the `ITransportAdapter` interface from `@robota-sdk/agent-sdk`:
3
+ WebSocket transport and wire protocol for the Robota SDK. Consumers that only need the WS message
4
+ types (e.g. browser monitors) depend on this package without pulling React/Ink/Hono.
70
5
 
71
6
  ```typescript
72
- import { createWsTransport } from '@robota-sdk/agent-transport-ws';
73
- import type { ITransportAdapter } from '@robota-sdk/agent-sdk';
74
-
75
- const transport: ITransportAdapter = createWsTransport({
76
- send: (msg) => ws.send(JSON.stringify(msg)),
77
- });
78
-
79
- transport.attach(interactiveSession);
80
- await transport.start();
81
-
82
- // Wire incoming messages
83
- ws.on('message', (data) => transport.onMessage(String(data)));
84
- ws.on('close', () => transport.stop());
7
+ import { WsTransport, createWsHandler } from '@robota-sdk/agent-transport-ws';
8
+ import type { TServerMessage, TClientMessage } from '@robota-sdk/agent-transport-ws';
85
9
  ```
86
10
 
87
- ## Dependencies
88
-
89
- - `@robota-sdk/agent-sdk` — `InteractiveSession`
90
- - No WebSocket library dependency (framework-agnostic)
91
-
92
- ## Background Work
93
-
94
- When the attached `InteractiveSession` emits background task updates, the WebSocket transport forwards them as protocol messages so clients can render subagent jobs, transcript references, and terminal states without polling session JSON.
11
+ See [docs/SPEC.md](./docs/SPEC.md) for the full contract.
@@ -1,394 +1 @@
1
- "use strict";
2
- var __defProp = Object.defineProperty;
3
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
- var __getOwnPropNames = Object.getOwnPropertyNames;
5
- var __hasOwnProp = Object.prototype.hasOwnProperty;
6
- var __export = (target, all) => {
7
- for (var name in all)
8
- __defProp(target, name, { get: all[name], enumerable: true });
9
- };
10
- var __copyProps = (to, from, except, desc) => {
11
- if (from && typeof from === "object" || typeof from === "function") {
12
- for (let key of __getOwnPropNames(from))
13
- if (!__hasOwnProp.call(to, key) && key !== except)
14
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
- }
16
- return to;
17
- };
18
- var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
-
20
- // src/index.ts
21
- var index_exports = {};
22
- __export(index_exports, {
23
- WsTransport: () => WsTransport,
24
- createWsHandler: () => createWsHandler,
25
- createWsTransport: () => createWsTransport
26
- });
27
- module.exports = __toCommonJS(index_exports);
28
-
29
- // src/ws-background-messages.ts
30
- function handleBackgroundQueryMessage(session, send, msg) {
31
- if (msg.type === "get-background-tasks") {
32
- send({ type: "background_tasks", tasks: session.listBackgroundTasks(msg.filter) });
33
- return;
34
- }
35
- if (msg.type === "get-background-task") {
36
- sendBackgroundTaskSnapshot(session, send, msg);
37
- return;
38
- }
39
- if (msg.type === "get-background-job-groups") {
40
- send({ type: "background_job_groups", groups: session.listBackgroundJobGroups() });
41
- return;
42
- }
43
- if (msg.type === "get-background-job-group") {
44
- sendBackgroundJobGroupSnapshot(session, send, msg);
45
- return;
46
- }
47
- if (msg.type === "wait-background-job-group") {
48
- sendBackgroundJobGroupWaitResult(session, send, msg);
49
- return;
50
- }
51
- sendBackgroundTaskLogPage(session, send, msg);
52
- }
53
- function handleBackgroundControlMessage(session, send, msg) {
54
- if (!msg.taskId) {
55
- send({ type: "protocol_error", message: "taskId is required" });
56
- return;
57
- }
58
- if (msg.type === "cancel-background-task") {
59
- sendBackgroundTaskControlResult(
60
- send,
61
- "cancel",
62
- msg.taskId,
63
- session.cancelBackgroundTask(msg.taskId, msg.reason)
64
- );
65
- return;
66
- }
67
- if (msg.type === "close-background-task") {
68
- sendBackgroundTaskControlResult(
69
- send,
70
- "close",
71
- msg.taskId,
72
- session.closeBackgroundTask(msg.taskId)
73
- );
74
- return;
75
- }
76
- sendBackgroundTaskInput(session, send, msg);
77
- }
78
- function sendBackgroundTaskSnapshot(session, send, msg) {
79
- if (!msg.taskId) {
80
- send({ type: "protocol_error", message: "taskId is required" });
81
- return;
82
- }
83
- send({
84
- type: "background_task",
85
- taskId: msg.taskId,
86
- task: session.getBackgroundTask(msg.taskId) ?? null
87
- });
88
- }
89
- function sendBackgroundTaskLogPage(session, send, msg) {
90
- if (!msg.taskId) {
91
- send({ type: "protocol_error", message: "taskId is required" });
92
- return;
93
- }
94
- session.readBackgroundTaskLog(msg.taskId, msg.cursor).then(
95
- (page) => send({ type: "background_task_log", taskId: msg.taskId, page }),
96
- (error) => send({ type: "protocol_error", message: error.message })
97
- );
98
- }
99
- function sendBackgroundJobGroupSnapshot(session, send, msg) {
100
- if (!msg.groupId) {
101
- send({ type: "protocol_error", message: "groupId is required" });
102
- return;
103
- }
104
- send({
105
- type: "background_job_group",
106
- groupId: msg.groupId,
107
- group: session.getBackgroundJobGroup(msg.groupId) ?? null
108
- });
109
- }
110
- function sendBackgroundJobGroupWaitResult(session, send, msg) {
111
- if (!msg.groupId) {
112
- send({ type: "protocol_error", message: "groupId is required" });
113
- return;
114
- }
115
- session.waitBackgroundJobGroup(msg.groupId).then(
116
- (group) => send({ type: "background_job_group", groupId: msg.groupId, group }),
117
- (error) => send({ type: "protocol_error", message: error.message })
118
- );
119
- }
120
- function sendBackgroundTaskInput(session, send, msg) {
121
- if (!msg.input) {
122
- send({ type: "protocol_error", message: "input is required" });
123
- return;
124
- }
125
- sendBackgroundTaskControlResult(
126
- send,
127
- "send",
128
- msg.taskId,
129
- session.sendBackgroundTask(msg.taskId, msg.input)
130
- );
131
- }
132
- function sendBackgroundTaskControlResult(send, action, taskId, operation) {
133
- operation.then(
134
- () => send({ type: "background_task_control_result", action, taskId, success: true }),
135
- (error) => send({
136
- type: "background_task_control_result",
137
- action,
138
- taskId,
139
- success: false,
140
- message: error.message
141
- })
142
- );
143
- }
144
-
145
- // src/ws-handler.ts
146
- function createWsHandler(options) {
147
- const cleanup = subscribeSessionEvents(options.session, options.send);
148
- const onMessage = createWsMessageHandler(options.session, options.send);
149
- return { onMessage, cleanup };
150
- }
151
- function subscribeSessionEvents(session, send) {
152
- const onUserMessage = (content) => send({ type: "user_message", content });
153
- const onTextDelta = (delta) => send({ type: "text_delta", delta });
154
- const onToolStart = (state) => send({ type: "tool_start", state });
155
- const onToolEnd = (state) => send({ type: "tool_end", state });
156
- const onThinking = (isThinking) => send({ type: "thinking", isThinking });
157
- const onComplete = (result) => send({ type: "complete", result });
158
- const onInterrupted = (result) => send({ type: "interrupted", result });
159
- const onError = (error) => send({ type: "error", message: error.message });
160
- const onBackgroundTaskEvent = (event) => send({ type: "background_task_event", event });
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);
164
- session.on("text_delta", onTextDelta);
165
- session.on("tool_start", onToolStart);
166
- session.on("tool_end", onToolEnd);
167
- session.on("thinking", onThinking);
168
- session.on("complete", onComplete);
169
- session.on("interrupted", onInterrupted);
170
- session.on("error", onError);
171
- session.on("background_task_event", onBackgroundTaskEvent);
172
- session.on("background_job_group_event", onBackgroundJobGroupEvent);
173
- session.on("execution_workspace_event", onExecutionWorkspace);
174
- return () => {
175
- session.off("user_message", onUserMessage);
176
- session.off("text_delta", onTextDelta);
177
- session.off("tool_start", onToolStart);
178
- session.off("tool_end", onToolEnd);
179
- session.off("thinking", onThinking);
180
- session.off("complete", onComplete);
181
- session.off("interrupted", onInterrupted);
182
- session.off("error", onError);
183
- session.off("background_task_event", onBackgroundTaskEvent);
184
- session.off("background_job_group_event", onBackgroundJobGroupEvent);
185
- session.off("execution_workspace_event", onExecutionWorkspace);
186
- };
187
- }
188
- function createWsMessageHandler(session, send) {
189
- return (data) => {
190
- const msg = parseClientMessage(data, send);
191
- if (!msg) return;
192
- handleClientMessage(session, send, msg);
193
- };
194
- }
195
- function parseClientMessage(data, send) {
196
- try {
197
- return JSON.parse(data);
198
- } catch {
199
- send({ type: "protocol_error", message: "Invalid JSON" });
200
- return null;
201
- }
202
- }
203
- function handleClientMessage(session, send, msg) {
204
- if (isSessionControlMessage(msg)) {
205
- handleSessionControlMessage(session, send, msg);
206
- return;
207
- }
208
- if (isSessionQueryMessage(msg)) {
209
- handleSessionQueryMessage(session, send, msg);
210
- return;
211
- }
212
- if (isBackgroundQueryMessage(msg)) {
213
- handleBackgroundQueryMessage(session, send, msg);
214
- return;
215
- }
216
- if (isBackgroundControlMessage(msg)) {
217
- handleBackgroundControlMessage(session, send, msg);
218
- return;
219
- }
220
- send({ type: "protocol_error", message: `Unknown message type: ${getMessageType(msg)}` });
221
- }
222
- function getMessageType(msg) {
223
- return msg.type;
224
- }
225
- function isSessionControlMessage(msg) {
226
- return msg.type === "submit" || msg.type === "command" || msg.type === "abort" || msg.type === "cancel-queue";
227
- }
228
- function isSessionQueryMessage(msg) {
229
- return msg.type === "get-messages" || msg.type === "get-context" || msg.type === "get-executing" || msg.type === "get-pending" || msg.type === "get-execution-workspace";
230
- }
231
- function isBackgroundQueryMessage(msg) {
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";
233
- }
234
- function isBackgroundControlMessage(msg) {
235
- return msg.type === "cancel-background-task" || msg.type === "close-background-task" || msg.type === "send-background-task";
236
- }
237
- function handleSessionControlMessage(session, send, msg) {
238
- if (msg.type === "submit") {
239
- if (!msg.prompt) {
240
- send({ type: "protocol_error", message: "prompt is required" });
241
- return;
242
- }
243
- session.submit(msg.prompt);
244
- } else if (msg.type === "command") {
245
- if (!msg.name) {
246
- send({ type: "protocol_error", message: "name is required" });
247
- return;
248
- }
249
- session.executeCommand(msg.name, msg.args ?? "").then((result) => {
250
- send({
251
- type: "command_result",
252
- name: msg.name,
253
- message: result?.message ?? `Unknown command: ${msg.name}`,
254
- success: result?.success ?? false,
255
- data: result?.data
256
- });
257
- });
258
- } else if (msg.type === "abort") {
259
- session.abort();
260
- } else {
261
- session.cancelQueue();
262
- }
263
- }
264
- function handleSessionQueryMessage(session, send, msg) {
265
- if (msg.type === "get-messages") {
266
- send({ type: "messages", messages: session.getMessages() });
267
- } else if (msg.type === "get-context") {
268
- send({ type: "context", state: session.getContextState() });
269
- } else if (msg.type === "get-executing") {
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
- });
276
- } else {
277
- send({ type: "pending", pending: session.getPendingPrompt() });
278
- }
279
- }
280
-
281
- // src/ws-transport.ts
282
- function createWsTransport(options) {
283
- let session = null;
284
- let cleanup = null;
285
- return {
286
- name: "ws",
287
- onMessage: null,
288
- attach(s) {
289
- session = s;
290
- },
291
- async start() {
292
- if (!session) throw new Error("No session attached. Call attach() first.");
293
- const handler = createWsHandler({ session, send: options.send });
294
- cleanup = handler.cleanup;
295
- this.onMessage = handler.onMessage;
296
- },
297
- async stop() {
298
- cleanup?.();
299
- cleanup = null;
300
- this.onMessage = null;
301
- }
302
- };
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
- };
389
- // Annotate the CommonJS export names for ESM import in node:
390
- 0 && (module.exports = {
391
- WsTransport,
392
- createWsHandler,
393
- createWsTransport
394
- });
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});let e=require("node:http"),t=require("ws");function n(e,t,n){if(n.type===`get-background-tasks`){t({type:`background_tasks`,tasks:e.listBackgroundTasks(n.filter)});return}if(n.type===`get-background-task`){i(e,t,n);return}if(n.type===`get-background-job-groups`){t({type:`background_job_groups`,groups:e.listBackgroundJobGroups()});return}if(n.type===`get-background-job-group`){o(e,t,n);return}if(n.type===`wait-background-job-group`){s(e,t,n);return}a(e,t,n)}function r(e,t,n){if(!n.taskId){t({type:`protocol_error`,message:`taskId is required`});return}if(n.type===`cancel-background-task`){l(t,`cancel`,n.taskId,e.cancelBackgroundTask(n.taskId,n.reason));return}if(n.type===`close-background-task`){l(t,`close`,n.taskId,e.closeBackgroundTask(n.taskId));return}c(e,t,n)}function i(e,t,n){if(!n.taskId){t({type:`protocol_error`,message:`taskId is required`});return}t({type:`background_task`,taskId:n.taskId,task:e.getBackgroundTask(n.taskId)??null})}function a(e,t,n){if(!n.taskId){t({type:`protocol_error`,message:`taskId is required`});return}e.readBackgroundTaskLog(n.taskId,n.cursor).then(e=>t({type:`background_task_log`,taskId:n.taskId,page:e}),e=>t({type:`protocol_error`,message:e.message}))}function o(e,t,n){if(!n.groupId){t({type:`protocol_error`,message:`groupId is required`});return}t({type:`background_job_group`,groupId:n.groupId,group:e.getBackgroundJobGroup(n.groupId)??null})}function s(e,t,n){if(!n.groupId){t({type:`protocol_error`,message:`groupId is required`});return}e.waitBackgroundJobGroup(n.groupId).then(e=>t({type:`background_job_group`,groupId:n.groupId,group:e}),e=>t({type:`protocol_error`,message:e.message}))}function c(e,t,n){if(!n.input){t({type:`protocol_error`,message:`input is required`});return}l(t,`send`,n.taskId,e.sendBackgroundTask(n.taskId,n.input))}function l(e,t,n,r){r.then(()=>e({type:`background_task_control_result`,action:t,taskId:n,success:!0}),r=>e({type:`background_task_control_result`,action:t,taskId:n,success:!1,message:r.message}))}function u(e){let t=d(e.session,e.send);return{onMessage:f(e.session,e.send),cleanup:t}}function d(e,t){let n=e=>t({type:`user_message`,content:e}),r=e=>t({type:`text_delta`,delta:e}),i=e=>t({type:`tool_start`,state:e}),a=e=>t({type:`tool_end`,state:e}),o=e=>t({type:`thinking`,isThinking:e}),s=e=>t({type:`complete`,result:e}),c=e=>t({type:`interrupted`,result:e}),l=e=>t({type:`error`,message:e.message}),u=e=>t({type:`background_task_event`,event:e}),d=e=>t({type:`background_job_group_event`,event:e}),f=e=>t({type:`execution_workspace_event`,snapshot:e.snapshot});return e.on(`user_message`,n),e.on(`text_delta`,r),e.on(`tool_start`,i),e.on(`tool_end`,a),e.on(`thinking`,o),e.on(`complete`,s),e.on(`interrupted`,c),e.on(`error`,l),e.on(`background_task_event`,u),e.on(`background_job_group_event`,d),e.on(`execution_workspace_event`,f),()=>{e.off(`user_message`,n),e.off(`text_delta`,r),e.off(`tool_start`,i),e.off(`tool_end`,a),e.off(`thinking`,o),e.off(`complete`,s),e.off(`interrupted`,c),e.off(`error`,l),e.off(`background_task_event`,u),e.off(`background_job_group_event`,d),e.off(`execution_workspace_event`,f)}}function f(e,t){return n=>{let r=p(n,t);r&&m(e,t,r)}}function p(e,t){try{return JSON.parse(e)}catch{return t({type:`protocol_error`,message:`Invalid JSON`}),null}}function m(e,t,i){if(g(i)){b(e,t,i);return}if(_(i)){x(e,t,i);return}if(v(i)){n(e,t,i);return}if(y(i)){r(e,t,i);return}t({type:`protocol_error`,message:`Unknown message type: ${h(i)}`})}function h(e){return e.type}function g(e){return e.type===`submit`||e.type===`command`||e.type===`abort`||e.type===`cancel-queue`}function _(e){return e.type===`get-messages`||e.type===`get-context`||e.type===`get-executing`||e.type===`get-pending`||e.type===`get-execution-workspace`}function v(e){return e.type===`get-background-tasks`||e.type===`get-background-task`||e.type===`read-background-task-log`||e.type===`get-background-job-groups`||e.type===`get-background-job-group`||e.type===`wait-background-job-group`}function y(e){return e.type===`cancel-background-task`||e.type===`close-background-task`||e.type===`send-background-task`}function b(e,t,n){if(n.type===`submit`){if(!n.prompt){t({type:`protocol_error`,message:`prompt is required`});return}e.submit(n.prompt)}else if(n.type===`command`){if(!n.name){t({type:`protocol_error`,message:`name is required`});return}e.executeCommand(n.name,n.args??``).then(e=>{t({type:`command_result`,name:n.name,message:e?.message??`Unknown command: ${n.name}`,success:e?.success??!1,data:e?.data})})}else n.type===`abort`?e.abort():e.cancelQueue()}function x(e,t,n){n.type===`get-messages`?t({type:`messages`,messages:e.getMessages()}):n.type===`get-context`?t({type:`context`,state:e.getContextState()}):n.type===`get-executing`?t({type:`executing`,executing:e.isExecuting()}):n.type===`get-execution-workspace`?t({type:`execution_workspace_event`,snapshot:e.getExecutionWorkspaceSnapshot()}):t({type:`pending`,pending:e.getPendingPrompt()})}function S(e){let t=null,n=null;return{name:`ws`,onMessage:null,attach(e){t=e},async start(){if(!t)throw Error(`No session attached. Call attach() first.`);let r=u({session:t,send:e.send});n=r.cleanup,this.onMessage=r.onMessage},async stop(){n?.(),n=null,this.onMessage=null}}}const C=7070;var w=class{name=`ws`;defaultEnabled=!0;optionsSchema={port:{type:`number`,description:`WebSocket server port`,default:C},maxRetries:{type:`number`,description:`Port retry attempts when port is occupied`,default:20}};session=null;stopFn=null;port;maxRetries;constructor(e={}){this.port=e.port??C,this.maxRetries=e.maxRetries??20}attach(e){this.session=e}async start(){if(!this.session)throw Error(`WsTransport: attach() must be called before start()`);let e=await this.bindWithRetry(this.session,this.port,this.maxRetries);this.stopFn=e.stop}async stop(){await this.stopFn?.(),this.stopFn=null}validateOptions(e){let{port:t,maxRetries:n}=e;return!(t!==void 0&&(typeof t!=`number`||t<1||t>65535)||n!==void 0&&(typeof n!=`number`||n<0))}bindWithRetry(e,t,n){return this.tryBind(e,t).catch(r=>{if(r.code===`EADDRINUSE`&&n>0)return this.bindWithRetry(e,t+1,n-1);throw r})}tryBind(n,r){return new Promise((i,a)=>{let o=(0,e.createServer)((e,t)=>{t.writeHead(400).end(`WebSocket endpoint`)});o.on(`error`,e=>{o.close(),a(e)}),o.listen(r,`127.0.0.1`,()=>{let e=new t.WebSocketServer({server:o});e.on(`connection`,e=>{let r=n=>{e.readyState===t.WebSocket.OPEN&&e.send(JSON.stringify(n))},{onMessage:i,cleanup:a}=u({session:n,send:r});e.on(`message`,e=>i(String(e))),e.on(`close`,a),e.on(`error`,a),r({type:`messages`,messages:n.getMessages()}),r({type:`execution_workspace_event`,snapshot:n.getExecutionWorkspaceSnapshot()})}),i({stop:()=>new Promise(t=>{e.close(()=>o.close(()=>t()))})})})})}};exports.WsTransport=w,exports.createWsHandler=u,exports.createWsTransport=S;