@canonmsg/agent-sdk 12.1.0 → 12.2.0
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-N722GWCP.js → chunk-HITMISWV.js} +4 -4
- package/dist/chunks/{chunk-7N6OPPW4.js → chunk-J63YVAKA.js} +13 -16
- package/dist/chunks/{chunk-QZ4PPRQW.js → chunk-O5C3ZGI4.js} +54 -244
- 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
|
@@ -29,7 +29,7 @@ import {
|
|
|
29
29
|
sendMessageWithRetryChunked,
|
|
30
30
|
splitTextByUtf8Bytes,
|
|
31
31
|
verifyCanonRuntimeConnection
|
|
32
|
-
} from "./chunk-
|
|
32
|
+
} from "./chunk-O5C3ZGI4.js";
|
|
33
33
|
import {
|
|
34
34
|
CONNECTOR_RELAY_ANSWER_WITHIN_MS,
|
|
35
35
|
CONNECTOR_RELAY_INSTALLATION_PATTERN,
|
|
@@ -58,7 +58,7 @@ import {
|
|
|
58
58
|
parseMlsDevGroupHead,
|
|
59
59
|
parseMlsDevGroupRoster,
|
|
60
60
|
resolveAllowedUploadMimeType
|
|
61
|
-
} from "./chunk-
|
|
61
|
+
} from "./chunk-J63YVAKA.js";
|
|
62
62
|
|
|
63
63
|
// src/runner/types.ts
|
|
64
64
|
var DriverSessionError = class extends Error {
|
|
@@ -1694,7 +1694,7 @@ async function loadRelayConfiguration(path, input) {
|
|
|
1694
1694
|
return invalid2(`expected ${keys.join(", ")}`);
|
|
1695
1695
|
}
|
|
1696
1696
|
if (value.version !== 1) invalid2("version");
|
|
1697
|
-
if (value.environmentId !== input.environmentId || value.environmentId
|
|
1697
|
+
if (value.environmentId !== input.environmentId || !isMlsEnvironmentId(value.environmentId)) invalid2("environmentId: the runner's own environment, one with MLS");
|
|
1698
1698
|
if (typeof value.agentId !== "string" || !value.agentId || value.agentId.length > 200 || value.agentId.includes("/")) invalid2("agentId");
|
|
1699
1699
|
const listen = value.listen;
|
|
1700
1700
|
if (!listen || typeof listen !== "object" || typeof listen.host !== "string" || !listen.host || !Number.isSafeInteger(listen.port) || listen.port < 1 || listen.port > 65535) invalid2("listen");
|
|
@@ -2759,7 +2759,7 @@ var STATE_LIMIT_BYTES = 64 * 1024;
|
|
|
2759
2759
|
var FAILURE_NOTICE = "Sorry, something went wrong and I could not finish this reply.";
|
|
2760
2760
|
var STOPS_SHORT_NOTICE = "Sorry, my reply could not be delivered in full.";
|
|
2761
2761
|
var RESTART_NOTICE = "Sorry, I was restarted before I could finish replying to this. Please send it again if you still need an answer.";
|
|
2762
|
-
var CLIENT_TYPES = { code: "generic", claude: "claude-code", codex: "codex",
|
|
2762
|
+
var CLIENT_TYPES = { code: "generic", claude: "claude-code", codex: "codex", connector: "generic" };
|
|
2763
2763
|
var objectSchema = /* @__PURE__ */ __name((properties, required = []) => ({ type: "object", properties, required, additionalProperties: false }), "objectSchema");
|
|
2764
2764
|
var MAX_QUESTION = 4e3;
|
|
2765
2765
|
var MAX_OPTION = 120;
|
|
@@ -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");
|
|
@@ -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) {
|
|
@@ -11453,7 +11270,7 @@ function isProcessAlive(pid) {
|
|
|
11453
11270
|
}
|
|
11454
11271
|
__name(isProcessAlive, "isProcessAlive");
|
|
11455
11272
|
function isProfileLocked(profile, home) {
|
|
11456
|
-
const lockPath =
|
|
11273
|
+
const lockPath = join5(canonPaths(home).locks, `${profile}.lock`);
|
|
11457
11274
|
try {
|
|
11458
11275
|
const lock = parseLock(readFileSync2(lockPath, "utf-8"));
|
|
11459
11276
|
const pid = lock?.pid;
|
|
@@ -11477,7 +11294,7 @@ __name(parseLock, "parseLock");
|
|
|
11477
11294
|
function acquireLock(profile, home) {
|
|
11478
11295
|
const locks = canonPaths(home).locks;
|
|
11479
11296
|
mkdirSync(locks, { recursive: true });
|
|
11480
|
-
const lockPath =
|
|
11297
|
+
const lockPath = join5(locks, `${profile}.lock`);
|
|
11481
11298
|
const token = randomUUID5();
|
|
11482
11299
|
const payload = JSON.stringify({
|
|
11483
11300
|
pid: process.pid,
|
|
@@ -11528,7 +11345,7 @@ function acquireLock(profile, home) {
|
|
|
11528
11345
|
}
|
|
11529
11346
|
__name(acquireLock, "acquireLock");
|
|
11530
11347
|
function releaseLock(profile, token, home) {
|
|
11531
|
-
const lockPath =
|
|
11348
|
+
const lockPath = join5(canonPaths(home).locks, `${profile}.lock`);
|
|
11532
11349
|
if (token) {
|
|
11533
11350
|
try {
|
|
11534
11351
|
const lock = parseLock(readFileSync2(lockPath, "utf-8"));
|
|
@@ -11548,13 +11365,6 @@ function normalizePendingRegistration(name, value) {
|
|
|
11548
11365
|
throw new Error(`${PENDING_REGISTRATIONS_PATH} entry "${name}" must be an object`);
|
|
11549
11366
|
}
|
|
11550
11367
|
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
11368
|
const requiredStrings = [
|
|
11559
11369
|
"profile",
|
|
11560
11370
|
"localRegistrationId",
|
|
@@ -12695,7 +12505,7 @@ var DEFAULT_TURN_VERBOSITY_BY_CONVERSATION_TYPE = {
|
|
|
12695
12505
|
};
|
|
12696
12506
|
function normalizeTurnVerbosityConversationType(value) {
|
|
12697
12507
|
const normalized = typeof value === "string" ? value.trim().toLowerCase() : "";
|
|
12698
|
-
if (normalized === "direct"
|
|
12508
|
+
if (normalized === "direct") return "direct";
|
|
12699
12509
|
if (normalized === "group") return "group";
|
|
12700
12510
|
return "unknown";
|
|
12701
12511
|
}
|
|
@@ -13519,11 +13329,11 @@ import {
|
|
|
13519
13329
|
unlinkSync as unlinkSync3,
|
|
13520
13330
|
writeFileSync as writeFileSync3
|
|
13521
13331
|
} from "node:fs";
|
|
13522
|
-
import { join as
|
|
13332
|
+
import { join as join6 } from "node:path";
|
|
13523
13333
|
function createCompletedOutputJournal(directory, scope5, options) {
|
|
13524
13334
|
const label = options.label;
|
|
13525
|
-
const folder =
|
|
13526
|
-
const filename = /* @__PURE__ */ __name((turnId) =>
|
|
13335
|
+
const folder = join6(directory, createHash7("sha256").update(JSON.stringify(scope5)).digest("hex"));
|
|
13336
|
+
const filename = /* @__PURE__ */ __name((turnId) => join6(folder, `${createHash7("sha256").update(turnId).digest("hex")}.json`), "filename");
|
|
13527
13337
|
function syncDirectory() {
|
|
13528
13338
|
const fd = openSync3(folder, "r");
|
|
13529
13339
|
try {
|
|
@@ -13549,7 +13359,7 @@ function createCompletedOutputJournal(directory, scope5, options) {
|
|
|
13549
13359
|
} catch (error) {
|
|
13550
13360
|
if (error.code !== "ESRCH") continue;
|
|
13551
13361
|
}
|
|
13552
|
-
const path =
|
|
13362
|
+
const path = join6(folder, name);
|
|
13553
13363
|
let record7;
|
|
13554
13364
|
try {
|
|
13555
13365
|
record7 = JSON.parse(readFileSync4(path, "utf8"));
|
|
@@ -13559,7 +13369,7 @@ function createCompletedOutputJournal(directory, scope5, options) {
|
|
|
13559
13369
|
throw new Error(`Incomplete ${label} output write removed; reconcile its native transcript`);
|
|
13560
13370
|
}
|
|
13561
13371
|
const destination = filename(record7.turnId);
|
|
13562
|
-
if (destination !==
|
|
13372
|
+
if (destination !== join6(folder, `${match[1]}.json`)) {
|
|
13563
13373
|
throw new Error(`Completed ${label} output temporary file identity mismatch`);
|
|
13564
13374
|
}
|
|
13565
13375
|
if (existsSync2(destination)) {
|
|
@@ -13605,9 +13415,9 @@ function createCompletedOutputJournal(directory, scope5, options) {
|
|
|
13605
13415
|
if (!existsSync2(folder)) return [];
|
|
13606
13416
|
recoverTemporaryFiles();
|
|
13607
13417
|
return readdirSync(folder).filter((name) => name.endsWith(".json")).map((name) => {
|
|
13608
|
-
const record7 = JSON.parse(readFileSync4(
|
|
13418
|
+
const record7 = JSON.parse(readFileSync4(join6(folder, name), "utf8"));
|
|
13609
13419
|
validate(record7);
|
|
13610
|
-
if (filename(record7.turnId) !==
|
|
13420
|
+
if (filename(record7.turnId) !== join6(folder, name)) throw new Error(`Completed ${label} output identity mismatch`);
|
|
13611
13421
|
return record7;
|
|
13612
13422
|
}).sort((a, b) => a.createdAt.localeCompare(b.createdAt));
|
|
13613
13423
|
},
|
|
@@ -13672,7 +13482,7 @@ var ANTHROPIC_IMAGE_MIME_TYPES = /* @__PURE__ */ new Set([
|
|
|
13672
13482
|
"image/gif",
|
|
13673
13483
|
"image/webp"
|
|
13674
13484
|
]);
|
|
13675
|
-
var DEFAULT_MEDIA_CACHE_DIR =
|
|
13485
|
+
var DEFAULT_MEDIA_CACHE_DIR = join7(CANON_DIR, "media-cache");
|
|
13676
13486
|
var DEFAULT_MEDIA_MATERIALIZATION_BYTES = 10 * 1024 * 1024;
|
|
13677
13487
|
var MAX_CANON_MEDIA_BYTES = 100 * 1024 * 1024;
|
|
13678
13488
|
var MAX_ANTHROPIC_IMAGE_RAW_BYTES = 7 * 1024 * 1024;
|
|
@@ -13740,7 +13550,7 @@ function inferExtension(input) {
|
|
|
13740
13550
|
}
|
|
13741
13551
|
__name(inferExtension, "inferExtension");
|
|
13742
13552
|
function conversationMediaCacheDir(input) {
|
|
13743
|
-
return
|
|
13553
|
+
return join7(
|
|
13744
13554
|
input.rootDir ?? DEFAULT_MEDIA_CACHE_DIR,
|
|
13745
13555
|
sanitizeSegment(input.agentId, "agent"),
|
|
13746
13556
|
sanitizeSegment(input.conversationId, "conversation")
|
|
@@ -13756,7 +13566,7 @@ function buildCachePath(input) {
|
|
|
13756
13566
|
const fallbackName = `${input.attachment.kind}-${input.index}${extension}`;
|
|
13757
13567
|
const preferredName = input.attachment.fileName ? sanitizeDownloadFileName(input.attachment.fileName, fallbackName) : fallbackName;
|
|
13758
13568
|
const fileName = extname(preferredName) ? preferredName : `${preferredName}${extension}`;
|
|
13759
|
-
return
|
|
13569
|
+
return join7(
|
|
13760
13570
|
conversationMediaCacheDir({ agentId: input.agentId, conversationId: input.conversationId, rootDir }),
|
|
13761
13571
|
sanitizeSegment(input.messageId, "message"),
|
|
13762
13572
|
`${String(input.index).padStart(2, "0")}-${fileName}`
|