@getpaseo/server 0.1.91-beta.2 → 0.1.92

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.
Files changed (60) hide show
  1. package/dist/scripts/supervisor.js +21 -0
  2. package/dist/server/server/agent/agent-manager.d.ts +13 -5
  3. package/dist/server/server/agent/agent-manager.js +110 -74
  4. package/dist/server/server/agent/agent-projections.d.ts +4 -2
  5. package/dist/server/server/agent/agent-projections.js +8 -28
  6. package/dist/server/server/agent/agent-sdk-types.d.ts +30 -10
  7. package/dist/server/server/agent/import-sessions.d.ts +3 -2
  8. package/dist/server/server/agent/import-sessions.js +23 -55
  9. package/dist/server/server/agent/prompt-attachments.js +8 -0
  10. package/dist/server/server/agent/provider-registry.d.ts +0 -1
  11. package/dist/server/server/agent/provider-registry.js +55 -16
  12. package/dist/server/server/agent/provider-session-import.d.ts +10 -0
  13. package/dist/server/server/agent/provider-session-import.js +49 -0
  14. package/dist/server/server/agent/providers/acp-agent.d.ts +12 -2
  15. package/dist/server/server/agent/providers/acp-agent.js +78 -36
  16. package/dist/server/server/agent/providers/claude/agent.d.ts +3 -2
  17. package/dist/server/server/agent/providers/claude/agent.js +28 -24
  18. package/dist/server/server/agent/providers/codex-app-server-agent.d.ts +3 -2
  19. package/dist/server/server/agent/providers/codex-app-server-agent.js +29 -26
  20. package/dist/server/server/agent/providers/cursor-acp-agent.d.ts +1 -0
  21. package/dist/server/server/agent/providers/cursor-acp-agent.js +1 -0
  22. package/dist/server/server/agent/providers/generic-acp-agent.d.ts +9 -0
  23. package/dist/server/server/agent/providers/generic-acp-agent.js +18 -1
  24. package/dist/server/server/agent/providers/mock-load-test-agent.d.ts +3 -2
  25. package/dist/server/server/agent/providers/mock-load-test-agent.js +11 -1
  26. package/dist/server/server/agent/providers/mock-slow-provider.d.ts +1 -2
  27. package/dist/server/server/agent/providers/mock-slow-provider.js +0 -3
  28. package/dist/server/server/agent/providers/opencode/test-utils/test-opencode-runtime.d.ts +3 -0
  29. package/dist/server/server/agent/providers/opencode/test-utils/test-opencode-runtime.js +12 -0
  30. package/dist/server/server/agent/providers/opencode-agent.d.ts +18 -3
  31. package/dist/server/server/agent/providers/opencode-agent.js +135 -36
  32. package/dist/server/server/agent/providers/pi/agent.d.ts +25 -2
  33. package/dist/server/server/agent/providers/pi/agent.js +243 -14
  34. package/dist/server/server/agent/providers/pi/cli-runtime.js +9 -0
  35. package/dist/server/server/agent/providers/pi/rpc-types.d.ts +9 -0
  36. package/dist/server/server/agent/providers/pi/runtime.d.ts +2 -0
  37. package/dist/server/server/agent/providers/pi/session-descriptor.d.ts +11 -0
  38. package/dist/server/server/agent/providers/pi/session-descriptor.js +284 -0
  39. package/dist/server/server/agent/providers/pi/test-utils/fake-pi.d.ts +8 -0
  40. package/dist/server/server/agent/providers/pi/test-utils/fake-pi.js +22 -0
  41. package/dist/server/server/agent/runtime-mcp-config.d.ts +8 -0
  42. package/dist/server/server/agent/runtime-mcp-config.js +50 -0
  43. package/dist/server/server/auto-archive-on-merge/archive-if-safe.js +2 -2
  44. package/dist/server/server/daemon-worker.js +84 -1
  45. package/dist/server/server/file-upload/index.d.ts +27 -0
  46. package/dist/server/server/file-upload/index.js +158 -0
  47. package/dist/server/server/loop-service.d.ts +12 -12
  48. package/dist/server/server/persisted-config.d.ts +8 -0
  49. package/dist/server/server/persisted-config.js +1 -1
  50. package/dist/server/server/persistence-hooks.js +6 -4
  51. package/dist/server/server/session.d.ts +5 -2
  52. package/dist/server/server/session.js +20 -3
  53. package/dist/server/server/speech/providers/local/runtime.js +1 -0
  54. package/dist/server/server/speech/providers/local/worker-client.d.ts +14 -1
  55. package/dist/server/server/speech/providers/local/worker-client.js +169 -7
  56. package/dist/server/server/websocket-server.d.ts +2 -0
  57. package/dist/server/server/websocket-server.js +20 -7
  58. package/dist/server/server/workspace-registry.d.ts +4 -4
  59. package/dist/src/server/persisted-config.js +1 -1
  60. package/package.json +5 -5
