@camerontaylor/paseo-client 0.11.2-fork.1 → 0.12.0-beta.1.fork.1
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/daemon-client-relay-e2ee-transport.js +9 -2
- package/dist/daemon-client-transport-utils.d.ts +1 -0
- package/dist/daemon-client-transport-utils.js +4 -0
- package/dist/daemon-client.d.ts +15 -1
- package/dist/daemon-client.js +121 -21
- package/dist/index.d.ts +2 -0
- package/dist/index.js +5 -1
- package/package.json +3 -3
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
import { MAX_RELAY_WEBSOCKET_MESSAGE_BYTES } from "@camerontaylor/paseo-protocol/websocket-message-limit";
|
|
1
2
|
import { createClientChannel, } from "@camerontaylor/paseo-relay/e2ee";
|
|
2
|
-
import { extractRelayMessage, normalizeTransportPayload } from "./daemon-client-transport-utils.js";
|
|
3
|
+
import { extractRelayMessage, normalizeTransportPayload, messageTooLargeError, } from "./daemon-client-transport-utils.js";
|
|
3
4
|
export function createRelayE2eeTransportFactory(args) {
|
|
4
5
|
return ({ url }) => {
|
|
5
6
|
const base = args.baseFactory({ url });
|
|
@@ -102,7 +103,13 @@ export function createEncryptedTransport(base, daemonPublicKeyB64, logger) {
|
|
|
102
103
|
if (!channel) {
|
|
103
104
|
throw new Error("Encrypted channel not ready");
|
|
104
105
|
}
|
|
105
|
-
|
|
106
|
+
const payload = normalizeTransportPayload(data);
|
|
107
|
+
// Reject synchronously so the requesting operation fails without closing the transport.
|
|
108
|
+
const wireBytes = channel.outboundWireByteLength(payload);
|
|
109
|
+
if (wireBytes > MAX_RELAY_WEBSOCKET_MESSAGE_BYTES) {
|
|
110
|
+
throw messageTooLargeError(wireBytes, MAX_RELAY_WEBSOCKET_MESSAGE_BYTES);
|
|
111
|
+
}
|
|
112
|
+
void channel.send(payload).catch((error) => {
|
|
106
113
|
emitError(error);
|
|
107
114
|
});
|
|
108
115
|
},
|
|
@@ -10,4 +10,5 @@ export declare function describeTransportError(event?: unknown): string;
|
|
|
10
10
|
export declare function safeRandomId(): string;
|
|
11
11
|
export declare function decodeMessageData(data: unknown): string | null;
|
|
12
12
|
export declare function encodeUtf8String(value: string): Uint8Array;
|
|
13
|
+
export declare function messageTooLargeError(size: number, limit: number): Error;
|
|
13
14
|
//# sourceMappingURL=daemon-client-transport-utils.d.ts.map
|
|
@@ -120,4 +120,8 @@ export function encodeUtf8String(value) {
|
|
|
120
120
|
}
|
|
121
121
|
return out;
|
|
122
122
|
}
|
|
123
|
+
export function messageTooLargeError(size, limit) {
|
|
124
|
+
const toMiB = (bytes) => (bytes / (1024 * 1024)).toFixed(1);
|
|
125
|
+
return new Error(`Message is too large to send (${toMiB(size)} MiB). The limit is ${toMiB(limit)} MiB, so send fewer or smaller attachments.`);
|
|
126
|
+
}
|
|
123
127
|
//# sourceMappingURL=daemon-client-transport-utils.js.map
|
package/dist/daemon-client.d.ts
CHANGED
|
@@ -147,6 +147,7 @@ export interface DaemonClientTrace {
|
|
|
147
147
|
endSection(): void;
|
|
148
148
|
}
|
|
149
149
|
export interface SendMessageOptions {
|
|
150
|
+
sourceAgentId?: string;
|
|
150
151
|
messageId?: string;
|
|
151
152
|
/** What happens when the agent is mid-turn. The daemon interrupts the turn when omitted. */
|
|
152
153
|
activeTurnBehavior?: ActiveTurnBehavior;
|
|
@@ -182,6 +183,7 @@ export interface CreateAgentRequestOptions extends AgentConfigOverrides {
|
|
|
182
183
|
git?: GitSetupOptions;
|
|
183
184
|
worktree?: CreateAgentRequestMessage["worktree"];
|
|
184
185
|
autoArchive?: CreateAgentRequestMessage["autoArchive"];
|
|
186
|
+
background?: CreateAgentRequestMessage["background"];
|
|
185
187
|
worktreeName?: string;
|
|
186
188
|
requestId?: string;
|
|
187
189
|
labels?: Record<string, string>;
|
|
@@ -189,6 +191,9 @@ export interface CreateAgentRequestOptions extends AgentConfigOverrides {
|
|
|
189
191
|
export interface CreateWorkspaceRequestOptions {
|
|
190
192
|
source: WorkspaceCreateRequest["source"];
|
|
191
193
|
title?: string;
|
|
194
|
+
/** Workspace discovery visibility; contents retain normal durability. */
|
|
195
|
+
background?: boolean;
|
|
196
|
+
callerAgentId?: string;
|
|
192
197
|
idempotencyKey?: string;
|
|
193
198
|
workspaceId?: string;
|
|
194
199
|
agent?: Omit<CreateAgentRequestOptions, "workspaceId" | "onEvent" | "worktree" | "git" | "worktreeName" | "idempotencyKey" | "requestId">;
|
|
@@ -740,6 +745,7 @@ export declare class DaemonClient {
|
|
|
740
745
|
signal?: AbortSignal;
|
|
741
746
|
notifications?: boolean;
|
|
742
747
|
}): OwnedSubscription<CorrelatedResponsePayload<"session.events.set_subscription.response">>;
|
|
748
|
+
private filterSupportedEventSelectors;
|
|
743
749
|
observeAgents(options?: Omit<FetchAgentsOptions, "subscribe">): OwnedSubscription<FetchAgentsPayload>;
|
|
744
750
|
observeWorkspaces(options?: Omit<FetchWorkspacesOptions, "subscribe">): OwnedSubscription<FetchWorkspacesPayload>;
|
|
745
751
|
fetchAgents(options: FetchAgentsOptions & {
|
|
@@ -827,6 +833,8 @@ export declare class DaemonClient {
|
|
|
827
833
|
fetchAgent(agentId: string, requestId?: string): Promise<FetchAgentResult | null>;
|
|
828
834
|
fetchAgent(agentId: string, options?: LegacyFetchAgentOptions): Promise<FetchAgentResult | null>;
|
|
829
835
|
private readonly creations;
|
|
836
|
+
private requireBackgroundWorkspaces;
|
|
837
|
+
supportsBackgroundWorkspaces(): boolean;
|
|
830
838
|
createAgent(options: CreateAgentRequestOptions): Promise<AgentSnapshotPayload>;
|
|
831
839
|
private createLegacyAgent;
|
|
832
840
|
deleteAgent(agentId: string): Promise<void>;
|
|
@@ -1139,7 +1147,12 @@ export declare class DaemonClient {
|
|
|
1139
1147
|
}, requestId?: string): Promise<DirectorySuggestionsPayload>;
|
|
1140
1148
|
private requestFileExplorer;
|
|
1141
1149
|
listDirectory(cwd: string, path: string, requestId?: string): Promise<FileExplorerDirectoryPayload>;
|
|
1142
|
-
|
|
1150
|
+
/**
|
|
1151
|
+
* `timeout` bounds the whole transfer, not each frame. `0` disables the timer (as
|
|
1152
|
+
* `workspace.create` does) for files that legitimately take longer than the default over a
|
|
1153
|
+
* slow link; a dead connection still rejects on disconnect.
|
|
1154
|
+
*/
|
|
1155
|
+
readFile(cwd: string, path: string, requestId?: string, maxBytes?: number, timeout?: number): Promise<FileReadResult>;
|
|
1143
1156
|
observeFile(input: {
|
|
1144
1157
|
cwd: string;
|
|
1145
1158
|
path: string;
|
|
@@ -1397,6 +1410,7 @@ export declare class DaemonClient {
|
|
|
1397
1410
|
private requireHubRelationshipSupport;
|
|
1398
1411
|
private requireDaemonConfigReloadSupport;
|
|
1399
1412
|
private requireSideConversationSupport;
|
|
1413
|
+
private requireCheckoutBaseRefSupport;
|
|
1400
1414
|
private resolveTransportUrlForAttempt;
|
|
1401
1415
|
private sendHelloMessage;
|
|
1402
1416
|
private disposeTransport;
|
package/dist/daemon-client.js
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
|
+
import { messageTooLargeError } from "./daemon-client-transport-utils.js";
|
|
1
2
|
import { legacyUsageIcon } from "./legacy-usage-icons.js";
|
|
2
3
|
import { subscribeTimeline } from "./timeline-subscription/index.js";
|
|
3
4
|
import { ProviderSnapshotUpdates } from "./provider-snapshots/index.js";
|
|
4
5
|
import { ConnectionSubscriptions, DEFAULT_CLIENT_CAPABILITIES, } from "./connection/index.js";
|
|
5
6
|
import { CreationClient } from "./creation/index.js";
|
|
6
7
|
import { CLIENT_CAPS } from "@camerontaylor/paseo-protocol/client-capabilities";
|
|
8
|
+
import { MAX_WEBSOCKET_MESSAGE_BYTES } from "@camerontaylor/paseo-protocol/websocket-message-limit";
|
|
7
9
|
import { AgentCreateFailedStatusPayloadSchema, AgentCreatedStatusPayloadSchema, AgentRefreshedStatusPayloadSchema, AgentResumedStatusPayloadSchema, parseServerInfoStatusPayload, RestartRequestedStatusPayloadSchema, ShutdownRequestedStatusPayloadSchema, DaemonUpdateResponseSchema, SessionInboundMessageSchema, } from "@camerontaylor/paseo-protocol/messages";
|
|
8
10
|
import { validateWSOutboundMessage } from "@camerontaylor/paseo-protocol/validation/ws-outbound";
|
|
9
11
|
import { isRelayClientWebSocketUrl } from "@camerontaylor/paseo-protocol/daemon-endpoints";
|
|
@@ -246,6 +248,19 @@ function getTransportFrameSize(frame) {
|
|
|
246
248
|
}
|
|
247
249
|
return frame.byteLength;
|
|
248
250
|
}
|
|
251
|
+
function assertTransportFrameFits(frame) {
|
|
252
|
+
if (typeof frame !== "string") {
|
|
253
|
+
if (frame.byteLength > MAX_WEBSOCKET_MESSAGE_BYTES)
|
|
254
|
+
throw messageTooLargeError(frame.byteLength, MAX_WEBSOCKET_MESSAGE_BYTES);
|
|
255
|
+
return;
|
|
256
|
+
}
|
|
257
|
+
// UTF-8 needs at most three bytes per UTF-16 unit, so only a string near the limit is measured.
|
|
258
|
+
if (frame.length * 3 <= MAX_WEBSOCKET_MESSAGE_BYTES)
|
|
259
|
+
return;
|
|
260
|
+
const size = new TextEncoder().encode(frame).byteLength;
|
|
261
|
+
if (size > MAX_WEBSOCKET_MESSAGE_BYTES)
|
|
262
|
+
throw messageTooLargeError(size, MAX_WEBSOCKET_MESSAGE_BYTES);
|
|
263
|
+
}
|
|
249
264
|
function describeInboundTransportFrame(frame, rawBytes) {
|
|
250
265
|
if (typeof frame === "string") {
|
|
251
266
|
return { kind: "text", size: String(frame.length) };
|
|
@@ -288,6 +303,34 @@ function toReasonCode(reason) {
|
|
|
288
303
|
export function supportsUsageReports(features) {
|
|
289
304
|
return features?.usageSources === true || features?.providerUsageList === true;
|
|
290
305
|
}
|
|
306
|
+
/**
|
|
307
|
+
* Event selectors a host only understands behind a handshake feature. Anything absent from this
|
|
308
|
+
* map is part of the base protocol and always sent. The client drops mapped selectors the
|
|
309
|
+
* connected host does not advertise, so an older daemon never parses a selector it does not know.
|
|
310
|
+
*/
|
|
311
|
+
const EVENT_SELECTOR_FEATURES = {
|
|
312
|
+
// COMPAT(sideConversationEvents): added in 0.12.0-beta.1.fork.1.
|
|
313
|
+
"agent.side_conversation.update": "sideConversationEvents",
|
|
314
|
+
"agent.side_conversation.removed": "sideConversationEvents",
|
|
315
|
+
// COMPAT(durableAgentQueueV1): added in 0.12.0-beta.1.fork.1. An old daemon rejects the whole
|
|
316
|
+
// subscription request when it parses an unknown selector, so this one is dropped before send.
|
|
317
|
+
"agent.queue.update": "durableAgentQueueV1",
|
|
318
|
+
};
|
|
319
|
+
function buildFetchAgentsRequest(options, requestId) {
|
|
320
|
+
return SessionInboundMessageSchema.parse({
|
|
321
|
+
type: "fetch_agents_request",
|
|
322
|
+
requestId,
|
|
323
|
+
...(options?.scope ? { scope: options.scope } : {}),
|
|
324
|
+
...(options?.filter ? { filter: options.filter } : {}),
|
|
325
|
+
...(options?.sort ? { sort: options.sort } : {}),
|
|
326
|
+
...(options?.page ? { page: options.page } : {}),
|
|
327
|
+
...(options?.subscribe ? { subscribe: options.subscribe } : {}),
|
|
328
|
+
...(options?.sync ? { sync: options.sync } : {}),
|
|
329
|
+
...(options?.includeBackground !== undefined
|
|
330
|
+
? { includeBackground: options.includeBackground }
|
|
331
|
+
: {}),
|
|
332
|
+
});
|
|
333
|
+
}
|
|
291
334
|
export class DaemonClient {
|
|
292
335
|
constructor(config) {
|
|
293
336
|
this.config = config;
|
|
@@ -813,6 +856,8 @@ export class DaemonClient {
|
|
|
813
856
|
if (!this.transport) {
|
|
814
857
|
throw new DaemonConnectionError("Transport not connected");
|
|
815
858
|
}
|
|
859
|
+
// The daemon closes the connection on an oversized message, and creations resubmit on reconnect.
|
|
860
|
+
assertTransportFrameFits(frame);
|
|
816
861
|
const isOpen = this.beginTraceSection("paseo.ws.frame.outbound", {
|
|
817
862
|
kind: typeof frame === "string" ? "text" : "binary",
|
|
818
863
|
size: String(getTransportFrameSize(frame)),
|
|
@@ -1246,7 +1291,8 @@ export class DaemonClient {
|
|
|
1246
1291
|
return this.owned.observeRequest(message, async (query, accept) => {
|
|
1247
1292
|
resetSource = false;
|
|
1248
1293
|
const requestId = this.createRequestId(options?.requestId);
|
|
1249
|
-
const
|
|
1294
|
+
const prepared = options?.prepare ? options.prepare({ ...query }) : query;
|
|
1295
|
+
const request = SessionInboundMessageSchema.parse({ ...prepared, requestId });
|
|
1250
1296
|
try {
|
|
1251
1297
|
return await this.sendCorrelatedRequest({
|
|
1252
1298
|
message: request,
|
|
@@ -1287,17 +1333,37 @@ export class DaemonClient {
|
|
|
1287
1333
|
type: "session.events.set_subscription.request",
|
|
1288
1334
|
events,
|
|
1289
1335
|
notifications: options?.notifications,
|
|
1290
|
-
},
|
|
1336
|
+
}, {
|
|
1337
|
+
signal: options?.signal,
|
|
1338
|
+
prepare: (message) => ({
|
|
1339
|
+
...message,
|
|
1340
|
+
// Re-evaluated on every send, including the restore after a reconnect, so a downgrade
|
|
1341
|
+
// to a host without the feature never sees the selector.
|
|
1342
|
+
events: this.filterSupportedEventSelectors(message.events),
|
|
1343
|
+
}),
|
|
1344
|
+
});
|
|
1345
|
+
}
|
|
1346
|
+
filterSupportedEventSelectors(events) {
|
|
1347
|
+
return events.filter((event) => {
|
|
1348
|
+
const feature = EVENT_SELECTOR_FEATURES[event];
|
|
1349
|
+
return feature === undefined || this.lastServerInfoMessage?.features?.[feature] === true;
|
|
1350
|
+
});
|
|
1291
1351
|
}
|
|
1292
1352
|
observeAgents(options = {}) {
|
|
1353
|
+
if (options.includeBackground || options.filter?.includeBackground)
|
|
1354
|
+
this.requireBackgroundWorkspaces();
|
|
1293
1355
|
const { signal, requestId, timeout, ...query } = options;
|
|
1294
1356
|
return this.observe("fetch_agents_response", { ...query, type: "fetch_agents_request", subscribe: {} }, { signal, requestId, timeout });
|
|
1295
1357
|
}
|
|
1296
1358
|
observeWorkspaces(options = {}) {
|
|
1359
|
+
if (options.includeBackground || options.filter?.includeBackground)
|
|
1360
|
+
this.requireBackgroundWorkspaces();
|
|
1297
1361
|
const { signal, requestId, ...query } = options;
|
|
1298
1362
|
return this.observe("fetch_workspaces_response", { ...query, type: "fetch_workspaces_request", subscribe: {} }, { signal, requestId });
|
|
1299
1363
|
}
|
|
1300
1364
|
async fetchAgents(options) {
|
|
1365
|
+
if (options?.includeBackground || options?.filter?.includeBackground)
|
|
1366
|
+
this.requireBackgroundWorkspaces();
|
|
1301
1367
|
if (options?.subscribe) {
|
|
1302
1368
|
if (options.subscribe.subscriptionId !== undefined)
|
|
1303
1369
|
throw new Error("Subscription IDs are assigned by the host");
|
|
@@ -1305,16 +1371,7 @@ export class DaemonClient {
|
|
|
1305
1371
|
return { ...(await subscription.ready), subscription };
|
|
1306
1372
|
}
|
|
1307
1373
|
const resolvedRequestId = this.createRequestId(options?.requestId);
|
|
1308
|
-
const message =
|
|
1309
|
-
type: "fetch_agents_request",
|
|
1310
|
-
requestId: resolvedRequestId,
|
|
1311
|
-
...(options?.scope ? { scope: options.scope } : {}),
|
|
1312
|
-
...(options?.filter ? { filter: options.filter } : {}),
|
|
1313
|
-
...(options?.sort ? { sort: options.sort } : {}),
|
|
1314
|
-
...(options?.page ? { page: options.page } : {}),
|
|
1315
|
-
...(options?.subscribe ? { subscribe: options.subscribe } : {}),
|
|
1316
|
-
...(options?.sync ? { sync: options.sync } : {}),
|
|
1317
|
-
});
|
|
1374
|
+
const message = buildFetchAgentsRequest(options, resolvedRequestId);
|
|
1318
1375
|
return this.sendRequest({
|
|
1319
1376
|
requestId: resolvedRequestId,
|
|
1320
1377
|
message,
|
|
@@ -1332,6 +1389,8 @@ export class DaemonClient {
|
|
|
1332
1389
|
});
|
|
1333
1390
|
}
|
|
1334
1391
|
async fetchAgentHistory(options) {
|
|
1392
|
+
if (options?.filter?.includeBackground)
|
|
1393
|
+
this.requireBackgroundWorkspaces();
|
|
1335
1394
|
const resolvedRequestId = this.createRequestId(options?.requestId);
|
|
1336
1395
|
const message = SessionInboundMessageSchema.parse({
|
|
1337
1396
|
type: "fetch_agent_history_request",
|
|
@@ -1383,6 +1442,8 @@ export class DaemonClient {
|
|
|
1383
1442
|
});
|
|
1384
1443
|
}
|
|
1385
1444
|
async fetchWorkspaces(options) {
|
|
1445
|
+
if (options?.includeBackground || options?.filter?.includeBackground)
|
|
1446
|
+
this.requireBackgroundWorkspaces();
|
|
1386
1447
|
if (options?.subscribe) {
|
|
1387
1448
|
if (options.subscribe.subscriptionId !== undefined)
|
|
1388
1449
|
throw new Error("Subscription IDs are assigned by the host");
|
|
@@ -1392,12 +1453,8 @@ export class DaemonClient {
|
|
|
1392
1453
|
const resolvedRequestId = this.createRequestId(options?.requestId);
|
|
1393
1454
|
const message = SessionInboundMessageSchema.parse({
|
|
1394
1455
|
type: "fetch_workspaces_request",
|
|
1456
|
+
...options,
|
|
1395
1457
|
requestId: resolvedRequestId,
|
|
1396
|
-
...(options?.filter ? { filter: options.filter } : {}),
|
|
1397
|
-
...(options?.sort ? { sort: options.sort } : {}),
|
|
1398
|
-
...(options?.page ? { page: options.page } : {}),
|
|
1399
|
-
...(options?.subscribe ? { subscribe: options.subscribe } : {}),
|
|
1400
|
-
...(options?.sync ? { sync: options.sync } : {}),
|
|
1401
1458
|
});
|
|
1402
1459
|
return this.sendRequest({
|
|
1403
1460
|
requestId: resolvedRequestId,
|
|
@@ -1626,7 +1683,18 @@ export class DaemonClient {
|
|
|
1626
1683
|
}
|
|
1627
1684
|
return { agent: payload.agent, project: payload.project ?? null };
|
|
1628
1685
|
}
|
|
1686
|
+
// Background workspaces are gated once at the transport boundary.
|
|
1687
|
+
requireBackgroundWorkspaces() {
|
|
1688
|
+
if (this.lastServerInfoMessage?.features?.backgroundWorkspaces !== true) {
|
|
1689
|
+
throw new Error("Update the host to use background workspaces.");
|
|
1690
|
+
}
|
|
1691
|
+
}
|
|
1692
|
+
supportsBackgroundWorkspaces() {
|
|
1693
|
+
return this.lastServerInfoMessage?.features?.backgroundWorkspaces === true;
|
|
1694
|
+
}
|
|
1629
1695
|
async createAgent(options) {
|
|
1696
|
+
if (options.background !== undefined)
|
|
1697
|
+
this.requireBackgroundWorkspaces();
|
|
1630
1698
|
const result = await this.creations.createAgent({
|
|
1631
1699
|
...options,
|
|
1632
1700
|
config: resolveAgentConfig(options),
|
|
@@ -1656,6 +1724,7 @@ export class DaemonClient {
|
|
|
1656
1724
|
...(options.git ? { git: options.git } : {}),
|
|
1657
1725
|
...(options.worktree ? { worktree: options.worktree } : {}),
|
|
1658
1726
|
...(options.autoArchive !== undefined ? { autoArchive: options.autoArchive } : {}),
|
|
1727
|
+
...(options.background !== undefined ? { background: options.background } : {}),
|
|
1659
1728
|
...(options.worktreeName ? { worktreeName: options.worktreeName } : {}),
|
|
1660
1729
|
...(options.labels && Object.keys(options.labels).length > 0
|
|
1661
1730
|
? { labels: options.labels }
|
|
@@ -2212,6 +2281,10 @@ export class DaemonClient {
|
|
|
2212
2281
|
// Agent Interaction
|
|
2213
2282
|
// ============================================================================
|
|
2214
2283
|
async sendAgentMessage(agentId, text, options) {
|
|
2284
|
+
if (options?.sourceAgentId &&
|
|
2285
|
+
this.lastServerInfoMessage?.features?.agentMessageProvenance !== true) {
|
|
2286
|
+
throw new Error("Update the Paseo host to send messages with agent provenance.");
|
|
2287
|
+
}
|
|
2215
2288
|
const requestId = this.createRequestId();
|
|
2216
2289
|
const messageId = options?.messageId ?? crypto.randomUUID();
|
|
2217
2290
|
const message = SessionInboundMessageSchema.parse({
|
|
@@ -2219,6 +2292,7 @@ export class DaemonClient {
|
|
|
2219
2292
|
requestId,
|
|
2220
2293
|
agentId,
|
|
2221
2294
|
text,
|
|
2295
|
+
sourceAgentId: options?.sourceAgentId,
|
|
2222
2296
|
...(messageId ? { messageId } : {}),
|
|
2223
2297
|
...(options?.activeTurnBehavior ? { activeTurnBehavior: options.activeTurnBehavior } : {}),
|
|
2224
2298
|
...(options?.images ? { images: options.images } : {}),
|
|
@@ -3127,6 +3201,7 @@ export class DaemonClient {
|
|
|
3127
3201
|
});
|
|
3128
3202
|
}
|
|
3129
3203
|
async setCheckoutBaseRef(input) {
|
|
3204
|
+
this.requireCheckoutBaseRefSupport();
|
|
3130
3205
|
return this.sendCorrelatedSessionRequest({
|
|
3131
3206
|
requestId: input.requestId,
|
|
3132
3207
|
message: {
|
|
@@ -3216,6 +3291,10 @@ export class DaemonClient {
|
|
|
3216
3291
|
}
|
|
3217
3292
|
async createWorkspace(input, requestId) {
|
|
3218
3293
|
const resolvedRequestId = this.createRequestId(requestId ?? input.requestId);
|
|
3294
|
+
if (input.background !== undefined || input.callerAgentId)
|
|
3295
|
+
this.requireBackgroundWorkspaces();
|
|
3296
|
+
if (input.agent?.background !== undefined)
|
|
3297
|
+
throw new Error("Configure background on the workspace, not its initial agent.");
|
|
3219
3298
|
const result = await this.creations.createWorkspace({
|
|
3220
3299
|
...input,
|
|
3221
3300
|
requestId: resolvedRequestId,
|
|
@@ -3243,6 +3322,8 @@ export class DaemonClient {
|
|
|
3243
3322
|
? { idempotencyKey: input.idempotencyKey }
|
|
3244
3323
|
: {}),
|
|
3245
3324
|
...(input.title !== undefined ? { title: input.title } : {}),
|
|
3325
|
+
...(input.background !== undefined ? { background: input.background } : {}),
|
|
3326
|
+
...(input.callerAgentId ? { callerAgentId: input.callerAgentId } : {}),
|
|
3246
3327
|
...(input.firstAgentContext !== undefined
|
|
3247
3328
|
? { firstAgentContext: input.firstAgentContext }
|
|
3248
3329
|
: {}),
|
|
@@ -3320,7 +3401,7 @@ export class DaemonClient {
|
|
|
3320
3401
|
// ============================================================================
|
|
3321
3402
|
// File Explorer
|
|
3322
3403
|
// ============================================================================
|
|
3323
|
-
async requestFileExplorer(cwd, path, mode, requestId, acceptBinary = false, maxBytes) {
|
|
3404
|
+
async requestFileExplorer(cwd, path, mode, requestId, acceptBinary = false, maxBytes, timeout) {
|
|
3324
3405
|
return this.sendCorrelatedSessionRequest({
|
|
3325
3406
|
requestId,
|
|
3326
3407
|
message: {
|
|
@@ -3332,6 +3413,7 @@ export class DaemonClient {
|
|
|
3332
3413
|
...(maxBytes ? { maxBytes } : {}),
|
|
3333
3414
|
},
|
|
3334
3415
|
responseType: "file_explorer_response",
|
|
3416
|
+
timeout,
|
|
3335
3417
|
});
|
|
3336
3418
|
}
|
|
3337
3419
|
async listDirectory(cwd, path, requestId) {
|
|
@@ -3344,11 +3426,16 @@ export class DaemonClient {
|
|
|
3344
3426
|
}
|
|
3345
3427
|
return payload.directory;
|
|
3346
3428
|
}
|
|
3347
|
-
|
|
3429
|
+
/**
|
|
3430
|
+
* `timeout` bounds the whole transfer, not each frame. `0` disables the timer (as
|
|
3431
|
+
* `workspace.create` does) for files that legitimately take longer than the default over a
|
|
3432
|
+
* slow link; a dead connection still rejects on disconnect.
|
|
3433
|
+
*/
|
|
3434
|
+
async readFile(cwd, path, requestId, maxBytes, timeout) {
|
|
3348
3435
|
const resolvedRequestId = this.createRequestId(requestId);
|
|
3349
3436
|
this.pendingBinaryFileReads.set(resolvedRequestId, { cwd, path, maxBytes });
|
|
3350
3437
|
try {
|
|
3351
|
-
const payload = await this.requestFileExplorer(cwd, path, "file", resolvedRequestId, true, maxBytes);
|
|
3438
|
+
const payload = await this.requestFileExplorer(cwd, path, "file", resolvedRequestId, true, maxBytes, timeout);
|
|
3352
3439
|
if (payload.error) {
|
|
3353
3440
|
throw new Error(payload.error);
|
|
3354
3441
|
}
|
|
@@ -4512,6 +4599,13 @@ export class DaemonClient {
|
|
|
4512
4599
|
throw new Error("Update the host to use side conversations.");
|
|
4513
4600
|
}
|
|
4514
4601
|
}
|
|
4602
|
+
requireCheckoutBaseRefSupport() {
|
|
4603
|
+
// COMPAT(checkoutBaseRefSet): added in fork v0.10.1, remove after 2027-09-30. An older
|
|
4604
|
+
// daemon cannot parse the request, so the client must not send it.
|
|
4605
|
+
if (this.lastServerInfoMessage?.features?.checkoutBaseRefSet !== true) {
|
|
4606
|
+
throw new Error("Update the host to change the comparison base.");
|
|
4607
|
+
}
|
|
4608
|
+
}
|
|
4515
4609
|
resolveTransportUrlForAttempt() {
|
|
4516
4610
|
return this.config.url;
|
|
4517
4611
|
}
|
|
@@ -4775,6 +4869,12 @@ export class DaemonClient {
|
|
|
4775
4869
|
});
|
|
4776
4870
|
return;
|
|
4777
4871
|
}
|
|
4872
|
+
const received = transfer.chunks.reduce((total, chunk) => total + chunk.byteLength, 0);
|
|
4873
|
+
if (received !== transfer.size) {
|
|
4874
|
+
this.activeBinaryFileTransfers.delete(frame.requestId);
|
|
4875
|
+
this.rejectWaitersForRequestId(frame.requestId, new Error(`File transfer incomplete: expected ${transfer.size} bytes, received ${received}.`));
|
|
4876
|
+
return;
|
|
4877
|
+
}
|
|
4778
4878
|
const bytes = concatByteChunks(transfer.chunks, transfer.size);
|
|
4779
4879
|
this.activeBinaryFileTransfers.delete(frame.requestId);
|
|
4780
4880
|
this.completedBinaryFileReads.set(frame.requestId, {
|
|
@@ -5157,7 +5257,7 @@ export class DaemonClient {
|
|
|
5157
5257
|
}
|
|
5158
5258
|
}
|
|
5159
5259
|
function resolveAgentConfig(options) {
|
|
5160
|
-
const { config, provider, cwd, agentId: _agentId, onEvent: _onEvent, idempotencyKey: _idempotencyKey, clientMessageId: _clientMessageId, callerAgentId: _callerAgentId, outputSchema: _outputSchema, attachments: _attachments, worktree: _worktree, autoArchive: _autoArchive, env: _env, workspaceId: _workspaceId, initialPrompt: _initialPrompt, images: _images, git: _git, worktreeName: _worktreeName, requestId: _requestId, labels: _labels, ...overrides } = options;
|
|
5260
|
+
const { config, provider, cwd, agentId: _agentId, onEvent: _onEvent, idempotencyKey: _idempotencyKey, clientMessageId: _clientMessageId, callerAgentId: _callerAgentId, outputSchema: _outputSchema, attachments: _attachments, worktree: _worktree, autoArchive: _autoArchive, background: _background, env: _env, workspaceId: _workspaceId, initialPrompt: _initialPrompt, images: _images, git: _git, worktreeName: _worktreeName, requestId: _requestId, labels: _labels, ...overrides } = options;
|
|
5161
5261
|
const baseConfig = {
|
|
5162
5262
|
...(provider ? { provider } : {}),
|
|
5163
5263
|
...(cwd ? { cwd } : {}),
|
package/dist/index.d.ts
CHANGED
|
@@ -178,6 +178,8 @@ export interface PaseoAgentCreateOptions {
|
|
|
178
178
|
git?: CreateAgentRequestMessage["git"];
|
|
179
179
|
worktree?: CreateAgentRequestMessage["worktree"];
|
|
180
180
|
autoArchive?: CreateAgentRequestMessage["autoArchive"];
|
|
181
|
+
/** Visibility intent for a NEW workspace. Existing targets reject this option. */
|
|
182
|
+
background?: CreateAgentRequestMessage["background"];
|
|
181
183
|
requestId?: string;
|
|
182
184
|
labels?: Record<string, string>;
|
|
183
185
|
}
|
package/dist/index.js
CHANGED
|
@@ -224,9 +224,12 @@ function createWorkspaceHandleFactory(daemonClient, createAgent, terminals, list
|
|
|
224
224
|
const refresh = async (options) => {
|
|
225
225
|
let cursor;
|
|
226
226
|
let requestId = options?.requestId;
|
|
227
|
+
// A ref addresses one workspace by id, so a background one resolves too.
|
|
228
|
+
const filter = daemonClient.supportsBackgroundWorkspaces() ? { includeBackground: true } : {};
|
|
227
229
|
do {
|
|
228
230
|
const result = await daemonClient.fetchWorkspaces({
|
|
229
231
|
requestId,
|
|
232
|
+
filter,
|
|
230
233
|
page: { limit: 200, ...(cursor ? { cursor } : {}) },
|
|
231
234
|
});
|
|
232
235
|
const match = result.entries.find((entry) => entry.id === id);
|
|
@@ -450,7 +453,8 @@ async function waitForProvidersReady(daemonClient, observation, signal, options
|
|
|
450
453
|
const { timeoutMs = 60000, ...snapshotOptions } = options;
|
|
451
454
|
try {
|
|
452
455
|
await observation.ready;
|
|
453
|
-
signal.
|
|
456
|
+
if (signal.aborted)
|
|
457
|
+
throw new Error("Paseo API is disposed");
|
|
454
458
|
return await new Promise((resolve, reject) => {
|
|
455
459
|
let settled = false;
|
|
456
460
|
let requestId = null;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@camerontaylor/paseo-client",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.0-beta.1.fork.1",
|
|
4
4
|
"description": "Paseo client SDK package",
|
|
5
5
|
"files": [
|
|
6
6
|
"dist",
|
|
@@ -39,8 +39,8 @@
|
|
|
39
39
|
"test": "vitest run"
|
|
40
40
|
},
|
|
41
41
|
"dependencies": {
|
|
42
|
-
"@camerontaylor/paseo-protocol": "0.
|
|
43
|
-
"@camerontaylor/paseo-relay": "0.
|
|
42
|
+
"@camerontaylor/paseo-protocol": "0.12.0-beta.1.fork.1",
|
|
43
|
+
"@camerontaylor/paseo-relay": "0.12.0-beta.1.fork.1",
|
|
44
44
|
"zod": "^4.4.3"
|
|
45
45
|
},
|
|
46
46
|
"devDependencies": {
|