@getpaseo/server 0.1.3 → 0.1.6
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/server/client/daemon-client-relay-e2ee-transport.d.ts +8 -0
- package/dist/server/client/daemon-client-relay-e2ee-transport.d.ts.map +1 -0
- package/dist/server/client/daemon-client-relay-e2ee-transport.js +161 -0
- package/dist/server/client/daemon-client-relay-e2ee-transport.js.map +1 -0
- package/dist/server/client/daemon-client-terminal-stream-manager.d.ts +43 -0
- package/dist/server/client/daemon-client-terminal-stream-manager.d.ts.map +1 -0
- package/dist/server/client/daemon-client-terminal-stream-manager.js +134 -0
- package/dist/server/client/daemon-client-terminal-stream-manager.js.map +1 -0
- package/dist/server/client/daemon-client-transport-types.d.ts +34 -0
- package/dist/server/client/daemon-client-transport-types.d.ts.map +1 -0
- package/dist/server/client/daemon-client-transport-types.js +2 -0
- package/dist/server/client/daemon-client-transport-types.js.map +1 -0
- package/dist/server/client/daemon-client-transport-utils.d.ts +9 -0
- package/dist/server/client/daemon-client-transport-utils.d.ts.map +1 -0
- package/dist/server/client/daemon-client-transport-utils.js +121 -0
- package/dist/server/client/daemon-client-transport-utils.js.map +1 -0
- package/dist/server/client/daemon-client-transport.d.ts +5 -0
- package/dist/server/client/daemon-client-transport.d.ts.map +1 -0
- package/dist/server/client/daemon-client-transport.js +4 -0
- package/dist/server/client/daemon-client-transport.js.map +1 -0
- package/dist/server/client/daemon-client-websocket-transport.d.ts +7 -0
- package/dist/server/client/daemon-client-websocket-transport.d.ts.map +1 -0
- package/dist/server/client/daemon-client-websocket-transport.js +64 -0
- package/dist/server/client/daemon-client-websocket-transport.js.map +1 -0
- package/dist/server/client/daemon-client.d.ts +63 -46
- package/dist/server/client/daemon-client.d.ts.map +1 -1
- package/dist/server/client/daemon-client.js +497 -796
- package/dist/server/client/daemon-client.js.map +1 -1
- package/dist/server/server/agent/agent-management-mcp.d.ts +2 -0
- package/dist/server/server/agent/agent-management-mcp.d.ts.map +1 -1
- package/dist/server/server/agent/agent-management-mcp.js +29 -4
- package/dist/server/server/agent/agent-management-mcp.js.map +1 -1
- package/dist/server/server/agent/agent-manager.d.ts +48 -0
- package/dist/server/server/agent/agent-manager.d.ts.map +1 -1
- package/dist/server/server/agent/agent-manager.js +224 -14
- package/dist/server/server/agent/agent-manager.js.map +1 -1
- package/dist/server/server/agent/agent-sdk-types.d.ts +14 -0
- package/dist/server/server/agent/agent-sdk-types.d.ts.map +1 -1
- package/dist/server/server/agent/agent-storage.d.ts +4 -4
- package/dist/server/server/agent/mcp-server.d.ts +2 -0
- package/dist/server/server/agent/mcp-server.d.ts.map +1 -1
- package/dist/server/server/agent/mcp-server.js +30 -5
- package/dist/server/server/agent/mcp-server.js.map +1 -1
- package/dist/server/server/agent/providers/claude/tool-call-mapper.d.ts.map +1 -1
- package/dist/server/server/agent/providers/claude/tool-call-mapper.js +120 -6
- package/dist/server/server/agent/providers/claude/tool-call-mapper.js.map +1 -1
- package/dist/server/server/agent/providers/claude-agent.d.ts +7 -0
- package/dist/server/server/agent/providers/claude-agent.d.ts.map +1 -1
- package/dist/server/server/agent/providers/claude-agent.js +83 -9
- package/dist/server/server/agent/providers/claude-agent.js.map +1 -1
- package/dist/server/server/agent/providers/codex-app-server-agent.d.ts.map +1 -1
- package/dist/server/server/agent/providers/codex-app-server-agent.js +25 -0
- package/dist/server/server/agent/providers/codex-app-server-agent.js.map +1 -1
- package/dist/server/server/agent/providers/tool-call-detail-primitives.d.ts +42 -0
- package/dist/server/server/agent/providers/tool-call-detail-primitives.d.ts.map +1 -1
- package/dist/server/server/agent/stt-manager.d.ts +3 -2
- package/dist/server/server/agent/stt-manager.d.ts.map +1 -1
- package/dist/server/server/agent/stt-manager.js +5 -3
- package/dist/server/server/agent/stt-manager.js.map +1 -1
- package/dist/server/server/agent/timeline-append.d.ts +10 -0
- package/dist/server/server/agent/timeline-append.d.ts.map +1 -0
- package/dist/server/server/agent/timeline-append.js +27 -0
- package/dist/server/server/agent/timeline-append.js.map +1 -0
- package/dist/server/server/agent/timeline-projection.d.ts +19 -0
- package/dist/server/server/agent/timeline-projection.d.ts.map +1 -0
- package/dist/server/server/agent/timeline-projection.js +142 -0
- package/dist/server/server/agent/timeline-projection.js.map +1 -0
- package/dist/server/server/agent/tts-manager.d.ts +3 -2
- package/dist/server/server/agent/tts-manager.d.ts.map +1 -1
- package/dist/server/server/agent/tts-manager.js +5 -3
- package/dist/server/server/agent/tts-manager.js.map +1 -1
- package/dist/server/server/agent-attention-policy.d.ts +20 -0
- package/dist/server/server/agent-attention-policy.d.ts.map +1 -0
- package/dist/server/server/agent-attention-policy.js +40 -0
- package/dist/server/server/agent-attention-policy.js.map +1 -0
- package/dist/server/server/bootstrap.d.ts.map +1 -1
- package/dist/server/server/bootstrap.js +16 -18
- package/dist/server/server/bootstrap.js.map +1 -1
- package/dist/server/server/dictation/dictation-stream-manager.d.ts +10 -2
- package/dist/server/server/dictation/dictation-stream-manager.d.ts.map +1 -1
- package/dist/server/server/dictation/dictation-stream-manager.js +81 -14
- package/dist/server/server/dictation/dictation-stream-manager.js.map +1 -1
- package/dist/server/server/exports.d.ts +1 -1
- package/dist/server/server/exports.d.ts.map +1 -1
- package/dist/server/server/persisted-config.d.ts +12 -12
- package/dist/server/server/relay-transport.d.ts +3 -2
- package/dist/server/server/relay-transport.d.ts.map +1 -1
- package/dist/server/server/relay-transport.js +21 -5
- package/dist/server/server/relay-transport.js.map +1 -1
- package/dist/server/server/session.d.ts +51 -14
- package/dist/server/server/session.d.ts.map +1 -1
- package/dist/server/server/session.js +872 -250
- package/dist/server/server/session.js.map +1 -1
- package/dist/server/server/speech/provider-resolver.d.ts +3 -0
- package/dist/server/server/speech/provider-resolver.d.ts.map +1 -0
- package/dist/server/server/speech/provider-resolver.js +7 -0
- package/dist/server/server/speech/provider-resolver.js.map +1 -0
- package/dist/server/server/speech/providers/local/runtime.d.ts +1 -0
- package/dist/server/server/speech/providers/local/runtime.d.ts.map +1 -1
- package/dist/server/server/speech/providers/local/runtime.js +4 -3
- package/dist/server/server/speech/providers/local/runtime.js.map +1 -1
- package/dist/server/server/speech/providers/local/sherpa/model-downloader.d.ts.map +1 -1
- package/dist/server/server/speech/providers/local/sherpa/model-downloader.js +3 -66
- package/dist/server/server/speech/providers/local/sherpa/model-downloader.js.map +1 -1
- package/dist/server/server/speech/speech-runtime.d.ts +26 -3
- package/dist/server/server/speech/speech-runtime.d.ts.map +1 -1
- package/dist/server/server/speech/speech-runtime.js +466 -112
- package/dist/server/server/speech/speech-runtime.js.map +1 -1
- package/dist/server/server/websocket-server.d.ts +23 -7
- package/dist/server/server/websocket-server.d.ts.map +1 -1
- package/dist/server/server/websocket-server.js +288 -64
- package/dist/server/server/websocket-server.js.map +1 -1
- package/dist/server/server/worktree-bootstrap.d.ts +29 -0
- package/dist/server/server/worktree-bootstrap.d.ts.map +1 -0
- package/dist/server/server/worktree-bootstrap.js +407 -0
- package/dist/server/server/worktree-bootstrap.js.map +1 -0
- package/dist/server/shared/binary-mux.d.ts +31 -0
- package/dist/server/shared/binary-mux.d.ts.map +1 -0
- package/dist/server/shared/binary-mux.js +114 -0
- package/dist/server/shared/binary-mux.js.map +1 -0
- package/dist/server/shared/messages.d.ts +6437 -5839
- package/dist/server/shared/messages.d.ts.map +1 -1
- package/dist/server/shared/messages.js +238 -50
- package/dist/server/shared/messages.js.map +1 -1
- package/dist/server/shared/terminal-key-input.d.ts +9 -0
- package/dist/server/shared/terminal-key-input.d.ts.map +1 -0
- package/dist/server/shared/terminal-key-input.js +132 -0
- package/dist/server/shared/terminal-key-input.js.map +1 -0
- package/dist/server/shared/tool-call-display.d.ts.map +1 -1
- package/dist/server/shared/tool-call-display.js +4 -0
- package/dist/server/shared/tool-call-display.js.map +1 -1
- package/dist/server/terminal/terminal-manager.d.ts +11 -0
- package/dist/server/terminal/terminal-manager.d.ts.map +1 -1
- package/dist/server/terminal/terminal-manager.js +75 -24
- package/dist/server/terminal/terminal-manager.js.map +1 -1
- package/dist/server/terminal/terminal.d.ts +18 -0
- package/dist/server/terminal/terminal.d.ts.map +1 -1
- package/dist/server/terminal/terminal.js +142 -5
- package/dist/server/terminal/terminal.js.map +1 -1
- package/dist/server/utils/checkout-git.d.ts +4 -0
- package/dist/server/utils/checkout-git.d.ts.map +1 -1
- package/dist/server/utils/checkout-git.js +92 -0
- package/dist/server/utils/checkout-git.js.map +1 -1
- package/dist/server/utils/worktree.d.ts +32 -0
- package/dist/server/utils/worktree.d.ts.map +1 -1
- package/dist/server/utils/worktree.js +160 -10
- package/dist/server/utils/worktree.js.map +1 -1
- package/package.json +2 -2
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import { AgentCreateFailedStatusPayloadSchema, AgentCreatedStatusPayloadSchema, AgentRefreshedStatusPayloadSchema, AgentResumedStatusPayloadSchema, RestartRequestedStatusPayloadSchema, SessionInboundMessageSchema, WSOutboundMessageSchema, } from "../shared/messages.js";
|
|
2
2
|
import { getAgentProviderDefinition } from "../server/agent/provider-manifest.js";
|
|
3
|
-
import { createClientChannel, } from "@getpaseo/relay/e2ee";
|
|
4
3
|
import { isRelayClientWebSocketUrl } from "../shared/daemon-endpoints.js";
|
|
4
|
+
import { asUint8Array, decodeBinaryMuxFrame, encodeBinaryMuxFrame, BinaryMuxChannel, TerminalBinaryFlags, TerminalBinaryMessageType, } from "../shared/binary-mux.js";
|
|
5
|
+
import { encodeTerminalKeyInput, } from "../shared/terminal-key-input.js";
|
|
6
|
+
import { TerminalStreamManager, } from "./daemon-client-terminal-stream-manager.js";
|
|
7
|
+
import { createRelayE2eeTransportFactory, createWebSocketTransportFactory, decodeMessageData, defaultWebSocketFactory, describeTransportClose, describeTransportError, encodeUtf8String, safeRandomId, } from "./daemon-client-transport.js";
|
|
5
8
|
const consoleLogger = {
|
|
6
9
|
debug: (obj, msg) => console.debug(msg, obj),
|
|
7
10
|
info: (obj, msg) => console.info(msg, obj),
|
|
@@ -21,6 +24,12 @@ const DEFAULT_RECONNECT_BASE_DELAY_MS = 1500;
|
|
|
21
24
|
const DEFAULT_RECONNECT_MAX_DELAY_MS = 30000;
|
|
22
25
|
/** Default timeout for waiting for connection before sending queued messages */
|
|
23
26
|
const DEFAULT_SEND_QUEUE_TIMEOUT_MS = 10000;
|
|
27
|
+
const DEFAULT_DICTATION_FINISH_ACCEPT_TIMEOUT_MS = 15000;
|
|
28
|
+
const DEFAULT_DICTATION_FINISH_FALLBACK_TIMEOUT_MS = 5 * 60 * 1000;
|
|
29
|
+
const DEFAULT_DICTATION_FINISH_TIMEOUT_GRACE_MS = 5000;
|
|
30
|
+
function isWaiterTimeoutError(error) {
|
|
31
|
+
return (error instanceof Error && error.message.startsWith("Timeout waiting for message"));
|
|
32
|
+
}
|
|
24
33
|
export class DaemonClient {
|
|
25
34
|
constructor(config) {
|
|
26
35
|
this.config = config;
|
|
@@ -43,9 +52,21 @@ export class DaemonClient {
|
|
|
43
52
|
this.connectionState = { status: "idle" };
|
|
44
53
|
this.agentUpdateSubscriptions = new Map();
|
|
45
54
|
this.checkoutDiffSubscriptions = new Map();
|
|
55
|
+
this.terminalDirectorySubscriptions = new Set();
|
|
46
56
|
this.pendingSendQueue = [];
|
|
47
57
|
this.relayClientId = null;
|
|
48
58
|
this.logger = config.logger ?? consoleLogger;
|
|
59
|
+
this.terminalStreams = new TerminalStreamManager({
|
|
60
|
+
sendAck: (ack) => {
|
|
61
|
+
this.sendBinaryFrame({
|
|
62
|
+
channel: BinaryMuxChannel.Terminal,
|
|
63
|
+
messageType: TerminalBinaryMessageType.Ack,
|
|
64
|
+
streamId: ack.streamId,
|
|
65
|
+
offset: ack.offset,
|
|
66
|
+
payload: new Uint8Array(0),
|
|
67
|
+
});
|
|
68
|
+
},
|
|
69
|
+
});
|
|
49
70
|
// Relay requires a clientId so the daemon can create an independent
|
|
50
71
|
// socket + E2EE channel per connected client. Generate one per DaemonClient
|
|
51
72
|
// instance (stable across reconnects in this tab/app session).
|
|
@@ -132,6 +153,7 @@ export class DaemonClient {
|
|
|
132
153
|
this.updateConnectionState({ status: "connected" });
|
|
133
154
|
this.resubscribeAgentUpdates();
|
|
134
155
|
this.resubscribeCheckoutDiffSubscriptions();
|
|
156
|
+
this.resubscribeTerminalDirectorySubscriptions();
|
|
135
157
|
this.flushPendingSendQueue();
|
|
136
158
|
this.resolveConnect();
|
|
137
159
|
}),
|
|
@@ -235,6 +257,7 @@ export class DaemonClient {
|
|
|
235
257
|
}
|
|
236
258
|
this.disposeTransport(1000, "Client closed");
|
|
237
259
|
this.clearWaiters(new Error("Daemon client closed"));
|
|
260
|
+
this.terminalStreams.clearAll();
|
|
238
261
|
this.updateConnectionState({
|
|
239
262
|
status: "disconnected",
|
|
240
263
|
reason: "client_closed",
|
|
@@ -329,6 +352,23 @@ export class DaemonClient {
|
|
|
329
352
|
throw error instanceof Error ? error : new Error(String(error));
|
|
330
353
|
}
|
|
331
354
|
}
|
|
355
|
+
sendBinaryFrame(frame) {
|
|
356
|
+
if (!this.transport || this.connectionState.status !== "connected") {
|
|
357
|
+
if (this.config.suppressSendErrors) {
|
|
358
|
+
return;
|
|
359
|
+
}
|
|
360
|
+
throw new Error(`Transport not connected (status: ${this.connectionState.status})`);
|
|
361
|
+
}
|
|
362
|
+
try {
|
|
363
|
+
this.transport.send(encodeBinaryMuxFrame(frame));
|
|
364
|
+
}
|
|
365
|
+
catch (error) {
|
|
366
|
+
if (this.config.suppressSendErrors) {
|
|
367
|
+
return;
|
|
368
|
+
}
|
|
369
|
+
throw error instanceof Error ? error : new Error(String(error));
|
|
370
|
+
}
|
|
371
|
+
}
|
|
332
372
|
/**
|
|
333
373
|
* Send a session message for RPC methods that create waiters.
|
|
334
374
|
* If the connection is still being established ("connecting"), the message
|
|
@@ -428,6 +468,43 @@ export class DaemonClient {
|
|
|
428
468
|
}
|
|
429
469
|
return result.value;
|
|
430
470
|
}
|
|
471
|
+
async sendCorrelatedRequest(params) {
|
|
472
|
+
return this.sendRequest({
|
|
473
|
+
requestId: params.requestId,
|
|
474
|
+
message: params.message,
|
|
475
|
+
timeout: params.timeout,
|
|
476
|
+
options: params.options,
|
|
477
|
+
select: (msg) => {
|
|
478
|
+
const correlated = msg;
|
|
479
|
+
if (correlated.type !== params.responseType) {
|
|
480
|
+
return null;
|
|
481
|
+
}
|
|
482
|
+
const payload = correlated.payload;
|
|
483
|
+
if (payload.requestId !== params.requestId) {
|
|
484
|
+
return null;
|
|
485
|
+
}
|
|
486
|
+
if (!params.selectPayload) {
|
|
487
|
+
return payload;
|
|
488
|
+
}
|
|
489
|
+
return params.selectPayload(payload);
|
|
490
|
+
},
|
|
491
|
+
});
|
|
492
|
+
}
|
|
493
|
+
sendCorrelatedSessionRequest(params) {
|
|
494
|
+
const resolvedRequestId = this.createRequestId(params.requestId);
|
|
495
|
+
const message = SessionInboundMessageSchema.parse({
|
|
496
|
+
...params.message,
|
|
497
|
+
requestId: resolvedRequestId,
|
|
498
|
+
});
|
|
499
|
+
return this.sendCorrelatedRequest({
|
|
500
|
+
requestId: resolvedRequestId,
|
|
501
|
+
message,
|
|
502
|
+
responseType: params.responseType,
|
|
503
|
+
timeout: params.timeout,
|
|
504
|
+
options: { skipQueue: true },
|
|
505
|
+
...(params.selectPayload ? { selectPayload: params.selectPayload } : {}),
|
|
506
|
+
});
|
|
507
|
+
}
|
|
431
508
|
sendSessionMessageStrict(message) {
|
|
432
509
|
if (!this.transport || this.connectionState.status !== "connected") {
|
|
433
510
|
throw new Error("Transport not connected");
|
|
@@ -496,6 +573,8 @@ export class DaemonClient {
|
|
|
496
573
|
type: "fetch_agents_request",
|
|
497
574
|
requestId: resolvedRequestId,
|
|
498
575
|
...(options?.filter ? { filter: options.filter } : {}),
|
|
576
|
+
...(options?.sort ? { sort: options.sort } : {}),
|
|
577
|
+
...(options?.page ? { page: options.page } : {}),
|
|
499
578
|
});
|
|
500
579
|
return this.sendRequest({
|
|
501
580
|
requestId: resolvedRequestId,
|
|
@@ -509,29 +588,6 @@ export class DaemonClient {
|
|
|
509
588
|
if (msg.payload.requestId !== resolvedRequestId) {
|
|
510
589
|
return null;
|
|
511
590
|
}
|
|
512
|
-
return msg.payload.agents;
|
|
513
|
-
},
|
|
514
|
-
});
|
|
515
|
-
}
|
|
516
|
-
async fetchAgentsGroupedByProject(options) {
|
|
517
|
-
const resolvedRequestId = this.createRequestId(options?.requestId);
|
|
518
|
-
const message = SessionInboundMessageSchema.parse({
|
|
519
|
-
type: "fetch_agents_grouped_by_project_request",
|
|
520
|
-
requestId: resolvedRequestId,
|
|
521
|
-
...(options?.filter ? { filter: options.filter } : {}),
|
|
522
|
-
});
|
|
523
|
-
return this.sendRequest({
|
|
524
|
-
requestId: resolvedRequestId,
|
|
525
|
-
message,
|
|
526
|
-
timeout: 15000,
|
|
527
|
-
options: { skipQueue: true },
|
|
528
|
-
select: (msg) => {
|
|
529
|
-
if (msg.type !== "fetch_agents_grouped_by_project_response") {
|
|
530
|
-
return null;
|
|
531
|
-
}
|
|
532
|
-
if (msg.payload.requestId !== resolvedRequestId) {
|
|
533
|
-
return null;
|
|
534
|
-
}
|
|
535
591
|
return msg.payload;
|
|
536
592
|
},
|
|
537
593
|
});
|
|
@@ -610,6 +666,17 @@ export class DaemonClient {
|
|
|
610
666
|
this.sendSessionMessage(message);
|
|
611
667
|
}
|
|
612
668
|
}
|
|
669
|
+
resubscribeTerminalDirectorySubscriptions() {
|
|
670
|
+
if (this.terminalDirectorySubscriptions.size === 0) {
|
|
671
|
+
return;
|
|
672
|
+
}
|
|
673
|
+
for (const cwd of this.terminalDirectorySubscriptions) {
|
|
674
|
+
this.sendSessionMessage({
|
|
675
|
+
type: "subscribe_terminals_request",
|
|
676
|
+
cwd,
|
|
677
|
+
});
|
|
678
|
+
}
|
|
679
|
+
}
|
|
613
680
|
// ============================================================================
|
|
614
681
|
// Agent Lifecycle
|
|
615
682
|
// ============================================================================
|
|
@@ -783,20 +850,24 @@ export class DaemonClient {
|
|
|
783
850
|
},
|
|
784
851
|
});
|
|
785
852
|
}
|
|
786
|
-
async
|
|
787
|
-
const resolvedRequestId = this.createRequestId(requestId);
|
|
853
|
+
async fetchAgentTimeline(agentId, options = {}) {
|
|
854
|
+
const resolvedRequestId = this.createRequestId(options.requestId);
|
|
788
855
|
const message = SessionInboundMessageSchema.parse({
|
|
789
|
-
type: "
|
|
856
|
+
type: "fetch_agent_timeline_request",
|
|
790
857
|
agentId,
|
|
791
858
|
requestId: resolvedRequestId,
|
|
859
|
+
...(options.direction ? { direction: options.direction } : {}),
|
|
860
|
+
...(options.cursor ? { cursor: options.cursor } : {}),
|
|
861
|
+
...(typeof options.limit === "number" ? { limit: options.limit } : {}),
|
|
862
|
+
...(options.projection ? { projection: options.projection } : {}),
|
|
792
863
|
});
|
|
793
864
|
const payload = await this.sendRequest({
|
|
794
865
|
requestId: resolvedRequestId,
|
|
795
866
|
message,
|
|
796
|
-
timeout:
|
|
867
|
+
timeout: 15000,
|
|
797
868
|
options: { skipQueue: true },
|
|
798
869
|
select: (msg) => {
|
|
799
|
-
if (msg.type !== "
|
|
870
|
+
if (msg.type !== "fetch_agent_timeline_response") {
|
|
800
871
|
return null;
|
|
801
872
|
}
|
|
802
873
|
if (msg.payload.requestId !== resolvedRequestId) {
|
|
@@ -808,11 +879,7 @@ export class DaemonClient {
|
|
|
808
879
|
if (payload.error) {
|
|
809
880
|
throw new Error(payload.error);
|
|
810
881
|
}
|
|
811
|
-
|
|
812
|
-
if (!agent) {
|
|
813
|
-
throw new Error(`Agent not found after initialize: ${agentId}`);
|
|
814
|
-
}
|
|
815
|
-
return agent;
|
|
882
|
+
return payload;
|
|
816
883
|
}
|
|
817
884
|
// ============================================================================
|
|
818
885
|
// Agent Interaction
|
|
@@ -972,7 +1039,7 @@ export class DaemonClient {
|
|
|
972
1039
|
...(agentId ? { agentId } : {}),
|
|
973
1040
|
requestId,
|
|
974
1041
|
});
|
|
975
|
-
|
|
1042
|
+
const response = await this.sendRequest({
|
|
976
1043
|
requestId,
|
|
977
1044
|
message,
|
|
978
1045
|
timeout: 10000,
|
|
@@ -986,11 +1053,18 @@ export class DaemonClient {
|
|
|
986
1053
|
return msg.payload;
|
|
987
1054
|
},
|
|
988
1055
|
});
|
|
1056
|
+
if (!response.accepted) {
|
|
1057
|
+
const codeSuffix = typeof response.reasonCode === "string" && response.reasonCode.trim().length > 0
|
|
1058
|
+
? ` (${response.reasonCode})`
|
|
1059
|
+
: "";
|
|
1060
|
+
throw new Error((response.error ?? "Failed to set voice mode") + codeSuffix);
|
|
1061
|
+
}
|
|
1062
|
+
return response;
|
|
989
1063
|
}
|
|
990
1064
|
async sendVoiceAudioChunk(audio, format, isLast) {
|
|
991
1065
|
this.sendSessionMessage({ type: "voice_audio_chunk", audio, format, isLast });
|
|
992
1066
|
}
|
|
993
|
-
startDictationStream(dictationId, format) {
|
|
1067
|
+
async startDictationStream(dictationId, format) {
|
|
994
1068
|
const ack = this.waitForWithCancel((msg) => {
|
|
995
1069
|
if (msg.type !== "dictation_stream_ack") {
|
|
996
1070
|
return null;
|
|
@@ -1016,23 +1090,22 @@ export class DaemonClient {
|
|
|
1016
1090
|
const errorPromise = streamError.promise.then((payload) => {
|
|
1017
1091
|
throw new Error(payload.error);
|
|
1018
1092
|
});
|
|
1093
|
+
const cleanupError = new Error("Cancelled dictation start waiter");
|
|
1019
1094
|
try {
|
|
1020
1095
|
this.sendSessionMessageStrict({ type: "dictation_stream_start", dictationId, format });
|
|
1096
|
+
await Promise.race([ackPromise, errorPromise]);
|
|
1021
1097
|
}
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
streamError.cancel(err);
|
|
1098
|
+
finally {
|
|
1099
|
+
ack.cancel(cleanupError);
|
|
1100
|
+
streamError.cancel(cleanupError);
|
|
1026
1101
|
void ackPromise.catch(() => undefined);
|
|
1027
1102
|
void errorPromise.catch(() => undefined);
|
|
1028
|
-
throw err;
|
|
1029
1103
|
}
|
|
1030
|
-
return Promise.race([ackPromise, errorPromise]);
|
|
1031
1104
|
}
|
|
1032
1105
|
sendDictationStreamChunk(dictationId, seq, audio, format) {
|
|
1033
1106
|
this.sendSessionMessageStrict({ type: "dictation_stream_chunk", dictationId, seq, audio, format });
|
|
1034
1107
|
}
|
|
1035
|
-
finishDictationStream(dictationId, finalSeq) {
|
|
1108
|
+
async finishDictationStream(dictationId, finalSeq) {
|
|
1036
1109
|
const final = this.waitForWithCancel((msg) => {
|
|
1037
1110
|
if (msg.type !== "dictation_stream_final") {
|
|
1038
1111
|
return null;
|
|
@@ -1041,7 +1114,7 @@ export class DaemonClient {
|
|
|
1041
1114
|
return null;
|
|
1042
1115
|
}
|
|
1043
1116
|
return msg.payload;
|
|
1044
|
-
},
|
|
1117
|
+
}, 0, { skipQueue: true });
|
|
1045
1118
|
const streamError = this.waitForWithCancel((msg) => {
|
|
1046
1119
|
if (msg.type !== "dictation_stream_error") {
|
|
1047
1120
|
return null;
|
|
@@ -1050,23 +1123,96 @@ export class DaemonClient {
|
|
|
1050
1123
|
return null;
|
|
1051
1124
|
}
|
|
1052
1125
|
return msg.payload;
|
|
1053
|
-
},
|
|
1126
|
+
}, 0, { skipQueue: true });
|
|
1127
|
+
const finishAccepted = this.waitForWithCancel((msg) => {
|
|
1128
|
+
if (msg.type !== "dictation_stream_finish_accepted") {
|
|
1129
|
+
return null;
|
|
1130
|
+
}
|
|
1131
|
+
if (msg.payload.dictationId !== dictationId) {
|
|
1132
|
+
return null;
|
|
1133
|
+
}
|
|
1134
|
+
return msg.payload;
|
|
1135
|
+
}, DEFAULT_DICTATION_FINISH_ACCEPT_TIMEOUT_MS, { skipQueue: true });
|
|
1054
1136
|
const finalPromise = final.promise;
|
|
1055
1137
|
const errorPromise = streamError.promise.then((payload) => {
|
|
1056
1138
|
throw new Error(payload.error);
|
|
1057
1139
|
});
|
|
1140
|
+
const finishAcceptedPromise = finishAccepted.promise;
|
|
1141
|
+
const finalOutcomePromise = finalPromise.then((payload) => ({
|
|
1142
|
+
kind: "final",
|
|
1143
|
+
payload,
|
|
1144
|
+
}));
|
|
1145
|
+
const errorOutcomePromise = errorPromise.then(() => ({
|
|
1146
|
+
kind: "error",
|
|
1147
|
+
error: new Error("Unexpected dictation stream error state"),
|
|
1148
|
+
}), (error) => ({
|
|
1149
|
+
kind: "error",
|
|
1150
|
+
error: error instanceof Error ? error : new Error(String(error)),
|
|
1151
|
+
}));
|
|
1152
|
+
const finishAcceptedOutcomePromise = finishAcceptedPromise.then((payload) => ({ kind: "accepted", payload }), (error) => {
|
|
1153
|
+
if (isWaiterTimeoutError(error)) {
|
|
1154
|
+
return { kind: "accepted_timeout" };
|
|
1155
|
+
}
|
|
1156
|
+
return {
|
|
1157
|
+
kind: "accepted_error",
|
|
1158
|
+
error: error instanceof Error ? error : new Error(String(error)),
|
|
1159
|
+
};
|
|
1160
|
+
});
|
|
1161
|
+
const waitForFinalResult = async (timeoutMs) => {
|
|
1162
|
+
if (!Number.isFinite(timeoutMs) || timeoutMs <= 0) {
|
|
1163
|
+
const outcome = await Promise.race([finalOutcomePromise, errorOutcomePromise]);
|
|
1164
|
+
if (outcome.kind === "error") {
|
|
1165
|
+
throw outcome.error;
|
|
1166
|
+
}
|
|
1167
|
+
return outcome.payload;
|
|
1168
|
+
}
|
|
1169
|
+
let timeoutHandle = null;
|
|
1170
|
+
const timeoutPromise = new Promise((resolve) => {
|
|
1171
|
+
timeoutHandle = setTimeout(() => resolve({ kind: "timeout" }), timeoutMs);
|
|
1172
|
+
});
|
|
1173
|
+
const outcome = await Promise.race([
|
|
1174
|
+
finalOutcomePromise,
|
|
1175
|
+
errorOutcomePromise,
|
|
1176
|
+
timeoutPromise,
|
|
1177
|
+
]);
|
|
1178
|
+
if (timeoutHandle) {
|
|
1179
|
+
clearTimeout(timeoutHandle);
|
|
1180
|
+
}
|
|
1181
|
+
if (outcome.kind === "timeout") {
|
|
1182
|
+
throw new Error(`Timeout waiting for dictation finalization (${timeoutMs}ms)`);
|
|
1183
|
+
}
|
|
1184
|
+
if (outcome.kind === "error") {
|
|
1185
|
+
throw outcome.error;
|
|
1186
|
+
}
|
|
1187
|
+
return outcome.payload;
|
|
1188
|
+
};
|
|
1189
|
+
const cleanupError = new Error("Cancelled dictation finish waiter");
|
|
1058
1190
|
try {
|
|
1059
1191
|
this.sendSessionMessageStrict({ type: "dictation_stream_finish", dictationId, finalSeq });
|
|
1192
|
+
const firstOutcome = await Promise.race([
|
|
1193
|
+
finalOutcomePromise,
|
|
1194
|
+
errorOutcomePromise,
|
|
1195
|
+
finishAcceptedOutcomePromise,
|
|
1196
|
+
]);
|
|
1197
|
+
if (firstOutcome.kind === "final") {
|
|
1198
|
+
return firstOutcome.payload;
|
|
1199
|
+
}
|
|
1200
|
+
if (firstOutcome.kind === "error") {
|
|
1201
|
+
throw firstOutcome.error;
|
|
1202
|
+
}
|
|
1203
|
+
if (firstOutcome.kind === "accepted") {
|
|
1204
|
+
return await waitForFinalResult(firstOutcome.payload.timeoutMs + DEFAULT_DICTATION_FINISH_TIMEOUT_GRACE_MS);
|
|
1205
|
+
}
|
|
1206
|
+
return await waitForFinalResult(DEFAULT_DICTATION_FINISH_FALLBACK_TIMEOUT_MS);
|
|
1060
1207
|
}
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1208
|
+
finally {
|
|
1209
|
+
final.cancel(cleanupError);
|
|
1210
|
+
streamError.cancel(cleanupError);
|
|
1211
|
+
finishAccepted.cancel(cleanupError);
|
|
1065
1212
|
void finalPromise.catch(() => undefined);
|
|
1066
1213
|
void errorPromise.catch(() => undefined);
|
|
1067
|
-
|
|
1214
|
+
void finishAcceptedPromise.catch(() => undefined);
|
|
1068
1215
|
}
|
|
1069
|
-
return Promise.race([finalPromise, errorPromise]);
|
|
1070
1216
|
}
|
|
1071
1217
|
cancelDictationStream(dictationId) {
|
|
1072
1218
|
this.sendSessionMessageStrict({ type: "dictation_stream_cancel", dictationId });
|
|
@@ -1171,22 +1317,17 @@ export class DaemonClient {
|
|
|
1171
1317
|
requestId: resolvedRequestId,
|
|
1172
1318
|
});
|
|
1173
1319
|
try {
|
|
1174
|
-
return await this.
|
|
1320
|
+
return await this.sendCorrelatedRequest({
|
|
1175
1321
|
requestId: resolvedRequestId,
|
|
1176
1322
|
message,
|
|
1323
|
+
responseType: "subscribe_checkout_diff_response",
|
|
1177
1324
|
timeout: 60000,
|
|
1178
1325
|
options: { skipQueue: true },
|
|
1179
|
-
|
|
1180
|
-
if (
|
|
1181
|
-
return null;
|
|
1182
|
-
}
|
|
1183
|
-
if (msg.payload.requestId !== resolvedRequestId) {
|
|
1184
|
-
return null;
|
|
1185
|
-
}
|
|
1186
|
-
if (msg.payload.subscriptionId !== subscriptionId) {
|
|
1326
|
+
selectPayload: (payload) => {
|
|
1327
|
+
if (payload.subscriptionId !== subscriptionId) {
|
|
1187
1328
|
return null;
|
|
1188
1329
|
}
|
|
1189
|
-
return
|
|
1330
|
+
return payload;
|
|
1190
1331
|
},
|
|
1191
1332
|
});
|
|
1192
1333
|
}
|
|
@@ -1208,441 +1349,238 @@ export class DaemonClient {
|
|
|
1208
1349
|
});
|
|
1209
1350
|
}
|
|
1210
1351
|
async checkoutCommit(cwd, input, requestId) {
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
});
|
|
1219
|
-
return this.sendRequest({
|
|
1220
|
-
requestId: resolvedRequestId,
|
|
1221
|
-
message,
|
|
1222
|
-
timeout: 60000,
|
|
1223
|
-
options: { skipQueue: true },
|
|
1224
|
-
select: (msg) => {
|
|
1225
|
-
if (msg.type !== "checkout_commit_response") {
|
|
1226
|
-
return null;
|
|
1227
|
-
}
|
|
1228
|
-
if (msg.payload.requestId !== resolvedRequestId) {
|
|
1229
|
-
return null;
|
|
1230
|
-
}
|
|
1231
|
-
return msg.payload;
|
|
1352
|
+
return this.sendCorrelatedSessionRequest({
|
|
1353
|
+
requestId,
|
|
1354
|
+
message: {
|
|
1355
|
+
type: "checkout_commit_request",
|
|
1356
|
+
cwd,
|
|
1357
|
+
message: input.message,
|
|
1358
|
+
addAll: input.addAll,
|
|
1232
1359
|
},
|
|
1360
|
+
responseType: "checkout_commit_response",
|
|
1361
|
+
timeout: 60000,
|
|
1233
1362
|
});
|
|
1234
1363
|
}
|
|
1235
1364
|
async checkoutMerge(cwd, input, requestId) {
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
});
|
|
1245
|
-
return this.sendRequest({
|
|
1246
|
-
requestId: resolvedRequestId,
|
|
1247
|
-
message,
|
|
1248
|
-
timeout: 60000,
|
|
1249
|
-
options: { skipQueue: true },
|
|
1250
|
-
select: (msg) => {
|
|
1251
|
-
if (msg.type !== "checkout_merge_response") {
|
|
1252
|
-
return null;
|
|
1253
|
-
}
|
|
1254
|
-
if (msg.payload.requestId !== resolvedRequestId) {
|
|
1255
|
-
return null;
|
|
1256
|
-
}
|
|
1257
|
-
return msg.payload;
|
|
1365
|
+
return this.sendCorrelatedSessionRequest({
|
|
1366
|
+
requestId,
|
|
1367
|
+
message: {
|
|
1368
|
+
type: "checkout_merge_request",
|
|
1369
|
+
cwd,
|
|
1370
|
+
baseRef: input.baseRef,
|
|
1371
|
+
strategy: input.strategy,
|
|
1372
|
+
requireCleanTarget: input.requireCleanTarget,
|
|
1258
1373
|
},
|
|
1374
|
+
responseType: "checkout_merge_response",
|
|
1375
|
+
timeout: 60000,
|
|
1259
1376
|
});
|
|
1260
1377
|
}
|
|
1261
1378
|
async checkoutMergeFromBase(cwd, input, requestId) {
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
});
|
|
1270
|
-
return this.sendRequest({
|
|
1271
|
-
requestId: resolvedRequestId,
|
|
1272
|
-
message,
|
|
1273
|
-
timeout: 60000,
|
|
1274
|
-
options: { skipQueue: true },
|
|
1275
|
-
select: (msg) => {
|
|
1276
|
-
if (msg.type !== "checkout_merge_from_base_response") {
|
|
1277
|
-
return null;
|
|
1278
|
-
}
|
|
1279
|
-
if (msg.payload.requestId !== resolvedRequestId) {
|
|
1280
|
-
return null;
|
|
1281
|
-
}
|
|
1282
|
-
return msg.payload;
|
|
1379
|
+
return this.sendCorrelatedSessionRequest({
|
|
1380
|
+
requestId,
|
|
1381
|
+
message: {
|
|
1382
|
+
type: "checkout_merge_from_base_request",
|
|
1383
|
+
cwd,
|
|
1384
|
+
baseRef: input.baseRef,
|
|
1385
|
+
requireCleanTarget: input.requireCleanTarget,
|
|
1283
1386
|
},
|
|
1387
|
+
responseType: "checkout_merge_from_base_response",
|
|
1388
|
+
timeout: 60000,
|
|
1284
1389
|
});
|
|
1285
1390
|
}
|
|
1286
1391
|
async checkoutPush(cwd, requestId) {
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
});
|
|
1293
|
-
return this.sendRequest({
|
|
1294
|
-
requestId: resolvedRequestId,
|
|
1295
|
-
message,
|
|
1296
|
-
timeout: 60000,
|
|
1297
|
-
options: { skipQueue: true },
|
|
1298
|
-
select: (msg) => {
|
|
1299
|
-
if (msg.type !== "checkout_push_response") {
|
|
1300
|
-
return null;
|
|
1301
|
-
}
|
|
1302
|
-
if (msg.payload.requestId !== resolvedRequestId) {
|
|
1303
|
-
return null;
|
|
1304
|
-
}
|
|
1305
|
-
return msg.payload;
|
|
1392
|
+
return this.sendCorrelatedSessionRequest({
|
|
1393
|
+
requestId,
|
|
1394
|
+
message: {
|
|
1395
|
+
type: "checkout_push_request",
|
|
1396
|
+
cwd,
|
|
1306
1397
|
},
|
|
1398
|
+
responseType: "checkout_push_response",
|
|
1399
|
+
timeout: 60000,
|
|
1307
1400
|
});
|
|
1308
1401
|
}
|
|
1309
1402
|
async checkoutPrCreate(cwd, input, requestId) {
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
});
|
|
1319
|
-
return this.sendRequest({
|
|
1320
|
-
requestId: resolvedRequestId,
|
|
1321
|
-
message,
|
|
1322
|
-
timeout: 60000,
|
|
1323
|
-
options: { skipQueue: true },
|
|
1324
|
-
select: (msg) => {
|
|
1325
|
-
if (msg.type !== "checkout_pr_create_response") {
|
|
1326
|
-
return null;
|
|
1327
|
-
}
|
|
1328
|
-
if (msg.payload.requestId !== resolvedRequestId) {
|
|
1329
|
-
return null;
|
|
1330
|
-
}
|
|
1331
|
-
return msg.payload;
|
|
1403
|
+
return this.sendCorrelatedSessionRequest({
|
|
1404
|
+
requestId,
|
|
1405
|
+
message: {
|
|
1406
|
+
type: "checkout_pr_create_request",
|
|
1407
|
+
cwd,
|
|
1408
|
+
title: input.title,
|
|
1409
|
+
body: input.body,
|
|
1410
|
+
baseRef: input.baseRef,
|
|
1332
1411
|
},
|
|
1412
|
+
responseType: "checkout_pr_create_response",
|
|
1413
|
+
timeout: 60000,
|
|
1333
1414
|
});
|
|
1334
1415
|
}
|
|
1335
1416
|
async checkoutPrStatus(cwd, requestId) {
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
});
|
|
1342
|
-
return this.sendRequest({
|
|
1343
|
-
requestId: resolvedRequestId,
|
|
1344
|
-
message,
|
|
1345
|
-
timeout: 60000,
|
|
1346
|
-
options: { skipQueue: true },
|
|
1347
|
-
select: (msg) => {
|
|
1348
|
-
if (msg.type !== "checkout_pr_status_response") {
|
|
1349
|
-
return null;
|
|
1350
|
-
}
|
|
1351
|
-
if (msg.payload.requestId !== resolvedRequestId) {
|
|
1352
|
-
return null;
|
|
1353
|
-
}
|
|
1354
|
-
return msg.payload;
|
|
1417
|
+
return this.sendCorrelatedSessionRequest({
|
|
1418
|
+
requestId,
|
|
1419
|
+
message: {
|
|
1420
|
+
type: "checkout_pr_status_request",
|
|
1421
|
+
cwd,
|
|
1355
1422
|
},
|
|
1423
|
+
responseType: "checkout_pr_status_response",
|
|
1424
|
+
timeout: 60000,
|
|
1356
1425
|
});
|
|
1357
1426
|
}
|
|
1358
1427
|
async getPaseoWorktreeList(input, requestId) {
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
});
|
|
1366
|
-
return this.sendRequest({
|
|
1367
|
-
requestId: resolvedRequestId,
|
|
1368
|
-
message,
|
|
1369
|
-
timeout: 60000,
|
|
1370
|
-
options: { skipQueue: true },
|
|
1371
|
-
select: (msg) => {
|
|
1372
|
-
if (msg.type !== "paseo_worktree_list_response") {
|
|
1373
|
-
return null;
|
|
1374
|
-
}
|
|
1375
|
-
if (msg.payload.requestId !== resolvedRequestId) {
|
|
1376
|
-
return null;
|
|
1377
|
-
}
|
|
1378
|
-
return msg.payload;
|
|
1428
|
+
return this.sendCorrelatedSessionRequest({
|
|
1429
|
+
requestId,
|
|
1430
|
+
message: {
|
|
1431
|
+
type: "paseo_worktree_list_request",
|
|
1432
|
+
cwd: input.cwd,
|
|
1433
|
+
repoRoot: input.repoRoot,
|
|
1379
1434
|
},
|
|
1435
|
+
responseType: "paseo_worktree_list_response",
|
|
1436
|
+
timeout: 60000,
|
|
1380
1437
|
});
|
|
1381
1438
|
}
|
|
1382
1439
|
async archivePaseoWorktree(input, requestId) {
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
});
|
|
1391
|
-
return this.sendRequest({
|
|
1392
|
-
requestId: resolvedRequestId,
|
|
1393
|
-
message,
|
|
1394
|
-
timeout: 20000,
|
|
1395
|
-
options: { skipQueue: true },
|
|
1396
|
-
select: (msg) => {
|
|
1397
|
-
if (msg.type !== "paseo_worktree_archive_response") {
|
|
1398
|
-
return null;
|
|
1399
|
-
}
|
|
1400
|
-
if (msg.payload.requestId !== resolvedRequestId) {
|
|
1401
|
-
return null;
|
|
1402
|
-
}
|
|
1403
|
-
return msg.payload;
|
|
1440
|
+
return this.sendCorrelatedSessionRequest({
|
|
1441
|
+
requestId,
|
|
1442
|
+
message: {
|
|
1443
|
+
type: "paseo_worktree_archive_request",
|
|
1444
|
+
worktreePath: input.worktreePath,
|
|
1445
|
+
repoRoot: input.repoRoot,
|
|
1446
|
+
branchName: input.branchName,
|
|
1404
1447
|
},
|
|
1448
|
+
responseType: "paseo_worktree_archive_response",
|
|
1449
|
+
timeout: 20000,
|
|
1405
1450
|
});
|
|
1406
1451
|
}
|
|
1407
1452
|
async validateBranch(options, requestId) {
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
requestId: resolvedRequestId,
|
|
1417
|
-
message,
|
|
1453
|
+
return this.sendCorrelatedSessionRequest({
|
|
1454
|
+
requestId,
|
|
1455
|
+
message: {
|
|
1456
|
+
type: "validate_branch_request",
|
|
1457
|
+
cwd: options.cwd,
|
|
1458
|
+
branchName: options.branchName,
|
|
1459
|
+
},
|
|
1460
|
+
responseType: "validate_branch_response",
|
|
1418
1461
|
timeout: 10000,
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1462
|
+
});
|
|
1463
|
+
}
|
|
1464
|
+
async getBranchSuggestions(options, requestId) {
|
|
1465
|
+
return this.sendCorrelatedSessionRequest({
|
|
1466
|
+
requestId,
|
|
1467
|
+
message: {
|
|
1468
|
+
type: "branch_suggestions_request",
|
|
1469
|
+
cwd: options.cwd,
|
|
1470
|
+
query: options.query,
|
|
1471
|
+
limit: options.limit,
|
|
1428
1472
|
},
|
|
1473
|
+
responseType: "branch_suggestions_response",
|
|
1474
|
+
timeout: 10000,
|
|
1429
1475
|
});
|
|
1430
1476
|
}
|
|
1431
1477
|
// ============================================================================
|
|
1432
1478
|
// File Explorer
|
|
1433
1479
|
// ============================================================================
|
|
1434
1480
|
async exploreFileSystem(agentId, path, mode = "list", requestId) {
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
});
|
|
1443
|
-
return this.sendRequest({
|
|
1444
|
-
requestId: resolvedRequestId,
|
|
1445
|
-
message,
|
|
1446
|
-
timeout: 10000,
|
|
1447
|
-
options: { skipQueue: true },
|
|
1448
|
-
select: (msg) => {
|
|
1449
|
-
if (msg.type !== "file_explorer_response") {
|
|
1450
|
-
return null;
|
|
1451
|
-
}
|
|
1452
|
-
if (msg.payload.requestId !== resolvedRequestId) {
|
|
1453
|
-
return null;
|
|
1454
|
-
}
|
|
1455
|
-
return msg.payload;
|
|
1481
|
+
return this.sendCorrelatedSessionRequest({
|
|
1482
|
+
requestId,
|
|
1483
|
+
message: {
|
|
1484
|
+
type: "file_explorer_request",
|
|
1485
|
+
agentId,
|
|
1486
|
+
path,
|
|
1487
|
+
mode,
|
|
1456
1488
|
},
|
|
1489
|
+
responseType: "file_explorer_response",
|
|
1490
|
+
timeout: 10000,
|
|
1457
1491
|
});
|
|
1458
1492
|
}
|
|
1459
1493
|
async requestDownloadToken(agentId, path, requestId) {
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
});
|
|
1467
|
-
return this.sendRequest({
|
|
1468
|
-
requestId: resolvedRequestId,
|
|
1469
|
-
message,
|
|
1470
|
-
timeout: 10000,
|
|
1471
|
-
options: { skipQueue: true },
|
|
1472
|
-
select: (msg) => {
|
|
1473
|
-
if (msg.type !== "file_download_token_response") {
|
|
1474
|
-
return null;
|
|
1475
|
-
}
|
|
1476
|
-
if (msg.payload.requestId !== resolvedRequestId) {
|
|
1477
|
-
return null;
|
|
1478
|
-
}
|
|
1479
|
-
return msg.payload;
|
|
1494
|
+
return this.sendCorrelatedSessionRequest({
|
|
1495
|
+
requestId,
|
|
1496
|
+
message: {
|
|
1497
|
+
type: "file_download_token_request",
|
|
1498
|
+
agentId,
|
|
1499
|
+
path,
|
|
1480
1500
|
},
|
|
1501
|
+
responseType: "file_download_token_response",
|
|
1502
|
+
timeout: 10000,
|
|
1481
1503
|
});
|
|
1482
1504
|
}
|
|
1483
1505
|
async requestProjectIcon(cwd, requestId) {
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
});
|
|
1490
|
-
return this.sendRequest({
|
|
1491
|
-
requestId: resolvedRequestId,
|
|
1492
|
-
message,
|
|
1493
|
-
timeout: 10000,
|
|
1494
|
-
options: { skipQueue: true },
|
|
1495
|
-
select: (msg) => {
|
|
1496
|
-
if (msg.type !== "project_icon_response") {
|
|
1497
|
-
return null;
|
|
1498
|
-
}
|
|
1499
|
-
if (msg.payload.requestId !== resolvedRequestId) {
|
|
1500
|
-
return null;
|
|
1501
|
-
}
|
|
1502
|
-
return msg.payload;
|
|
1506
|
+
return this.sendCorrelatedSessionRequest({
|
|
1507
|
+
requestId,
|
|
1508
|
+
message: {
|
|
1509
|
+
type: "project_icon_request",
|
|
1510
|
+
cwd,
|
|
1503
1511
|
},
|
|
1512
|
+
responseType: "project_icon_response",
|
|
1513
|
+
timeout: 10000,
|
|
1504
1514
|
});
|
|
1505
1515
|
}
|
|
1506
1516
|
// ============================================================================
|
|
1507
1517
|
// Provider Models / Commands
|
|
1508
1518
|
// ============================================================================
|
|
1509
1519
|
async listProviderModels(provider, options) {
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
});
|
|
1517
|
-
return this.sendRequest({
|
|
1518
|
-
requestId: resolvedRequestId,
|
|
1519
|
-
message,
|
|
1520
|
-
timeout: 30000,
|
|
1521
|
-
options: { skipQueue: true },
|
|
1522
|
-
select: (msg) => {
|
|
1523
|
-
if (msg.type !== "list_provider_models_response") {
|
|
1524
|
-
return null;
|
|
1525
|
-
}
|
|
1526
|
-
if (msg.payload.requestId !== resolvedRequestId) {
|
|
1527
|
-
return null;
|
|
1528
|
-
}
|
|
1529
|
-
return msg.payload;
|
|
1520
|
+
return this.sendCorrelatedSessionRequest({
|
|
1521
|
+
requestId: options?.requestId,
|
|
1522
|
+
message: {
|
|
1523
|
+
type: "list_provider_models_request",
|
|
1524
|
+
provider,
|
|
1525
|
+
cwd: options?.cwd,
|
|
1530
1526
|
},
|
|
1527
|
+
responseType: "list_provider_models_response",
|
|
1528
|
+
timeout: 30000,
|
|
1531
1529
|
});
|
|
1532
1530
|
}
|
|
1533
1531
|
async listAvailableProviders(options) {
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
});
|
|
1539
|
-
return this.sendRequest({
|
|
1540
|
-
requestId: resolvedRequestId,
|
|
1541
|
-
message,
|
|
1542
|
-
timeout: 30000,
|
|
1543
|
-
options: { skipQueue: true },
|
|
1544
|
-
select: (msg) => {
|
|
1545
|
-
if (msg.type !== "list_available_providers_response") {
|
|
1546
|
-
return null;
|
|
1547
|
-
}
|
|
1548
|
-
if (msg.payload.requestId !== resolvedRequestId) {
|
|
1549
|
-
return null;
|
|
1550
|
-
}
|
|
1551
|
-
return msg.payload;
|
|
1532
|
+
return this.sendCorrelatedSessionRequest({
|
|
1533
|
+
requestId: options?.requestId,
|
|
1534
|
+
message: {
|
|
1535
|
+
type: "list_available_providers_request",
|
|
1552
1536
|
},
|
|
1537
|
+
responseType: "list_available_providers_response",
|
|
1538
|
+
timeout: 30000,
|
|
1553
1539
|
});
|
|
1554
1540
|
}
|
|
1555
1541
|
async listSpeechModels(requestId) {
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
});
|
|
1561
|
-
return this.sendRequest({
|
|
1562
|
-
requestId: resolvedRequestId,
|
|
1563
|
-
message,
|
|
1564
|
-
timeout: 30000,
|
|
1565
|
-
options: { skipQueue: true },
|
|
1566
|
-
select: (msg) => {
|
|
1567
|
-
if (msg.type !== "speech_models_list_response") {
|
|
1568
|
-
return null;
|
|
1569
|
-
}
|
|
1570
|
-
if (msg.payload.requestId !== resolvedRequestId) {
|
|
1571
|
-
return null;
|
|
1572
|
-
}
|
|
1573
|
-
return msg.payload;
|
|
1542
|
+
return this.sendCorrelatedSessionRequest({
|
|
1543
|
+
requestId,
|
|
1544
|
+
message: {
|
|
1545
|
+
type: "speech_models_list_request",
|
|
1574
1546
|
},
|
|
1547
|
+
responseType: "speech_models_list_response",
|
|
1548
|
+
timeout: 30000,
|
|
1575
1549
|
});
|
|
1576
1550
|
}
|
|
1577
1551
|
async downloadSpeechModels(options) {
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
});
|
|
1584
|
-
return this.sendRequest({
|
|
1585
|
-
requestId: resolvedRequestId,
|
|
1586
|
-
message,
|
|
1587
|
-
timeout: 30 * 60 * 1000,
|
|
1588
|
-
options: { skipQueue: true },
|
|
1589
|
-
select: (msg) => {
|
|
1590
|
-
if (msg.type !== "speech_models_download_response") {
|
|
1591
|
-
return null;
|
|
1592
|
-
}
|
|
1593
|
-
if (msg.payload.requestId !== resolvedRequestId) {
|
|
1594
|
-
return null;
|
|
1595
|
-
}
|
|
1596
|
-
return msg.payload;
|
|
1552
|
+
return this.sendCorrelatedSessionRequest({
|
|
1553
|
+
requestId: options?.requestId,
|
|
1554
|
+
message: {
|
|
1555
|
+
type: "speech_models_download_request",
|
|
1556
|
+
modelIds: options?.modelIds,
|
|
1597
1557
|
},
|
|
1558
|
+
responseType: "speech_models_download_response",
|
|
1559
|
+
timeout: 30 * 60 * 1000,
|
|
1598
1560
|
});
|
|
1599
1561
|
}
|
|
1600
1562
|
async listCommands(agentId, requestId) {
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
});
|
|
1607
|
-
return this.sendRequest({
|
|
1608
|
-
requestId: resolvedRequestId,
|
|
1609
|
-
message,
|
|
1610
|
-
timeout: 30000,
|
|
1611
|
-
options: { skipQueue: true },
|
|
1612
|
-
select: (msg) => {
|
|
1613
|
-
if (msg.type !== "list_commands_response") {
|
|
1614
|
-
return null;
|
|
1615
|
-
}
|
|
1616
|
-
if (msg.payload.requestId !== resolvedRequestId) {
|
|
1617
|
-
return null;
|
|
1618
|
-
}
|
|
1619
|
-
return msg.payload;
|
|
1563
|
+
return this.sendCorrelatedSessionRequest({
|
|
1564
|
+
requestId,
|
|
1565
|
+
message: {
|
|
1566
|
+
type: "list_commands_request",
|
|
1567
|
+
agentId,
|
|
1620
1568
|
},
|
|
1569
|
+
responseType: "list_commands_response",
|
|
1570
|
+
timeout: 30000,
|
|
1621
1571
|
});
|
|
1622
1572
|
}
|
|
1623
1573
|
async executeCommand(agentId, commandName, args, requestId) {
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
});
|
|
1632
|
-
return this.sendRequest({
|
|
1633
|
-
requestId: resolvedRequestId,
|
|
1634
|
-
message,
|
|
1635
|
-
timeout: 30000,
|
|
1636
|
-
options: { skipQueue: true },
|
|
1637
|
-
select: (msg) => {
|
|
1638
|
-
if (msg.type !== "execute_command_response") {
|
|
1639
|
-
return null;
|
|
1640
|
-
}
|
|
1641
|
-
if (msg.payload.requestId !== resolvedRequestId) {
|
|
1642
|
-
return null;
|
|
1643
|
-
}
|
|
1644
|
-
return msg.payload;
|
|
1574
|
+
return this.sendCorrelatedSessionRequest({
|
|
1575
|
+
requestId,
|
|
1576
|
+
message: {
|
|
1577
|
+
type: "execute_command_request",
|
|
1578
|
+
agentId,
|
|
1579
|
+
commandName,
|
|
1580
|
+
args,
|
|
1645
1581
|
},
|
|
1582
|
+
responseType: "execute_command_response",
|
|
1583
|
+
timeout: 30000,
|
|
1646
1584
|
});
|
|
1647
1585
|
}
|
|
1648
1586
|
// ============================================================================
|
|
@@ -1704,20 +1642,12 @@ export class DaemonClient {
|
|
|
1704
1642
|
agentId,
|
|
1705
1643
|
timeoutMs: timeout,
|
|
1706
1644
|
});
|
|
1707
|
-
const payload = await this.
|
|
1645
|
+
const payload = await this.sendCorrelatedRequest({
|
|
1708
1646
|
requestId,
|
|
1709
1647
|
message,
|
|
1648
|
+
responseType: "wait_for_finish_response",
|
|
1710
1649
|
timeout: timeout + 5000,
|
|
1711
1650
|
options: { skipQueue: true },
|
|
1712
|
-
select: (msg) => {
|
|
1713
|
-
if (msg.type !== "wait_for_finish_response") {
|
|
1714
|
-
return null;
|
|
1715
|
-
}
|
|
1716
|
-
if (msg.payload.requestId !== requestId) {
|
|
1717
|
-
return null;
|
|
1718
|
-
}
|
|
1719
|
-
return msg.payload;
|
|
1720
|
-
},
|
|
1721
1651
|
});
|
|
1722
1652
|
return {
|
|
1723
1653
|
status: payload.status,
|
|
@@ -1729,6 +1659,26 @@ export class DaemonClient {
|
|
|
1729
1659
|
// ============================================================================
|
|
1730
1660
|
// Terminals
|
|
1731
1661
|
// ============================================================================
|
|
1662
|
+
subscribeTerminals(input) {
|
|
1663
|
+
this.terminalDirectorySubscriptions.add(input.cwd);
|
|
1664
|
+
if (!this.transport || this.connectionState.status !== "connected") {
|
|
1665
|
+
return;
|
|
1666
|
+
}
|
|
1667
|
+
this.sendSessionMessage({
|
|
1668
|
+
type: "subscribe_terminals_request",
|
|
1669
|
+
cwd: input.cwd,
|
|
1670
|
+
});
|
|
1671
|
+
}
|
|
1672
|
+
unsubscribeTerminals(input) {
|
|
1673
|
+
this.terminalDirectorySubscriptions.delete(input.cwd);
|
|
1674
|
+
if (!this.transport || this.connectionState.status !== "connected") {
|
|
1675
|
+
return;
|
|
1676
|
+
}
|
|
1677
|
+
this.sendSessionMessage({
|
|
1678
|
+
type: "unsubscribe_terminals_request",
|
|
1679
|
+
cwd: input.cwd,
|
|
1680
|
+
});
|
|
1681
|
+
}
|
|
1732
1682
|
async listTerminals(cwd, requestId) {
|
|
1733
1683
|
const resolvedRequestId = this.createRequestId(requestId);
|
|
1734
1684
|
const message = SessionInboundMessageSchema.parse({
|
|
@@ -1736,20 +1686,12 @@ export class DaemonClient {
|
|
|
1736
1686
|
cwd,
|
|
1737
1687
|
requestId: resolvedRequestId,
|
|
1738
1688
|
});
|
|
1739
|
-
return this.
|
|
1689
|
+
return this.sendCorrelatedRequest({
|
|
1740
1690
|
requestId: resolvedRequestId,
|
|
1741
1691
|
message,
|
|
1692
|
+
responseType: "list_terminals_response",
|
|
1742
1693
|
timeout: 10000,
|
|
1743
1694
|
options: { skipQueue: true },
|
|
1744
|
-
select: (msg) => {
|
|
1745
|
-
if (msg.type !== "list_terminals_response") {
|
|
1746
|
-
return null;
|
|
1747
|
-
}
|
|
1748
|
-
if (msg.payload.requestId !== resolvedRequestId) {
|
|
1749
|
-
return null;
|
|
1750
|
-
}
|
|
1751
|
-
return msg.payload;
|
|
1752
|
-
},
|
|
1753
1695
|
});
|
|
1754
1696
|
}
|
|
1755
1697
|
async createTerminal(cwd, name, requestId) {
|
|
@@ -1760,20 +1702,12 @@ export class DaemonClient {
|
|
|
1760
1702
|
name,
|
|
1761
1703
|
requestId: resolvedRequestId,
|
|
1762
1704
|
});
|
|
1763
|
-
return this.
|
|
1705
|
+
return this.sendCorrelatedRequest({
|
|
1764
1706
|
requestId: resolvedRequestId,
|
|
1765
1707
|
message,
|
|
1708
|
+
responseType: "create_terminal_response",
|
|
1766
1709
|
timeout: 10000,
|
|
1767
1710
|
options: { skipQueue: true },
|
|
1768
|
-
select: (msg) => {
|
|
1769
|
-
if (msg.type !== "create_terminal_response") {
|
|
1770
|
-
return null;
|
|
1771
|
-
}
|
|
1772
|
-
if (msg.payload.requestId !== resolvedRequestId) {
|
|
1773
|
-
return null;
|
|
1774
|
-
}
|
|
1775
|
-
return msg.payload;
|
|
1776
|
-
},
|
|
1777
1711
|
});
|
|
1778
1712
|
}
|
|
1779
1713
|
async subscribeTerminal(terminalId, requestId) {
|
|
@@ -1783,20 +1717,12 @@ export class DaemonClient {
|
|
|
1783
1717
|
terminalId,
|
|
1784
1718
|
requestId: resolvedRequestId,
|
|
1785
1719
|
});
|
|
1786
|
-
return this.
|
|
1720
|
+
return this.sendCorrelatedRequest({
|
|
1787
1721
|
requestId: resolvedRequestId,
|
|
1788
1722
|
message,
|
|
1723
|
+
responseType: "subscribe_terminal_response",
|
|
1789
1724
|
timeout: 10000,
|
|
1790
1725
|
options: { skipQueue: true },
|
|
1791
|
-
select: (msg) => {
|
|
1792
|
-
if (msg.type !== "subscribe_terminal_response") {
|
|
1793
|
-
return null;
|
|
1794
|
-
}
|
|
1795
|
-
if (msg.payload.requestId !== resolvedRequestId) {
|
|
1796
|
-
return null;
|
|
1797
|
-
}
|
|
1798
|
-
return msg.payload;
|
|
1799
|
-
},
|
|
1800
1726
|
});
|
|
1801
1727
|
}
|
|
1802
1728
|
unsubscribeTerminal(terminalId) {
|
|
@@ -1819,20 +1745,94 @@ export class DaemonClient {
|
|
|
1819
1745
|
terminalId,
|
|
1820
1746
|
requestId: resolvedRequestId,
|
|
1821
1747
|
});
|
|
1822
|
-
return this.
|
|
1748
|
+
return this.sendCorrelatedRequest({
|
|
1823
1749
|
requestId: resolvedRequestId,
|
|
1824
1750
|
message,
|
|
1751
|
+
responseType: "kill_terminal_response",
|
|
1825
1752
|
timeout: 10000,
|
|
1826
1753
|
options: { skipQueue: true },
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1754
|
+
});
|
|
1755
|
+
}
|
|
1756
|
+
async attachTerminalStream(terminalId, options, requestId) {
|
|
1757
|
+
const resolvedRequestId = this.createRequestId(requestId);
|
|
1758
|
+
const message = SessionInboundMessageSchema.parse({
|
|
1759
|
+
type: "attach_terminal_stream_request",
|
|
1760
|
+
terminalId,
|
|
1761
|
+
requestId: resolvedRequestId,
|
|
1762
|
+
...(options?.resumeOffset !== undefined ? { resumeOffset: options.resumeOffset } : {}),
|
|
1763
|
+
...(options?.rows !== undefined ? { rows: options.rows } : {}),
|
|
1764
|
+
...(options?.cols !== undefined ? { cols: options.cols } : {}),
|
|
1765
|
+
});
|
|
1766
|
+
return this.sendCorrelatedRequest({
|
|
1767
|
+
requestId: resolvedRequestId,
|
|
1768
|
+
message,
|
|
1769
|
+
responseType: "attach_terminal_stream_response",
|
|
1770
|
+
timeout: 10000,
|
|
1771
|
+
options: { skipQueue: true },
|
|
1772
|
+
});
|
|
1773
|
+
}
|
|
1774
|
+
async detachTerminalStream(streamId, requestId) {
|
|
1775
|
+
const resolvedRequestId = this.createRequestId(requestId);
|
|
1776
|
+
const message = SessionInboundMessageSchema.parse({
|
|
1777
|
+
type: "detach_terminal_stream_request",
|
|
1778
|
+
streamId,
|
|
1779
|
+
requestId: resolvedRequestId,
|
|
1780
|
+
});
|
|
1781
|
+
const payload = await this.sendCorrelatedRequest({
|
|
1782
|
+
requestId: resolvedRequestId,
|
|
1783
|
+
message,
|
|
1784
|
+
responseType: "detach_terminal_stream_response",
|
|
1785
|
+
timeout: 10000,
|
|
1786
|
+
options: { skipQueue: true },
|
|
1787
|
+
});
|
|
1788
|
+
this.terminalStreams.clearStream({ streamId });
|
|
1789
|
+
return payload;
|
|
1790
|
+
}
|
|
1791
|
+
onTerminalStreamData(streamId, handler) {
|
|
1792
|
+
return this.terminalStreams.subscribe({ streamId, handler });
|
|
1793
|
+
}
|
|
1794
|
+
async waitForTerminalStreamData(streamId, predicate, timeout = 5000) {
|
|
1795
|
+
return new Promise((resolve, reject) => {
|
|
1796
|
+
const timeoutHandle = setTimeout(() => {
|
|
1797
|
+
unsubscribe();
|
|
1798
|
+
reject(new Error(`Timeout waiting for terminal stream data (${timeout}ms)`));
|
|
1799
|
+
}, timeout);
|
|
1800
|
+
const unsubscribe = this.onTerminalStreamData(streamId, (chunk) => {
|
|
1801
|
+
if (!predicate(chunk)) {
|
|
1802
|
+
return;
|
|
1833
1803
|
}
|
|
1834
|
-
|
|
1835
|
-
|
|
1804
|
+
clearTimeout(timeoutHandle);
|
|
1805
|
+
unsubscribe();
|
|
1806
|
+
resolve(chunk);
|
|
1807
|
+
});
|
|
1808
|
+
});
|
|
1809
|
+
}
|
|
1810
|
+
sendTerminalStreamInput(streamId, data) {
|
|
1811
|
+
const payload = typeof data === "string" ? encodeUtf8String(data) : data;
|
|
1812
|
+
this.sendBinaryFrame({
|
|
1813
|
+
channel: BinaryMuxChannel.Terminal,
|
|
1814
|
+
messageType: TerminalBinaryMessageType.InputUtf8,
|
|
1815
|
+
streamId,
|
|
1816
|
+
offset: 0,
|
|
1817
|
+
payload,
|
|
1818
|
+
});
|
|
1819
|
+
}
|
|
1820
|
+
sendTerminalStreamKey(streamId, input) {
|
|
1821
|
+
const encoded = encodeTerminalKeyInput(input);
|
|
1822
|
+
if (!encoded) {
|
|
1823
|
+
return;
|
|
1824
|
+
}
|
|
1825
|
+
this.sendTerminalStreamInput(streamId, encoded);
|
|
1826
|
+
}
|
|
1827
|
+
sendTerminalStreamAck(streamId, offset) {
|
|
1828
|
+
const normalizedOffset = Math.max(0, Math.floor(offset));
|
|
1829
|
+
this.terminalStreams.noteAck({ streamId, offset: normalizedOffset });
|
|
1830
|
+
this.sendBinaryFrame({
|
|
1831
|
+
channel: BinaryMuxChannel.Terminal,
|
|
1832
|
+
messageType: TerminalBinaryMessageType.Ack,
|
|
1833
|
+
streamId,
|
|
1834
|
+
offset: normalizedOffset,
|
|
1835
|
+
payload: new Uint8Array(0),
|
|
1836
1836
|
});
|
|
1837
1837
|
}
|
|
1838
1838
|
async waitForTerminalOutput(terminalId, timeout = 5000) {
|
|
@@ -1883,6 +1883,14 @@ export class DaemonClient {
|
|
|
1883
1883
|
const rawData = data && typeof data === "object" && "data" in data
|
|
1884
1884
|
? data.data
|
|
1885
1885
|
: data;
|
|
1886
|
+
const rawBytes = asUint8Array(rawData);
|
|
1887
|
+
if (rawBytes) {
|
|
1888
|
+
const frame = decodeBinaryMuxFrame(rawBytes);
|
|
1889
|
+
if (frame) {
|
|
1890
|
+
this.handleBinaryFrame(frame);
|
|
1891
|
+
return;
|
|
1892
|
+
}
|
|
1893
|
+
}
|
|
1886
1894
|
const payload = decodeMessageData(rawData);
|
|
1887
1895
|
if (!payload) {
|
|
1888
1896
|
return;
|
|
@@ -1905,6 +1913,20 @@ export class DaemonClient {
|
|
|
1905
1913
|
}
|
|
1906
1914
|
this.handleSessionMessage(parsed.data.message);
|
|
1907
1915
|
}
|
|
1916
|
+
handleBinaryFrame(frame) {
|
|
1917
|
+
if (frame.channel === BinaryMuxChannel.Terminal &&
|
|
1918
|
+
frame.messageType === TerminalBinaryMessageType.OutputUtf8) {
|
|
1919
|
+
const chunk = {
|
|
1920
|
+
streamId: frame.streamId,
|
|
1921
|
+
offset: frame.offset,
|
|
1922
|
+
endOffset: frame.offset + (frame.payload?.byteLength ?? 0),
|
|
1923
|
+
replay: Boolean((frame.flags ?? 0) & TerminalBinaryFlags.Replay),
|
|
1924
|
+
data: frame.payload ?? new Uint8Array(0),
|
|
1925
|
+
};
|
|
1926
|
+
this.terminalStreams.receiveChunk({ chunk });
|
|
1927
|
+
return;
|
|
1928
|
+
}
|
|
1929
|
+
}
|
|
1908
1930
|
updateConnectionState(next) {
|
|
1909
1931
|
this.connectionState = next;
|
|
1910
1932
|
for (const listener of this.connectionListeners) {
|
|
@@ -1937,6 +1959,7 @@ export class DaemonClient {
|
|
|
1937
1959
|
// and responses from the previous connection will never arrive.
|
|
1938
1960
|
this.clearWaiters(new Error(reason ?? "Connection lost"));
|
|
1939
1961
|
this.rejectPendingSendQueue(new Error(reason ?? "Connection lost"));
|
|
1962
|
+
this.terminalStreams.clearAll();
|
|
1940
1963
|
this.updateConnectionState({
|
|
1941
1964
|
status: "disconnected",
|
|
1942
1965
|
...(reason ? { reason } : {}),
|
|
@@ -1950,6 +1973,9 @@ export class DaemonClient {
|
|
|
1950
1973
|
}, delay);
|
|
1951
1974
|
}
|
|
1952
1975
|
handleSessionMessage(msg) {
|
|
1976
|
+
if (msg.type === "terminal_stream_exit") {
|
|
1977
|
+
this.terminalStreams.clearStream({ streamId: msg.payload.streamId });
|
|
1978
|
+
}
|
|
1953
1979
|
if (this.rawMessageListeners.size > 0) {
|
|
1954
1980
|
for (const handler of this.rawMessageListeners) {
|
|
1955
1981
|
try {
|
|
@@ -2016,6 +2042,8 @@ export class DaemonClient {
|
|
|
2016
2042
|
agentId: msg.payload.agentId,
|
|
2017
2043
|
event: msg.payload.event,
|
|
2018
2044
|
timestamp: msg.payload.timestamp,
|
|
2045
|
+
...(typeof msg.payload.seq === "number" ? { seq: msg.payload.seq } : {}),
|
|
2046
|
+
...(typeof msg.payload.epoch === "string" ? { epoch: msg.payload.epoch } : {}),
|
|
2019
2047
|
};
|
|
2020
2048
|
case "status":
|
|
2021
2049
|
return { type: "status", payload: msg.payload };
|
|
@@ -2101,333 +2129,6 @@ export class DaemonClient {
|
|
|
2101
2129
|
return { promise, cancel };
|
|
2102
2130
|
}
|
|
2103
2131
|
}
|
|
2104
|
-
// ============================================================================
|
|
2105
|
-
// Helpers
|
|
2106
|
-
// ============================================================================
|
|
2107
|
-
function defaultWebSocketFactory(url, _options) {
|
|
2108
|
-
const globalWs = globalThis.WebSocket;
|
|
2109
|
-
if (!globalWs) {
|
|
2110
|
-
throw new Error("WebSocket is not available in this runtime");
|
|
2111
|
-
}
|
|
2112
|
-
return new globalWs(url);
|
|
2113
|
-
}
|
|
2114
|
-
function createWebSocketTransportFactory(factory) {
|
|
2115
|
-
return ({ url, headers }) => {
|
|
2116
|
-
const ws = factory(url, { headers });
|
|
2117
|
-
return {
|
|
2118
|
-
send: (data) => {
|
|
2119
|
-
if (typeof ws.readyState === "number" && ws.readyState !== 1) {
|
|
2120
|
-
throw new Error(`WebSocket not open (readyState=${ws.readyState})`);
|
|
2121
|
-
}
|
|
2122
|
-
ws.send(data);
|
|
2123
|
-
},
|
|
2124
|
-
close: (code, reason) => ws.close(code, reason),
|
|
2125
|
-
onOpen: (handler) => bindWsHandler(ws, "open", handler),
|
|
2126
|
-
onClose: (handler) => bindWsHandler(ws, "close", handler),
|
|
2127
|
-
onError: (handler) => bindWsHandler(ws, "error", handler),
|
|
2128
|
-
onMessage: (handler) => bindWsHandler(ws, "message", handler),
|
|
2129
|
-
};
|
|
2130
|
-
};
|
|
2131
|
-
}
|
|
2132
|
-
function createRelayE2eeTransportFactory(args) {
|
|
2133
|
-
return ({ url, headers }) => {
|
|
2134
|
-
const base = args.baseFactory({ url, headers });
|
|
2135
|
-
return createEncryptedTransport(base, args.daemonPublicKeyB64, args.logger);
|
|
2136
|
-
};
|
|
2137
|
-
}
|
|
2138
|
-
function createEncryptedTransport(base, daemonPublicKeyB64, logger) {
|
|
2139
|
-
let channel = null;
|
|
2140
|
-
let opened = false;
|
|
2141
|
-
let closed = false;
|
|
2142
|
-
const openHandlers = new Set();
|
|
2143
|
-
const closeHandlers = new Set();
|
|
2144
|
-
const errorHandlers = new Set();
|
|
2145
|
-
const messageHandlers = new Set();
|
|
2146
|
-
const emitOpen = () => {
|
|
2147
|
-
if (opened || closed)
|
|
2148
|
-
return;
|
|
2149
|
-
opened = true;
|
|
2150
|
-
for (const handler of openHandlers) {
|
|
2151
|
-
try {
|
|
2152
|
-
handler();
|
|
2153
|
-
}
|
|
2154
|
-
catch {
|
|
2155
|
-
// no-op
|
|
2156
|
-
}
|
|
2157
|
-
}
|
|
2158
|
-
};
|
|
2159
|
-
const emitClose = (event) => {
|
|
2160
|
-
if (closed)
|
|
2161
|
-
return;
|
|
2162
|
-
closed = true;
|
|
2163
|
-
for (const handler of closeHandlers) {
|
|
2164
|
-
try {
|
|
2165
|
-
handler(event);
|
|
2166
|
-
}
|
|
2167
|
-
catch {
|
|
2168
|
-
// no-op
|
|
2169
|
-
}
|
|
2170
|
-
}
|
|
2171
|
-
};
|
|
2172
|
-
const emitError = (event) => {
|
|
2173
|
-
if (closed)
|
|
2174
|
-
return;
|
|
2175
|
-
for (const handler of errorHandlers) {
|
|
2176
|
-
try {
|
|
2177
|
-
handler(event);
|
|
2178
|
-
}
|
|
2179
|
-
catch {
|
|
2180
|
-
// no-op
|
|
2181
|
-
}
|
|
2182
|
-
}
|
|
2183
|
-
};
|
|
2184
|
-
const emitMessage = (data) => {
|
|
2185
|
-
if (closed)
|
|
2186
|
-
return;
|
|
2187
|
-
for (const handler of messageHandlers) {
|
|
2188
|
-
try {
|
|
2189
|
-
handler(data);
|
|
2190
|
-
}
|
|
2191
|
-
catch {
|
|
2192
|
-
// no-op
|
|
2193
|
-
}
|
|
2194
|
-
}
|
|
2195
|
-
};
|
|
2196
|
-
const relayTransport = {
|
|
2197
|
-
send: (data) => {
|
|
2198
|
-
if (typeof data === "string") {
|
|
2199
|
-
base.send(data);
|
|
2200
|
-
return;
|
|
2201
|
-
}
|
|
2202
|
-
if (data instanceof ArrayBuffer) {
|
|
2203
|
-
if (typeof TextDecoder !== "undefined") {
|
|
2204
|
-
base.send(new TextDecoder().decode(data));
|
|
2205
|
-
return;
|
|
2206
|
-
}
|
|
2207
|
-
if (typeof Buffer !== "undefined") {
|
|
2208
|
-
base.send(Buffer.from(data).toString("utf8"));
|
|
2209
|
-
return;
|
|
2210
|
-
}
|
|
2211
|
-
base.send(String(data));
|
|
2212
|
-
return;
|
|
2213
|
-
}
|
|
2214
|
-
base.send(String(data));
|
|
2215
|
-
},
|
|
2216
|
-
close: (code, reason) => base.close(code, reason),
|
|
2217
|
-
onmessage: null,
|
|
2218
|
-
onclose: null,
|
|
2219
|
-
onerror: null,
|
|
2220
|
-
};
|
|
2221
|
-
const startHandshake = async () => {
|
|
2222
|
-
try {
|
|
2223
|
-
channel = await createClientChannel(relayTransport, daemonPublicKeyB64, {
|
|
2224
|
-
onopen: emitOpen,
|
|
2225
|
-
onmessage: (data) => emitMessage(data),
|
|
2226
|
-
onclose: (code, reason) => emitClose({ code, reason }),
|
|
2227
|
-
onerror: (error) => emitError(error),
|
|
2228
|
-
});
|
|
2229
|
-
}
|
|
2230
|
-
catch (error) {
|
|
2231
|
-
logger.warn({ err: error }, "relay_e2ee_handshake_failed");
|
|
2232
|
-
emitError(error);
|
|
2233
|
-
base.close(1011, "E2EE handshake failed");
|
|
2234
|
-
}
|
|
2235
|
-
};
|
|
2236
|
-
base.onOpen(() => {
|
|
2237
|
-
void startHandshake();
|
|
2238
|
-
});
|
|
2239
|
-
base.onMessage((event) => {
|
|
2240
|
-
relayTransport.onmessage?.(extractRelayMessageData(event));
|
|
2241
|
-
});
|
|
2242
|
-
base.onClose((event) => {
|
|
2243
|
-
const record = event;
|
|
2244
|
-
relayTransport.onclose?.(record?.code ?? 0, record?.reason ?? "");
|
|
2245
|
-
emitClose(event);
|
|
2246
|
-
});
|
|
2247
|
-
base.onError((event) => {
|
|
2248
|
-
relayTransport.onerror?.(event instanceof Error ? event : new Error(String(event)));
|
|
2249
|
-
emitError(event);
|
|
2250
|
-
});
|
|
2251
|
-
return {
|
|
2252
|
-
send: (data) => {
|
|
2253
|
-
if (!channel) {
|
|
2254
|
-
throw new Error("Encrypted channel not ready");
|
|
2255
|
-
}
|
|
2256
|
-
void channel.send(data).catch((error) => {
|
|
2257
|
-
emitError(error);
|
|
2258
|
-
});
|
|
2259
|
-
},
|
|
2260
|
-
close: (code, reason) => {
|
|
2261
|
-
if (channel) {
|
|
2262
|
-
channel.close(code, reason);
|
|
2263
|
-
}
|
|
2264
|
-
else {
|
|
2265
|
-
base.close(code, reason);
|
|
2266
|
-
}
|
|
2267
|
-
emitClose({ code, reason });
|
|
2268
|
-
},
|
|
2269
|
-
onMessage: (handler) => {
|
|
2270
|
-
messageHandlers.add(handler);
|
|
2271
|
-
return () => messageHandlers.delete(handler);
|
|
2272
|
-
},
|
|
2273
|
-
onOpen: (handler) => {
|
|
2274
|
-
openHandlers.add(handler);
|
|
2275
|
-
if (opened) {
|
|
2276
|
-
try {
|
|
2277
|
-
handler();
|
|
2278
|
-
}
|
|
2279
|
-
catch {
|
|
2280
|
-
// no-op
|
|
2281
|
-
}
|
|
2282
|
-
}
|
|
2283
|
-
return () => openHandlers.delete(handler);
|
|
2284
|
-
},
|
|
2285
|
-
onClose: (handler) => {
|
|
2286
|
-
closeHandlers.add(handler);
|
|
2287
|
-
if (closed) {
|
|
2288
|
-
try {
|
|
2289
|
-
handler();
|
|
2290
|
-
}
|
|
2291
|
-
catch {
|
|
2292
|
-
// no-op
|
|
2293
|
-
}
|
|
2294
|
-
}
|
|
2295
|
-
return () => closeHandlers.delete(handler);
|
|
2296
|
-
},
|
|
2297
|
-
onError: (handler) => {
|
|
2298
|
-
errorHandlers.add(handler);
|
|
2299
|
-
return () => errorHandlers.delete(handler);
|
|
2300
|
-
},
|
|
2301
|
-
};
|
|
2302
|
-
}
|
|
2303
|
-
function extractRelayMessageData(event) {
|
|
2304
|
-
const raw = event && typeof event === "object" && "data" in event
|
|
2305
|
-
? event.data
|
|
2306
|
-
: event;
|
|
2307
|
-
if (typeof raw === "string")
|
|
2308
|
-
return raw;
|
|
2309
|
-
if (raw instanceof ArrayBuffer)
|
|
2310
|
-
return raw;
|
|
2311
|
-
if (ArrayBuffer.isView(raw)) {
|
|
2312
|
-
const view = new Uint8Array(raw.buffer, raw.byteOffset, raw.byteLength);
|
|
2313
|
-
const out = new Uint8Array(view.byteLength);
|
|
2314
|
-
out.set(view);
|
|
2315
|
-
return out.buffer;
|
|
2316
|
-
}
|
|
2317
|
-
return String(raw ?? "");
|
|
2318
|
-
}
|
|
2319
|
-
function bindWsHandler(ws, event, handler) {
|
|
2320
|
-
if (typeof ws.addEventListener === "function") {
|
|
2321
|
-
ws.addEventListener(event, handler);
|
|
2322
|
-
return () => {
|
|
2323
|
-
if (typeof ws.removeEventListener === "function") {
|
|
2324
|
-
ws.removeEventListener(event, handler);
|
|
2325
|
-
}
|
|
2326
|
-
};
|
|
2327
|
-
}
|
|
2328
|
-
if (typeof ws.on === "function") {
|
|
2329
|
-
ws.on(event, handler);
|
|
2330
|
-
return () => {
|
|
2331
|
-
if (typeof ws.off === "function") {
|
|
2332
|
-
ws.off(event, handler);
|
|
2333
|
-
return;
|
|
2334
|
-
}
|
|
2335
|
-
if (typeof ws.removeListener === "function") {
|
|
2336
|
-
ws.removeListener(event, handler);
|
|
2337
|
-
}
|
|
2338
|
-
};
|
|
2339
|
-
}
|
|
2340
|
-
const prop = `on${event}`;
|
|
2341
|
-
const previous = ws[prop];
|
|
2342
|
-
ws[prop] = handler;
|
|
2343
|
-
return () => {
|
|
2344
|
-
if (ws[prop] === handler) {
|
|
2345
|
-
ws[prop] = previous ?? null;
|
|
2346
|
-
}
|
|
2347
|
-
};
|
|
2348
|
-
}
|
|
2349
|
-
function describeTransportClose(event) {
|
|
2350
|
-
if (!event) {
|
|
2351
|
-
return "Transport closed";
|
|
2352
|
-
}
|
|
2353
|
-
if (event instanceof Error) {
|
|
2354
|
-
return event.message;
|
|
2355
|
-
}
|
|
2356
|
-
if (typeof event === "string") {
|
|
2357
|
-
return event;
|
|
2358
|
-
}
|
|
2359
|
-
if (typeof event === "object") {
|
|
2360
|
-
const record = event;
|
|
2361
|
-
if (typeof record.reason === "string" && record.reason.trim().length > 0) {
|
|
2362
|
-
return record.reason.trim();
|
|
2363
|
-
}
|
|
2364
|
-
if (typeof record.message === "string" && record.message.trim().length > 0) {
|
|
2365
|
-
return record.message.trim();
|
|
2366
|
-
}
|
|
2367
|
-
if (typeof record.code === "number") {
|
|
2368
|
-
return `Transport closed (code ${record.code})`;
|
|
2369
|
-
}
|
|
2370
|
-
}
|
|
2371
|
-
return "Transport closed";
|
|
2372
|
-
}
|
|
2373
|
-
function describeTransportError(event) {
|
|
2374
|
-
if (!event) {
|
|
2375
|
-
return "Transport error";
|
|
2376
|
-
}
|
|
2377
|
-
if (event instanceof Error) {
|
|
2378
|
-
return event.message;
|
|
2379
|
-
}
|
|
2380
|
-
if (typeof event === "string") {
|
|
2381
|
-
return event;
|
|
2382
|
-
}
|
|
2383
|
-
if (typeof event === "object") {
|
|
2384
|
-
const record = event;
|
|
2385
|
-
if (typeof record.message === "string" && record.message.trim().length > 0) {
|
|
2386
|
-
return record.message.trim();
|
|
2387
|
-
}
|
|
2388
|
-
}
|
|
2389
|
-
return "Transport error";
|
|
2390
|
-
}
|
|
2391
|
-
function safeRandomId() {
|
|
2392
|
-
try {
|
|
2393
|
-
if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
|
|
2394
|
-
return crypto.randomUUID();
|
|
2395
|
-
}
|
|
2396
|
-
}
|
|
2397
|
-
catch {
|
|
2398
|
-
// ignore
|
|
2399
|
-
}
|
|
2400
|
-
return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`;
|
|
2401
|
-
}
|
|
2402
|
-
function decodeMessageData(data) {
|
|
2403
|
-
if (data === null || data === undefined) {
|
|
2404
|
-
return null;
|
|
2405
|
-
}
|
|
2406
|
-
if (typeof data === "string") {
|
|
2407
|
-
return data;
|
|
2408
|
-
}
|
|
2409
|
-
if (typeof ArrayBuffer !== "undefined" && data instanceof ArrayBuffer) {
|
|
2410
|
-
if (typeof Buffer !== "undefined") {
|
|
2411
|
-
return Buffer.from(data).toString("utf8");
|
|
2412
|
-
}
|
|
2413
|
-
if (typeof TextDecoder !== "undefined") {
|
|
2414
|
-
return new TextDecoder().decode(data);
|
|
2415
|
-
}
|
|
2416
|
-
}
|
|
2417
|
-
if (ArrayBuffer.isView(data)) {
|
|
2418
|
-
const view = new Uint8Array(data.buffer, data.byteOffset, data.byteLength);
|
|
2419
|
-
if (typeof Buffer !== "undefined") {
|
|
2420
|
-
return Buffer.from(view).toString("utf8");
|
|
2421
|
-
}
|
|
2422
|
-
if (typeof TextDecoder !== "undefined") {
|
|
2423
|
-
return new TextDecoder().decode(view);
|
|
2424
|
-
}
|
|
2425
|
-
}
|
|
2426
|
-
if (typeof data.toString === "function") {
|
|
2427
|
-
return data.toString();
|
|
2428
|
-
}
|
|
2429
|
-
return null;
|
|
2430
|
-
}
|
|
2431
2132
|
function resolveAgentConfig(options) {
|
|
2432
2133
|
const { config, provider, cwd, initialPrompt: _initialPrompt, images: _images, git: _git, worktreeName: _worktreeName, requestId: _requestId, labels: _labels, ...overrides } = options;
|
|
2433
2134
|
const baseConfig = {
|