@@ -8,6 +8,7 @@ import { CLIENT_CAPS } from "@getpaseo/protocol/client-capabilities";
8
8
  import { serializeAgentStreamEvent, } from "./messages.js";
9
9
  import { TerminalSessionController } from "../terminal/terminal-session-controller.js";
10
10
  import { encodeFileTransferFrame, FileTransferOpcode, } from "@getpaseo/protocol/binary-frames/index";
11
+ import { FileUploadStore } from "./file-upload/index.js";
11
12
  import { CursorError } from "./pagination/cursor.js";
12
13
  import { SortablePager } from "./pagination/sortable-pager.js";
13
14
  import { TTSManager } from "./agent/tts-manager.js";
@@ -327,6 +328,7 @@ export class Session {
327
328
  this.onLifecycleIntent = onLifecycleIntent ?? null;
328
329
  this.downloadTokenStore = downloadTokenStore;
329
330
  this.pushTokenStore = pushTokenStore;
331
+ this.fileUploads = new FileUploadStore({ paseoHome });
330
332
  this.paseoHome = paseoHome;
331
333
  this.worktreesRoot = worktreesRoot;
332
334
  this.sessionLogger = logger.child({
@@ -1322,6 +1324,9 @@ export class Session {
1322
1324
  return this.handleProjectIconRequest(msg);
1323
1325
  case "file_download_token_request":
1324
1326
  return this.handleFileDownloadTokenRequest(msg);
1327
+ case "file.upload.request":
1328
+ this.handleFileUploadRequest(msg);
1329
+ return undefined;
1325
1330
  default:
1326
1331
  return undefined;
1327
1332
  }
@@ -1419,8 +1424,12 @@ export class Session {
1419
1424
  resetPeakInflight() {
1420
1425
  this.peakInflightRequests = this.inflightRequests;
1421
1426
  }
1422
- handleBinaryFrame(frame) {
1423
- this.terminalController.handleBinaryFrame(frame);
1427
+ async handleBinaryFrame(binaryFrame) {
1428
+ if (binaryFrame.kind === "file_transfer") {
1429
+ await this.handleFileTransferFrame(binaryFrame.frame);
1430
+ return;
1431
+ }
1432
+ this.terminalController.handleBinaryFrame(binaryFrame.frame);
1424
1433
  }
1425
1434
  async handleRestartServerRequest(requestId, reason) {
1426
1435
  const payload = {
@@ -2260,7 +2269,6 @@ export class Session {
2260
2269
  logger: this.sessionLogger,
2261
2270
  });
2262
2271
  await this.registerWorkspaceForImportedAgent(snapshot.cwd);
2263
- await this.forwardAgentUpdate(snapshot);
2264
2272
  const agentPayload = await this.buildAgentPayload(snapshot);
2265
2273
  this.emit({
2266
2274
  type: "status",
@@ -4395,6 +4403,15 @@ export class Session {
4395
4403
  });
4396
4404
  }
4397
4405
  }
4406
+ handleFileUploadRequest(request) {
4407
+ this.fileUploads.beginUpload(request);
4408
+ }
4409
+ async handleFileTransferFrame(frame) {
4410
+ const response = await this.fileUploads.receiveFrame(frame);
4411
+ if (response) {
4412
+ this.emit(response);
4413
+ }
4414
+ }
4398
4415
  /**
4399
4416
  * Handle project icon request for a given cwd
4400
4417
  */
@@ -67,6 +67,7 @@ export async function initializeLocalSpeechServices(params) {
67
67
  });
68
68
  const workerClient = localConfig
69
69
  ? new LocalSpeechWorkerClient({
70
+ logger,
70
71
  config: {
71
72
  modelsDir: localConfig.modelsDir,
72
73
  voiceSttModel: localModels.voiceLocalSttModel,
@@ -1,4 +1,5 @@
1
1
  import { EventEmitter } from "node:events";
2
+ import { Readable } from "node:stream";
2
3
  import type pino from "pino";
3
4
  import type { SpeechStreamResult, SpeechToTextProvider, StreamingTranscriptionSession, TextToSpeechProvider } from "../../speech-provider.js";
4
5
  import type { TurnDetectionProvider, TurnDetectionSession } from "../../turn-detection-provider.js";
@@ -6,20 +7,24 @@ import type { LocalSpeechSessionKind, LocalSpeechTranscriptionResult, LocalSpeec
6
7
  interface LocalSpeechWorkerProcess {
7
8
  connected: boolean;
8
9
  killed: boolean;
10
+ pid?: number;
11
+ stderr?: Readable | null;
9
12
  send(message: LocalSpeechWorkerRequest, callback: (error: Error | null) => void): boolean;
10
13
  disconnect(): void;
11
14
  kill(): boolean;
12
15
  on(event: "message", listener: (message: LocalSpeechWorkerToParentMessage) => void): this;
13
- on(event: "exit", listener: (code: number | null, signal: NodeJS.Signals | null) => void): this;
16
+ on(event: "close", listener: (code: number | null, signal: NodeJS.Signals | null) => void): this;
14
17
  }
15
18
  interface LocalSpeechWorkerClientOptions {
16
19
  config: LocalSpeechWorkerConfig;
20
+ logger: pino.Logger;
17
21
  requestTimeoutMs?: number;
18
22
  idleTtlMs?: number;
19
23
  forkWorker?: () => LocalSpeechWorkerProcess;
20
24
  }
21
25
  export declare class LocalSpeechWorkerClient {
22
26
  private readonly config;
27
+ private readonly logger;
23
28
  private readonly requestTimeoutMs;
24
29
  private readonly idleTtlMs;
25
30
  private readonly forkWorker;
@@ -27,8 +32,12 @@ export declare class LocalSpeechWorkerClient {
27
32
  private readonly activeSessionIds;
28
33
  private readonly sessionEmitters;
29
34
  private worker;
35
+ private workerPid;
36
+ private stderrTail;
37
+ private stderrLineBuffer;
30
38
  private inFlightRequests;
31
39
  private idleTimer;
40
+ private readonly intentionalWorkerCloses;
32
41
  constructor(options: LocalSpeechWorkerClientOptions);
33
42
  synthesizeSpeech(text: string): Promise<SpeechStreamResult>;
34
43
  transcribeVoice(audio: Buffer, format: string): Promise<LocalSpeechTranscriptionResult>;
@@ -45,10 +54,14 @@ export declare class LocalSpeechWorkerClient {
45
54
  shutdown(): void;
46
55
  private sendRequest;
47
56
  private ensureWorker;
57
+ private handleWorkerStderr;
48
58
  private handleWorkerMessage;
49
59
  private handleWorkerExit;
60
+ private describePendingRequests;
61
+ private getStderrTail;
50
62
  private rejectAllPending;
51
63
  private emitSessionError;
64
+ private emitErrorIfObserved;
52
65
  private scheduleIdleShutdownIfReady;
53
66
  private clearIdleTimer;
54
67
  }
@@ -8,6 +8,8 @@ import { bufferToWorkerBytes, workerBytesToBuffer } from "./worker-bytes.js";
8
8
  const DEFAULT_REQUEST_TIMEOUT_MS = 30000;
9
9
  const DEFAULT_IDLE_TTL_MS = 5 * 60 * 1000;
10
10
  const DEFAULT_LOCAL_SAMPLE_RATE = 16000;
11
+ const STDERR_TAIL_MAX_CHARS = 8000;
12
+ const USER_ERROR_STDERR_MAX_CHARS = 1000;
11
13
  function resolveWorkerUrl() {
12
14
  const currentUrl = import.meta.url;
13
15
  if (currentUrl.endsWith(".ts")) {
@@ -38,21 +40,81 @@ function forkLocalSpeechWorker() {
38
40
  env,
39
41
  execArgv: resolveWorkerExecArgv(),
40
42
  serialization: "advanced",
41
- stdio: ["ignore", "ignore", "inherit", "ipc"],
43
+ stdio: ["ignore", "ignore", "pipe", "ipc"],
42
44
  });
43
45
  }
44
46
  function isResponse(message) {
45
47
  return message.type === "response";
46
48
  }
49
+ function truncateStart(value, maxChars) {
50
+ if (value.length <= maxChars) {
51
+ return value;
52
+ }
53
+ return value.slice(value.length - maxChars);
54
+ }
55
+ function summarizeWorkerRequest(message) {
56
+ switch (message.type) {
57
+ case "session.create":
58
+ return { kind: message.kind, sessionId: message.sessionId };
59
+ case "session.append":
60
+ return { sessionId: message.sessionId, audioBytes: message.audio.byteLength };
61
+ case "session.commit":
62
+ case "session.clear":
63
+ case "session.flush":
64
+ case "session.reset":
65
+ case "session.close":
66
+ return { sessionId: message.sessionId };
67
+ case "stt.transcribe":
68
+ return {
69
+ model: message.model,
70
+ audioBytes: message.audio.byteLength,
71
+ format: message.format,
72
+ };
73
+ case "tts.synthesize":
74
+ return { textLength: message.text.length };
75
+ }
76
+ }
77
+ function formatExitStatus(code, signal) {
78
+ const parts = [];
79
+ if (code !== null) {
80
+ parts.push(`code ${code}`);
81
+ }
82
+ if (signal) {
83
+ parts.push(`signal ${signal}`);
84
+ }
85
+ return parts.length > 0 ? parts.join(", ") : "unknown exit status";
86
+ }
87
+ function formatPendingRequestForMessage(request) {
88
+ const type = typeof request.type === "string" ? request.type : "unknown request";
89
+ const kind = typeof request.kind === "string" ? ` (${request.kind})` : "";
90
+ return `${type}${kind}`;
91
+ }
92
+ function buildWorkerExitMessage(params) {
93
+ const pending = params.pendingRequests.length > 0
94
+ ? ` while handling ${params.pendingRequests
95
+ .slice(0, 3)
96
+ .map(formatPendingRequestForMessage)
97
+ .join(", ")}`
98
+ : "";
99
+ const stderr = params.stderrTail
100
+ ? ` Last stderr: ${truncateStart(params.stderrTail, USER_ERROR_STDERR_MAX_CHARS)}`
101
+ : " Check daemon.log and macOS DiagnosticReports for Paseo Voice crash details.";
102
+ return `Local speech worker exited (${formatExitStatus(params.code, params.signal)})${pending}.${stderr}`;
103
+ }
47
104
  export class LocalSpeechWorkerClient {
48
105
  constructor(options) {
49
106
  this.pendingRequests = new Map();
50
107
  this.activeSessionIds = new Set();
51
108
  this.sessionEmitters = new Map();
52
109
  this.worker = null;
110
+ this.workerPid = null;
111
+ this.stderrTail = "";
112
+ this.stderrLineBuffer = "";
53
113
  this.inFlightRequests = 0;
54
114
  this.idleTimer = null;
115
+ this.intentionalWorkerCloses = new WeakSet();
55
116
  this.config = options.config;
117
+ this.logger = options.logger.child({ component: "local-speech-worker-client" });
56
118
  this.requestTimeoutMs = options.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;
57
119
  this.idleTtlMs = options.idleTtlMs ?? DEFAULT_IDLE_TTL_MS;
58
120
  this.forkWorker = options.forkWorker ?? forkLocalSpeechWorker;
@@ -141,7 +203,9 @@ export class LocalSpeechWorkerClient {
141
203
  this.sessionEmitters.clear();
142
204
  const worker = this.worker;
143
205
  this.worker = null;
206
+ this.workerPid = null;
144
207
  if (worker && !worker.killed) {
208
+ this.intentionalWorkerCloses.add(worker);
145
209
  try {
146
210
  worker.disconnect();
147
211
  }
@@ -160,6 +224,7 @@ export class LocalSpeechWorkerClient {
160
224
  const worker = this.ensureWorker();
161
225
  const requestId = randomUUID();
162
226
  const message = { ...input, requestId };
227
+ const requestSummary = summarizeWorkerRequest(message);
163
228
  this.inFlightRequests++;
164
229
  this.clearIdleTimer();
165
230
  return new Promise((resolve, reject) => {
@@ -173,6 +238,9 @@ export class LocalSpeechWorkerClient {
173
238
  resolve: (value) => resolve(value),
174
239
  reject,
175
240
  timeout,
241
+ type: message.type,
242
+ summary: requestSummary,
243
+ startedAt: Date.now(),
176
244
  });
177
245
  worker.send(message, (error) => {
178
246
  if (!error) {
@@ -196,10 +264,35 @@ export class LocalSpeechWorkerClient {
196
264
  }
197
265
  const worker = this.forkWorker();
198
266
  this.worker = worker;
267
+ this.workerPid = worker.pid ?? null;
268
+ this.stderrTail = "";
269
+ this.stderrLineBuffer = "";
270
+ this.logger.info({
271
+ workerPid: this.workerPid,
272
+ modelsDir: this.config.modelsDir,
273
+ voiceSttModel: this.config.voiceSttModel,
274
+ dictationSttModel: this.config.dictationSttModel,
275
+ voiceTtsModel: this.config.voiceTtsModel,
276
+ }, "Local speech worker spawned");
277
+ worker.stderr?.on("data", (chunk) => this.handleWorkerStderr(chunk));
199
278
  worker.on("message", (message) => this.handleWorkerMessage(message));
200
- worker.on("exit", () => this.handleWorkerExit());
279
+ worker.on("close", (code, signal) => this.handleWorkerExit(worker, code, signal));
201
280
  return worker;
202
281
  }
282
+ handleWorkerStderr(chunk) {
283
+ const text = Buffer.isBuffer(chunk) ? chunk.toString("utf8") : chunk;
284
+ this.stderrTail = truncateStart(this.stderrTail + text, STDERR_TAIL_MAX_CHARS);
285
+ this.stderrLineBuffer += text;
286
+ const lines = this.stderrLineBuffer.split(/\r?\n/);
287
+ this.stderrLineBuffer = lines.pop() ?? "";
288
+ for (const line of lines) {
289
+ const trimmed = line.trim();
290
+ if (!trimmed) {
291
+ continue;
292
+ }
293
+ this.logger.warn({ workerPid: this.workerPid, stderr: trimmed }, "Local speech worker stderr");
294
+ }
295
+ }
203
296
  handleWorkerMessage(message) {
204
297
  if (isResponse(message)) {
205
298
  const pending = this.pendingRequests.get(message.requestId);
@@ -240,19 +333,81 @@ export class LocalSpeechWorkerClient {
240
333
  return;
241
334
  }
242
335
  }
243
- handleWorkerExit() {
244
- this.worker = null;
336
+ handleWorkerExit(worker, code, signal) {
337
+ const wasCurrentWorker = this.worker === worker;
338
+ const wasIntentionalClose = this.intentionalWorkerCloses.has(worker);
339
+ this.intentionalWorkerCloses.delete(worker);
340
+ const workerPid = worker.pid ?? (wasCurrentWorker ? this.workerPid : null);
341
+ const pendingRequests = this.describePendingRequests();
342
+ const activeSessionCount = this.activeSessionIds.size;
343
+ const stderrTail = this.getStderrTail();
344
+ if (wasIntentionalClose) {
345
+ this.logger.info({
346
+ workerPid,
347
+ code,
348
+ signal,
349
+ pendingRequests,
350
+ activeSessionCount,
351
+ stderrTail: stderrTail || null,
352
+ }, "Local speech worker closed after shutdown");
353
+ if (wasCurrentWorker) {
354
+ this.worker = null;
355
+ this.workerPid = null;
356
+ }
357
+ return;
358
+ }
359
+ if (!wasCurrentWorker) {
360
+ this.logger.warn({
361
+ workerPid,
362
+ code,
363
+ signal,
364
+ pendingRequests,
365
+ activeSessionCount,
366
+ stderrTail: stderrTail || null,
367
+ }, "Stale local speech worker closed");
368
+ return;
369
+ }
370
+ const error = new Error(buildWorkerExitMessage({
371
+ code,
372
+ signal,
373
+ pendingRequests,
374
+ stderrTail,
375
+ }));
376
+ this.logger.error({
377
+ err: error,
378
+ workerPid,
379
+ code,
380
+ signal,
381
+ pendingRequests,
382
+ activeSessionCount,
383
+ stderrTail: stderrTail || null,
384
+ }, "Local speech worker exited");
385
+ if (wasCurrentWorker) {
386
+ this.worker = null;
387
+ this.workerPid = null;
388
+ }
245
389
  this.clearIdleTimer();
246
- this.rejectAllPending(new Error("Local speech worker exited"));
390
+ this.rejectAllPending(error);
247
391
  for (const [sessionId, emitter] of this.sessionEmitters) {
248
392
  if (this.activeSessionIds.has(sessionId)) {
249
- emitter.emit("error", new Error("Local speech worker exited"));
393
+ this.emitErrorIfObserved(sessionId, emitter, error, workerPid);
250
394
  }
251
395
  }
252
396
  this.activeSessionIds.clear();
253
397
  this.sessionEmitters.clear();
254
398
  this.inFlightRequests = 0;
255
399
  }
400
+ describePendingRequests() {
401
+ const now = Date.now();
402
+ return Array.from(this.pendingRequests.entries()).map(([requestId, request]) => Object.assign({
403
+ requestId,
404
+ type: request.type,
405
+ ageMs: Math.max(0, now - request.startedAt),
406
+ }, request.summary));
407
+ }
408
+ getStderrTail() {
409
+ return truncateStart(this.stderrTail.trim(), STDERR_TAIL_MAX_CHARS);
410
+ }
256
411
  rejectAllPending(error) {
257
412
  for (const [requestId, pending] of this.pendingRequests) {
258
413
  clearTimeout(pending.timeout);
@@ -265,7 +420,14 @@ export class LocalSpeechWorkerClient {
265
420
  if (!emitter) {
266
421
  return;
267
422
  }
268
- emitter.emit("error", error instanceof Error ? error : new Error(String(error)));
423
+ this.emitErrorIfObserved(sessionId, emitter, error instanceof Error ? error : new Error(String(error)));
424
+ }
425
+ emitErrorIfObserved(sessionId, emitter, error, workerPid = this.workerPid) {
426
+ if (emitter.listenerCount("error") > 0) {
427
+ emitter.emit("error", error);
428
+ return;
429
+ }
430
+ this.logger.warn({ err: error, workerPid, sessionId }, "Local speech worker session error had no listener");
269
431
  }
270
432
  scheduleIdleShutdownIfReady() {
271
433
  if (!this.worker || this.inFlightRequests > 0 || this.activeSessionIds.size > 0) {
@@ -51,6 +51,7 @@ export declare class VoiceAssistantWebSocketServer {
51
51
  private readonly pendingConnections;
52
52
  private readonly sessions;
53
53
  private readonly externalSessionsByKey;
54
+ private readonly socketMessageQueues;
54
55
  private readonly serverId;
55
56
  private readonly daemonVersion;
56
57
  private readonly daemonRuntimeConfig;
@@ -129,6 +130,7 @@ export declare class VoiceAssistantWebSocketServer {
129
130
  private broadcastCapabilitiesUpdate;
130
131
  private broadcastDaemonConfigChanged;
131
132
  private bindSocketHandlers;
133
+ private enqueueRawMessage;
132
134
  resolveVoiceSpeakHandler(callerAgentId: string): VoiceSpeakHandler | null;
133
135
  resolveVoiceCallerContext(callerAgentId: string): VoiceCallerContext | null;
134
136
  private detachSocket;
@@ -2,7 +2,7 @@ import { WebSocketServer } from "ws";
2
2
  import { basename, join } from "path";
3
3
  import { hostname as getHostname } from "node:os";
4
4
  import { WSInboundMessageSchema, wrapSessionMessage, } from "./messages.js";
5
- import { asUint8Array, decodeTerminalStreamFrame } from "@getpaseo/protocol/binary-frames/index";
5
+ import { asUint8Array, decodeBinaryFrame } from "@getpaseo/protocol/binary-frames/index";
6
6
  import { isHostnameAllowed } from "./hostnames.js";
7
7
  import { Session } from "./session.js";
8
8
  import { buildWorkspaceGitMetadataFromSnapshot } from "./workspace-git-metadata.js";
@@ -206,6 +206,7 @@ export class VoiceAssistantWebSocketServer {
206
206
  this.pendingConnections = new Map();
207
207
  this.sessions = new Map();
208
208
  this.externalSessionsByKey = new Map();
209
+ this.socketMessageQueues = new Map();
209
210
  this.voiceSpeakHandlers = new Map();
210
211
  this.voiceCallerContexts = new Map();
211
212
  this.workspaceSetupSnapshots = new Map();
@@ -756,7 +757,7 @@ export class VoiceAssistantWebSocketServer {
756
757
  bindSocketHandlers(ws) {
757
758
  ws.on("message", (...args) => {
758
759
  const data = args[0];
759
- void this.handleRawMessage(ws, data);
760
+ this.enqueueRawMessage(ws, data);
760
761
  });
761
762
  ws.on("close", async (...args) => {
762
763
  const code = args[0];
@@ -776,6 +777,18 @@ export class VoiceAssistantWebSocketServer {
776
777
  await this.detachSocket(ws, { error: err });
777
778
  });
778
779
  }
780
+ enqueueRawMessage(ws, data) {
781
+ const previous = this.socketMessageQueues.get(ws) ?? Promise.resolve();
782
+ const next = previous.then(() => this.handleRawMessage(ws, data), () => this.handleRawMessage(ws, data));
783
+ this.socketMessageQueues.set(ws, next);
784
+ void next
785
+ .catch(() => undefined)
786
+ .finally(() => {
787
+ if (this.socketMessageQueues.get(ws) === next) {
788
+ this.socketMessageQueues.delete(ws);
789
+ }
790
+ });
791
+ }
779
792
  resolveVoiceSpeakHandler(callerAgentId) {
780
793
  return this.voiceSpeakHandlers.get(callerAgentId) ?? null;
781
794
  }
@@ -897,14 +910,14 @@ export class VoiceAssistantWebSocketServer {
897
910
  },
898
911
  }));
899
912
  }
900
- maybeHandleBinaryFrame(params) {
913
+ async maybeHandleBinaryFrame(params) {
901
914
  const { ws, buffer, activeConnection, log } = params;
902
915
  const asBytes = asUint8Array(buffer);
903
916
  if (!asBytes) {
904
917
  return false;
905
918
  }
906
- const frame = decodeTerminalStreamFrame(asBytes);
907
- if (!frame) {
919
+ const decodedFrame = decodeBinaryFrame(asBytes);
920
+ if (!decodedFrame) {
908
921
  return false;
909
922
  }
910
923
  if (!activeConnection) {
@@ -919,7 +932,7 @@ export class VoiceAssistantWebSocketServer {
919
932
  }
920
933
  return true;
921
934
  }
922
- activeConnection.session.handleBinaryFrame(frame);
935
+ await activeConnection.session.handleBinaryFrame(decodedFrame);
923
936
  return true;
924
937
  }
925
938
  handlePendingConnectionMessage(params) {
@@ -950,7 +963,7 @@ export class VoiceAssistantWebSocketServer {
950
963
  const log = activeConnection?.connectionLogger ?? pendingConnection?.connectionLogger ?? this.logger;
951
964
  try {
952
965
  const buffer = bufferFromWsData(data);
953
- const binaryHandled = this.maybeHandleBinaryFrame({
966
+ const binaryHandled = await this.maybeHandleBinaryFrame({
954
967
  ws,
955
968
  buffer,
956
969
  activeConnection,
@@ -16,8 +16,8 @@ declare const PersistedProjectRecordSchema: z.ZodObject<{
16
16
  archivedAt: string | null;
17
17
  kind: "git" | "non_git";
18
18
  projectId: string;
19
- rootPath: string;
20
19
  displayName: string;
20
+ rootPath: string;
21
21
  customName: string | null;
22
22
  }, {
23
23
  createdAt: string;
@@ -25,8 +25,8 @@ declare const PersistedProjectRecordSchema: z.ZodObject<{
25
25
  archivedAt: string | null;
26
26
  kind: "git" | "non_git";
27
27
  projectId: string;
28
- rootPath: string;
29
28
  displayName: string;
29
+ rootPath: string;
30
30
  customName?: string | null | undefined;
31
31
  }>;
32
32
  declare const PersistedWorkspaceRecordSchema: z.ZodObject<{
@@ -44,7 +44,7 @@ declare const PersistedWorkspaceRecordSchema: z.ZodObject<{
44
44
  createdAt: string;
45
45
  updatedAt: string;
46
46
  archivedAt: string | null;
47
- kind: "worktree" | "local_checkout" | "directory";
47
+ kind: "local_checkout" | "worktree" | "directory";
48
48
  projectId: string;
49
49
  displayName: string;
50
50
  }, {
@@ -53,7 +53,7 @@ declare const PersistedWorkspaceRecordSchema: z.ZodObject<{
53
53
  createdAt: string;
54
54
  updatedAt: string;
55
55
  archivedAt: string | null;
56
- kind: "worktree" | "local_checkout" | "directory";
56
+ kind: "local_checkout" | "worktree" | "directory";
57
57
  projectId: string;
58
58
  displayName: string;
59
59
  }>;
@@ -129,7 +129,7 @@ const AgentMetadataGenerationSchema = z
129
129
  providers: z.array(StructuredGenerationProviderConfigSchema).optional(),
130
130
  })
131
131
  .strict();
132
- const BUILTIN_PROVIDER_IDS = ["claude", "codex", "copilot", "opencode", "pi"];
132
+ const BUILTIN_PROVIDER_IDS = ["claude", "codex", "copilot", "opencode", "pi", "omp"];
133
133
  function isLegacyProviderEntry(value) {
134
134
  if (!value || typeof value !== "object" || Array.isArray(value)) {
135
135
  return false;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@getpaseo/server",
3
- "version": "0.1.91-beta.2",
3
+ "version": "0.1.92",
4
4
  "description": "Paseo backend server",
5
5
  "files": [
6
6
  "dist/server",
@@ -59,10 +59,10 @@
59
59
  "dependencies": {
60
60
  "@agentclientprotocol/sdk": "^0.17.1",
61
61
  "@anthropic-ai/claude-agent-sdk": "^0.2.133",
62
- "@getpaseo/client": "0.1.91-beta.2",
63
- "@getpaseo/highlight": "0.1.91-beta.2",
64
- "@getpaseo/protocol": "0.1.91-beta.2",
65
- "@getpaseo/relay": "0.1.91-beta.2",
62
+ "@getpaseo/client": "0.1.92",
63
+ "@getpaseo/highlight": "0.1.92",
64
+ "@getpaseo/protocol": "0.1.92",
65
+ "@getpaseo/relay": "0.1.92",
66
66
  "@isaacs/ttlcache": "^2.1.4",
67
67
  "@modelcontextprotocol/sdk": "^1.20.1",
68
68
  "@opencode-ai/sdk": "1.14.46",