@canonmsg/agent-sdk 12.1.0 → 12.2.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/chunks/{chunk-QZ4PPRQW.js → chunk-FCG2Z57F.js} +79 -248
- package/dist/chunks/{chunk-7N6OPPW4.js → chunk-J63YVAKA.js} +13 -16
- package/dist/chunks/{chunk-N722GWCP.js → chunk-LFTYXZUY.js} +19 -7
- package/dist/chunks/{mls-crypto-LMNQZOS6.js → mls-crypto-ITVGUEP7.js} +1 -1
- package/dist/cli.js +4 -5
- package/dist/index.js +3 -3
- package/dist/internal/backend-contracts/clientFloor.d.ts +12 -1
- package/dist/internal/backend-contracts/endpoint.d.ts +1 -1
- package/dist/internal/backend-contracts/message.d.ts +1 -1
- package/dist/internal/backend-contracts/mlsDevEnrollment.d.ts +3 -0
- package/dist/internal/backend-contracts/mlsPrincipalAlias.d.ts +3 -0
- package/dist/internal/backend-contracts/runtimeCardFields.d.ts +0 -4
- package/dist/internal/backend-contracts/verbContract.d.ts +8 -12
- package/dist/internal/backend-contracts/verbSchemas.d.ts +3 -4
- package/dist/internal/core/client.d.ts +2 -97
- package/dist/internal/core/endpoint-client.d.ts +2 -2
- package/dist/internal/core/index.d.ts +2 -4
- package/dist/internal/core/turn-verbosity.d.ts +5 -11
- package/dist/internal/core/types.d.ts +0 -181
- package/dist/media.js +2 -2
- package/dist/runner/types.d.ts +1 -1
- package/package.json +1 -1
- package/dist/internal/core/stream.d.ts +0 -78
|
@@ -109,13 +109,13 @@ import {
|
|
|
109
109
|
readMembershipChangeMetadata,
|
|
110
110
|
resolveAllowedUploadMimeType,
|
|
111
111
|
sanitizeDownloadFileName
|
|
112
|
-
} from "./chunk-
|
|
112
|
+
} from "./chunk-J63YVAKA.js";
|
|
113
113
|
|
|
114
114
|
// src/media.ts
|
|
115
115
|
import { createWriteStream as createWriteStream2 } from "node:fs";
|
|
116
116
|
import { randomUUID as randomUUID8 } from "node:crypto";
|
|
117
117
|
import { mkdir as mkdir3, open as open4, rename as rename2, stat as stat2, unlink as unlink2 } from "node:fs/promises";
|
|
118
|
-
import { basename, dirname as dirname3, extname, join as
|
|
118
|
+
import { basename, dirname as dirname3, extname, join as join7 } from "node:path";
|
|
119
119
|
import { Readable, Transform as Transform2 } from "node:stream";
|
|
120
120
|
import { pipeline as pipeline2 } from "node:stream/promises";
|
|
121
121
|
|
|
@@ -1876,9 +1876,6 @@ var DEFAULT_BASE_URL = DEFAULT_CANON_RUNTIME_ENDPOINTS.apiBaseUrl;
|
|
|
1876
1876
|
|
|
1877
1877
|
// ../core/src/client.ts
|
|
1878
1878
|
import { createHash as createHash6, randomUUID as randomUUID2 } from "node:crypto";
|
|
1879
|
-
import { mkdtemp, writeFile, rm } from "node:fs/promises";
|
|
1880
|
-
import { tmpdir } from "node:os";
|
|
1881
|
-
import { join as join5 } from "node:path";
|
|
1882
1879
|
|
|
1883
1880
|
// ../endpoint/src/store.ts
|
|
1884
1881
|
function copyJson(value) {
|
|
@@ -2374,10 +2371,11 @@ function createEndpointQueryObserver(options) {
|
|
|
2374
2371
|
const intervalMs = Math.max(250, options.intervalMs ?? 3e3);
|
|
2375
2372
|
function schedule(entry) {
|
|
2376
2373
|
if (closed || entry.listeners.size === 0) return;
|
|
2374
|
+
const delay = entry.retryAt === void 0 ? options.getPollInterval?.() ?? intervalMs : entry.retryAt - Date.now();
|
|
2377
2375
|
entry.timer = setTimeout(() => {
|
|
2378
2376
|
entry.timer = void 0;
|
|
2379
2377
|
void refresh(entry);
|
|
2380
|
-
}, Math.max(250,
|
|
2378
|
+
}, Math.max(250, delay));
|
|
2381
2379
|
}
|
|
2382
2380
|
__name(schedule, "schedule");
|
|
2383
2381
|
async function refresh(entry) {
|
|
@@ -2388,11 +2386,17 @@ function createEndpointQueryObserver(options) {
|
|
|
2388
2386
|
}
|
|
2389
2387
|
if (entry.timer) clearTimeout(entry.timer);
|
|
2390
2388
|
entry.timer = void 0;
|
|
2389
|
+
if (entry.retryAt !== void 0 && entry.retryAt > Date.now()) {
|
|
2390
|
+
schedule(entry);
|
|
2391
|
+
return;
|
|
2392
|
+
}
|
|
2393
|
+
delete entry.retryAt;
|
|
2391
2394
|
entry.running = true;
|
|
2392
2395
|
entry.dirty = false;
|
|
2393
2396
|
try {
|
|
2394
2397
|
const value = await options.query(entry.query, entry.parameters);
|
|
2395
2398
|
if (closed || entry.listeners.size === 0) return;
|
|
2399
|
+
entry.retryAttempts = 0;
|
|
2396
2400
|
if (!entry.dirty) {
|
|
2397
2401
|
const fingerprint = canonicalEndpointJson(value);
|
|
2398
2402
|
if (!entry.hasValue || entry.hadError || fingerprint !== entry.fingerprint) {
|
|
@@ -2412,6 +2416,13 @@ function createEndpointQueryObserver(options) {
|
|
|
2412
2416
|
} catch (error) {
|
|
2413
2417
|
entry.hadError = true;
|
|
2414
2418
|
entry.lastError = error;
|
|
2419
|
+
if (error instanceof EndpointError && error.retryable) {
|
|
2420
|
+
const retryAfter = Number.isFinite(error.retryAfterMs) ? Math.max(0, Math.min(3e5, error.retryAfterMs)) : 0;
|
|
2421
|
+
const fastDelay = entry.retryAttempts < 3 ? 1e3 * 2 ** entry.retryAttempts++ : void 0;
|
|
2422
|
+
if (fastDelay !== void 0 || retryAfter > 0) {
|
|
2423
|
+
entry.retryAt = Date.now() + Math.max(retryAfter, fastDelay ?? options.getPollInterval?.() ?? intervalMs);
|
|
2424
|
+
}
|
|
2425
|
+
}
|
|
2415
2426
|
if (!closed) for (const listener of entry.listeners) {
|
|
2416
2427
|
try {
|
|
2417
2428
|
listener.error?.(error);
|
|
@@ -2431,7 +2442,7 @@ function createEndpointQueryObserver(options) {
|
|
|
2431
2442
|
const key = canonicalEndpointJson({ query, parameters });
|
|
2432
2443
|
let entry = entries2.get(key);
|
|
2433
2444
|
if (!entry) {
|
|
2434
|
-
entry = { query, parameters, listeners: /* @__PURE__ */ new Set(), running: false, dirty: false, hasValue: false, hadError: false };
|
|
2445
|
+
entry = { query, parameters, listeners: /* @__PURE__ */ new Set(), running: false, dirty: false, hasValue: false, hadError: false, retryAttempts: 0 };
|
|
2435
2446
|
entries2.set(key, entry);
|
|
2436
2447
|
}
|
|
2437
2448
|
const observer = { value: listener, error: onError };
|
|
@@ -6638,6 +6649,16 @@ function createMlsDevGroups(options, context, keyPackages, bridge) {
|
|
|
6638
6649
|
const current = envelope(state.protocolState, init.binding, conversationId);
|
|
6639
6650
|
const observed = authority(await tx.get(authorityKey(conversationId)), conversationId);
|
|
6640
6651
|
const conversation = await tx.get(`conversation/${conversationId}`);
|
|
6652
|
+
const roomDependencies = conversation ? Object.fromEntries([
|
|
6653
|
+
"membershipRevision",
|
|
6654
|
+
"memberIds",
|
|
6655
|
+
"mls",
|
|
6656
|
+
"mlsRequired",
|
|
6657
|
+
"mlsProtocol",
|
|
6658
|
+
"mlsRemoval",
|
|
6659
|
+
"mlsIdentities",
|
|
6660
|
+
"mlsInviteeId"
|
|
6661
|
+
].map((key) => [key, conversation[key] ?? null])) : null;
|
|
6641
6662
|
const cursor = await tx.get(`cursor/${conversationId}`);
|
|
6642
6663
|
const protocol = await tx.get(mlsDevTextProtocolKey(conversationId));
|
|
6643
6664
|
if (protocol !== void 0 && !isMlsDevTextProtocol(protocol) || current && current.protocol !== protocol) fail6("MLS_DEV_TEXT_PROTOCOL_UNSUPPORTED");
|
|
@@ -6647,7 +6668,7 @@ function createMlsDevGroups(options, context, keyPackages, bridge) {
|
|
|
6647
6668
|
return { device, envelope: current, authority: observed, feedOrdinal, ...protocol ? { protocol } : {}, fingerprint: canonicalEndpointJson({
|
|
6648
6669
|
device: device.fingerprint,
|
|
6649
6670
|
authority: observed ?? null,
|
|
6650
|
-
conversation:
|
|
6671
|
+
conversation: roomDependencies,
|
|
6651
6672
|
cursor: cursor ?? null,
|
|
6652
6673
|
protocol: protocol ?? null,
|
|
6653
6674
|
feedOrdinal
|
|
@@ -10011,7 +10032,7 @@ async function acquireAgentEndpoint(input) {
|
|
|
10011
10032
|
}
|
|
10012
10033
|
const requestedMls = input.options.mlsDev;
|
|
10013
10034
|
const mls = requestedMls ? await (async () => {
|
|
10014
|
-
const crypto2 = await import("./mls-crypto-
|
|
10035
|
+
const crypto2 = await import("./mls-crypto-ITVGUEP7.js");
|
|
10015
10036
|
const configuration = crypto2.parseMlsDevClientConfiguration(requestedMls.configuration);
|
|
10016
10037
|
if (configuration.environmentId !== environmentId || baseUrl !== CANON_RUNTIME_ENDPOINT_DEFAULTS[configuration.environmentId]?.apiBaseUrl || configuration.principalIds && !configuration.principalIds.includes(input.principalId) || !Number.isSafeInteger(input.ownershipVersion) || input.ownershipVersion < 0) throw new Error("MLS_DEV_AGENT_CONFIGURATION_BINDING_MISMATCH");
|
|
10017
10038
|
const custody = requestedMls.custody;
|
|
@@ -10312,45 +10333,6 @@ var CanonClient = class {
|
|
|
10312
10333
|
}
|
|
10313
10334
|
return result;
|
|
10314
10335
|
}
|
|
10315
|
-
/**
|
|
10316
|
-
* List conversations. Called with no options this is the historical request —
|
|
10317
|
-
* no query string, every conversation the agent belongs to, most recent
|
|
10318
|
-
* first. Passing `limit`/`before` opts into paging; use
|
|
10319
|
-
* `getConversationsPage` when you need the cursor back.
|
|
10320
|
-
*/
|
|
10321
|
-
async getConversations(options) {
|
|
10322
|
-
if (options) return (await this.fetchConversationsPage(options)).conversations;
|
|
10323
|
-
const conversations = [];
|
|
10324
|
-
const seen = /* @__PURE__ */ new Set();
|
|
10325
|
-
let before;
|
|
10326
|
-
do {
|
|
10327
|
-
const page = await this.fetchConversationsPage({ limit: 100, ...before ? { before } : {} });
|
|
10328
|
-
conversations.push(...page.conversations);
|
|
10329
|
-
if (page.nextBefore && seen.has(page.nextBefore)) throw new Error("Canon conversation cursor did not advance.");
|
|
10330
|
-
before = page.nextBefore ?? void 0;
|
|
10331
|
-
if (before) seen.add(before);
|
|
10332
|
-
} while (before);
|
|
10333
|
-
return conversations;
|
|
10334
|
-
}
|
|
10335
|
-
/**
|
|
10336
|
-
* One page of conversations plus the cursor to continue from. Pages are cut
|
|
10337
|
-
* in conversation-id order; keep calling with `before: page.nextBefore` until
|
|
10338
|
-
* `nextBefore` is null. Do not stop on an empty page and do not derive the
|
|
10339
|
-
* cursor from the returned array — the array is recency-sorted within the
|
|
10340
|
-
* page and hidden conversations are filtered after the page is read.
|
|
10341
|
-
*/
|
|
10342
|
-
async getConversationsPage(options) {
|
|
10343
|
-
return this.fetchConversationsPage(options);
|
|
10344
|
-
}
|
|
10345
|
-
async fetchConversationsPage(options) {
|
|
10346
|
-
const data = await this.endpointQuery("list_conversations", { ...options });
|
|
10347
|
-
return {
|
|
10348
|
-
conversations: data.conversations,
|
|
10349
|
-
// Absent on the unpaginated shape, and on any Canon deployment that
|
|
10350
|
-
// predates conversation paging — both mean "no further pages".
|
|
10351
|
-
nextBefore: typeof data.nextBefore === "string" ? data.nextBefore : null
|
|
10352
|
-
};
|
|
10353
|
-
}
|
|
10354
10336
|
/** Targeted membership read; session hosts must not poll the entire account. */
|
|
10355
10337
|
async getConversation(conversationId) {
|
|
10356
10338
|
return await (await this.getEndpoint()).getConversation(conversationId);
|
|
@@ -10363,15 +10345,6 @@ var CanonClient = class {
|
|
|
10363
10345
|
const data = await this.endpointQuery("list_messages", { conversationId, limit, includeBehavior: true, ...before ? { before } : {} });
|
|
10364
10346
|
return { ...data, messages: data.messages.map(normalizeMessage) };
|
|
10365
10347
|
}
|
|
10366
|
-
/** Read one explicitly referenced message without extending ordinary history. */
|
|
10367
|
-
async getMessage(conversationId, messageId) {
|
|
10368
|
-
const data = await this.endpointQuery("get_messages", {
|
|
10369
|
-
conversationId,
|
|
10370
|
-
messageIds: [messageId]
|
|
10371
|
-
});
|
|
10372
|
-
const message = data.messages.find((candidate) => candidate.id === messageId);
|
|
10373
|
-
return message ? normalizeMessage(message) : null;
|
|
10374
|
-
}
|
|
10375
10348
|
async sendMessage(conversationId, text, options, delivery) {
|
|
10376
10349
|
return this.endpointAction(
|
|
10377
10350
|
"send_message",
|
|
@@ -10394,78 +10367,27 @@ var CanonClient = class {
|
|
|
10394
10367
|
}
|
|
10395
10368
|
/** Core's chunker records all ordered message intents before publishing any part. */
|
|
10396
10369
|
async prepareMessageGroup(conversationId, messageId, parts, delivery) {
|
|
10397
|
-
const id2 = delivery?.operationId ?? stableOperationId(
|
|
10370
|
+
const id2 = delivery?.operationId ?? stableOperationId("send_group", conversationId, messageId);
|
|
10398
10371
|
try {
|
|
10399
10372
|
const endpoint = await this.getEndpoint();
|
|
10400
10373
|
const group = await endpoint.registerMessageGroup({ id: id2, ...delivery?.replacesOperationId ? { replacesOperationId: delivery.replacesOperationId } : {}, children: parts.map((part, index) => ({
|
|
10401
|
-
operationId: delivery?.operationId ? `${delivery.operationId.slice(0, 160 - `-part-${index + 1}`.length)}-part-${index + 1}` :
|
|
10402
|
-
intent:
|
|
10374
|
+
operationId: delivery?.operationId ? `${delivery.operationId.slice(0, 160 - `-part-${index + 1}`.length)}-part-${index + 1}` : stableOperationId("send_message", conversationId, part.options.messageId),
|
|
10375
|
+
intent: { action: "send_message", body: { conversationId, text: part.text, ...part.options } }
|
|
10403
10376
|
})) }, { signal: delivery?.signal });
|
|
10404
10377
|
return { id: id2, sendPart: /* @__PURE__ */ __name(async (index, options) => {
|
|
10405
10378
|
const operationId = group.childOperationIds?.[index];
|
|
10406
10379
|
if (!operationId) throw new Error("Unknown message group part");
|
|
10407
10380
|
if (options?.signal?.aborted) throw new CanonApiError(409, JSON.stringify({ code: "OPERATION_PENDING", message: "Reply delivery was interrupted" }));
|
|
10408
10381
|
await endpoint.retry(operationId);
|
|
10409
|
-
return
|
|
10382
|
+
return this.waitForOperation(operationId, options);
|
|
10410
10383
|
}, "sendPart") };
|
|
10411
10384
|
} catch (error) {
|
|
10412
10385
|
throw asCanonApiError(error);
|
|
10413
10386
|
}
|
|
10414
10387
|
}
|
|
10415
|
-
/** Publish a native-formatted ordered plan through the same endpoint engine. */
|
|
10416
|
-
async sendMessageGroup(conversationId, messageId, parts, delivery) {
|
|
10417
|
-
const root = messageId ?? randomUUID2();
|
|
10418
|
-
const prepared = parts.map((part, index) => ({ ...part, options: {
|
|
10419
|
-
...part.options,
|
|
10420
|
-
messageId: part.options.messageId ?? `${root.slice(0, 160 - `-part-${index + 1}`.length)}-part-${index + 1}`
|
|
10421
|
-
} }));
|
|
10422
|
-
const group = await this.prepareMessageGroup(conversationId, root, prepared, delivery);
|
|
10423
|
-
try {
|
|
10424
|
-
for (let index = 0; index < prepared.length; index++) await group.sendPart(index);
|
|
10425
|
-
return await this.waitForOperation(group.id);
|
|
10426
|
-
} catch (error) {
|
|
10427
|
-
if (error instanceof CanonApiError) error.operationId = group.id;
|
|
10428
|
-
throw error;
|
|
10429
|
-
}
|
|
10430
|
-
}
|
|
10431
10388
|
async cancelMessageGroup(id2) {
|
|
10432
10389
|
await (await this.getEndpoint()).cancelMessageGroup(id2);
|
|
10433
10390
|
}
|
|
10434
|
-
/**
|
|
10435
|
-
* Send outside an active inbound turn through the canonical policy boundary.
|
|
10436
|
-
* Harness reply paths should keep using sendMessage; schedulers, notifications,
|
|
10437
|
-
* and other detached work must use this method.
|
|
10438
|
-
*/
|
|
10439
|
-
async sendProactiveMessage(conversationId, text, options, delivery) {
|
|
10440
|
-
const result = await this.sendTo({
|
|
10441
|
-
targetConversationId: conversationId,
|
|
10442
|
-
text,
|
|
10443
|
-
...options ? { messageOptions: options } : {}
|
|
10444
|
-
}, delivery);
|
|
10445
|
-
if (result.status !== "messaged" || !result.messageId) {
|
|
10446
|
-
const reason = "reason" in result ? result.reason : result.status;
|
|
10447
|
-
throw new CanonApiError(
|
|
10448
|
-
403,
|
|
10449
|
-
`Canon proactive send was not admitted: ${reason}`
|
|
10450
|
-
);
|
|
10451
|
-
}
|
|
10452
|
-
return {
|
|
10453
|
-
conversationId: result.conversationId,
|
|
10454
|
-
messageId: result.messageId
|
|
10455
|
-
};
|
|
10456
|
-
}
|
|
10457
|
-
/** Reconcile an existing publication without rebuilding or resubmitting its body. */
|
|
10458
|
-
async waitForProactiveMessage(operationId, options) {
|
|
10459
|
-
if (await (await this.getEndpoint()).getMessageGroup(operationId)) return this.waitForOperation(operationId, options);
|
|
10460
|
-
const response = await this.waitForOperation(operationId, options);
|
|
10461
|
-
const value = unwrapJsonVerbResponse(response, "send_to");
|
|
10462
|
-
if (!isSendToResult(value)) throw new CanonApiError(502, "Canon returned a malformed send_to result");
|
|
10463
|
-
if (value.status !== "messaged" || !value.messageId) {
|
|
10464
|
-
const reason = "reason" in value ? value.reason : value.status;
|
|
10465
|
-
throw new CanonApiError(403, `Canon proactive send was not admitted: ${reason}`);
|
|
10466
|
-
}
|
|
10467
|
-
return { conversationId: value.conversationId, messageId: value.messageId };
|
|
10468
|
-
}
|
|
10469
10391
|
/**
|
|
10470
10392
|
* Execute a canonical verb over the wire contract (canon.verb-wire.v1).
|
|
10471
10393
|
* Bindings build the request with projectVerbIntentToWire; the endpoint
|
|
@@ -10703,98 +10625,9 @@ var CanonClient = class {
|
|
|
10703
10625
|
}
|
|
10704
10626
|
return result.status === "unavailable" && result.reason === "opening-text-required" ? void 0 : result;
|
|
10705
10627
|
}
|
|
10706
|
-
/** Create a group. Direct conversations must go through `communicate`. */
|
|
10707
|
-
async createGroup(options) {
|
|
10708
|
-
return this.endpointAction("create_conversation", { ...options });
|
|
10709
|
-
}
|
|
10710
|
-
async createMemoStream(options) {
|
|
10711
|
-
return this.endpointPublish("create_memo_stream", { streamId: randomUUID2(), ...options });
|
|
10712
|
-
}
|
|
10713
|
-
async appendMemoStreamChunk(conversationId, streamId, options) {
|
|
10714
|
-
return this.endpointPublish("append_memo_chunk", { conversationId, streamId, ...options });
|
|
10715
|
-
}
|
|
10716
|
-
async finalizeMemoStream(conversationId, streamId, messageId) {
|
|
10717
|
-
await this.endpointAction("finalize_memo_stream", { conversationId, streamId, messageId });
|
|
10718
|
-
}
|
|
10719
|
-
async abortMemoStream(conversationId, streamId) {
|
|
10720
|
-
await this.endpointAction("abort_memo_stream", { conversationId, streamId });
|
|
10721
|
-
}
|
|
10722
|
-
async createVoiceSession(options) {
|
|
10723
|
-
return this.endpointAction("create_voice_session", { ...options });
|
|
10724
|
-
}
|
|
10725
|
-
async getVoiceSessionState(conversationId, sessionId) {
|
|
10726
|
-
return this.endpointQuery("get_voice_session", { conversationId, sessionId });
|
|
10727
|
-
}
|
|
10728
|
-
async declineVoiceSession(conversationId, sessionId) {
|
|
10729
|
-
await this.endpointAction("decline_voice_session", { conversationId, sessionId });
|
|
10730
|
-
}
|
|
10731
|
-
async joinVoiceSession(conversationId, sessionId, options) {
|
|
10732
|
-
return this.endpointAction("join_voice_session", { conversationId, sessionId, ...options ?? {} });
|
|
10733
|
-
}
|
|
10734
|
-
async endVoiceSession(conversationId, sessionId) {
|
|
10735
|
-
await this.endpointAction("end_voice_session", { conversationId, sessionId });
|
|
10736
|
-
}
|
|
10737
|
-
async listContacts() {
|
|
10738
|
-
const data = await this.endpointQuery("list_contacts");
|
|
10739
|
-
return data.contacts;
|
|
10740
|
-
}
|
|
10741
|
-
async getContact(contactId) {
|
|
10742
|
-
try {
|
|
10743
|
-
return await this.endpointQuery("get_contact", { contactId });
|
|
10744
|
-
} catch (error) {
|
|
10745
|
-
if (error instanceof CanonApiError && error.status === 404) return null;
|
|
10746
|
-
throw error;
|
|
10747
|
-
}
|
|
10748
|
-
}
|
|
10749
|
-
async deleteContact(contactId) {
|
|
10750
|
-
await this.endpointAction("remove_contact", { contactId });
|
|
10751
|
-
}
|
|
10752
10628
|
async blockUser(userId) {
|
|
10753
10629
|
await this.endpointAction("set_block", { userId, blocked: true });
|
|
10754
10630
|
}
|
|
10755
|
-
async unblockUser(userId) {
|
|
10756
|
-
await this.endpointAction("set_block", { userId, blocked: false });
|
|
10757
|
-
}
|
|
10758
|
-
async resolveAdmission(target) {
|
|
10759
|
-
const body = typeof target === "string" ? { targetUserId: target } : target;
|
|
10760
|
-
return this.endpointQuery("resolve_admission", { ...body });
|
|
10761
|
-
}
|
|
10762
|
-
async uploadMedia(conversationId, data, mimeType, fileName, options) {
|
|
10763
|
-
const bytes = Buffer.from(data, "base64");
|
|
10764
|
-
if (!bytes.length || bytes.length > 100 * 1024 * 1024) throw new CanonApiError(400, "Canon media must contain from 1 byte to 100 MiB");
|
|
10765
|
-
const directory = await mkdtemp(join5(tmpdir(), "canon-upload-"));
|
|
10766
|
-
const source = join5(directory, "bytes");
|
|
10767
|
-
try {
|
|
10768
|
-
await writeFile(source, bytes, { mode: 384 });
|
|
10769
|
-
const media = await this.getFileMediaPipeline();
|
|
10770
|
-
const [attachment] = await media.prepare({
|
|
10771
|
-
conversationId,
|
|
10772
|
-
attachmentId: options?.attachmentId ?? randomUUID2(),
|
|
10773
|
-
files: [{ source, descriptor: { mimeType, sizeBytes: bytes.length, ...fileName ? { fileName } : {} } }]
|
|
10774
|
-
});
|
|
10775
|
-
if (typeof attachment?.url !== "string") throw new CanonApiError(502, "Canon returned an invalid prepared attachment");
|
|
10776
|
-
return { url: attachment.url, attachment };
|
|
10777
|
-
} catch (error) {
|
|
10778
|
-
throw asCanonApiError(error);
|
|
10779
|
-
} finally {
|
|
10780
|
-
await rm(directory, { recursive: true, force: true });
|
|
10781
|
-
}
|
|
10782
|
-
}
|
|
10783
|
-
/**
|
|
10784
|
-
* Start a private direct-to-Storage upload. The returned upload URL is a
|
|
10785
|
-
* short-lived bearer capability; send exactly `sizeBytes` raw bytes to it,
|
|
10786
|
-
* then call `finalizeResumableMediaUpload`.
|
|
10787
|
-
*/
|
|
10788
|
-
async createResumableMediaUpload(conversationId, sizeBytes, mimeType, fileName) {
|
|
10789
|
-
return this.endpointAction("create_upload", { conversationId, sizeBytes, mimeType, ...fileName ? { fileName } : {} });
|
|
10790
|
-
}
|
|
10791
|
-
/** Promote validated pending bytes into a canonical Canon attachment. */
|
|
10792
|
-
async finalizeResumableMediaUpload(uploadId) {
|
|
10793
|
-
return this.endpointAction("finalize_upload", { uploadId });
|
|
10794
|
-
}
|
|
10795
|
-
async updateTopic(conversationId, topic) {
|
|
10796
|
-
await this.endpointAction("update_conversation", { conversationId, topic });
|
|
10797
|
-
}
|
|
10798
10631
|
async deleteMessage(conversationId, messageId) {
|
|
10799
10632
|
await this.endpointAction("delete_message", { conversationId, messageId });
|
|
10800
10633
|
}
|
|
@@ -10804,25 +10637,9 @@ var CanonClient = class {
|
|
|
10804
10637
|
async markAsRead(conversationId) {
|
|
10805
10638
|
await this.endpointAction("mark_read", { conversationId });
|
|
10806
10639
|
}
|
|
10807
|
-
async leaveConversation(conversationId) {
|
|
10808
|
-
await this.endpointAction("leave_conversation", { conversationId });
|
|
10809
|
-
}
|
|
10810
10640
|
async react(conversationId, messageId, emoji) {
|
|
10811
10641
|
await this.endpointAction("react", { conversationId, messageId, emoji });
|
|
10812
10642
|
}
|
|
10813
|
-
async updateConversationName(conversationId, name) {
|
|
10814
|
-
await this.endpointAction("update_conversation", { conversationId, name });
|
|
10815
|
-
}
|
|
10816
|
-
async addMember(conversationId, userId) {
|
|
10817
|
-
const result = await this.endpointAction("add_member", { conversationId, userId });
|
|
10818
|
-
if (!result || typeof result !== "object" || !("status" in result) || result.status !== "added" && !(result.status === "pending" && "requestId" in result && typeof result.requestId === "string" && result.requestId)) {
|
|
10819
|
-
throw new CanonApiError(502, "Canon returned a malformed add_member result");
|
|
10820
|
-
}
|
|
10821
|
-
return result;
|
|
10822
|
-
}
|
|
10823
|
-
async removeMember(conversationId, userId) {
|
|
10824
|
-
await this.endpointAction("remove_member", { conversationId, userId });
|
|
10825
|
-
}
|
|
10826
10643
|
async setStreaming(options) {
|
|
10827
10644
|
await this.endpointPublish("publish_streaming", { ...options });
|
|
10828
10645
|
}
|
|
@@ -11256,7 +11073,7 @@ __name(resumeRegistration, "resumeRegistration");
|
|
|
11256
11073
|
import { existsSync, readFileSync as readFileSync2, renameSync, writeFileSync as writeFileSync2, unlinkSync as unlinkSync2, mkdirSync } from "node:fs";
|
|
11257
11074
|
import { closeSync as closeSync2, openSync as openSync2 } from "node:fs";
|
|
11258
11075
|
import { randomUUID as randomUUID5 } from "node:crypto";
|
|
11259
|
-
import { join as
|
|
11076
|
+
import { join as join5, resolve as resolve4 } from "node:path";
|
|
11260
11077
|
import { homedir as homedir2 } from "node:os";
|
|
11261
11078
|
|
|
11262
11079
|
// ../core/src/process-lock.ts
|
|
@@ -11297,15 +11114,15 @@ function claimStaleLockReaper(lockPath) {
|
|
|
11297
11114
|
__name(claimStaleLockReaper, "claimStaleLockReaper");
|
|
11298
11115
|
|
|
11299
11116
|
// ../core/src/agent-profiles.ts
|
|
11300
|
-
var CANON_DIR = process.env.CANON_HOME ? resolve4(process.env.CANON_HOME) :
|
|
11301
|
-
var AGENTS_PATH =
|
|
11302
|
-
var LOCKS_DIR =
|
|
11303
|
-
var PENDING_REGISTRATIONS_PATH =
|
|
11117
|
+
var CANON_DIR = process.env.CANON_HOME ? resolve4(process.env.CANON_HOME) : join5(homedir2(), ".canon");
|
|
11118
|
+
var AGENTS_PATH = join5(CANON_DIR, "agents.json");
|
|
11119
|
+
var LOCKS_DIR = join5(CANON_DIR, "locks");
|
|
11120
|
+
var PENDING_REGISTRATIONS_PATH = join5(CANON_DIR, "pending-registrations.json");
|
|
11304
11121
|
var AGENTS_JSON_BOOTSTRAP_ENV = "CANON_AGENTS_JSON_BOOTSTRAP";
|
|
11305
11122
|
function canonPaths(home) {
|
|
11306
11123
|
if (!home) return { dir: CANON_DIR, agents: AGENTS_PATH, locks: LOCKS_DIR };
|
|
11307
11124
|
const dir = resolve4(home);
|
|
11308
|
-
return { dir, agents:
|
|
11125
|
+
return { dir, agents: join5(dir, "agents.json"), locks: join5(dir, "locks") };
|
|
11309
11126
|
}
|
|
11310
11127
|
__name(canonPaths, "canonPaths");
|
|
11311
11128
|
function normalizeAgentProfile(name, value, source) {
|
|
@@ -11452,12 +11269,31 @@ function isProcessAlive(pid) {
|
|
|
11452
11269
|
}
|
|
11453
11270
|
}
|
|
11454
11271
|
__name(isProcessAlive, "isProcessAlive");
|
|
11272
|
+
function processIdentity(pid) {
|
|
11273
|
+
if (process.platform !== "linux") return void 0;
|
|
11274
|
+
try {
|
|
11275
|
+
const bootId = readFileSync2("/proc/sys/kernel/random/boot_id", "utf8").trim();
|
|
11276
|
+
const stat3 = readFileSync2(`/proc/${pid}/stat`, "utf8");
|
|
11277
|
+
const startTicks = stat3.slice(stat3.lastIndexOf(")") + 2).split(/\s+/)[19];
|
|
11278
|
+
if (!/^[a-f0-9-]{36}$/.test(bootId) || !startTicks || !/^\d+$/.test(startTicks)) return void 0;
|
|
11279
|
+
return { bootId, startTicks };
|
|
11280
|
+
} catch {
|
|
11281
|
+
return void 0;
|
|
11282
|
+
}
|
|
11283
|
+
}
|
|
11284
|
+
__name(processIdentity, "processIdentity");
|
|
11285
|
+
function sameProcess(owner) {
|
|
11286
|
+
if (!owner.processIdentity) return true;
|
|
11287
|
+
const current = processIdentity(owner.pid);
|
|
11288
|
+
return !current || current.bootId === owner.processIdentity.bootId && current.startTicks === owner.processIdentity.startTicks;
|
|
11289
|
+
}
|
|
11290
|
+
__name(sameProcess, "sameProcess");
|
|
11455
11291
|
function isProfileLocked(profile, home) {
|
|
11456
|
-
const lockPath =
|
|
11292
|
+
const lockPath = join5(canonPaths(home).locks, `${profile}.lock`);
|
|
11457
11293
|
try {
|
|
11458
11294
|
const lock = parseLock(readFileSync2(lockPath, "utf-8"));
|
|
11459
11295
|
const pid = lock?.pid;
|
|
11460
|
-
if (pid && isProcessAlive(pid)) return { locked: true, pid };
|
|
11296
|
+
if (pid && isProcessAlive(pid) && sameProcess(lock)) return { locked: true, pid };
|
|
11461
11297
|
} catch {
|
|
11462
11298
|
}
|
|
11463
11299
|
return { locked: false };
|
|
@@ -11468,7 +11304,8 @@ function parseLock(raw) {
|
|
|
11468
11304
|
if (!trimmed) return null;
|
|
11469
11305
|
try {
|
|
11470
11306
|
const data = JSON.parse(trimmed);
|
|
11471
|
-
|
|
11307
|
+
if (data.processIdentity !== void 0 && (!data.processIdentity || typeof data.processIdentity.bootId !== "string" || !/^[a-f0-9-]{36}$/.test(data.processIdentity.bootId) || typeof data.processIdentity.startTicks !== "string" || !/^\d+$/.test(data.processIdentity.startTicks))) return null;
|
|
11308
|
+
return typeof data.pid === "number" ? { pid: data.pid, token: typeof data.token === "string" ? data.token : void 0, processIdentity: data.processIdentity } : null;
|
|
11472
11309
|
} catch {
|
|
11473
11310
|
return null;
|
|
11474
11311
|
}
|
|
@@ -11477,12 +11314,13 @@ __name(parseLock, "parseLock");
|
|
|
11477
11314
|
function acquireLock(profile, home) {
|
|
11478
11315
|
const locks = canonPaths(home).locks;
|
|
11479
11316
|
mkdirSync(locks, { recursive: true });
|
|
11480
|
-
const lockPath =
|
|
11317
|
+
const lockPath = join5(locks, `${profile}.lock`);
|
|
11481
11318
|
const token = randomUUID5();
|
|
11482
11319
|
const payload = JSON.stringify({
|
|
11483
11320
|
pid: process.pid,
|
|
11484
11321
|
token,
|
|
11485
|
-
acquiredAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
11322
|
+
acquiredAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
11323
|
+
processIdentity: processIdentity(process.pid)
|
|
11486
11324
|
});
|
|
11487
11325
|
for (let attempt = 0; attempt < 2; attempt += 1) {
|
|
11488
11326
|
try {
|
|
@@ -11514,7 +11352,7 @@ function acquireLock(profile, home) {
|
|
|
11514
11352
|
}
|
|
11515
11353
|
try {
|
|
11516
11354
|
process.kill(owner.pid, 0);
|
|
11517
|
-
throw new Error(`Agent "${profile}" is in use by another session (PID ${owner.pid})`);
|
|
11355
|
+
if (sameProcess(owner)) throw new Error(`Agent "${profile}" is in use by another session (PID ${owner.pid})`);
|
|
11518
11356
|
} catch (probeError) {
|
|
11519
11357
|
if (probeError.code !== "ESRCH") throw probeError;
|
|
11520
11358
|
}
|
|
@@ -11528,7 +11366,7 @@ function acquireLock(profile, home) {
|
|
|
11528
11366
|
}
|
|
11529
11367
|
__name(acquireLock, "acquireLock");
|
|
11530
11368
|
function releaseLock(profile, token, home) {
|
|
11531
|
-
const lockPath =
|
|
11369
|
+
const lockPath = join5(canonPaths(home).locks, `${profile}.lock`);
|
|
11532
11370
|
if (token) {
|
|
11533
11371
|
try {
|
|
11534
11372
|
const lock = parseLock(readFileSync2(lockPath, "utf-8"));
|
|
@@ -11548,13 +11386,6 @@ function normalizePendingRegistration(name, value) {
|
|
|
11548
11386
|
throw new Error(`${PENDING_REGISTRATIONS_PATH} entry "${name}" must be an object`);
|
|
11549
11387
|
}
|
|
11550
11388
|
const pending = { ...value };
|
|
11551
|
-
if (typeof pending.localRegistrationId === "string" && pending.localRegistrationId.startsWith("hermes-")) {
|
|
11552
|
-
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
11553
|
-
pending.profile ??= name;
|
|
11554
|
-
pending.clientType ??= "hermes";
|
|
11555
|
-
pending.createdAt ??= typeof pending.submittedAt === "string" ? pending.submittedAt : now;
|
|
11556
|
-
pending.updatedAt ??= pending.createdAt;
|
|
11557
|
-
}
|
|
11558
11389
|
const requiredStrings = [
|
|
11559
11390
|
"profile",
|
|
11560
11391
|
"localRegistrationId",
|
|
@@ -12695,7 +12526,7 @@ var DEFAULT_TURN_VERBOSITY_BY_CONVERSATION_TYPE = {
|
|
|
12695
12526
|
};
|
|
12696
12527
|
function normalizeTurnVerbosityConversationType(value) {
|
|
12697
12528
|
const normalized = typeof value === "string" ? value.trim().toLowerCase() : "";
|
|
12698
|
-
if (normalized === "direct"
|
|
12529
|
+
if (normalized === "direct") return "direct";
|
|
12699
12530
|
if (normalized === "group") return "group";
|
|
12700
12531
|
return "unknown";
|
|
12701
12532
|
}
|
|
@@ -13519,11 +13350,11 @@ import {
|
|
|
13519
13350
|
unlinkSync as unlinkSync3,
|
|
13520
13351
|
writeFileSync as writeFileSync3
|
|
13521
13352
|
} from "node:fs";
|
|
13522
|
-
import { join as
|
|
13353
|
+
import { join as join6 } from "node:path";
|
|
13523
13354
|
function createCompletedOutputJournal(directory, scope5, options) {
|
|
13524
13355
|
const label = options.label;
|
|
13525
|
-
const folder =
|
|
13526
|
-
const filename = /* @__PURE__ */ __name((turnId) =>
|
|
13356
|
+
const folder = join6(directory, createHash7("sha256").update(JSON.stringify(scope5)).digest("hex"));
|
|
13357
|
+
const filename = /* @__PURE__ */ __name((turnId) => join6(folder, `${createHash7("sha256").update(turnId).digest("hex")}.json`), "filename");
|
|
13527
13358
|
function syncDirectory() {
|
|
13528
13359
|
const fd = openSync3(folder, "r");
|
|
13529
13360
|
try {
|
|
@@ -13549,7 +13380,7 @@ function createCompletedOutputJournal(directory, scope5, options) {
|
|
|
13549
13380
|
} catch (error) {
|
|
13550
13381
|
if (error.code !== "ESRCH") continue;
|
|
13551
13382
|
}
|
|
13552
|
-
const path =
|
|
13383
|
+
const path = join6(folder, name);
|
|
13553
13384
|
let record7;
|
|
13554
13385
|
try {
|
|
13555
13386
|
record7 = JSON.parse(readFileSync4(path, "utf8"));
|
|
@@ -13559,7 +13390,7 @@ function createCompletedOutputJournal(directory, scope5, options) {
|
|
|
13559
13390
|
throw new Error(`Incomplete ${label} output write removed; reconcile its native transcript`);
|
|
13560
13391
|
}
|
|
13561
13392
|
const destination = filename(record7.turnId);
|
|
13562
|
-
if (destination !==
|
|
13393
|
+
if (destination !== join6(folder, `${match[1]}.json`)) {
|
|
13563
13394
|
throw new Error(`Completed ${label} output temporary file identity mismatch`);
|
|
13564
13395
|
}
|
|
13565
13396
|
if (existsSync2(destination)) {
|
|
@@ -13605,9 +13436,9 @@ function createCompletedOutputJournal(directory, scope5, options) {
|
|
|
13605
13436
|
if (!existsSync2(folder)) return [];
|
|
13606
13437
|
recoverTemporaryFiles();
|
|
13607
13438
|
return readdirSync(folder).filter((name) => name.endsWith(".json")).map((name) => {
|
|
13608
|
-
const record7 = JSON.parse(readFileSync4(
|
|
13439
|
+
const record7 = JSON.parse(readFileSync4(join6(folder, name), "utf8"));
|
|
13609
13440
|
validate(record7);
|
|
13610
|
-
if (filename(record7.turnId) !==
|
|
13441
|
+
if (filename(record7.turnId) !== join6(folder, name)) throw new Error(`Completed ${label} output identity mismatch`);
|
|
13611
13442
|
return record7;
|
|
13612
13443
|
}).sort((a, b) => a.createdAt.localeCompare(b.createdAt));
|
|
13613
13444
|
},
|
|
@@ -13672,7 +13503,7 @@ var ANTHROPIC_IMAGE_MIME_TYPES = /* @__PURE__ */ new Set([
|
|
|
13672
13503
|
"image/gif",
|
|
13673
13504
|
"image/webp"
|
|
13674
13505
|
]);
|
|
13675
|
-
var DEFAULT_MEDIA_CACHE_DIR =
|
|
13506
|
+
var DEFAULT_MEDIA_CACHE_DIR = join7(CANON_DIR, "media-cache");
|
|
13676
13507
|
var DEFAULT_MEDIA_MATERIALIZATION_BYTES = 10 * 1024 * 1024;
|
|
13677
13508
|
var MAX_CANON_MEDIA_BYTES = 100 * 1024 * 1024;
|
|
13678
13509
|
var MAX_ANTHROPIC_IMAGE_RAW_BYTES = 7 * 1024 * 1024;
|
|
@@ -13740,7 +13571,7 @@ function inferExtension(input) {
|
|
|
13740
13571
|
}
|
|
13741
13572
|
__name(inferExtension, "inferExtension");
|
|
13742
13573
|
function conversationMediaCacheDir(input) {
|
|
13743
|
-
return
|
|
13574
|
+
return join7(
|
|
13744
13575
|
input.rootDir ?? DEFAULT_MEDIA_CACHE_DIR,
|
|
13745
13576
|
sanitizeSegment(input.agentId, "agent"),
|
|
13746
13577
|
sanitizeSegment(input.conversationId, "conversation")
|
|
@@ -13756,7 +13587,7 @@ function buildCachePath(input) {
|
|
|
13756
13587
|
const fallbackName = `${input.attachment.kind}-${input.index}${extension}`;
|
|
13757
13588
|
const preferredName = input.attachment.fileName ? sanitizeDownloadFileName(input.attachment.fileName, fallbackName) : fallbackName;
|
|
13758
13589
|
const fileName = extname(preferredName) ? preferredName : `${preferredName}${extension}`;
|
|
13759
|
-
return
|
|
13590
|
+
return join7(
|
|
13760
13591
|
conversationMediaCacheDir({ agentId: input.agentId, conversationId: input.conversationId, rootDir }),
|
|
13761
13592
|
sanitizeSegment(input.messageId, "message"),
|
|
13762
13593
|
`${String(input.index).padStart(2, "0")}-${fileName}`
|
|
@@ -366,16 +366,12 @@ function installation(value) {
|
|
|
366
366
|
return value;
|
|
367
367
|
}
|
|
368
368
|
__name(installation, "installation");
|
|
369
|
+
function isMlsPrincipalId(value) {
|
|
370
|
+
return typeof value === "string" && value.length > 0 && value.length <= 512 && !/[\u0000-\u001f\u007f-\u009f]/.test(value) && !/[\ud800-\udfff]/.test(value.replace(/[\ud800-\udbff][\udc00-\udfff]/g, "")) && utf8.encode(value).length <= 512;
|
|
371
|
+
}
|
|
372
|
+
__name(isMlsPrincipalId, "isMlsPrincipalId");
|
|
369
373
|
function principalId(value) {
|
|
370
|
-
if (
|
|
371
|
-
for (let index = 0; index < value.length; index++) {
|
|
372
|
-
const unit = value.charCodeAt(index);
|
|
373
|
-
if (unit >= 55296 && unit <= 56319) {
|
|
374
|
-
const next = value.charCodeAt(++index);
|
|
375
|
-
if (!(next >= 56320 && next <= 57343)) invalid2();
|
|
376
|
-
} else if (unit >= 56320 && unit <= 57343) invalid2();
|
|
377
|
-
}
|
|
378
|
-
if (utf8.encode(value).length > 512) invalid2();
|
|
374
|
+
if (!isMlsPrincipalId(value)) invalid2();
|
|
379
375
|
return value;
|
|
380
376
|
}
|
|
381
377
|
__name(principalId, "principalId");
|
|
@@ -2324,9 +2320,7 @@ var VERB_LIMITS = {
|
|
|
2324
2320
|
externalSourceLabelChars: 80,
|
|
2325
2321
|
/**
|
|
2326
2322
|
* Contact-request note. Values longer than this are truncated by senders
|
|
2327
|
-
* (slice(0,497)+'...')
|
|
2328
|
-
* functions/src/api/sendContextualMessage.ts, packages/core/src/reach-out.ts
|
|
2329
|
-
* and the hermes plugin; this constant is the canonical figure.
|
|
2323
|
+
* (slice(0,497)+'...'); this constant is the canonical figure.
|
|
2330
2324
|
*/
|
|
2331
2325
|
contactRequestNoteChars: 500,
|
|
2332
2326
|
/** Group membership cap incl. creator (functions/src/utils/conversations.ts MAX_GROUP_MEMBERS). */
|
|
@@ -2747,7 +2741,7 @@ var send_to_input = {
|
|
|
2747
2741
|
]
|
|
2748
2742
|
};
|
|
2749
2743
|
var send_to_result = {
|
|
2750
|
-
description: "Canonical outcome vocabulary.
|
|
2744
|
+
description: "Canonical outcome vocabulary.",
|
|
2751
2745
|
oneOf: [
|
|
2752
2746
|
{
|
|
2753
2747
|
type: "object",
|
|
@@ -2832,7 +2826,7 @@ var request_input_input = {
|
|
|
2832
2826
|
responseUserId: {
|
|
2833
2827
|
type: "string",
|
|
2834
2828
|
pattern: VERB_ID_PATTERNS.runtimeId,
|
|
2835
|
-
description: "Must be a human conversation member. Trust-critical bindings
|
|
2829
|
+
description: "Must be a human conversation member. Trust-critical bindings derive this from turn provenance and ignore model-supplied values."
|
|
2836
2830
|
},
|
|
2837
2831
|
native: REF("native"),
|
|
2838
2832
|
turnId: { type: "string", maxLength: VERB_LIMITS.turnIdChars },
|
|
@@ -5148,9 +5142,12 @@ function isMlsPrincipalAlias(value) {
|
|
|
5148
5142
|
return typeof value === "string" && ALIAS_PATTERN.test(value);
|
|
5149
5143
|
}
|
|
5150
5144
|
__name(isMlsPrincipalAlias, "isMlsPrincipalAlias");
|
|
5145
|
+
function isMlsAccountId(value) {
|
|
5146
|
+
return isMlsPrincipalId(value) && !value.includes("/") && value !== "." && value !== "..";
|
|
5147
|
+
}
|
|
5148
|
+
__name(isMlsAccountId, "isMlsAccountId");
|
|
5151
5149
|
function accountId(value) {
|
|
5152
|
-
if (
|
|
5153
|
-
if (ALIAS_PATTERN.test(value)) invalid15();
|
|
5150
|
+
if (!isMlsAccountId(value) || ALIAS_PATTERN.test(value)) invalid15();
|
|
5154
5151
|
return value;
|
|
5155
5152
|
}
|
|
5156
5153
|
__name(accountId, "accountId");
|