@beanx/cathygo-protocol 0.1.2 → 0.1.4
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/index.d.ts +41 -2
- package/dist/index.js +151 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -26,6 +26,11 @@ type CathyGOConnectResult = {
|
|
|
26
26
|
max_payload_bytes: number;
|
|
27
27
|
max_upload_bytes: number;
|
|
28
28
|
heartbeat_interval_ms: number;
|
|
29
|
+
attachment_relay_max_files_per_send?: number;
|
|
30
|
+
attachment_relay_max_concurrent_transfers?: number;
|
|
31
|
+
attachment_relay_chunk_bytes?: number;
|
|
32
|
+
attachment_relay_max_file_bytes?: number;
|
|
33
|
+
attachment_relay_progress_min_interval_ms?: number;
|
|
29
34
|
};
|
|
30
35
|
};
|
|
31
36
|
type CathyGORequest = {
|
|
@@ -97,7 +102,21 @@ type AgentPresenceEvent = {
|
|
|
97
102
|
[key: string]: unknown;
|
|
98
103
|
};
|
|
99
104
|
};
|
|
100
|
-
type
|
|
105
|
+
type AttachmentRelayProgress = {
|
|
106
|
+
transfer_id: string;
|
|
107
|
+
received_bytes: number;
|
|
108
|
+
total_bytes: number;
|
|
109
|
+
percent: number;
|
|
110
|
+
batch_id?: string;
|
|
111
|
+
index?: number;
|
|
112
|
+
};
|
|
113
|
+
type AttachmentRelayProgressEvent = {
|
|
114
|
+
type: "event";
|
|
115
|
+
event: "attachment.relay.progress";
|
|
116
|
+
seq?: number;
|
|
117
|
+
payload: AttachmentRelayProgress;
|
|
118
|
+
};
|
|
119
|
+
type CathyGOIncomingFrame = CathyGOResponse | LearningTurnEvent | AgentPresenceEvent | AttachmentRelayProgressEvent;
|
|
101
120
|
type CathyGOFrame = CathyGORequest | CathyGOIncomingFrame;
|
|
102
121
|
type TextTurnInput = {
|
|
103
122
|
text: string;
|
|
@@ -348,6 +367,7 @@ type AttachmentCommitParams = {
|
|
|
348
367
|
blob?: AttachmentCommitBlob;
|
|
349
368
|
metadata?: Record<string, unknown>;
|
|
350
369
|
};
|
|
370
|
+
type AttachmentRelayProgressHandler = (progress: AttachmentRelayProgress) => void;
|
|
351
371
|
type LearningTurnEventHandler = (event: LearningTurnEvent) => void;
|
|
352
372
|
type CathyGOProtocolClientCallbacks = {
|
|
353
373
|
onOpen?: () => void;
|
|
@@ -355,6 +375,7 @@ type CathyGOProtocolClientCallbacks = {
|
|
|
355
375
|
onError?: () => void;
|
|
356
376
|
onFrame?: (frame: CathyGOIncomingFrame) => void;
|
|
357
377
|
onTurnEvent?: LearningTurnEventHandler;
|
|
378
|
+
onAttachmentRelayProgress?: AttachmentRelayProgressHandler;
|
|
358
379
|
};
|
|
359
380
|
|
|
360
381
|
type CathyGOTransportFrameHandler = (frame: CathyGOIncomingFrame) => void;
|
|
@@ -382,6 +403,7 @@ declare class CathyGOProtocolClient {
|
|
|
382
403
|
private requestSeq;
|
|
383
404
|
private pending;
|
|
384
405
|
private turnHandlers;
|
|
406
|
+
private attachmentRelayProgressHandlers;
|
|
385
407
|
private lastConnect?;
|
|
386
408
|
private lastEventSeq;
|
|
387
409
|
private connectOptions;
|
|
@@ -390,6 +412,7 @@ declare class CathyGOProtocolClient {
|
|
|
390
412
|
updateConnectOptions(patch: Partial<CathyGOConnectOptions>): void;
|
|
391
413
|
updateCallbacks(callbacks: CathyGOProtocolClientCallbacks): void;
|
|
392
414
|
onTurnEvent(handler: LearningTurnEventHandler): () => void;
|
|
415
|
+
onAttachmentRelayProgress(handler: AttachmentRelayProgressHandler): () => void;
|
|
393
416
|
connect(): Promise<CathyGOConnectResult>;
|
|
394
417
|
reconnect(options?: Partial<CathyGOConnectOptions>): Promise<CathyGOConnectResult>;
|
|
395
418
|
disconnect(): void;
|
|
@@ -398,6 +421,11 @@ declare class CathyGOProtocolClient {
|
|
|
398
421
|
max_payload_bytes: number;
|
|
399
422
|
max_upload_bytes: number;
|
|
400
423
|
heartbeat_interval_ms: number;
|
|
424
|
+
attachment_relay_max_files_per_send?: number;
|
|
425
|
+
attachment_relay_max_concurrent_transfers?: number;
|
|
426
|
+
attachment_relay_chunk_bytes?: number;
|
|
427
|
+
attachment_relay_max_file_bytes?: number;
|
|
428
|
+
attachment_relay_progress_min_interval_ms?: number;
|
|
401
429
|
} | undefined;
|
|
402
430
|
createSession(metadata?: Record<string, unknown>): Promise<string>;
|
|
403
431
|
listSessions(limit?: number): Promise<ConversationSummary[]>;
|
|
@@ -423,6 +451,17 @@ declare function connectPayload(options: CathyGOConnectOptions): Record<string,
|
|
|
423
451
|
declare function parseConnectPayload(payload: Record<string, unknown>): CathyGOConnectResult;
|
|
424
452
|
declare function settingsFromPayload(payload: Record<string, unknown>): GatewayVisibleSettings;
|
|
425
453
|
declare function isLearningTurnEvent(frame: CathyGOIncomingFrame): frame is LearningTurnEvent;
|
|
454
|
+
declare function isAttachmentRelayProgressEvent(frame: CathyGOIncomingFrame): frame is AttachmentRelayProgressEvent;
|
|
455
|
+
|
|
456
|
+
type UploadAttachmentViaRelayOptions = {
|
|
457
|
+
transferId?: string;
|
|
458
|
+
sessionId?: string;
|
|
459
|
+
metadata?: Record<string, unknown>;
|
|
460
|
+
chunkBytes?: number;
|
|
461
|
+
onProgress?: (progress: AttachmentRelayProgress) => void;
|
|
462
|
+
signal?: AbortSignal;
|
|
463
|
+
};
|
|
464
|
+
declare function uploadAttachmentViaRelay(client: CathyGOProtocolClient, file: File, options?: UploadAttachmentViaRelayOptions): Promise<GatewayAttachment>;
|
|
426
465
|
|
|
427
466
|
type LearningCapability = NonNullable<SessionInputOptions["capability"]>;
|
|
428
467
|
declare function textTurnInput(text: string): TurnInput;
|
|
@@ -430,4 +469,4 @@ declare function photoQuestionTurnInput(text: string, attachments: GatewayAttach
|
|
|
430
469
|
declare function capabilityForTurn(attachments: GatewayAttachment[]): LearningCapability;
|
|
431
470
|
declare function learningTurnInput(text: string, attachments: GatewayAttachment[]): TurnInput;
|
|
432
471
|
|
|
433
|
-
export { type AgentActivity, type AgentPresenceEvent, type AttachmentCommitBlob, type AttachmentCommitParams, type AttachmentInput, type BeanXAccountRuntimeConfig, CathyGOClientError, type CathyGOClientRole, type CathyGOConnectOptions, type CathyGOConnectResult, type CathyGODeviceContext, type CathyGOFrame, type CathyGOIncomingFrame, CathyGOProtocolClient, type CathyGOProtocolClientCallbacks, type CathyGOProtocolClientOptions, type CathyGORequest, type CathyGOResponse, type CathyGOTransport, type CathyGOTransportEventHandler, type CathyGOTransportFrameHandler, type ConversationDetail, type ConversationMessage, type ConversationMessagePart, type ConversationSummary, type GatewayAgentProfile, type GatewayAttachment, type GatewayModelConfig, type GatewayModelConfigUpdate, type GatewayModelOption, type GatewayModelStatus, type GatewayVisibleSettings, type LearningCapability, type LearningTurnEvent, type LearningTurnEventHandler, type LearningTurnEventPayload, type ListResponse, type MessagePartInput, type MultimodalTurnInput, type SameSessionPolicy, type SessionInputOptions, type SessionInputResult, type SessionRuntimeSnapshot, type SessionStopResult, type SettingsItem, type SettingsItemKind, type SettingsSection, type TextTurnInput, type TurnInput, type UploadScope, capabilityForTurn, connectPayload, isLearningTurnEvent, learningTurnInput, parseConnectPayload, photoQuestionTurnInput, settingsFromPayload, textTurnInput };
|
|
472
|
+
export { type AgentActivity, type AgentPresenceEvent, type AttachmentCommitBlob, type AttachmentCommitParams, type AttachmentInput, type AttachmentRelayProgress, type AttachmentRelayProgressEvent, type AttachmentRelayProgressHandler, type BeanXAccountRuntimeConfig, CathyGOClientError, type CathyGOClientRole, type CathyGOConnectOptions, type CathyGOConnectResult, type CathyGODeviceContext, type CathyGOFrame, type CathyGOIncomingFrame, CathyGOProtocolClient, type CathyGOProtocolClientCallbacks, type CathyGOProtocolClientOptions, type CathyGORequest, type CathyGOResponse, type CathyGOTransport, type CathyGOTransportEventHandler, type CathyGOTransportFrameHandler, type ConversationDetail, type ConversationMessage, type ConversationMessagePart, type ConversationSummary, type GatewayAgentProfile, type GatewayAttachment, type GatewayModelConfig, type GatewayModelConfigUpdate, type GatewayModelOption, type GatewayModelStatus, type GatewayVisibleSettings, type LearningCapability, type LearningTurnEvent, type LearningTurnEventHandler, type LearningTurnEventPayload, type ListResponse, type MessagePartInput, type MultimodalTurnInput, type SameSessionPolicy, type SessionInputOptions, type SessionInputResult, type SessionRuntimeSnapshot, type SessionStopResult, type SettingsItem, type SettingsItemKind, type SettingsSection, type TextTurnInput, type TurnInput, type UploadAttachmentViaRelayOptions, type UploadScope, capabilityForTurn, connectPayload, isAttachmentRelayProgressEvent, isLearningTurnEvent, learningTurnInput, parseConnectPayload, photoQuestionTurnInput, settingsFromPayload, textTurnInput, uploadAttachmentViaRelay };
|
package/dist/index.js
CHANGED
|
@@ -26,6 +26,7 @@ var CathyGOProtocolClient = class {
|
|
|
26
26
|
requestSeq = 1;
|
|
27
27
|
pending = /* @__PURE__ */ new Map();
|
|
28
28
|
turnHandlers = /* @__PURE__ */ new Set();
|
|
29
|
+
attachmentRelayProgressHandlers = /* @__PURE__ */ new Set();
|
|
29
30
|
lastConnect;
|
|
30
31
|
lastEventSeq = 0;
|
|
31
32
|
connectOptions;
|
|
@@ -40,6 +41,10 @@ var CathyGOProtocolClient = class {
|
|
|
40
41
|
this.turnHandlers.add(handler);
|
|
41
42
|
return () => this.turnHandlers.delete(handler);
|
|
42
43
|
}
|
|
44
|
+
onAttachmentRelayProgress(handler) {
|
|
45
|
+
this.attachmentRelayProgressHandlers.add(handler);
|
|
46
|
+
return () => this.attachmentRelayProgressHandlers.delete(handler);
|
|
47
|
+
}
|
|
43
48
|
async connect() {
|
|
44
49
|
if (this.isConnected() && this.lastConnect) {
|
|
45
50
|
return this.lastConnect;
|
|
@@ -205,6 +210,12 @@ var CathyGOProtocolClient = class {
|
|
|
205
210
|
handler(frame);
|
|
206
211
|
}
|
|
207
212
|
}
|
|
213
|
+
if (isAttachmentRelayProgressEvent(frame)) {
|
|
214
|
+
this.callbacks.onAttachmentRelayProgress?.(frame.payload);
|
|
215
|
+
for (const handler of this.attachmentRelayProgressHandlers) {
|
|
216
|
+
handler(frame.payload);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
208
219
|
this.callbacks.onFrame?.(frame);
|
|
209
220
|
}
|
|
210
221
|
};
|
|
@@ -254,6 +265,9 @@ function settingsFromPayload(payload) {
|
|
|
254
265
|
function isLearningTurnEvent(frame) {
|
|
255
266
|
return frame.type === "event" && (frame.event.startsWith("session.") || frame.event.startsWith("turn.") || frame.event.startsWith("agent.activity.") || frame.event.startsWith("agent.progress.") || frame.event.startsWith("assistant.") || frame.event.startsWith("debug.") || frame.event.startsWith("experimental."));
|
|
256
267
|
}
|
|
268
|
+
function isAttachmentRelayProgressEvent(frame) {
|
|
269
|
+
return frame.type === "event" && frame.event === "attachment.relay.progress";
|
|
270
|
+
}
|
|
257
271
|
function turnStartResultFromPayload(payload, fallbackSessionId) {
|
|
258
272
|
return {
|
|
259
273
|
session_id: String(payload.session_id ?? fallbackSessionId),
|
|
@@ -280,6 +294,140 @@ function compactParams(params) {
|
|
|
280
294
|
return Object.fromEntries(Object.entries(params).filter(([, value]) => value !== void 0));
|
|
281
295
|
}
|
|
282
296
|
|
|
297
|
+
// src/relay-attachment-upload.ts
|
|
298
|
+
async function uploadAttachmentViaRelay(client, file, options = {}) {
|
|
299
|
+
const transferId = options.transferId ?? createTransferId();
|
|
300
|
+
const mimeType = file.type || "application/octet-stream";
|
|
301
|
+
const buffer = await file.arrayBuffer();
|
|
302
|
+
const bytes = new Uint8Array(buffer);
|
|
303
|
+
const sha256 = await sha256Hex(buffer);
|
|
304
|
+
const limits = client.getLimits();
|
|
305
|
+
const chunkBytes = options.chunkBytes ?? Number(limits?.attachment_relay_chunk_bytes ?? DEFAULT_RELAY_CHUNK_BYTES);
|
|
306
|
+
if (options.signal?.aborted) {
|
|
307
|
+
throw new DOMException("Attachment relay upload aborted.", "AbortError");
|
|
308
|
+
}
|
|
309
|
+
const openPayload = await client.request("attachment.relay.open", {
|
|
310
|
+
transfer_id: transferId,
|
|
311
|
+
session_id: options.sessionId,
|
|
312
|
+
mime_type: mimeType,
|
|
313
|
+
size_bytes: bytes.byteLength,
|
|
314
|
+
sha256,
|
|
315
|
+
original_name: file.name || void 0,
|
|
316
|
+
metadata: options.metadata
|
|
317
|
+
});
|
|
318
|
+
const negotiatedChunkBytes = Number(openPayload.chunk_bytes ?? chunkBytes) || chunkBytes;
|
|
319
|
+
try {
|
|
320
|
+
let seq = 0;
|
|
321
|
+
for (let offset = 0; offset < bytes.byteLength; offset += negotiatedChunkBytes) {
|
|
322
|
+
if (options.signal?.aborted) {
|
|
323
|
+
await client.request("attachment.relay.abort", { transfer_id: transferId });
|
|
324
|
+
throw new DOMException("Attachment relay upload aborted.", "AbortError");
|
|
325
|
+
}
|
|
326
|
+
const chunk = bytes.subarray(offset, offset + negotiatedChunkBytes);
|
|
327
|
+
const pushPayload = await client.request("attachment.relay.push", {
|
|
328
|
+
transfer_id: transferId,
|
|
329
|
+
seq,
|
|
330
|
+
data_base64: bytesToBase64(chunk)
|
|
331
|
+
});
|
|
332
|
+
seq += 1;
|
|
333
|
+
if (seq % 2 === 0) {
|
|
334
|
+
await yieldToMainThread();
|
|
335
|
+
}
|
|
336
|
+
options.onProgress?.({
|
|
337
|
+
transfer_id: transferId,
|
|
338
|
+
received_bytes: Number(pushPayload.received_bytes ?? offset + chunk.byteLength),
|
|
339
|
+
total_bytes: Number(pushPayload.total_bytes ?? bytes.byteLength),
|
|
340
|
+
percent: percent(
|
|
341
|
+
Number(pushPayload.received_bytes ?? offset + chunk.byteLength),
|
|
342
|
+
Number(pushPayload.total_bytes ?? bytes.byteLength)
|
|
343
|
+
),
|
|
344
|
+
batch_id: stringOrUndefined(options.metadata?.batch_id),
|
|
345
|
+
index: numberOrUndefined(options.metadata?.index)
|
|
346
|
+
});
|
|
347
|
+
}
|
|
348
|
+
const closePayload = await client.request("attachment.relay.close", {
|
|
349
|
+
transfer_id: transferId
|
|
350
|
+
});
|
|
351
|
+
return normalizeGatewayAttachment(closePayload.attachment);
|
|
352
|
+
} catch (error) {
|
|
353
|
+
if (!(error instanceof DOMException && error.name === "AbortError")) {
|
|
354
|
+
try {
|
|
355
|
+
await client.request("attachment.relay.abort", { transfer_id: transferId });
|
|
356
|
+
} catch {
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
throw error;
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
var DEFAULT_RELAY_CHUNK_BYTES = 65536;
|
|
363
|
+
function createTransferId() {
|
|
364
|
+
if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
|
|
365
|
+
return `xfer_${crypto.randomUUID().replace(/-/g, "").slice(0, 16)}`;
|
|
366
|
+
}
|
|
367
|
+
return `xfer_${Date.now().toString(36)}`;
|
|
368
|
+
}
|
|
369
|
+
function percent(receivedBytes, totalBytes) {
|
|
370
|
+
if (totalBytes <= 0) return 0;
|
|
371
|
+
return Math.min(100, Math.max(0, Math.round(receivedBytes * 100 / totalBytes)));
|
|
372
|
+
}
|
|
373
|
+
function bytesToBase64(bytes) {
|
|
374
|
+
const chunkSize = 32768;
|
|
375
|
+
let binary = "";
|
|
376
|
+
for (let offset = 0; offset < bytes.length; offset += chunkSize) {
|
|
377
|
+
const chunk = bytes.subarray(offset, Math.min(offset + chunkSize, bytes.length));
|
|
378
|
+
binary += String.fromCharCode.apply(null, chunk);
|
|
379
|
+
}
|
|
380
|
+
return btoa(binary);
|
|
381
|
+
}
|
|
382
|
+
async function yieldToMainThread() {
|
|
383
|
+
await new Promise((resolve) => {
|
|
384
|
+
if (typeof requestAnimationFrame === "function") {
|
|
385
|
+
requestAnimationFrame(() => resolve());
|
|
386
|
+
return;
|
|
387
|
+
}
|
|
388
|
+
setTimeout(resolve, 0);
|
|
389
|
+
});
|
|
390
|
+
}
|
|
391
|
+
async function sha256Hex(buffer) {
|
|
392
|
+
const digest = await crypto.subtle.digest("SHA-256", buffer);
|
|
393
|
+
return Array.from(new Uint8Array(digest)).map((byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
394
|
+
}
|
|
395
|
+
function normalizeGatewayAttachment(value) {
|
|
396
|
+
const attachment = value ?? {};
|
|
397
|
+
const id = String(attachment.id ?? attachment.attachment_id ?? "");
|
|
398
|
+
if (!id) {
|
|
399
|
+
throw new Error("attachment.relay.close did not return an attachment id.");
|
|
400
|
+
}
|
|
401
|
+
return {
|
|
402
|
+
id,
|
|
403
|
+
kind: "image",
|
|
404
|
+
uri: String(attachment.uri ?? ""),
|
|
405
|
+
original_name: stringOrNull(attachment.original_name),
|
|
406
|
+
mime_type: stringOrNull(attachment.mime_type),
|
|
407
|
+
size_bytes: numberOrNull(attachment.size_bytes),
|
|
408
|
+
sha256: stringOrNull(attachment.sha256),
|
|
409
|
+
width: numberOrNull(attachment.width),
|
|
410
|
+
height: numberOrNull(attachment.height),
|
|
411
|
+
created_at: stringOrNull(attachment.created_at),
|
|
412
|
+
thumbnail: attachment.thumbnail
|
|
413
|
+
};
|
|
414
|
+
}
|
|
415
|
+
function stringOrUndefined(value) {
|
|
416
|
+
if (typeof value !== "string") return void 0;
|
|
417
|
+
const text = value.trim();
|
|
418
|
+
return text || void 0;
|
|
419
|
+
}
|
|
420
|
+
function stringOrNull(value) {
|
|
421
|
+
return stringOrUndefined(value) ?? null;
|
|
422
|
+
}
|
|
423
|
+
function numberOrUndefined(value) {
|
|
424
|
+
if (typeof value !== "number" || Number.isNaN(value)) return void 0;
|
|
425
|
+
return value;
|
|
426
|
+
}
|
|
427
|
+
function numberOrNull(value) {
|
|
428
|
+
return numberOrUndefined(value) ?? null;
|
|
429
|
+
}
|
|
430
|
+
|
|
283
431
|
// src/turn-input.ts
|
|
284
432
|
function textTurnInput(text) {
|
|
285
433
|
return { text };
|
|
@@ -315,10 +463,12 @@ export {
|
|
|
315
463
|
CathyGOProtocolClient,
|
|
316
464
|
capabilityForTurn,
|
|
317
465
|
connectPayload,
|
|
466
|
+
isAttachmentRelayProgressEvent,
|
|
318
467
|
isLearningTurnEvent,
|
|
319
468
|
learningTurnInput,
|
|
320
469
|
parseConnectPayload,
|
|
321
470
|
photoQuestionTurnInput,
|
|
322
471
|
settingsFromPayload,
|
|
323
|
-
textTurnInput
|
|
472
|
+
textTurnInput,
|
|
473
|
+
uploadAttachmentViaRelay
|
|
324
474
|
};
|