@agentvault/muse-bridge 0.1.15 → 0.1.17

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.js CHANGED
@@ -334,6 +334,58 @@ function classifyCommitFailure(messageEpoch, groupEpoch) {
334
334
  if (msg === null || grp === null) return "count";
335
335
  return msg < grp ? "already-applied" : "count";
336
336
  }
337
+ function parseMentions(text) {
338
+ const mentions = [];
339
+ const re2 = new RegExp(MENTION_RE.source, MENTION_RE.flags);
340
+ let match;
341
+ while ((match = re2.exec(text)) !== null) {
342
+ mentions.push(match[1].toLowerCase());
343
+ }
344
+ return mentions;
345
+ }
346
+ function resolveOwn(mention, names) {
347
+ if (names.has(mention)) return mention;
348
+ const parts = mention.split("-");
349
+ for (let i2 = parts.length - 1; i2 > 0; i2--) {
350
+ const candidate = parts.slice(0, i2).join("-");
351
+ if (names.has(candidate)) return candidate;
352
+ }
353
+ return null;
354
+ }
355
+ function owesRoomReply(plaintext, agentName, accountId, senderIsAgent, roomDisplayName, ownDisplayName, selfResolved = true) {
356
+ const mentions = parseMentions(plaintext);
357
+ if (mentions.length === 0) return false;
358
+ const ownNames = ownRoomNames(agentName, accountId, roomDisplayName, ownDisplayName);
359
+ if (mentions.some((m2) => resolveOwn(m2, ownNames) !== null)) return true;
360
+ if (mentions.some((m2) => resolveOwn(m2, BROADCAST_NAMES) !== null)) {
361
+ return senderIsAgent !== true;
362
+ }
363
+ if (!selfResolved) return true;
364
+ return false;
365
+ }
366
+ function ownRoomNames(agentName, accountId, roomDisplayName, ownDisplayName) {
367
+ const names = /* @__PURE__ */ new Set();
368
+ const add4 = (n2) => {
369
+ if (!n2) return;
370
+ const lower = n2.toLowerCase();
371
+ names.add(lower);
372
+ names.add(lower.split(/[\s(]/)[0]);
373
+ };
374
+ add4(agentName);
375
+ add4(accountId);
376
+ add4(roomDisplayName);
377
+ add4(ownDisplayName);
378
+ return names;
379
+ }
380
+ function stripMentions(text, agentName, accountId, roomDisplayName, ownDisplayName) {
381
+ const ownNames = ownRoomNames(agentName, accountId, roomDisplayName, ownDisplayName);
382
+ return text.replace(new RegExp(MENTION_RE.source, MENTION_RE.flags), (full, name) => {
383
+ const lower = name.toLowerCase();
384
+ const matched = resolveOwn(lower, ownNames) ?? resolveOwn(lower, BROADCAST_NAMES);
385
+ if (matched === null) return full;
386
+ return name.slice(matched.length);
387
+ }).replace(/^\s+/, "").trim();
388
+ }
337
389
  function _(e) {
338
390
  if ("function" == typeof TextEncoder) return new TextEncoder().encode(e);
339
391
  e = unescape(encodeURIComponent(e));
@@ -11465,11 +11517,11 @@ async function joinWithFirstMatchingKp(welcomeBytes, candidates) {
11465
11517
  return null;
11466
11518
  }
11467
11519
  function ownIdentity() {
11468
- const v2 = true ? "0.23.39" : FALLBACK;
11520
+ const v2 = true ? "0.23.41" : FALLBACK;
11469
11521
  return `${PACKAGE}@${v2}`;
11470
11522
  }
11471
11523
  function buildSha() {
11472
- const raw = true ? "9982fc8-dirty" : "";
11524
+ const raw = true ? "916d6c5-dirty" : "";
11473
11525
  const cleaned = (raw ?? "").trim();
11474
11526
  return !cleaned || cleaned === "unknown" ? null : cleaned;
11475
11527
  }
@@ -12474,58 +12526,6 @@ async function checkGateway(options) {
12474
12526
  return { ok: false, error: friendlyError(err) };
12475
12527
  }
12476
12528
  }
12477
- function parseMentions(text) {
12478
- const mentions = [];
12479
- const re2 = new RegExp(MENTION_RE.source, MENTION_RE.flags);
12480
- let match;
12481
- while ((match = re2.exec(text)) !== null) {
12482
- mentions.push(match[1].toLowerCase());
12483
- }
12484
- return mentions;
12485
- }
12486
- function resolveOwn(mention, names) {
12487
- if (names.has(mention)) return mention;
12488
- const parts = mention.split("-");
12489
- for (let i2 = parts.length - 1; i2 > 0; i2--) {
12490
- const candidate = parts.slice(0, i2).join("-");
12491
- if (names.has(candidate)) return candidate;
12492
- }
12493
- return null;
12494
- }
12495
- function owesRoomReply(plaintext, agentName, accountId, senderIsAgent, roomDisplayName, ownDisplayName, selfResolved = true) {
12496
- const mentions = parseMentions(plaintext);
12497
- if (mentions.length === 0) return false;
12498
- const ownNames = ownRoomNames(agentName, accountId, roomDisplayName, ownDisplayName);
12499
- if (mentions.some((m2) => resolveOwn(m2, ownNames) !== null)) return true;
12500
- if (mentions.some((m2) => resolveOwn(m2, BROADCAST_NAMES) !== null)) {
12501
- return senderIsAgent !== true;
12502
- }
12503
- if (!selfResolved) return true;
12504
- return false;
12505
- }
12506
- function ownRoomNames(agentName, accountId, roomDisplayName, ownDisplayName) {
12507
- const names = /* @__PURE__ */ new Set();
12508
- const add4 = (n2) => {
12509
- if (!n2) return;
12510
- const lower = n2.toLowerCase();
12511
- names.add(lower);
12512
- names.add(lower.split(/[\s(]/)[0]);
12513
- };
12514
- add4(agentName);
12515
- add4(accountId);
12516
- add4(roomDisplayName);
12517
- add4(ownDisplayName);
12518
- return names;
12519
- }
12520
- function stripMentions(text, agentName, accountId, roomDisplayName, ownDisplayName) {
12521
- const ownNames = ownRoomNames(agentName, accountId, roomDisplayName, ownDisplayName);
12522
- return text.replace(new RegExp(MENTION_RE.source, MENTION_RE.flags), (full, name) => {
12523
- const lower = name.toLowerCase();
12524
- const matched = resolveOwn(lower, ownNames) ?? resolveOwn(lower, BROADCAST_NAMES);
12525
- if (matched === null) return full;
12526
- return name.slice(matched.length);
12527
- }).replace(/^\s+/, "").trim();
12528
- }
12529
12529
  function getErrorMap() {
12530
12530
  return overrideErrorMap;
12531
12531
  }
@@ -17056,7 +17056,7 @@ function reportSkillInvocation(opts) {
17056
17056
  spanBuffer.push(span);
17057
17057
  return span;
17058
17058
  }
17059
- var __create, __defProp2, __getOwnPropDesc, __getOwnPropNames2, __getProtoOf, __hasOwnProp, __esm2, __commonJS, __export2, __copyProps, __toESM, SENTINEL, WRAPPERS, INSTRUCTION, init_abstention, init_mls_commit_classify, __filename, __dirname, url2, path, Module, Module, root, window_, crypto_, randomValuesStandard, crypto4, randomValueNodeJS, _Module, libsodium_sumo_default, init_libsodium_sumo, a, r, t, c, libsodium_wrappers_default, init_libsodium_wrappers, init_keys, init_x3dh, MAX_SKIP, CHAIN_KEY_SEED, MSG_KEY_SEED, HEADER_KEY_SEED, DoubleRatchet, init_ratchet, init_file_crypto, DOMAIN_DID_DOCUMENT, DOMAIN_TRANSFER_INTENT, DOMAIN_TRANSFER_ACCEPT, BASE58_ALPHABET, init_did, BUILTIN_PATTERNS, ScanEngine, init_scan_engine, init_merkle, VC_CONTEXT_V2, AV_CREDENTIAL_CONTEXT, CRYPTOSUITE, BASE58_ALPHABET2, init_vc, init_transport, init_telemetry, TelemetryReporter, init_telemetry_reporter, CROCKFORD, ARGON2_OPSLIMIT, ARGON2_MEMLIMIT, SALT_BYTES, NONCE_BYTES, KEY_BYTES, BACKUP_VERSION, MAX_BACKUP_SIZE, init_backup, init_approval, encVoid, init_tlsEncoder, uint8Encoder, encodeUint8, decodeUint8, uint16Encoder, encodeUint16, decodeUint16, uint32Encoder, encodeUint32, decodeUint32, uint64Encoder, encodeUint64, decodeUint64, init_number, init_tlsDecoder, init_enumHelpers, defaultProposalTypes, defaultProposalTypeEncoder, encodeDefaultProposalType, decodeDefaultProposalType, init_defaultProposalType, defaultExtensionTypes, defaultExtensionTypeEncoder, encodeDefaultExtensionType, decodeDefaultExtensionType, init_defaultExtensionType, acceptAll, init_incomingMessageAction, MlsError, ValidationError, CodecError, UsageError, DependencyError, CryptoVerificationError, CryptoError, InternalError, init_mlsError, init_byteArray, varLenDataEncoder, decodeVarLenData, init_variableLength, init_constantTimeCompare, extensionTypeEncoder, encodeExtensionType, decodeExtensionType, extensionEncoder, encodeExtension, decodeExtension, init_extension, credentialTypes, credentialTypeEncoder, encodeCredentialType, decodeCredentialType, init_credentialType, credentialBasicEncoder, encodeCredentialBasic, credentialX509Encoder, encodeCredentialX509, credentialCustomEncoder, encodeCredentialCustom, credentialEncoder, encodeCredential, decodeCredentialBasic, decodeCredentialX509, decodeCredential, init_credential, externalSenderEncoder, encodeExternalSender, decodeExternalSender, init_externalSender, init_hash, init_optional, ciphersuites, ciphersuiteEncoder, encodeCiphersuite, decodeCiphersuite, ciphersuiteValues, init_ciphersuite, init_signature, protocolVersions, protocolVersionEncoder, encodeProtocolVersion, decodeProtocolVersion, init_protocolVersion, capabilitiesEncoder, encodeCapabilities, decodeCapabilities, init_capabilities, leafNodeSources, leafNodeSourceEncoder, encodeLeafNodeSource, decodeLeafNodeSource, init_leafNodeSource, lifetimeEncoder, encodeLifetime, decodeLifetime, defaultLifetime, init_lifetime, leafNodeDataEncoder, encodeLeafNodeData, decodeLeafNodeData, leafNodeInfoKeyPackageEncoder, encodeLeafNodeInfoKeyPackage, leafNodeInfoUpdateOmittedEncoder, encodeLeafNodeInfoUpdateOmitted, leafNodeInfoCommitOmittedEncoder, encodeLeafNodeInfoCommitOmitted, leafNodeInfoOmittedEncoder, encodeLeafNodeInfoOmitted, decodeLeafNodeInfoKeyPackage, decodeLeafNodeInfoUpdateOmitted, decodeLeafNodeInfoCommitOmitted, decodeLeafNodeInfoOmitted, leafNodeInfoUpdateEncoder, encodeLeafNodeInfoUpdate, leafNodeInfoCommitEncoder, encodeLeafNodeInfoCommit, leafNodeInfoEncoder, encodeLeafNodeInfo, decodeLeafNodeInfoUpdate, decodeLeafNodeInfoCommit, decodeLeafNodeInfo, leafNodeTBSEncoder, encodeLeafNodeTBS, leafNodeEncoder, encodeLeafNode, decodeLeafNode, decodeLeafNodeKeyPackage, decodeLeafNodeCommit, decodeLeafNodeUpdate, init_leafNode, keyPackageTBSEncoder, encodeKeyPackageTBS, decodeKeyPackageTBS, keyPackageEncoder, encodeKeyPackage, decodeKeyPackage, init_keyPackage, init_kdf, pskTypes, pskTypeEncoder, encodePskType, decodePskType, resumptionPSKUsages, resumptionPSKUsageEncoder, encodeResumptionPSKUsage, decodeResumptionPSKUsage, encodePskInfoExternal, encodePskInfoResumption, decodePskInfoResumption, pskInfoEncoder, encodePskInfo, decodePskInfo, pskIdEncoder, encodePskId, decodePskId, pskLabelEncoder, encodePskLabel, decodePskLabel, init_presharedkey, addEncoder, encodeAdd, decodeAdd, updateEncoder, encodeUpdate, decodeUpdate, removeEncoder, encodeRemove, decodeRemove, pskEncoder, encodePSK, decodePSK, reinitEncoder, encodeReinit, decodeReinit, externalInitEncoder, encodeExternalInit, decodeExternalInit, groupContextExtensionsEncoder, encodeGroupContextExtensions, decodeGroupContextExtensions, proposalAddEncoder, encodeProposalAdd, proposalUpdateEncoder, encodeProposalUpdate, proposalRemoveEncoder, encodeProposalRemove, proposalPSKEncoder, encodeProposalPSK, proposalReinitEncoder, encodeProposalReinit, proposalExternalInitEncoder, encodeProposalExternalInit, proposalGroupContextExtensionsEncoder, encodeProposalGroupContextExtensions, proposalCustomEncoder, encodeProposalCustom, proposalEncoder, encodeProposal, decodeProposalAdd, decodeProposalUpdate, decodeProposalRemove, decodeProposalPSK, decodeProposalReinit, decodeProposalExternalInit, decodeProposalGroupContextExtensions, decodeProposal, init_proposal, proposalOrRefTypes, proposalOrRefTypeEncoder, encodeProposalOrRefType, decodeProposalOrRefType, proposalOrRefProposalEncoder, encodeProposalOrRefProposal, proposalOrRefProposalRefEncoder, encodeProposalOrRefProposalRef, proposalOrRefEncoder, encodeProposalOrRef, decodeProposalOrRef, init_proposalOrRefType, init_hpke, groupContextEncoder, encodeGroupContext, decodeGroupContext, init_groupContext, nodeTypes, nodeTypeEncoder, encodeNodeType, decodeNodeType, init_nodeType, parentNodeEncoder, encodeParentNode, decodeParentNode, init_parentNode, init_treemath, nodeEncoder, encodeNode, decodeNode, ratchetTreeEncoder, encodeRatchetTree, decodeRatchetTree, init_ratchetTree, leafNodeHashInputEncoder, encodeLeafNodeHashInput, decodeLeafNodeHashInput, parentNodeHashInputEncoder, encodeParentNodeHashInput, decodeParentNodeHashInput, treeHashInputEncoder, encodeTreeHashInput, decodeTreeHashInput, init_treeHash, parentHashInputEncoder, encodeParentHashInput, decodeParentHashInput, init_parentHash, hpkeCiphertextEncoder, encodeHpkeCiphertext, decodeHpkeCiphertext, init_hpkeCiphertext, updatePathNodeEncoder, encodeUpdatePathNode, decodeUpdatePathNode, updatePathEncoder, encodeUpdatePath, decodeUpdatePath, init_updatePath, commitEncoder, encodeCommit, decodeCommit, init_commit, contentTypes, contentTypeEncoder, encodeContentType, decodeContentType, init_contentType, wireformats, wireformatEncoder, encodeWireformat, decodeWireformat, init_wireformat, senderTypes, senderTypeEncoder, encodeSenderType, decodeSenderType, senderEncoder, encodeSender, decodeSender, reuseGuardEncoder, encodeReuseGuard, decodeReuseGuard, senderDataEncoder, encodeSenderData, decodeSenderData, senderDataAADEncoder, encodeSenderDataAAD, decodeSenderDataAAD, init_sender, framedContentApplicationDataEncoder, encodeFramedContentApplicationData, framedContentProposalDataEncoder, encodeFramedContentProposalData, framedContentCommitDataEncoder, encodeFramedContentCommitData, framedContentInfoEncoder, encodeFramedContentInfo, decodeFramedContentApplicationData, decodeFramedContentProposalData, decodeFramedContentCommitData, decodeFramedContentInfo, framedContentEncoder, encodeFramedContent, decodeFramedContent, senderInfoEncoder, encodeSenderInfo, framedContentTBSEncoder, encodeFramedContentTBS, encodeFramedContentAuthDataContent, encodeFramedContentAuthDataCommit, framedContentAuthDataEncoder, encodeFramedContentAuthData, decodeFramedContentAuthDataCommit, init_framedContent, authenticatedContentEncoder, encodeAuthenticatedContent, decodeAuthenticatedContent, authenticatedContentTBMEncoder, encodeAuthenticatedContentTBM, init_authenticatedContent, publicMessageInfoEncoder, encodePublicMessageInfo, publicMessageEncoder, encodePublicMessage, decodePublicMessage, init_publicMessage, init_messageProtectionPublic, init_externalProposal, requiredCapabilitiesEncoder, encodeRequiredCapabilities, decodeRequiredCapabilities, init_requiredCapabilities, defaultAuthenticationService, init_authenticationService, defaultPaddingConfig, init_paddingConfig, defaultKeyPackageEqualityConfig, init_keyPackageEqualityConfig, defaultLifetimeConfig, init_lifetimeConfig, defaultKeyRetentionConfig, init_keyRetentionConfig, groupInfoTBSEncoder, encodeGroupInfoTBS, decodeGroupInfoTBS, groupInfoEncoder, encodeGroupInfo, decodeGroupInfo, init_groupInfo, keyScheduleEncoder, encodeKeySchedule, decodeKeySchedule, init_keySchedule, generationSecretEncoder, decodeGenerationSecret, secretTreeNodeEncoder, decodeSecretTreeNode, secretTreeEncoder, decodeSecretTree, init_secretTree, confirmedTranscriptHashInputEncoder, encodeConfirmedTranscriptHashInput, decodeConfirmedTranscriptHashInput, init_transcriptHash, groupSecretsEncoder, encodeGroupSecrets, decodeGroupSecrets, init_groupSecrets, encryptedGroupSecretsEncoder, encodeEncryptedGroupSecrets, decodeEncryptedGroupSecrets, welcomeEncoder, encodeWelcome, decodeWelcome, init_welcome, init_pathSecrets, privateKeyPathEncoder, decodePrivateKeyPath, init_privateKeyPath, proposalWithSenderEncoder, decodeProposalWithSender, unappliedProposalsEncoder, decodeUnappliedProposals, init_unappliedProposals, emptyPskIndex, init_pskIndex, init_addToMap, defaultClientConfig, init_clientConfig, init_array, stringEncoder, encodeString, decodeString, init_string, activeEncoder, suspendedPendingReinitEncoder, removedFromGroupEncoder, groupActiveStateEncoder, decodeGroupActiveState, init_groupActiveState, epochReceiverDataEncoder, decodeEpochReceiverData, init_epochReceiverData, groupStateEncoder, encodeGroupState, decodeGroupState, groupStateEncoderWithoutTree, encodeGroupStateWithoutTree, emptyProposals, init_clientState, privateMessageEncoder, encodePrivateMessage, decodePrivateMessage, privateContentAADEncoder, encodePrivateContentAAD, decodePrivateContentAAD, init_privateMessage, init_messageProtection, init_createMessage, init_createCommit, init_processMessages, init_makeHashImpl, HpkeError, InvalidParamError, SerializeError, DeserializeError, EncapError, DecapError, ExportError, SealError, OpenError, MessageLimitReachedError, DeriveKeyPairError, NotSupportedError, init_errors, dntGlobals, dntGlobalThis, init_dnt_shims, NativeAlgorithm, init_algorithm, Mode, KemId, KdfId, AeadId, init_identifiers, INPUT_LENGTH_LIMIT, INFO_LENGTH_LIMIT, MINIMUM_PSK_LENGTH, EMPTY, BYTE_TO_BIGINT_256, init_consts, SUITE_ID_HEADER_KEM, init_kemInterface, HPKE_VERSION, HkdfNative, HkdfSha256Native, HkdfSha384Native, HkdfSha512Native, init_hkdf, isCryptoKeyPair, init_misc, LABEL_EAE_PRK, LABEL_SHARED_SECRET, Dhkem, init_dhkem, KEM_USAGES, LABEL_DKP_PRK, LABEL_SK, init_dhkemPrimitives, Bignum, init_bignum, LABEL_CANDIDATE, ORDER_P_256, ORDER_P_384, ORDER_P_521, PKCS8_ALG_ID_P_256, PKCS8_ALG_ID_P_384, PKCS8_ALG_ID_P_521, EC_P_256_PARAMS, EC_P_384_PARAMS, EC_P_521_PARAMS, Ec, init_ec, init_xCryptoKey, init_xCurve, init_hybridkem, AEAD_USAGES, init_aeadEncryptionContext, _endianTestBuffer, _endianTestBytes, isLE, init_noble, init_hash2, _HMAC, hmac, init_hmac, init_md, U32_MASK64, _32n, init_u64, init_sha2, _0n, _1n, _2n, _7n, _256n, _0x71n, SHA3_PI, SHA3_ROTL, _SHA3_IOTA, IOTAS, SHA3_IOTA_H, SHA3_IOTA_L, init_sha3, init_modular, init_curve, init_montgomery, init_mod, AesGcmContext, Aes128Gcm, Aes256Gcm, init_aesGcm, init_exportOnly, init_emitNotSupported, LABEL_SEC, ExporterContextImpl, RecipientExporterContextImpl, SenderExporterContextImpl, init_exporterContext, EncryptionContextImpl, init_encryptionContext, __classPrivateFieldGet, __classPrivateFieldSet, _Mutex_locked, Mutex, init_mutex, __classPrivateFieldGet2, __classPrivateFieldSet2, _RecipientContextImpl_mutex, RecipientContextImpl, init_recipientContext, __classPrivateFieldGet3, __classPrivateFieldSet3, _SenderContextImpl_mutex, SenderContextImpl, init_senderContext, LABEL_BASE_NONCE, LABEL_EXP, LABEL_INFO_HASH, LABEL_KEY, LABEL_PSK_ID_HASH, LABEL_SECRET, SUITE_ID_HEADER_HPKE, CipherSuiteNative, init_cipherSuiteNative, DhkemP256HkdfSha256Native, DhkemP384HkdfSha384Native, DhkemP521HkdfSha512Native, init_dhkemNative, CipherSuite, DhkemP256HkdfSha256, DhkemP384HkdfSha384, DhkemP521HkdfSha512, HkdfSha256, HkdfSha384, HkdfSha512, init_native, ALG_NAME, PKCS8_ALG_ID_X25519, BASE_POINT_X25519, X25519, init_x25519, DhkemX25519HkdfSha256, init_dhkemX25519, PKCS8_ALG_ID_X448, init_x448, init_dhkemX448, init_mod2, init_hpke2, isLE2, wrapCipher, init_utils, encodeStr, sigma16, sigma32, sigma16_32, sigma32_32, BLOCK_LEN, BLOCK_LEN32, MAX_COUNTER, U32_EMPTY, _XorStreamPRG, createPRG, init_arx, Poly1305, poly1305, init_poly1305, chacha_exports, chacha20orig, chacha20, xchacha20, chacha8, chacha12, ZEROS16, updatePadded, ZEROS32, _poly1305_aead, chacha20poly1305, xchacha20poly1305, rngChacha20, rngChacha8, init_chacha, init_makeAead, init_makeKdfImpl, init_makeDhKem, init_makeHpke, defaultRng, init_rng, crypto2, init_cryptoNode, isLE3, swap32IfBE2, hasHexBuiltin, hexes, asciis, Hash, init_utils2, HashMD2, SHA256_IV2, SHA384_IV2, SHA512_IV2, init_md2, U32_MASK642, _32n2, shrSH2, shrSL2, rotrSH2, rotrSL2, rotrBH2, rotrBL2, rotlSH2, rotlSL2, rotlBH2, rotlBL2, add3L2, add3H2, add4L2, add4H2, add5L2, add5H2, init_u642, SHA256_K, SHA256_W, SHA256, K512, SHA512_Kh, SHA512_Kl, SHA512_W_H, SHA512_W_L, SHA512, SHA384, sha2563, sha5122, sha3842, init_sha22, _0n2, _1n2, isPosBig, bitMask, notImplemented, init_utils3, _0n3, _1n3, _2n2, _3n, _4n, _5n, _7n2, _8n, _9n, _16n, isNegativeLE, FIELD_FIELDS, init_modular2, _0n4, _1n4, pointPrecomputes, pointWindowSizes, wNAF, init_curve2, _0n5, _1n5, _2n3, _8n2, PrimeEdwardsPoint, init_edwards, os2ip, _DST_scalar, init_hash_to_curve, _0n6, _1n6, _2n4, init_montgomery2, ed25519_exports, _0n7, _1n7, _2n5, _3n2, _5n2, _8n3, ed25519_CURVE_p, ed25519_CURVE, ED25519_SQRT_M1, Fp, Fn, ed25519Defaults, ed25519, ed25519ctx, ed25519ph, x25519, ELL2_C1, ELL2_C2, ELL2_C3, ELL2_C1_EDWARDS, ed25519_hasher, SQRT_M1, SQRT_AD_MINUS_ONE, INVSQRT_A_MINUS_D, ONE_MINUS_D_SQ, D_MINUS_ONE_SQ, invertSqrt, MAX_255B, bytes255ToNumberLE, _RistrettoPoint, ristretto255, ristretto255_hasher, ED25519_TORSION_SUBGROUP, edwardsToMontgomery, RistrettoPoint, hashToCurve, encodeToCurve, hashToRistretto255, hash_to_ristretto255, init_ed25519, _0n8, _1n8, _2n6, _7n3, _256n2, _0x71n2, SHA3_PI2, SHA3_ROTL2, _SHA3_IOTA2, IOTAS2, SHA3_IOTA_H2, SHA3_IOTA_L2, rotlH, rotlL, Keccak, genShake, shake2562, init_sha32, ed448_exports, ed448_CURVE, E448_CURVE, shake256_114, shake256_64, _1n9, _2n7, _3n3, _4n2, _11n, _22n, _44n, _88n, _223n, Fp2, Fn2, Fp448, Fn448, ED448_DEF, ed448, ed448ph, E448, x448, ELL2_C12, ELL2_J, ed448_hasher, ONE_MINUS_D, ONE_MINUS_TWO_D, SQRT_MINUS_D, INVSQRT_MINUS_D, invertSqrt2, _DecafPoint, decaf448, decaf448_hasher, ED448_TORSION_SUBGROUP, DecafPoint, hashToCurve2, encodeToCurve2, hashToDecaf448, hash_to_decaf448, edwardsToMontgomery2, init_ed448, HMAC, hmac2, init_hmac2, divNearest, DERErr, DER, _0n9, _1n10, _2n8, _3n4, _4n3, init_weierstrass, init_shortw_utils, nist_exports, p256_CURVE, p384_CURVE, p521_CURVE, Fp256, Fp384, Fp521, p256, p256_hasher, p384, p384_hasher, p521, secp256r1, secp384r1, secp521r1, p521_hasher, init_nist, init_makeNobleSignatureImpl, defaultCryptoProvider, init_provider, init_getCiphersuiteImpl, init_resumption, oidNist2, init_utils4, HashMD3, SHA256_IV3, SHA384_IV3, SHA512_IV3, init_md3, U32_MASK643, _32n3, shrSH3, shrSL3, rotrSH3, rotrSL3, rotrBH3, rotrBL3, add3L3, add3H3, add4L3, add4H3, add5L3, add5H3, init_u643, SHA256_K2, SHA256_W2, SHA2_32B, _SHA256, K5122, SHA512_Kh2, SHA512_Kl2, SHA512_W_H2, SHA512_W_L2, SHA2_64B, _SHA512, _SHA384, sha2564, sha5123, sha3843, init_sha23, _HMAC2, hmac3, init_hmac3, init_makeHashImpl2, init_makeNobleSignatureImpl2, BLOCK_SIZE, ZEROS162, ZEROS322, POLY, mul2, swapLE, estimateWindow, GHASH, Polyval, ghash, polyval, init_polyval, BLOCK_SIZE2, BLOCK_SIZE32, EMPTY_BLOCK, POLY2, sbox, rotr32_8, rotl32_8, tableEncoding, xPowers, gcm, _CMAC, cmac, init_aes, init_makeAead2, init_makeKdfImpl2, init_makeDhKem2, init_makeHpke2, init_rng2, nobleCryptoProvider, init_provider2, mlsPublicMessageEncoder, encodeMlsPublicMessage, mlsWelcomeEncoder, encodeMlsWelcome, mlsPrivateMessageEncoder, encodeMlsPrivateMessage, mlsGroupInfoEncoder, encodeMlsGroupInfo, mlsKeyPackageEncoder, encodeMlsKeyPackage, mlsMessageContentEncoder, encodeMlsMessageContent, decodeMlsMessageContent, mlsMessageEncoder, encodeMlsMessage, decodeMlsMessage, init_message, greaseValues, defaultGreaseConfig, init_grease, init_defaultCapabilities, init_src, DEFAULT_CIPHERSUITE, MLSGroupManager, init_mls_group, DOMAIN, NONCE_BYTES2, init_owner_sync, init_mls_delivery_order, init_mls_presend_epoch, dist_exports2, init_dist2, DIR_MODE, FILE_MODE, init_secure_file, SYNC_LOCK_STALE_MS, pendingKpPath, init_mls_state, init_mls_kp_pool, PACKAGE, FALLBACK, MAX_LEN, DISALLOWED, init_client_version, CredentialStore, init_credential_store, init_crypto_helpers, STATE_FILE, LEGACY_STATE_FILE, init_state, init_transport2, A2ALogGate, init_a2a_log_gate, init_mls_membership_probe, openclaw_compat_exports, defaultImporter, HEARTBEAT_CANDIDATES, AGENT_EVENT_CANDIDATES, TRANSCRIPT_CANDIDATES, _heartbeatFn, _agentEventFn, _transcriptFn, init_openclaw_compat, http_handlers_exports, init_http_handlers, mcp_proxy_helpers_exports, init_mcp_proxy_helpers, workspace_handlers_exports, MAX_FILE_SIZE, init_workspace_handlers, ROOM_AGENT_TYPES, CREDENTIAL_MESSAGE_TYPES, CATCHUP_MESSAGE_TYPES, POLL_INTERVAL_MS, RECONNECT_BASE_MS, RECONNECT_MAX_MS, PENDING_POLL_INTERVAL_MS, OUTBOUND_DEDUPE_WINDOW_MS, OUTBOUND_DEDUPE_MAX_SUPPRESS_MS, OUTBOUND_DEDUPE_MAX, OUTBOUND_DEDUPE_TYPES, RETRY_CANDIDATE_TTL_MS, RETRY_CANDIDATE_MAX, ROOM_FULFIL_COOLDOWN_MS, SecureChannel, init_channel, init_types, DEFAULT_API_URL, DEFAULT_HTTP_PORT, init_account_config, init_lifecycle, _ocRuntime, _channels, agentVaultPlugin, init_openclaw_plugin, init_gateway_send, MENTION_RE, BROADCAST_NAMES, init_room_protocol, init_llm_response_parser, init_llm_cost, traceStore, init_fetch_interceptor, init_tool_audit, init_harness_compat, isUsingManagedRoutes, init_openclaw_entry, util, objectUtil, ZodParsedType, getParsedType, init_util, ZodIssueCode, ZodError, init_ZodError, errorMap, en_default, init_en, overrideErrorMap, init_errors2, makeIssue, ParseStatus, INVALID, DIRTY, OK, isAborted, isDirty, isValid, isAsync, init_parseUtil, init_typeAliases, errorUtil, init_errorUtil, ParseInputLazyPath, handleResult, ZodType, cuidRegex, cuid2Regex, ulidRegex, uuidRegex, nanoidRegex, jwtRegex, durationRegex, emailRegex, _emojiRegex, emojiRegex, ipv4Regex, ipv4CidrRegex, ipv6Regex, ipv6CidrRegex, base64Regex, base64urlRegex, dateRegexSource, dateRegex, ZodString, ZodNumber, ZodBigInt, ZodBoolean, ZodDate, ZodSymbol, ZodUndefined, ZodNull, ZodAny, ZodUnknown, ZodNever, ZodVoid, ZodArray, ZodObject, ZodUnion, getDiscriminator, ZodDiscriminatedUnion, ZodIntersection, ZodTuple, ZodRecord, ZodMap, ZodSet, ZodFunction, ZodLazy, ZodLiteral, ZodEnum, ZodNativeEnum, ZodPromise, ZodEffects, ZodOptional, ZodNullable, ZodDefault, ZodCatch, ZodNaN, ZodBranded, ZodPipeline, ZodReadonly, late, ZodFirstPartyTypeKind, stringType, numberType, nanType, bigIntType, booleanType, dateType, symbolType, undefinedType, nullType, anyType, unknownType, neverType, voidType, arrayType, objectType, strictObjectType, unionType, discriminatedUnionType, intersectionType, tupleType, recordType, mapType, setType, functionType, lazyType, literalType, enumType, nativeEnumType, promiseType, effectsType, optionalType, nullableType, preprocessType, pipelineType, init_types2, init_external, init_v3, NEVER, $ZodAsyncError, $ZodEncodeError, globalConfig, init_core, util_exports, EVALUATING, captureStackTrace, allowsEval, getParsedType2, propertyKeyTypes, primitiveTypes, NUMBER_FORMAT_RANGES, BIGINT_FORMAT_RANGES, Class, init_util2, initializer, $ZodError, $ZodRealError, init_errors3, _parse, parse, _parseAsync, parseAsync, _safeParse, safeParse, _safeParseAsync, safeParseAsync, _encode, _decode, _encodeAsync, _decodeAsync, _safeEncode, _safeDecode, _safeEncodeAsync, _safeDecodeAsync, init_parse, regexes_exports, cuid, cuid2, ulid, xid, ksuid, nanoid, duration, extendedDuration, guid, uuid, uuid4, uuid6, uuid7, email, html5Email, rfc5322Email, unicodeEmail, idnEmail, browserEmail, _emoji, ipv4, ipv6, mac, cidrv4, cidrv6, base64, base64url, hostname, domain, e164, dateSource, date, string, bigint, integer, number, boolean, _null, _undefined, lowercase, uppercase, hex, md5_hex, md5_base64, md5_base64url, sha1_hex, sha1_base64, sha1_base64url, sha256_hex, sha256_base64, sha256_base64url, sha384_hex, sha384_base64, sha384_base64url, sha512_hex, sha512_base64, sha512_base64url, init_regexes, $ZodCheck, numericOriginMap, $ZodCheckLessThan, $ZodCheckGreaterThan, $ZodCheckMultipleOf, $ZodCheckNumberFormat, $ZodCheckBigIntFormat, $ZodCheckMaxSize, $ZodCheckMinSize, $ZodCheckSizeEquals, $ZodCheckMaxLength, $ZodCheckMinLength, $ZodCheckLengthEquals, $ZodCheckStringFormat, $ZodCheckRegex, $ZodCheckLowerCase, $ZodCheckUpperCase, $ZodCheckIncludes, $ZodCheckStartsWith, $ZodCheckEndsWith, $ZodCheckProperty, $ZodCheckMimeType, $ZodCheckOverwrite, init_checks, Doc, init_doc, version, init_versions, $ZodType, $ZodString, $ZodStringFormat, $ZodGUID, $ZodUUID, $ZodEmail, $ZodURL, $ZodEmoji, $ZodNanoID, $ZodCUID, $ZodCUID2, $ZodULID, $ZodXID, $ZodKSUID, $ZodISODateTime, $ZodISODate, $ZodISOTime, $ZodISODuration, $ZodIPv4, $ZodIPv6, $ZodMAC, $ZodCIDRv4, $ZodCIDRv6, $ZodBase64, $ZodBase64URL, $ZodE164, $ZodJWT, $ZodCustomStringFormat, $ZodNumber, $ZodNumberFormat, $ZodBoolean, $ZodBigInt, $ZodBigIntFormat, $ZodSymbol, $ZodUndefined, $ZodNull, $ZodAny, $ZodUnknown, $ZodNever, $ZodVoid, $ZodDate, $ZodArray, $ZodObject, $ZodObjectJIT, $ZodUnion, $ZodXor, $ZodDiscriminatedUnion, $ZodIntersection, $ZodTuple, $ZodRecord, $ZodMap, $ZodSet, $ZodEnum, $ZodLiteral, $ZodFile, $ZodTransform, $ZodOptional, $ZodExactOptional, $ZodNullable, $ZodDefault, $ZodPrefault, $ZodNonOptional, $ZodSuccess, $ZodCatch, $ZodNaN, $ZodPipe, $ZodCodec, $ZodReadonly, $ZodTemplateLiteral, $ZodFunction, $ZodPromise, $ZodLazy, $ZodCustom, init_schemas, init_ar, init_az, init_be, init_bg, init_ca, init_cs, init_da, init_de, error, init_en2, init_eo, init_es, init_fa, init_fi, init_fr, init_fr_CA, init_he, init_hu, init_hy, init_id, init_is, init_it, init_ja, init_ka, init_km, init_kh, init_ko, init_lt, init_mk, init_ms, init_nl, init_no, init_ota, init_ps, init_pl, init_pt, init_ru, init_sl, init_sv, init_ta, init_th, init_tr, init_uk, init_ua, init_ur, init_uz, init_vi, init_zh_CN, init_zh_TW, init_yo, init_locales, _a2, $ZodRegistry, globalRegistry, init_registries, init_api, createToJSONSchemaMethod, createStandardJSONSchemaMethod, init_to_json_schema, formatMap, stringProcessor, numberProcessor, booleanProcessor, bigintProcessor, symbolProcessor, nullProcessor, undefinedProcessor, voidProcessor, neverProcessor, anyProcessor, unknownProcessor, dateProcessor, enumProcessor, literalProcessor, nanProcessor, templateLiteralProcessor, fileProcessor, successProcessor, customProcessor, functionProcessor, transformProcessor, mapProcessor, setProcessor, arrayProcessor, objectProcessor, unionProcessor, intersectionProcessor, tupleProcessor, recordProcessor, nullableProcessor, nonoptionalProcessor, defaultProcessor, prefaultProcessor, catchProcessor, pipeProcessor, readonlyProcessor, promiseProcessor, optionalProcessor, lazyProcessor, allProcessors, init_json_schema_processors, init_json_schema_generator, init_json_schema, init_core2, init_parse2, ZodMiniType, ZodMiniObject, init_schemas2, init_checks2, init_iso, init_coerce, init_external2, init_v4_mini, init_zod_compat, checks_exports2, init_checks3, iso_exports2, ZodISODateTime, ZodISODate, ZodISOTime, ZodISODuration, init_iso2, initializer2, ZodError2, ZodRealError, init_errors4, parse2, parseAsync2, safeParse3, safeParseAsync3, encode3, decode2, encodeAsync2, decodeAsync2, safeEncode2, safeDecode2, safeEncodeAsync2, safeDecodeAsync2, init_parse3, schemas_exports3, ZodType2, _ZodString, ZodString2, ZodStringFormat, ZodEmail, ZodGUID, ZodUUID, ZodURL, ZodEmoji, ZodNanoID, ZodCUID, ZodCUID2, ZodULID, ZodXID, ZodKSUID, ZodIPv4, ZodMAC, ZodIPv6, ZodCIDRv4, ZodCIDRv6, ZodBase64, ZodBase64URL, ZodE164, ZodJWT, ZodCustomStringFormat, ZodNumber2, ZodNumberFormat, ZodBoolean2, ZodBigInt2, ZodBigIntFormat, ZodSymbol2, ZodUndefined2, ZodNull2, ZodAny2, ZodUnknown2, ZodNever2, ZodVoid2, ZodDate2, ZodArray2, ZodObject2, ZodUnion2, ZodXor, ZodDiscriminatedUnion2, ZodIntersection2, ZodTuple2, ZodRecord2, ZodMap2, ZodSet2, ZodEnum2, ZodLiteral2, ZodFile, ZodTransform, ZodOptional2, ZodExactOptional, ZodNullable2, ZodDefault2, ZodPrefault, ZodNonOptional, ZodSuccess, ZodCatch2, ZodNaN2, ZodPipe, ZodCodec, ZodReadonly2, ZodTemplateLiteral, ZodLazy2, ZodPromise2, ZodFunction2, ZodCustom, describe2, meta2, stringbool, init_schemas3, ZodFirstPartyTypeKind2, init_compat, z2, init_from_json_schema, init_coerce2, init_external3, init_classic, init_v4, LATEST_PROTOCOL_VERSION, DEFAULT_NEGOTIATED_PROTOCOL_VERSION, SUPPORTED_PROTOCOL_VERSIONS, RELATED_TASK_META_KEY, JSONRPC_VERSION, AssertObjectSchema, ProgressTokenSchema, CursorSchema, TaskCreationParamsSchema, TaskMetadataSchema, RelatedTaskMetadataSchema, RequestMetaSchema, BaseRequestParamsSchema, TaskAugmentedRequestParamsSchema, isTaskAugmentedRequestParams, RequestSchema, NotificationsParamsSchema, NotificationSchema, ResultSchema, RequestIdSchema, JSONRPCRequestSchema, isJSONRPCRequest, JSONRPCNotificationSchema, isJSONRPCNotification, JSONRPCResultResponseSchema, isJSONRPCResultResponse, ErrorCode, JSONRPCErrorResponseSchema, isJSONRPCErrorResponse, JSONRPCMessageSchema, JSONRPCResponseSchema, EmptyResultSchema, CancelledNotificationParamsSchema, CancelledNotificationSchema, IconSchema, IconsSchema, BaseMetadataSchema, ImplementationSchema, FormElicitationCapabilitySchema, ElicitationCapabilitySchema, ClientTasksCapabilitySchema, ServerTasksCapabilitySchema, ClientCapabilitiesSchema, InitializeRequestParamsSchema, InitializeRequestSchema, isInitializeRequest, ServerCapabilitiesSchema, InitializeResultSchema, InitializedNotificationSchema, PingRequestSchema, ProgressSchema, ProgressNotificationParamsSchema, ProgressNotificationSchema, PaginatedRequestParamsSchema, PaginatedRequestSchema, PaginatedResultSchema, TaskStatusSchema, TaskSchema, CreateTaskResultSchema, TaskStatusNotificationParamsSchema, TaskStatusNotificationSchema, GetTaskRequestSchema, GetTaskResultSchema, GetTaskPayloadRequestSchema, GetTaskPayloadResultSchema, ListTasksRequestSchema, ListTasksResultSchema, CancelTaskRequestSchema, CancelTaskResultSchema, ResourceContentsSchema, TextResourceContentsSchema, Base64Schema, BlobResourceContentsSchema, RoleSchema, AnnotationsSchema, ResourceSchema, ResourceTemplateSchema, ListResourcesRequestSchema, ListResourcesResultSchema, ListResourceTemplatesRequestSchema, ListResourceTemplatesResultSchema, ResourceRequestParamsSchema, ReadResourceRequestParamsSchema, ReadResourceRequestSchema, ReadResourceResultSchema, ResourceListChangedNotificationSchema, SubscribeRequestParamsSchema, SubscribeRequestSchema, UnsubscribeRequestParamsSchema, UnsubscribeRequestSchema, ResourceUpdatedNotificationParamsSchema, ResourceUpdatedNotificationSchema, PromptArgumentSchema, PromptSchema, ListPromptsRequestSchema, ListPromptsResultSchema, GetPromptRequestParamsSchema, GetPromptRequestSchema, TextContentSchema, ImageContentSchema, AudioContentSchema, ToolUseContentSchema, EmbeddedResourceSchema, ResourceLinkSchema, ContentBlockSchema, PromptMessageSchema, GetPromptResultSchema, PromptListChangedNotificationSchema, ToolAnnotationsSchema, ToolExecutionSchema, ToolSchema, ListToolsRequestSchema, ListToolsResultSchema, CallToolResultSchema, CompatibilityCallToolResultSchema, CallToolRequestParamsSchema, CallToolRequestSchema, ToolListChangedNotificationSchema, ListChangedOptionsBaseSchema, LoggingLevelSchema, SetLevelRequestParamsSchema, SetLevelRequestSchema, LoggingMessageNotificationParamsSchema, LoggingMessageNotificationSchema, ModelHintSchema, ModelPreferencesSchema, ToolChoiceSchema, ToolResultContentSchema, SamplingContentSchema, SamplingMessageContentBlockSchema, SamplingMessageSchema, CreateMessageRequestParamsSchema, CreateMessageRequestSchema, CreateMessageResultSchema, CreateMessageResultWithToolsSchema, BooleanSchemaSchema, StringSchemaSchema, NumberSchemaSchema, UntitledSingleSelectEnumSchemaSchema, TitledSingleSelectEnumSchemaSchema, LegacyTitledEnumSchemaSchema, SingleSelectEnumSchemaSchema, UntitledMultiSelectEnumSchemaSchema, TitledMultiSelectEnumSchemaSchema, MultiSelectEnumSchemaSchema, EnumSchemaSchema, PrimitiveSchemaDefinitionSchema, ElicitRequestFormParamsSchema, ElicitRequestURLParamsSchema, ElicitRequestParamsSchema, ElicitRequestSchema, ElicitationCompleteNotificationParamsSchema, ElicitationCompleteNotificationSchema, ElicitResultSchema, ResourceTemplateReferenceSchema, PromptReferenceSchema, CompleteRequestParamsSchema, CompleteRequestSchema, CompleteResultSchema, RootSchema, ListRootsRequestSchema, ListRootsResultSchema, RootsListChangedNotificationSchema, ClientRequestSchema, ClientNotificationSchema, ClientResultSchema, ServerRequestSchema, ServerNotificationSchema, ServerResultSchema, McpError, UrlElicitationRequiredError, init_types3, init_interfaces, ignoreOverride, defaultOptions, getDefaultOptions, init_Options, getRefs, init_Refs, init_errorMessages, getRelativePath, init_getRelativePath, init_any, init_array2, init_bigint, init_boolean, init_branded, parseCatchDef, init_catch, integerDateParser, init_date, init_default, init_effects, init_enum, isJsonSchema7AllOfType, init_intersection, init_literal, emojiRegex2, zodPatterns, ALPHA_NUMERIC, init_string2, init_record, init_map, init_nativeEnum, init_never, init_null, primitiveMappings, asAnyOf, init_union, init_nullable, init_number2, init_object, parseOptionalDef, init_optional2, parsePipelineDef, init_pipeline, init_promise, init_set, init_tuple, init_undefined, init_unknown, parseReadonlyDef, init_readonly, selectParser, init_selectParser, get$ref, addMeta, init_parseDef, init_parseTypes, zodToJsonSchema, init_zodToJsonSchema, init_esm, init_zod_json_schema_compat, DEFAULT_REQUEST_TIMEOUT_MSEC, Protocol, init_protocol, require_code, require_scope, require_codegen, require_util, require_names, require_errors, require_boolSchema, require_rules, require_applicability, require_dataType, require_defaults, require_code2, require_keyword, require_subschema, require_fast_deep_equal, require_json_schema_traverse, require_resolve, require_validate, require_validation_error, require_ref_error, require_compile, require_data, require_utils, require_schemes, require_fast_uri, require_uri, require_core, require_id, require_ref, require_core2, require_limitNumber, require_multipleOf, require_ucs2length, require_limitLength, require_pattern, require_limitProperties, require_required, require_limitItems, require_equal, require_uniqueItems, require_const, require_enum, require_validation, require_additionalItems, require_items, require_prefixItems, require_items2020, require_contains, require_dependencies, require_propertyNames, require_additionalProperties, require_properties, require_patternProperties, require_not, require_anyOf, require_oneOf, require_allOf, require_if, require_thenElse, require_applicator, require_format, require_format2, require_metadata, require_draft7, require_types, require_discriminator, require_json_schema_draft_07, require_ajv, require_formats, require_limit, require_dist, import_ajv, import_ajv_formats, AjvJsonSchemaValidator, init_ajv_provider, ExperimentalServerTasks, init_server, init_helpers, Server, init_server2, COMPLETABLE_SYMBOL, McpZodTypeKind, init_completable, init_uriTemplate, TOOL_NAME_REGEX, init_toolNameValidation, ExperimentalMcpServerTasks, init_mcp_server, init_zod, McpServer, EMPTY_OBJECT_JSON_SCHEMA, EMPTY_COMPLETION_RESULT, init_mcp, RequestError, toRequestError, GlobalRequest, Request2, newHeadersFromIncoming, wrapBodyStream, newRequestFromIncoming, getRequestCache, requestCache, incomingKey, urlKey, headersKey, abortControllerKey, getAbortController, requestPrototype, newRequest, responseCache, getResponseCache, cacheKey, GlobalResponse, Response2, buildOutgoingHttpHeaders, X_ALREADY_SENT, outgoingEnded, handleRequestError, handleFetchError, handleResponseError, flushHeaders, responseViaCache, isPromise, responseViaResponseObject, getRequestListener, init_dist22, WebStandardStreamableHTTPServerTransport, init_webStandardStreamableHttp, StreamableHTTPServerTransport, init_streamableHttp, AgentVaultMcpServer, init_mcp_server2, init_mcp_handlers, init_skill_manifest, init_skill_invoker, spanBuffer, init_skill_telemetry, PolicyEnforcer, init_policy_enforcer, VERSION, init_index;
17059
+ var __create, __defProp2, __getOwnPropDesc, __getOwnPropNames2, __getProtoOf, __hasOwnProp, __esm2, __commonJS, __export2, __copyProps, __toESM, SENTINEL, WRAPPERS, INSTRUCTION, init_abstention, init_mls_commit_classify, MENTION_RE, BROADCAST_NAMES, init_room_protocol, __filename, __dirname, url2, path, Module, Module, root, window_, crypto_, randomValuesStandard, crypto4, randomValueNodeJS, _Module, libsodium_sumo_default, init_libsodium_sumo, a, r, t, c, libsodium_wrappers_default, init_libsodium_wrappers, init_keys, init_x3dh, MAX_SKIP, CHAIN_KEY_SEED, MSG_KEY_SEED, HEADER_KEY_SEED, DoubleRatchet, init_ratchet, init_file_crypto, DOMAIN_DID_DOCUMENT, DOMAIN_TRANSFER_INTENT, DOMAIN_TRANSFER_ACCEPT, BASE58_ALPHABET, init_did, BUILTIN_PATTERNS, ScanEngine, init_scan_engine, init_merkle, VC_CONTEXT_V2, AV_CREDENTIAL_CONTEXT, CRYPTOSUITE, BASE58_ALPHABET2, init_vc, init_transport, init_telemetry, TelemetryReporter, init_telemetry_reporter, CROCKFORD, ARGON2_OPSLIMIT, ARGON2_MEMLIMIT, SALT_BYTES, NONCE_BYTES, KEY_BYTES, BACKUP_VERSION, MAX_BACKUP_SIZE, init_backup, init_approval, encVoid, init_tlsEncoder, uint8Encoder, encodeUint8, decodeUint8, uint16Encoder, encodeUint16, decodeUint16, uint32Encoder, encodeUint32, decodeUint32, uint64Encoder, encodeUint64, decodeUint64, init_number, init_tlsDecoder, init_enumHelpers, defaultProposalTypes, defaultProposalTypeEncoder, encodeDefaultProposalType, decodeDefaultProposalType, init_defaultProposalType, defaultExtensionTypes, defaultExtensionTypeEncoder, encodeDefaultExtensionType, decodeDefaultExtensionType, init_defaultExtensionType, acceptAll, init_incomingMessageAction, MlsError, ValidationError, CodecError, UsageError, DependencyError, CryptoVerificationError, CryptoError, InternalError, init_mlsError, init_byteArray, varLenDataEncoder, decodeVarLenData, init_variableLength, init_constantTimeCompare, extensionTypeEncoder, encodeExtensionType, decodeExtensionType, extensionEncoder, encodeExtension, decodeExtension, init_extension, credentialTypes, credentialTypeEncoder, encodeCredentialType, decodeCredentialType, init_credentialType, credentialBasicEncoder, encodeCredentialBasic, credentialX509Encoder, encodeCredentialX509, credentialCustomEncoder, encodeCredentialCustom, credentialEncoder, encodeCredential, decodeCredentialBasic, decodeCredentialX509, decodeCredential, init_credential, externalSenderEncoder, encodeExternalSender, decodeExternalSender, init_externalSender, init_hash, init_optional, ciphersuites, ciphersuiteEncoder, encodeCiphersuite, decodeCiphersuite, ciphersuiteValues, init_ciphersuite, init_signature, protocolVersions, protocolVersionEncoder, encodeProtocolVersion, decodeProtocolVersion, init_protocolVersion, capabilitiesEncoder, encodeCapabilities, decodeCapabilities, init_capabilities, leafNodeSources, leafNodeSourceEncoder, encodeLeafNodeSource, decodeLeafNodeSource, init_leafNodeSource, lifetimeEncoder, encodeLifetime, decodeLifetime, defaultLifetime, init_lifetime, leafNodeDataEncoder, encodeLeafNodeData, decodeLeafNodeData, leafNodeInfoKeyPackageEncoder, encodeLeafNodeInfoKeyPackage, leafNodeInfoUpdateOmittedEncoder, encodeLeafNodeInfoUpdateOmitted, leafNodeInfoCommitOmittedEncoder, encodeLeafNodeInfoCommitOmitted, leafNodeInfoOmittedEncoder, encodeLeafNodeInfoOmitted, decodeLeafNodeInfoKeyPackage, decodeLeafNodeInfoUpdateOmitted, decodeLeafNodeInfoCommitOmitted, decodeLeafNodeInfoOmitted, leafNodeInfoUpdateEncoder, encodeLeafNodeInfoUpdate, leafNodeInfoCommitEncoder, encodeLeafNodeInfoCommit, leafNodeInfoEncoder, encodeLeafNodeInfo, decodeLeafNodeInfoUpdate, decodeLeafNodeInfoCommit, decodeLeafNodeInfo, leafNodeTBSEncoder, encodeLeafNodeTBS, leafNodeEncoder, encodeLeafNode, decodeLeafNode, decodeLeafNodeKeyPackage, decodeLeafNodeCommit, decodeLeafNodeUpdate, init_leafNode, keyPackageTBSEncoder, encodeKeyPackageTBS, decodeKeyPackageTBS, keyPackageEncoder, encodeKeyPackage, decodeKeyPackage, init_keyPackage, init_kdf, pskTypes, pskTypeEncoder, encodePskType, decodePskType, resumptionPSKUsages, resumptionPSKUsageEncoder, encodeResumptionPSKUsage, decodeResumptionPSKUsage, encodePskInfoExternal, encodePskInfoResumption, decodePskInfoResumption, pskInfoEncoder, encodePskInfo, decodePskInfo, pskIdEncoder, encodePskId, decodePskId, pskLabelEncoder, encodePskLabel, decodePskLabel, init_presharedkey, addEncoder, encodeAdd, decodeAdd, updateEncoder, encodeUpdate, decodeUpdate, removeEncoder, encodeRemove, decodeRemove, pskEncoder, encodePSK, decodePSK, reinitEncoder, encodeReinit, decodeReinit, externalInitEncoder, encodeExternalInit, decodeExternalInit, groupContextExtensionsEncoder, encodeGroupContextExtensions, decodeGroupContextExtensions, proposalAddEncoder, encodeProposalAdd, proposalUpdateEncoder, encodeProposalUpdate, proposalRemoveEncoder, encodeProposalRemove, proposalPSKEncoder, encodeProposalPSK, proposalReinitEncoder, encodeProposalReinit, proposalExternalInitEncoder, encodeProposalExternalInit, proposalGroupContextExtensionsEncoder, encodeProposalGroupContextExtensions, proposalCustomEncoder, encodeProposalCustom, proposalEncoder, encodeProposal, decodeProposalAdd, decodeProposalUpdate, decodeProposalRemove, decodeProposalPSK, decodeProposalReinit, decodeProposalExternalInit, decodeProposalGroupContextExtensions, decodeProposal, init_proposal, proposalOrRefTypes, proposalOrRefTypeEncoder, encodeProposalOrRefType, decodeProposalOrRefType, proposalOrRefProposalEncoder, encodeProposalOrRefProposal, proposalOrRefProposalRefEncoder, encodeProposalOrRefProposalRef, proposalOrRefEncoder, encodeProposalOrRef, decodeProposalOrRef, init_proposalOrRefType, init_hpke, groupContextEncoder, encodeGroupContext, decodeGroupContext, init_groupContext, nodeTypes, nodeTypeEncoder, encodeNodeType, decodeNodeType, init_nodeType, parentNodeEncoder, encodeParentNode, decodeParentNode, init_parentNode, init_treemath, nodeEncoder, encodeNode, decodeNode, ratchetTreeEncoder, encodeRatchetTree, decodeRatchetTree, init_ratchetTree, leafNodeHashInputEncoder, encodeLeafNodeHashInput, decodeLeafNodeHashInput, parentNodeHashInputEncoder, encodeParentNodeHashInput, decodeParentNodeHashInput, treeHashInputEncoder, encodeTreeHashInput, decodeTreeHashInput, init_treeHash, parentHashInputEncoder, encodeParentHashInput, decodeParentHashInput, init_parentHash, hpkeCiphertextEncoder, encodeHpkeCiphertext, decodeHpkeCiphertext, init_hpkeCiphertext, updatePathNodeEncoder, encodeUpdatePathNode, decodeUpdatePathNode, updatePathEncoder, encodeUpdatePath, decodeUpdatePath, init_updatePath, commitEncoder, encodeCommit, decodeCommit, init_commit, contentTypes, contentTypeEncoder, encodeContentType, decodeContentType, init_contentType, wireformats, wireformatEncoder, encodeWireformat, decodeWireformat, init_wireformat, senderTypes, senderTypeEncoder, encodeSenderType, decodeSenderType, senderEncoder, encodeSender, decodeSender, reuseGuardEncoder, encodeReuseGuard, decodeReuseGuard, senderDataEncoder, encodeSenderData, decodeSenderData, senderDataAADEncoder, encodeSenderDataAAD, decodeSenderDataAAD, init_sender, framedContentApplicationDataEncoder, encodeFramedContentApplicationData, framedContentProposalDataEncoder, encodeFramedContentProposalData, framedContentCommitDataEncoder, encodeFramedContentCommitData, framedContentInfoEncoder, encodeFramedContentInfo, decodeFramedContentApplicationData, decodeFramedContentProposalData, decodeFramedContentCommitData, decodeFramedContentInfo, framedContentEncoder, encodeFramedContent, decodeFramedContent, senderInfoEncoder, encodeSenderInfo, framedContentTBSEncoder, encodeFramedContentTBS, encodeFramedContentAuthDataContent, encodeFramedContentAuthDataCommit, framedContentAuthDataEncoder, encodeFramedContentAuthData, decodeFramedContentAuthDataCommit, init_framedContent, authenticatedContentEncoder, encodeAuthenticatedContent, decodeAuthenticatedContent, authenticatedContentTBMEncoder, encodeAuthenticatedContentTBM, init_authenticatedContent, publicMessageInfoEncoder, encodePublicMessageInfo, publicMessageEncoder, encodePublicMessage, decodePublicMessage, init_publicMessage, init_messageProtectionPublic, init_externalProposal, requiredCapabilitiesEncoder, encodeRequiredCapabilities, decodeRequiredCapabilities, init_requiredCapabilities, defaultAuthenticationService, init_authenticationService, defaultPaddingConfig, init_paddingConfig, defaultKeyPackageEqualityConfig, init_keyPackageEqualityConfig, defaultLifetimeConfig, init_lifetimeConfig, defaultKeyRetentionConfig, init_keyRetentionConfig, groupInfoTBSEncoder, encodeGroupInfoTBS, decodeGroupInfoTBS, groupInfoEncoder, encodeGroupInfo, decodeGroupInfo, init_groupInfo, keyScheduleEncoder, encodeKeySchedule, decodeKeySchedule, init_keySchedule, generationSecretEncoder, decodeGenerationSecret, secretTreeNodeEncoder, decodeSecretTreeNode, secretTreeEncoder, decodeSecretTree, init_secretTree, confirmedTranscriptHashInputEncoder, encodeConfirmedTranscriptHashInput, decodeConfirmedTranscriptHashInput, init_transcriptHash, groupSecretsEncoder, encodeGroupSecrets, decodeGroupSecrets, init_groupSecrets, encryptedGroupSecretsEncoder, encodeEncryptedGroupSecrets, decodeEncryptedGroupSecrets, welcomeEncoder, encodeWelcome, decodeWelcome, init_welcome, init_pathSecrets, privateKeyPathEncoder, decodePrivateKeyPath, init_privateKeyPath, proposalWithSenderEncoder, decodeProposalWithSender, unappliedProposalsEncoder, decodeUnappliedProposals, init_unappliedProposals, emptyPskIndex, init_pskIndex, init_addToMap, defaultClientConfig, init_clientConfig, init_array, stringEncoder, encodeString, decodeString, init_string, activeEncoder, suspendedPendingReinitEncoder, removedFromGroupEncoder, groupActiveStateEncoder, decodeGroupActiveState, init_groupActiveState, epochReceiverDataEncoder, decodeEpochReceiverData, init_epochReceiverData, groupStateEncoder, encodeGroupState, decodeGroupState, groupStateEncoderWithoutTree, encodeGroupStateWithoutTree, emptyProposals, init_clientState, privateMessageEncoder, encodePrivateMessage, decodePrivateMessage, privateContentAADEncoder, encodePrivateContentAAD, decodePrivateContentAAD, init_privateMessage, init_messageProtection, init_createMessage, init_createCommit, init_processMessages, init_makeHashImpl, HpkeError, InvalidParamError, SerializeError, DeserializeError, EncapError, DecapError, ExportError, SealError, OpenError, MessageLimitReachedError, DeriveKeyPairError, NotSupportedError, init_errors, dntGlobals, dntGlobalThis, init_dnt_shims, NativeAlgorithm, init_algorithm, Mode, KemId, KdfId, AeadId, init_identifiers, INPUT_LENGTH_LIMIT, INFO_LENGTH_LIMIT, MINIMUM_PSK_LENGTH, EMPTY, BYTE_TO_BIGINT_256, init_consts, SUITE_ID_HEADER_KEM, init_kemInterface, HPKE_VERSION, HkdfNative, HkdfSha256Native, HkdfSha384Native, HkdfSha512Native, init_hkdf, isCryptoKeyPair, init_misc, LABEL_EAE_PRK, LABEL_SHARED_SECRET, Dhkem, init_dhkem, KEM_USAGES, LABEL_DKP_PRK, LABEL_SK, init_dhkemPrimitives, Bignum, init_bignum, LABEL_CANDIDATE, ORDER_P_256, ORDER_P_384, ORDER_P_521, PKCS8_ALG_ID_P_256, PKCS8_ALG_ID_P_384, PKCS8_ALG_ID_P_521, EC_P_256_PARAMS, EC_P_384_PARAMS, EC_P_521_PARAMS, Ec, init_ec, init_xCryptoKey, init_xCurve, init_hybridkem, AEAD_USAGES, init_aeadEncryptionContext, _endianTestBuffer, _endianTestBytes, isLE, init_noble, init_hash2, _HMAC, hmac, init_hmac, init_md, U32_MASK64, _32n, init_u64, init_sha2, _0n, _1n, _2n, _7n, _256n, _0x71n, SHA3_PI, SHA3_ROTL, _SHA3_IOTA, IOTAS, SHA3_IOTA_H, SHA3_IOTA_L, init_sha3, init_modular, init_curve, init_montgomery, init_mod, AesGcmContext, Aes128Gcm, Aes256Gcm, init_aesGcm, init_exportOnly, init_emitNotSupported, LABEL_SEC, ExporterContextImpl, RecipientExporterContextImpl, SenderExporterContextImpl, init_exporterContext, EncryptionContextImpl, init_encryptionContext, __classPrivateFieldGet, __classPrivateFieldSet, _Mutex_locked, Mutex, init_mutex, __classPrivateFieldGet2, __classPrivateFieldSet2, _RecipientContextImpl_mutex, RecipientContextImpl, init_recipientContext, __classPrivateFieldGet3, __classPrivateFieldSet3, _SenderContextImpl_mutex, SenderContextImpl, init_senderContext, LABEL_BASE_NONCE, LABEL_EXP, LABEL_INFO_HASH, LABEL_KEY, LABEL_PSK_ID_HASH, LABEL_SECRET, SUITE_ID_HEADER_HPKE, CipherSuiteNative, init_cipherSuiteNative, DhkemP256HkdfSha256Native, DhkemP384HkdfSha384Native, DhkemP521HkdfSha512Native, init_dhkemNative, CipherSuite, DhkemP256HkdfSha256, DhkemP384HkdfSha384, DhkemP521HkdfSha512, HkdfSha256, HkdfSha384, HkdfSha512, init_native, ALG_NAME, PKCS8_ALG_ID_X25519, BASE_POINT_X25519, X25519, init_x25519, DhkemX25519HkdfSha256, init_dhkemX25519, PKCS8_ALG_ID_X448, init_x448, init_dhkemX448, init_mod2, init_hpke2, isLE2, wrapCipher, init_utils, encodeStr, sigma16, sigma32, sigma16_32, sigma32_32, BLOCK_LEN, BLOCK_LEN32, MAX_COUNTER, U32_EMPTY, _XorStreamPRG, createPRG, init_arx, Poly1305, poly1305, init_poly1305, chacha_exports, chacha20orig, chacha20, xchacha20, chacha8, chacha12, ZEROS16, updatePadded, ZEROS32, _poly1305_aead, chacha20poly1305, xchacha20poly1305, rngChacha20, rngChacha8, init_chacha, init_makeAead, init_makeKdfImpl, init_makeDhKem, init_makeHpke, defaultRng, init_rng, crypto2, init_cryptoNode, isLE3, swap32IfBE2, hasHexBuiltin, hexes, asciis, Hash, init_utils2, HashMD2, SHA256_IV2, SHA384_IV2, SHA512_IV2, init_md2, U32_MASK642, _32n2, shrSH2, shrSL2, rotrSH2, rotrSL2, rotrBH2, rotrBL2, rotlSH2, rotlSL2, rotlBH2, rotlBL2, add3L2, add3H2, add4L2, add4H2, add5L2, add5H2, init_u642, SHA256_K, SHA256_W, SHA256, K512, SHA512_Kh, SHA512_Kl, SHA512_W_H, SHA512_W_L, SHA512, SHA384, sha2563, sha5122, sha3842, init_sha22, _0n2, _1n2, isPosBig, bitMask, notImplemented, init_utils3, _0n3, _1n3, _2n2, _3n, _4n, _5n, _7n2, _8n, _9n, _16n, isNegativeLE, FIELD_FIELDS, init_modular2, _0n4, _1n4, pointPrecomputes, pointWindowSizes, wNAF, init_curve2, _0n5, _1n5, _2n3, _8n2, PrimeEdwardsPoint, init_edwards, os2ip, _DST_scalar, init_hash_to_curve, _0n6, _1n6, _2n4, init_montgomery2, ed25519_exports, _0n7, _1n7, _2n5, _3n2, _5n2, _8n3, ed25519_CURVE_p, ed25519_CURVE, ED25519_SQRT_M1, Fp, Fn, ed25519Defaults, ed25519, ed25519ctx, ed25519ph, x25519, ELL2_C1, ELL2_C2, ELL2_C3, ELL2_C1_EDWARDS, ed25519_hasher, SQRT_M1, SQRT_AD_MINUS_ONE, INVSQRT_A_MINUS_D, ONE_MINUS_D_SQ, D_MINUS_ONE_SQ, invertSqrt, MAX_255B, bytes255ToNumberLE, _RistrettoPoint, ristretto255, ristretto255_hasher, ED25519_TORSION_SUBGROUP, edwardsToMontgomery, RistrettoPoint, hashToCurve, encodeToCurve, hashToRistretto255, hash_to_ristretto255, init_ed25519, _0n8, _1n8, _2n6, _7n3, _256n2, _0x71n2, SHA3_PI2, SHA3_ROTL2, _SHA3_IOTA2, IOTAS2, SHA3_IOTA_H2, SHA3_IOTA_L2, rotlH, rotlL, Keccak, genShake, shake2562, init_sha32, ed448_exports, ed448_CURVE, E448_CURVE, shake256_114, shake256_64, _1n9, _2n7, _3n3, _4n2, _11n, _22n, _44n, _88n, _223n, Fp2, Fn2, Fp448, Fn448, ED448_DEF, ed448, ed448ph, E448, x448, ELL2_C12, ELL2_J, ed448_hasher, ONE_MINUS_D, ONE_MINUS_TWO_D, SQRT_MINUS_D, INVSQRT_MINUS_D, invertSqrt2, _DecafPoint, decaf448, decaf448_hasher, ED448_TORSION_SUBGROUP, DecafPoint, hashToCurve2, encodeToCurve2, hashToDecaf448, hash_to_decaf448, edwardsToMontgomery2, init_ed448, HMAC, hmac2, init_hmac2, divNearest, DERErr, DER, _0n9, _1n10, _2n8, _3n4, _4n3, init_weierstrass, init_shortw_utils, nist_exports, p256_CURVE, p384_CURVE, p521_CURVE, Fp256, Fp384, Fp521, p256, p256_hasher, p384, p384_hasher, p521, secp256r1, secp384r1, secp521r1, p521_hasher, init_nist, init_makeNobleSignatureImpl, defaultCryptoProvider, init_provider, init_getCiphersuiteImpl, init_resumption, oidNist2, init_utils4, HashMD3, SHA256_IV3, SHA384_IV3, SHA512_IV3, init_md3, U32_MASK643, _32n3, shrSH3, shrSL3, rotrSH3, rotrSL3, rotrBH3, rotrBL3, add3L3, add3H3, add4L3, add4H3, add5L3, add5H3, init_u643, SHA256_K2, SHA256_W2, SHA2_32B, _SHA256, K5122, SHA512_Kh2, SHA512_Kl2, SHA512_W_H2, SHA512_W_L2, SHA2_64B, _SHA512, _SHA384, sha2564, sha5123, sha3843, init_sha23, _HMAC2, hmac3, init_hmac3, init_makeHashImpl2, init_makeNobleSignatureImpl2, BLOCK_SIZE, ZEROS162, ZEROS322, POLY, mul2, swapLE, estimateWindow, GHASH, Polyval, ghash, polyval, init_polyval, BLOCK_SIZE2, BLOCK_SIZE32, EMPTY_BLOCK, POLY2, sbox, rotr32_8, rotl32_8, tableEncoding, xPowers, gcm, _CMAC, cmac, init_aes, init_makeAead2, init_makeKdfImpl2, init_makeDhKem2, init_makeHpke2, init_rng2, nobleCryptoProvider, init_provider2, mlsPublicMessageEncoder, encodeMlsPublicMessage, mlsWelcomeEncoder, encodeMlsWelcome, mlsPrivateMessageEncoder, encodeMlsPrivateMessage, mlsGroupInfoEncoder, encodeMlsGroupInfo, mlsKeyPackageEncoder, encodeMlsKeyPackage, mlsMessageContentEncoder, encodeMlsMessageContent, decodeMlsMessageContent, mlsMessageEncoder, encodeMlsMessage, decodeMlsMessage, init_message, greaseValues, defaultGreaseConfig, init_grease, init_defaultCapabilities, init_src, DEFAULT_CIPHERSUITE, MLSGroupManager, init_mls_group, DOMAIN, NONCE_BYTES2, init_owner_sync, init_mls_delivery_order, init_mls_presend_epoch, dist_exports2, init_dist2, DIR_MODE, FILE_MODE, init_secure_file, SYNC_LOCK_STALE_MS, pendingKpPath, init_mls_state, init_mls_kp_pool, PACKAGE, FALLBACK, MAX_LEN, DISALLOWED, init_client_version, CredentialStore, init_credential_store, init_crypto_helpers, STATE_FILE, LEGACY_STATE_FILE, init_state, init_transport2, A2ALogGate, init_a2a_log_gate, init_mls_membership_probe, openclaw_compat_exports, defaultImporter, HEARTBEAT_CANDIDATES, AGENT_EVENT_CANDIDATES, TRANSCRIPT_CANDIDATES, _heartbeatFn, _agentEventFn, _transcriptFn, init_openclaw_compat, http_handlers_exports, init_http_handlers, mcp_proxy_helpers_exports, init_mcp_proxy_helpers, workspace_handlers_exports, MAX_FILE_SIZE, init_workspace_handlers, ROOM_AGENT_TYPES, CREDENTIAL_MESSAGE_TYPES, CATCHUP_MESSAGE_TYPES, POLL_INTERVAL_MS, RECONNECT_BASE_MS, UUID_RE, RECONNECT_MAX_MS, PENDING_POLL_INTERVAL_MS, OUTBOUND_DEDUPE_WINDOW_MS, OUTBOUND_DEDUPE_MAX_SUPPRESS_MS, OUTBOUND_DEDUPE_MAX, OUTBOUND_DEDUPE_TYPES, RETRY_CANDIDATE_TTL_MS, RETRY_CANDIDATE_MAX, ROOM_FULFIL_COOLDOWN_MS, SecureChannel, init_channel, init_types, DEFAULT_API_URL, DEFAULT_HTTP_PORT, init_account_config, init_lifecycle, _ocRuntime, _channels, agentVaultPlugin, init_openclaw_plugin, init_gateway_send, init_llm_response_parser, init_llm_cost, traceStore, init_fetch_interceptor, init_tool_audit, init_harness_compat, isUsingManagedRoutes, init_openclaw_entry, util, objectUtil, ZodParsedType, getParsedType, init_util, ZodIssueCode, ZodError, init_ZodError, errorMap, en_default, init_en, overrideErrorMap, init_errors2, makeIssue, ParseStatus, INVALID, DIRTY, OK, isAborted, isDirty, isValid, isAsync, init_parseUtil, init_typeAliases, errorUtil, init_errorUtil, ParseInputLazyPath, handleResult, ZodType, cuidRegex, cuid2Regex, ulidRegex, uuidRegex, nanoidRegex, jwtRegex, durationRegex, emailRegex, _emojiRegex, emojiRegex, ipv4Regex, ipv4CidrRegex, ipv6Regex, ipv6CidrRegex, base64Regex, base64urlRegex, dateRegexSource, dateRegex, ZodString, ZodNumber, ZodBigInt, ZodBoolean, ZodDate, ZodSymbol, ZodUndefined, ZodNull, ZodAny, ZodUnknown, ZodNever, ZodVoid, ZodArray, ZodObject, ZodUnion, getDiscriminator, ZodDiscriminatedUnion, ZodIntersection, ZodTuple, ZodRecord, ZodMap, ZodSet, ZodFunction, ZodLazy, ZodLiteral, ZodEnum, ZodNativeEnum, ZodPromise, ZodEffects, ZodOptional, ZodNullable, ZodDefault, ZodCatch, ZodNaN, ZodBranded, ZodPipeline, ZodReadonly, late, ZodFirstPartyTypeKind, stringType, numberType, nanType, bigIntType, booleanType, dateType, symbolType, undefinedType, nullType, anyType, unknownType, neverType, voidType, arrayType, objectType, strictObjectType, unionType, discriminatedUnionType, intersectionType, tupleType, recordType, mapType, setType, functionType, lazyType, literalType, enumType, nativeEnumType, promiseType, effectsType, optionalType, nullableType, preprocessType, pipelineType, init_types2, init_external, init_v3, NEVER, $ZodAsyncError, $ZodEncodeError, globalConfig, init_core, util_exports, EVALUATING, captureStackTrace, allowsEval, getParsedType2, propertyKeyTypes, primitiveTypes, NUMBER_FORMAT_RANGES, BIGINT_FORMAT_RANGES, Class, init_util2, initializer, $ZodError, $ZodRealError, init_errors3, _parse, parse, _parseAsync, parseAsync, _safeParse, safeParse, _safeParseAsync, safeParseAsync, _encode, _decode, _encodeAsync, _decodeAsync, _safeEncode, _safeDecode, _safeEncodeAsync, _safeDecodeAsync, init_parse, regexes_exports, cuid, cuid2, ulid, xid, ksuid, nanoid, duration, extendedDuration, guid, uuid, uuid4, uuid6, uuid7, email, html5Email, rfc5322Email, unicodeEmail, idnEmail, browserEmail, _emoji, ipv4, ipv6, mac, cidrv4, cidrv6, base64, base64url, hostname, domain, e164, dateSource, date, string, bigint, integer, number, boolean, _null, _undefined, lowercase, uppercase, hex, md5_hex, md5_base64, md5_base64url, sha1_hex, sha1_base64, sha1_base64url, sha256_hex, sha256_base64, sha256_base64url, sha384_hex, sha384_base64, sha384_base64url, sha512_hex, sha512_base64, sha512_base64url, init_regexes, $ZodCheck, numericOriginMap, $ZodCheckLessThan, $ZodCheckGreaterThan, $ZodCheckMultipleOf, $ZodCheckNumberFormat, $ZodCheckBigIntFormat, $ZodCheckMaxSize, $ZodCheckMinSize, $ZodCheckSizeEquals, $ZodCheckMaxLength, $ZodCheckMinLength, $ZodCheckLengthEquals, $ZodCheckStringFormat, $ZodCheckRegex, $ZodCheckLowerCase, $ZodCheckUpperCase, $ZodCheckIncludes, $ZodCheckStartsWith, $ZodCheckEndsWith, $ZodCheckProperty, $ZodCheckMimeType, $ZodCheckOverwrite, init_checks, Doc, init_doc, version, init_versions, $ZodType, $ZodString, $ZodStringFormat, $ZodGUID, $ZodUUID, $ZodEmail, $ZodURL, $ZodEmoji, $ZodNanoID, $ZodCUID, $ZodCUID2, $ZodULID, $ZodXID, $ZodKSUID, $ZodISODateTime, $ZodISODate, $ZodISOTime, $ZodISODuration, $ZodIPv4, $ZodIPv6, $ZodMAC, $ZodCIDRv4, $ZodCIDRv6, $ZodBase64, $ZodBase64URL, $ZodE164, $ZodJWT, $ZodCustomStringFormat, $ZodNumber, $ZodNumberFormat, $ZodBoolean, $ZodBigInt, $ZodBigIntFormat, $ZodSymbol, $ZodUndefined, $ZodNull, $ZodAny, $ZodUnknown, $ZodNever, $ZodVoid, $ZodDate, $ZodArray, $ZodObject, $ZodObjectJIT, $ZodUnion, $ZodXor, $ZodDiscriminatedUnion, $ZodIntersection, $ZodTuple, $ZodRecord, $ZodMap, $ZodSet, $ZodEnum, $ZodLiteral, $ZodFile, $ZodTransform, $ZodOptional, $ZodExactOptional, $ZodNullable, $ZodDefault, $ZodPrefault, $ZodNonOptional, $ZodSuccess, $ZodCatch, $ZodNaN, $ZodPipe, $ZodCodec, $ZodReadonly, $ZodTemplateLiteral, $ZodFunction, $ZodPromise, $ZodLazy, $ZodCustom, init_schemas, init_ar, init_az, init_be, init_bg, init_ca, init_cs, init_da, init_de, error, init_en2, init_eo, init_es, init_fa, init_fi, init_fr, init_fr_CA, init_he, init_hu, init_hy, init_id, init_is, init_it, init_ja, init_ka, init_km, init_kh, init_ko, init_lt, init_mk, init_ms, init_nl, init_no, init_ota, init_ps, init_pl, init_pt, init_ru, init_sl, init_sv, init_ta, init_th, init_tr, init_uk, init_ua, init_ur, init_uz, init_vi, init_zh_CN, init_zh_TW, init_yo, init_locales, _a2, $ZodRegistry, globalRegistry, init_registries, init_api, createToJSONSchemaMethod, createStandardJSONSchemaMethod, init_to_json_schema, formatMap, stringProcessor, numberProcessor, booleanProcessor, bigintProcessor, symbolProcessor, nullProcessor, undefinedProcessor, voidProcessor, neverProcessor, anyProcessor, unknownProcessor, dateProcessor, enumProcessor, literalProcessor, nanProcessor, templateLiteralProcessor, fileProcessor, successProcessor, customProcessor, functionProcessor, transformProcessor, mapProcessor, setProcessor, arrayProcessor, objectProcessor, unionProcessor, intersectionProcessor, tupleProcessor, recordProcessor, nullableProcessor, nonoptionalProcessor, defaultProcessor, prefaultProcessor, catchProcessor, pipeProcessor, readonlyProcessor, promiseProcessor, optionalProcessor, lazyProcessor, allProcessors, init_json_schema_processors, init_json_schema_generator, init_json_schema, init_core2, init_parse2, ZodMiniType, ZodMiniObject, init_schemas2, init_checks2, init_iso, init_coerce, init_external2, init_v4_mini, init_zod_compat, checks_exports2, init_checks3, iso_exports2, ZodISODateTime, ZodISODate, ZodISOTime, ZodISODuration, init_iso2, initializer2, ZodError2, ZodRealError, init_errors4, parse2, parseAsync2, safeParse3, safeParseAsync3, encode3, decode2, encodeAsync2, decodeAsync2, safeEncode2, safeDecode2, safeEncodeAsync2, safeDecodeAsync2, init_parse3, schemas_exports3, ZodType2, _ZodString, ZodString2, ZodStringFormat, ZodEmail, ZodGUID, ZodUUID, ZodURL, ZodEmoji, ZodNanoID, ZodCUID, ZodCUID2, ZodULID, ZodXID, ZodKSUID, ZodIPv4, ZodMAC, ZodIPv6, ZodCIDRv4, ZodCIDRv6, ZodBase64, ZodBase64URL, ZodE164, ZodJWT, ZodCustomStringFormat, ZodNumber2, ZodNumberFormat, ZodBoolean2, ZodBigInt2, ZodBigIntFormat, ZodSymbol2, ZodUndefined2, ZodNull2, ZodAny2, ZodUnknown2, ZodNever2, ZodVoid2, ZodDate2, ZodArray2, ZodObject2, ZodUnion2, ZodXor, ZodDiscriminatedUnion2, ZodIntersection2, ZodTuple2, ZodRecord2, ZodMap2, ZodSet2, ZodEnum2, ZodLiteral2, ZodFile, ZodTransform, ZodOptional2, ZodExactOptional, ZodNullable2, ZodDefault2, ZodPrefault, ZodNonOptional, ZodSuccess, ZodCatch2, ZodNaN2, ZodPipe, ZodCodec, ZodReadonly2, ZodTemplateLiteral, ZodLazy2, ZodPromise2, ZodFunction2, ZodCustom, describe2, meta2, stringbool, init_schemas3, ZodFirstPartyTypeKind2, init_compat, z2, init_from_json_schema, init_coerce2, init_external3, init_classic, init_v4, LATEST_PROTOCOL_VERSION, DEFAULT_NEGOTIATED_PROTOCOL_VERSION, SUPPORTED_PROTOCOL_VERSIONS, RELATED_TASK_META_KEY, JSONRPC_VERSION, AssertObjectSchema, ProgressTokenSchema, CursorSchema, TaskCreationParamsSchema, TaskMetadataSchema, RelatedTaskMetadataSchema, RequestMetaSchema, BaseRequestParamsSchema, TaskAugmentedRequestParamsSchema, isTaskAugmentedRequestParams, RequestSchema, NotificationsParamsSchema, NotificationSchema, ResultSchema, RequestIdSchema, JSONRPCRequestSchema, isJSONRPCRequest, JSONRPCNotificationSchema, isJSONRPCNotification, JSONRPCResultResponseSchema, isJSONRPCResultResponse, ErrorCode, JSONRPCErrorResponseSchema, isJSONRPCErrorResponse, JSONRPCMessageSchema, JSONRPCResponseSchema, EmptyResultSchema, CancelledNotificationParamsSchema, CancelledNotificationSchema, IconSchema, IconsSchema, BaseMetadataSchema, ImplementationSchema, FormElicitationCapabilitySchema, ElicitationCapabilitySchema, ClientTasksCapabilitySchema, ServerTasksCapabilitySchema, ClientCapabilitiesSchema, InitializeRequestParamsSchema, InitializeRequestSchema, isInitializeRequest, ServerCapabilitiesSchema, InitializeResultSchema, InitializedNotificationSchema, PingRequestSchema, ProgressSchema, ProgressNotificationParamsSchema, ProgressNotificationSchema, PaginatedRequestParamsSchema, PaginatedRequestSchema, PaginatedResultSchema, TaskStatusSchema, TaskSchema, CreateTaskResultSchema, TaskStatusNotificationParamsSchema, TaskStatusNotificationSchema, GetTaskRequestSchema, GetTaskResultSchema, GetTaskPayloadRequestSchema, GetTaskPayloadResultSchema, ListTasksRequestSchema, ListTasksResultSchema, CancelTaskRequestSchema, CancelTaskResultSchema, ResourceContentsSchema, TextResourceContentsSchema, Base64Schema, BlobResourceContentsSchema, RoleSchema, AnnotationsSchema, ResourceSchema, ResourceTemplateSchema, ListResourcesRequestSchema, ListResourcesResultSchema, ListResourceTemplatesRequestSchema, ListResourceTemplatesResultSchema, ResourceRequestParamsSchema, ReadResourceRequestParamsSchema, ReadResourceRequestSchema, ReadResourceResultSchema, ResourceListChangedNotificationSchema, SubscribeRequestParamsSchema, SubscribeRequestSchema, UnsubscribeRequestParamsSchema, UnsubscribeRequestSchema, ResourceUpdatedNotificationParamsSchema, ResourceUpdatedNotificationSchema, PromptArgumentSchema, PromptSchema, ListPromptsRequestSchema, ListPromptsResultSchema, GetPromptRequestParamsSchema, GetPromptRequestSchema, TextContentSchema, ImageContentSchema, AudioContentSchema, ToolUseContentSchema, EmbeddedResourceSchema, ResourceLinkSchema, ContentBlockSchema, PromptMessageSchema, GetPromptResultSchema, PromptListChangedNotificationSchema, ToolAnnotationsSchema, ToolExecutionSchema, ToolSchema, ListToolsRequestSchema, ListToolsResultSchema, CallToolResultSchema, CompatibilityCallToolResultSchema, CallToolRequestParamsSchema, CallToolRequestSchema, ToolListChangedNotificationSchema, ListChangedOptionsBaseSchema, LoggingLevelSchema, SetLevelRequestParamsSchema, SetLevelRequestSchema, LoggingMessageNotificationParamsSchema, LoggingMessageNotificationSchema, ModelHintSchema, ModelPreferencesSchema, ToolChoiceSchema, ToolResultContentSchema, SamplingContentSchema, SamplingMessageContentBlockSchema, SamplingMessageSchema, CreateMessageRequestParamsSchema, CreateMessageRequestSchema, CreateMessageResultSchema, CreateMessageResultWithToolsSchema, BooleanSchemaSchema, StringSchemaSchema, NumberSchemaSchema, UntitledSingleSelectEnumSchemaSchema, TitledSingleSelectEnumSchemaSchema, LegacyTitledEnumSchemaSchema, SingleSelectEnumSchemaSchema, UntitledMultiSelectEnumSchemaSchema, TitledMultiSelectEnumSchemaSchema, MultiSelectEnumSchemaSchema, EnumSchemaSchema, PrimitiveSchemaDefinitionSchema, ElicitRequestFormParamsSchema, ElicitRequestURLParamsSchema, ElicitRequestParamsSchema, ElicitRequestSchema, ElicitationCompleteNotificationParamsSchema, ElicitationCompleteNotificationSchema, ElicitResultSchema, ResourceTemplateReferenceSchema, PromptReferenceSchema, CompleteRequestParamsSchema, CompleteRequestSchema, CompleteResultSchema, RootSchema, ListRootsRequestSchema, ListRootsResultSchema, RootsListChangedNotificationSchema, ClientRequestSchema, ClientNotificationSchema, ClientResultSchema, ServerRequestSchema, ServerNotificationSchema, ServerResultSchema, McpError, UrlElicitationRequiredError, init_types3, init_interfaces, ignoreOverride, defaultOptions, getDefaultOptions, init_Options, getRefs, init_Refs, init_errorMessages, getRelativePath, init_getRelativePath, init_any, init_array2, init_bigint, init_boolean, init_branded, parseCatchDef, init_catch, integerDateParser, init_date, init_default, init_effects, init_enum, isJsonSchema7AllOfType, init_intersection, init_literal, emojiRegex2, zodPatterns, ALPHA_NUMERIC, init_string2, init_record, init_map, init_nativeEnum, init_never, init_null, primitiveMappings, asAnyOf, init_union, init_nullable, init_number2, init_object, parseOptionalDef, init_optional2, parsePipelineDef, init_pipeline, init_promise, init_set, init_tuple, init_undefined, init_unknown, parseReadonlyDef, init_readonly, selectParser, init_selectParser, get$ref, addMeta, init_parseDef, init_parseTypes, zodToJsonSchema, init_zodToJsonSchema, init_esm, init_zod_json_schema_compat, DEFAULT_REQUEST_TIMEOUT_MSEC, Protocol, init_protocol, require_code, require_scope, require_codegen, require_util, require_names, require_errors, require_boolSchema, require_rules, require_applicability, require_dataType, require_defaults, require_code2, require_keyword, require_subschema, require_fast_deep_equal, require_json_schema_traverse, require_resolve, require_validate, require_validation_error, require_ref_error, require_compile, require_data, require_utils, require_schemes, require_fast_uri, require_uri, require_core, require_id, require_ref, require_core2, require_limitNumber, require_multipleOf, require_ucs2length, require_limitLength, require_pattern, require_limitProperties, require_required, require_limitItems, require_equal, require_uniqueItems, require_const, require_enum, require_validation, require_additionalItems, require_items, require_prefixItems, require_items2020, require_contains, require_dependencies, require_propertyNames, require_additionalProperties, require_properties, require_patternProperties, require_not, require_anyOf, require_oneOf, require_allOf, require_if, require_thenElse, require_applicator, require_format, require_format2, require_metadata, require_draft7, require_types, require_discriminator, require_json_schema_draft_07, require_ajv, require_formats, require_limit, require_dist, import_ajv, import_ajv_formats, AjvJsonSchemaValidator, init_ajv_provider, ExperimentalServerTasks, init_server, init_helpers, Server, init_server2, COMPLETABLE_SYMBOL, McpZodTypeKind, init_completable, init_uriTemplate, TOOL_NAME_REGEX, init_toolNameValidation, ExperimentalMcpServerTasks, init_mcp_server, init_zod, McpServer, EMPTY_OBJECT_JSON_SCHEMA, EMPTY_COMPLETION_RESULT, init_mcp, RequestError, toRequestError, GlobalRequest, Request2, newHeadersFromIncoming, wrapBodyStream, newRequestFromIncoming, getRequestCache, requestCache, incomingKey, urlKey, headersKey, abortControllerKey, getAbortController, requestPrototype, newRequest, responseCache, getResponseCache, cacheKey, GlobalResponse, Response2, buildOutgoingHttpHeaders, X_ALREADY_SENT, outgoingEnded, handleRequestError, handleFetchError, handleResponseError, flushHeaders, responseViaCache, isPromise, responseViaResponseObject, getRequestListener, init_dist22, WebStandardStreamableHTTPServerTransport, init_webStandardStreamableHttp, StreamableHTTPServerTransport, init_streamableHttp, AgentVaultMcpServer, init_mcp_server2, init_mcp_handlers, init_skill_manifest, init_skill_invoker, spanBuffer, init_skill_telemetry, PolicyEnforcer, init_policy_enforcer, VERSION, init_index;
17060
17060
  var init_dist = __esm({
17061
17061
  async "../plugin/dist/index.js"() {
17062
17062
  "use strict";
@@ -17105,6 +17105,13 @@ var init_dist = __esm({
17105
17105
  "use strict";
17106
17106
  }
17107
17107
  });
17108
+ init_room_protocol = __esm2({
17109
+ "src/room-protocol.ts"() {
17110
+ "use strict";
17111
+ MENTION_RE = /@([\w-]+)/gi;
17112
+ BROADCAST_NAMES = /* @__PURE__ */ new Set(["all", "everyone"]);
17113
+ }
17114
+ });
17108
17115
  init_libsodium_sumo = __esm2({
17109
17116
  async "../../node_modules/libsodium-sumo/dist/modules-sumo-esm/libsodium-sumo.mjs"() {
17110
17117
  try {
@@ -68296,10 +68303,10 @@ var init_dist = __esm({
68296
68303
  const commits = [];
68297
68304
  if (leavesToRemove.length > 0) {
68298
68305
  const { commit } = await this.removeMembers(leavesToRemove);
68299
- commits.push({ commit, epoch: this.epoch });
68306
+ commits.push({ commit, epoch: this.epoch, state: this.exportState() });
68300
68307
  }
68301
68308
  const added = await this.addMembers(toAdd);
68302
- commits.push({ commit: added.commit, epoch: this.epoch });
68309
+ commits.push({ commit: added.commit, epoch: this.epoch, state: this.exportState() });
68303
68310
  return { commits, welcome: added.welcome };
68304
68311
  }
68305
68312
  async removeMembers(memberIndices) {
@@ -68891,6 +68898,7 @@ var init_dist = __esm({
68891
68898
  "use strict";
68892
68899
  init_abstention();
68893
68900
  init_mls_commit_classify();
68901
+ init_room_protocol();
68894
68902
  await init_libsodium_wrappers();
68895
68903
  await init_dist2();
68896
68904
  await init_dist2();
@@ -68922,6 +68930,7 @@ var init_dist = __esm({
68922
68930
  ]);
68923
68931
  POLL_INTERVAL_MS = 6e3;
68924
68932
  RECONNECT_BASE_MS = 1e3;
68933
+ UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
68925
68934
  RECONNECT_MAX_MS = 3e4;
68926
68935
  PENDING_POLL_INTERVAL_MS = 15e3;
68927
68936
  OUTBOUND_DEDUPE_WINDOW_MS = 3e3;
@@ -69093,6 +69102,35 @@ var init_dist = __esm({
69093
69102
 
69094
69103
  ${text}`;
69095
69104
  }
69105
+ /**
69106
+ * #1161: room attachments whose caption addressed nobody, NOT yet fetched.
69107
+ * The reply gate lives in every consumer of `room_message` (openclaw-entry
69108
+ * and the three native bridges), but its first clause is shared and needs no
69109
+ * identity: a caption with no mention token owes nobody a reply (#1107), so
69110
+ * nobody needs the file yet. Kept per room, newest last, and fetched on the
69111
+ * room's next ADDRESSED turn — the owner attaches, then asks (#1159/#1181).
69112
+ * Hermes does the same (`_defer_room_attachment`).
69113
+ */
69114
+ _deferredRoomAttachments = /* @__PURE__ */ new Map();
69115
+ static DEFERRED_ROOM_ATTACHMENT_MAX = 5;
69116
+ static DEFERRED_ROOM_ATTACHMENT_MS = 60 * 60 * 1e3;
69117
+ _deferRoomAttachment(roomId, info) {
69118
+ const list = this._deferredRoomAttachments.get(roomId) ?? [];
69119
+ list.push({ info, at: Date.now() });
69120
+ while (list.length > _SecureChannel.DEFERRED_ROOM_ATTACHMENT_MAX) list.shift();
69121
+ this._deferredRoomAttachments.set(roomId, list);
69122
+ console.log(
69123
+ `[SecureChannel] room ${roomId.slice(0, 8)}: attachment ${String(info?.filename ?? "file")} deferred \u2014 caption addresses nobody, fetch on the next @-addressed turn (#1161)`
69124
+ );
69125
+ }
69126
+ /** Drain the room's deferred attachments that are still fresh (oldest first). */
69127
+ _takeDeferredRoomAttachments(roomId) {
69128
+ const list = this._deferredRoomAttachments.get(roomId);
69129
+ if (!list?.length) return [];
69130
+ this._deferredRoomAttachments.delete(roomId);
69131
+ const cutoff = Date.now() - _SecureChannel.DEFERRED_ROOM_ATTACHMENT_MS;
69132
+ return list.filter((d2) => d2.at >= cutoff).map((d2) => d2.info);
69133
+ }
69096
69134
  /** In-flight roster-refresh promises (per room) — dedup concurrent refreshes. */
69097
69135
  _roomRosterRefreshInFlight = /* @__PURE__ */ new Map();
69098
69136
  /** Dedup buffer for A2A message IDs (prevents double-delivery via direct + Redis) */
@@ -70188,7 +70226,7 @@ ${text}`;
70188
70226
  */
70189
70227
  sendActivitySpan(spanData) {
70190
70228
  if (!this._ws || this._ws.readyState !== WebSocket.OPEN) return;
70191
- const pluginVersion = true ? "0.23.39" : "0.0.0-dev";
70229
+ const pluginVersion = true ? "0.23.41" : "0.0.0-dev";
70192
70230
  const agentName = this.config.agentName ?? "Agent";
70193
70231
  const resource = {
70194
70232
  "service.name": "agentvault-agent",
@@ -72126,7 +72164,7 @@ ${text}`;
72126
72164
  agentVersion: this.config.agentVersion ?? "0.0.0",
72127
72165
  // __AV_VERSION__ is injected by esbuild from package.json at build time.
72128
72166
  // Falls back to "0.0.0-dev" in non-bundled contexts (tests).
72129
- pluginVersion: true ? "0.23.39" : "0.0.0-dev"
72167
+ pluginVersion: true ? "0.23.41" : "0.0.0-dev"
72130
72168
  });
72131
72169
  this._telemetryReporter.startAutoFlush(3e4);
72132
72170
  }
@@ -72450,7 +72488,7 @@ ${text}`;
72450
72488
  agentVersion: this.config.agentVersion ?? "0.0.0",
72451
72489
  // __AV_VERSION__ is injected by esbuild from package.json at build time.
72452
72490
  // Falls back to "0.0.0-dev" in non-bundled contexts (tests).
72453
- pluginVersion: true ? "0.23.39" : "0.0.0-dev"
72491
+ pluginVersion: true ? "0.23.41" : "0.0.0-dev"
72454
72492
  });
72455
72493
  this._telemetryReporter.startAutoFlush(3e4);
72456
72494
  }
@@ -72665,45 +72703,78 @@ ${text}`;
72665
72703
  const mlsGroup = this._mlsGroups.get(`a2a:${channelId}`);
72666
72704
  if (mlsGroup?.isInitialized) {
72667
72705
  try {
72668
- const kpRes = await fetch(
72669
- `${this.config.apiUrl}/api/v1/mls/key-packages?hub_id=${encodeURIComponent(participant.hub_id)}`,
72670
- { headers: { Authorization: `Bearer ${this._deviceJwt}` } }
72671
- );
72672
- if (kpRes.ok) {
72673
- const kpData = await kpRes.json();
72674
- if (kpData.key_package) {
72675
- const kpBytes = new Uint8Array(Buffer.from(kpData.key_package, "hex"));
72676
- const memberKp = MLSGroupManager.deserializeKeyPackage(kpBytes);
72677
- const { commit, welcome } = await mlsGroup.addMembers([memberKp]);
72678
- if (welcome && this._ws) {
72679
- this._ws.send(JSON.stringify({
72680
- event: "mls_commit",
72681
- data: {
72682
- group_id: entry.mlsGroupId,
72683
- epoch: Number(mlsGroup.epoch),
72684
- payload: Buffer.from(commit).toString("hex")
72685
- }
72686
- }));
72687
- const newDeviceId = participant.device_id || null;
72688
- if (!newDeviceId) {
72689
- console.warn(
72690
- `[SecureChannel] Skipping MLS Welcome to new participant ${participant.hub_address} for A2A ${channelId.slice(0, 8)}: no device_id on participant_joined payload.`
72691
- );
72692
- } else {
72693
- this._ws.send(JSON.stringify({
72694
- event: "mls_welcome",
72695
- data: {
72696
- group_id: entry.mlsGroupId,
72697
- payload: Buffer.from(welcome).toString("hex"),
72698
- target_device_id: newDeviceId,
72699
- a2a_channel_id: channelId,
72700
- target_hub_id: participant.hub_id
72701
- // observability only
72706
+ const newDeviceId = participant.device_id || null;
72707
+ const a2aLabel = `A2A ${channelId.slice(0, 8)} participant_joined`;
72708
+ if (!newDeviceId) {
72709
+ console.warn(
72710
+ `[SecureChannel] ${a2aLabel}: no device_id on the payload for ${participant.hub_address} \u2014 NOT adding (a Welcome could not be addressed)`
72711
+ );
72712
+ } else if (!this._wsIsOpen()) {
72713
+ console.warn(`[SecureChannel] ${a2aLabel}: socket is not OPEN \u2014 NOT advancing the group`);
72714
+ } else {
72715
+ const preHead = await this._readGroupHead(entry.mlsGroupId);
72716
+ const inFlight = preHead.ok && preHead.epoch !== Number(mlsGroup.epoch) ? null : this._reserveCommit(entry.mlsGroupId, a2aLabel);
72717
+ if (preHead.ok && preHead.epoch !== Number(mlsGroup.epoch)) {
72718
+ console.warn(
72719
+ `[SecureChannel] ${a2aLabel}: server head is at epoch ${preHead.epoch}, we are at ${Number(mlsGroup.epoch)} \u2014 NOT committing (it would be refused); resync catches up first`
72720
+ );
72721
+ await this._catchUpAfterPreflightSkip(entry.mlsGroupId, preHead.epoch, Number(mlsGroup.epoch), a2aLabel);
72722
+ } else if (inFlight) {
72723
+ try {
72724
+ const kpRes = await fetch(
72725
+ `${this.config.apiUrl}/api/v1/mls/key-packages?hub_id=${encodeURIComponent(participant.hub_id)}`,
72726
+ { headers: { Authorization: `Bearer ${this._deviceJwt}` } }
72727
+ );
72728
+ if (kpRes.ok) {
72729
+ const kpData = await kpRes.json();
72730
+ if (kpData.key_package) {
72731
+ const kpBytes = new Uint8Array(Buffer.from(kpData.key_package, "hex"));
72732
+ const memberKp = MLSGroupManager.deserializeKeyPackage(kpBytes);
72733
+ const stateBefore = mlsGroup.exportState();
72734
+ const { commit, welcome } = await mlsGroup.addMembers([memberKp]);
72735
+ const committedEpoch = Number(mlsGroup.epoch);
72736
+ if (!welcome) {
72737
+ mlsGroup.importState(stateBefore);
72738
+ console.warn(`[SecureChannel] ${a2aLabel}: addMembers produced no Welcome \u2014 rolled back`);
72739
+ } else {
72740
+ const verdict = await this._sendCommitConfirmed({
72741
+ mlsGroup,
72742
+ mlsGroupId: entry.mlsGroupId,
72743
+ label: a2aLabel,
72744
+ commit,
72745
+ epoch: committedEpoch,
72746
+ parent: preHead.ok ? preHead.digest : void 0,
72747
+ memberDeviceIds: this._commitRoster(
72748
+ (entry.participants ?? []).map((p2) => p2.deviceId),
72749
+ [newDeviceId]
72750
+ ),
72751
+ stateBefore,
72752
+ inFlight
72753
+ });
72754
+ if (verdict === "confirmed") {
72755
+ const sent = await this._sendFrameConfirmed({
72756
+ event: "mls_welcome",
72757
+ data: {
72758
+ group_id: entry.mlsGroupId,
72759
+ payload: Buffer.from(welcome).toString("hex"),
72760
+ target_device_id: newDeviceId,
72761
+ a2a_channel_id: channelId,
72762
+ target_hub_id: participant.hub_id
72763
+ // observability only
72764
+ }
72765
+ }, `${a2aLabel} welcome`);
72766
+ await saveMlsState(this.config.dataDir, entry.mlsGroupId, JSON.stringify(mlsGroup.exportState()));
72767
+ if (sent) {
72768
+ console.log(`[SecureChannel] Sent MLS Welcome to new participant ${participant.hub_address} (device ${String(newDeviceId).slice(0, 8)})`);
72769
+ } else {
72770
+ console.warn(`[SecureChannel] ${a2aLabel}: Welcome to ${String(newDeviceId).slice(0, 8)} was NOT written \u2014 the participant will request one`);
72771
+ }
72772
+ }
72702
72773
  }
72703
- }));
72704
- await saveMlsState(this.config.dataDir, entry.mlsGroupId, JSON.stringify(mlsGroup.exportState()));
72705
- console.log(`[SecureChannel] Sent MLS Welcome to new participant ${participant.hub_address} (device ${String(newDeviceId).slice(0, 8)})`);
72774
+ }
72706
72775
  }
72776
+ } finally {
72777
+ await this._releaseCommit(entry.mlsGroupId, inFlight, a2aLabel);
72707
72778
  }
72708
72779
  }
72709
72780
  }
@@ -72764,6 +72835,7 @@ ${text}`;
72764
72835
  `[deliver] delivery_result=REJECTED group=${rejectedGroup || "unknown"} detail=${detail}`
72765
72836
  );
72766
72837
  console.error(`[SecureChannel] Server error: ${detail}`);
72838
+ this._noteCommitRefusal(data.data || data);
72767
72839
  await this._handleServerError(data.data || data);
72768
72840
  this.emit("error", new Error(`Server: ${detail}`));
72769
72841
  }
@@ -73065,7 +73137,7 @@ ${text}`;
73065
73137
  try {
73066
73138
  const parsed = JSON.parse(plaintext);
73067
73139
  messageType = parsed.type || "message";
73068
- messageText = parsed.text || plaintext;
73140
+ messageText = parsed.text ?? plaintext;
73069
73141
  if (parsed.attachment) {
73070
73142
  attachmentInfo = parsed.attachment;
73071
73143
  }
@@ -73817,7 +73889,7 @@ ${baseText}`;
73817
73889
  try {
73818
73890
  const parsed = JSON.parse(rawPlaintext);
73819
73891
  messageType = parsed.type || "message";
73820
- messageText = parsed.text || rawPlaintext;
73892
+ messageText = parsed.text ?? rawPlaintext;
73821
73893
  if (parsed.attachment) roomAttachment = parsed.attachment;
73822
73894
  } catch {
73823
73895
  messageType = "message";
@@ -73858,10 +73930,19 @@ ${baseText}`;
73858
73930
  await this._refreshRoomRoster(resolvedRoomId);
73859
73931
  }
73860
73932
  }
73861
- if (roomAttachment) {
73862
- messageText = await this._augmentWithAttachment(roomAttachment, messageText, resolvedRoomId);
73933
+ const addressed = parseMentions(messageText).length > 0;
73934
+ if (roomAttachment && !addressed) {
73935
+ this._deferRoomAttachment(resolvedRoomId, roomAttachment);
73863
73936
  } else {
73864
- messageText = this._withRecentRoomAttachmentNote(resolvedRoomId, messageText);
73937
+ const deferred = addressed ? this._takeDeferredRoomAttachments(resolvedRoomId) : [];
73938
+ for (const info of deferred) {
73939
+ messageText = await this._augmentWithAttachment(info, messageText, resolvedRoomId);
73940
+ }
73941
+ if (roomAttachment) {
73942
+ messageText = await this._augmentWithAttachment(roomAttachment, messageText, resolvedRoomId);
73943
+ } else if (deferred.length === 0) {
73944
+ messageText = this._withRecentRoomAttachmentNote(resolvedRoomId, messageText);
73945
+ }
73865
73946
  }
73866
73947
  const roomMembers = this._persisted?.rooms?.[resolvedRoomId]?.members ?? [];
73867
73948
  const senderMember = roomMembers.find((m2) => m2.deviceId === senderDeviceId);
@@ -73985,6 +74066,12 @@ ${baseText}`;
73985
74066
  const groupId = data.group_id;
73986
74067
  const epoch = typeof data.epoch === "number" ? data.epoch : 0;
73987
74068
  this._noteGroupEpoch(groupId, data.epoch);
74069
+ const held = this._commitInFlight.get(groupId);
74070
+ if (held) {
74071
+ held.frames.push(data);
74072
+ console.log(`[SecureChannel] MLS commit for ${groupId.slice(0, 8)} held (epoch=${epoch}) \u2014 our own commit is awaiting confirmation (#1184)`);
74073
+ return true;
74074
+ }
73988
74075
  const target = this._resolveGroupFamily(groupId);
73989
74076
  if (target) {
73990
74077
  const { managerKey: groupKey, label } = target;
@@ -74217,9 +74304,34 @@ ${baseText}`;
74217
74304
  *
74218
74305
  * Returns the number of requests actually fulfilled.
74219
74306
  */
74307
+ /**
74308
+ * #1226: the roster to submit on a commit frame — the caller's known members,
74309
+ * the devices being admitted, and ALWAYS ourselves — deduplicated, UUIDs
74310
+ * only. Self is non-negotiable: once a roster is non-empty the server
74311
+ * requires the committer in it (#469), and our own head read
74312
+ * (`assert_group_member`) requires it too. So with no device id of our own
74313
+ * this returns `[]` and the frame carries NO roster (the pre-#1226 shape)
74314
+ * rather than a roster that locks us out. Non-UUID entries are dropped:
74315
+ * `known` can come off an inbound Welcome frame, and a malformed id in the
74316
+ * outbound frame is refused by the server (`MEMBER_DEVICE_IDS_MALFORMED`).
74317
+ * @internal exposed for tests
74318
+ */
74319
+ _commitRoster(known, targets) {
74320
+ if (!this._deviceId) return [];
74321
+ const out = [];
74322
+ const seen = /* @__PURE__ */ new Set();
74323
+ for (const id of [...known ?? [], ...targets, this._deviceId]) {
74324
+ if (typeof id !== "string" || !UUID_RE.test(id) || seen.has(id)) continue;
74325
+ seen.add(id);
74326
+ out.push(id);
74327
+ }
74328
+ return out;
74329
+ }
74220
74330
  async _fulfilWelcomeRequests(opts) {
74221
74331
  const { mlsGroup, mlsGroupId, label, welcomeFields, targets } = opts;
74222
74332
  if (targets.length === 0) return 0;
74333
+ const rosterBefore = this._commitRoster(opts.memberDeviceIds, []);
74334
+ const rosterAfter = this._commitRoster(opts.memberDeviceIds, targets.map((t2) => t2.deviceId));
74223
74335
  if (!this._wsIsOpen()) {
74224
74336
  console.warn(
74225
74337
  `[SecureChannel] ${label}: socket is not OPEN \u2014 NOT advancing the group; ${targets.length} request(s) stay pending for the next pull`
@@ -74227,73 +74339,154 @@ ${baseText}`;
74227
74339
  return 0;
74228
74340
  }
74229
74341
  const snapshot = mlsGroup.exportState();
74230
- let parent2 = await this._readGroupHeadDigest(mlsGroupId);
74231
- const { commits, welcome } = await mlsGroup.reAdmitMembers(targets.map((t2) => t2.keyPackage));
74232
- let sentCommits = 0;
74233
- for (const c2 of commits) {
74234
- const ok = await this._sendFrameConfirmed(
74235
- {
74236
- event: "mls_commit",
74237
- data: {
74238
- group_id: mlsGroupId,
74239
- // The epoch this commit PRODUCED. After two commits the manager
74240
- // only knows the last one, which is why the result carries them.
74241
- epoch: Number(c2.epoch),
74242
- payload: Buffer.from(c2.commit).toString("hex"),
74243
- ...parent2 ? { parent_digest: parent2 } : {}
74244
- }
74245
- },
74246
- `${label} commit ${sentCommits + 1}/${commits.length}`
74247
- );
74248
- if (!ok) break;
74249
- parent2 = this._commitDigest(c2.commit);
74250
- sentCommits++;
74251
- }
74252
- if (sentCommits === 0) {
74253
- mlsGroup.importState(snapshot);
74254
- console.warn(
74255
- `[SecureChannel] ${label}: commit was NOT written \u2014 rolled back to epoch ${Number(mlsGroup.epoch)}, nothing fulfilled, requests stay pending`
74256
- );
74257
- return 0;
74258
- }
74259
- await saveMlsState(this.config.dataDir, mlsGroupId, JSON.stringify(mlsGroup.exportState()));
74260
- if (sentCommits < commits.length) {
74342
+ const preHead = await this._readGroupHead(mlsGroupId);
74343
+ if (preHead.ok && preHead.epoch !== Number(mlsGroup.epoch)) {
74261
74344
  console.warn(
74262
- `[SecureChannel] ${label}: only ${sentCommits}/${commits.length} commits reached the wire \u2014 requests stay PENDING for the next pull`
74345
+ `[SecureChannel] ${label}: server head is at epoch ${preHead.epoch}, we are at ${Number(mlsGroup.epoch)} \u2014 NOT committing (it would be refused, #1184); ${targets.length} request(s) stay pending`
74263
74346
  );
74347
+ await this._catchUpAfterPreflightSkip(mlsGroupId, preHead.epoch, Number(mlsGroup.epoch), label);
74264
74348
  return 0;
74265
74349
  }
74266
- if (!welcome) {
74350
+ let parent2 = preHead.ok ? preHead.digest : void 0;
74351
+ if (this._commitInFlight.has(mlsGroupId)) {
74267
74352
  console.warn(
74268
- `[SecureChannel] ${label}: addMembers produced NO Welcome \u2014 ${targets.length} request(s) left pending rather than marked fulfilled`
74353
+ `[SecureChannel] ${label}: another commit for this group is in flight \u2014 NOT advancing; ${targets.length} request(s) stay pending for the next pull`
74269
74354
  );
74270
74355
  return 0;
74271
74356
  }
74272
- let fulfilled = 0;
74273
- for (const t2 of targets) {
74274
- const sent = await this._sendFrameConfirmed(
74275
- {
74276
- event: "mls_welcome",
74277
- data: {
74278
- target_device_id: t2.deviceId,
74279
- group_id: mlsGroupId,
74280
- ...welcomeFields,
74281
- payload: Buffer.from(welcome).toString("hex")
74282
- }
74283
- },
74284
- `${label} welcome\u2192${t2.deviceId.slice(0, 8)}`
74285
- );
74286
- if (!sent) {
74357
+ const inFlight = { frames: [], sent: [] };
74358
+ this._commitInFlight.set(mlsGroupId, inFlight);
74359
+ try {
74360
+ const { commits, welcome } = await mlsGroup.reAdmitMembers(targets.map((t2) => t2.keyPackage));
74361
+ let sentCommits = 0;
74362
+ let refusedAt = -1;
74363
+ let verdict = "confirmed";
74364
+ for (const c2 of commits) {
74365
+ const ours = this._commitDigest(c2.commit);
74366
+ inFlight.sent.push({
74367
+ epoch: Number(c2.epoch),
74368
+ digest: ours,
74369
+ verified: false,
74370
+ stateBefore: sentCommits === 0 ? snapshot : commits[sentCommits - 1].state
74371
+ });
74372
+ const ok = await this._sendFrameConfirmed(
74373
+ {
74374
+ event: "mls_commit",
74375
+ data: {
74376
+ group_id: mlsGroupId,
74377
+ // The epoch this commit PRODUCED. After two commits the manager
74378
+ // only knows the last one, which is why the result carries them.
74379
+ epoch: Number(c2.epoch),
74380
+ payload: Buffer.from(c2.commit).toString("hex"),
74381
+ ...parent2 ? { parent_digest: parent2 } : {},
74382
+ ...(sentCommits === commits.length - 1 ? rosterAfter : rosterBefore).length > 0 ? { member_device_ids: sentCommits === commits.length - 1 ? rosterAfter : rosterBefore } : {}
74383
+ }
74384
+ },
74385
+ `${label} commit ${sentCommits + 1}/${commits.length}`
74386
+ );
74387
+ if (!ok) {
74388
+ verdict = "unsent";
74389
+ inFlight.sent.pop();
74390
+ break;
74391
+ }
74392
+ verdict = await this._confirmCommitAtHead(mlsGroupId, Number(c2.epoch), ours, inFlight, label);
74393
+ if (verdict === "confirmed") {
74394
+ inFlight.sent[sentCommits].verified = true;
74395
+ } else if (verdict === "unverifiable") {
74396
+ console.warn(
74397
+ `[SecureChannel] ${label}: commit ${sentCommits + 1}/${commits.length} at epoch ${Number(c2.epoch)} could not be verified and was not refused \u2014 proceeding UNCHECKED (#1184)`
74398
+ );
74399
+ } else {
74400
+ refusedAt = sentCommits;
74401
+ break;
74402
+ }
74403
+ parent2 = ours;
74404
+ sentCommits++;
74405
+ }
74406
+ const cutFromRefusal = () => {
74407
+ const r2 = inFlight.refusal;
74408
+ if (r2 === void 0) return refusedAt;
74409
+ return refusedAt === -1 ? r2.from : Math.min(refusedAt, r2.from);
74410
+ };
74411
+ const rollBackTo = async (cut2, stage, persisted) => {
74412
+ const why = inFlight.refusal?.detail ?? verdict.toUpperCase();
74413
+ mlsGroup.importState(inFlight.sent[cut2].stateBefore);
74414
+ sentCommits = cut2;
74287
74415
  console.warn(
74288
- `[SecureChannel] ${label}: Welcome for ${t2.deviceId.slice(0, 8)} was NOT written \u2014 leaving the request PENDING so the next pull retries it`
74416
+ `[SecureChannel] ${label}: commit ${cut2 + 1}/${commits.length} ${stage} \u2014 "${why}" \u2014 rolled back to epoch ${Number(mlsGroup.epoch)}` + (cut2 === 0 ? ", nothing" : ` (after commit ${cut2}), only that`) + ` persisted, ${targets.length} request(s) stay pending`
74289
74417
  );
74290
- continue;
74418
+ if (persisted) await saveMlsState(this.config.dataDir, mlsGroupId, JSON.stringify(mlsGroup.exportState()));
74419
+ };
74420
+ let cut = cutFromRefusal();
74421
+ if (cut !== -1) {
74422
+ await rollBackTo(cut, "was refused by the server", false);
74423
+ if (sentCommits === 0) return 0;
74424
+ }
74425
+ if (sentCommits === 0) {
74426
+ mlsGroup.importState(snapshot);
74427
+ console.warn(
74428
+ `[SecureChannel] ${label}: commit was NOT written \u2014 rolled back to epoch ${Number(mlsGroup.epoch)}, nothing fulfilled, requests stay pending`
74429
+ );
74430
+ return 0;
74431
+ }
74432
+ await saveMlsState(this.config.dataDir, mlsGroupId, JSON.stringify(mlsGroup.exportState()));
74433
+ cut = cutFromRefusal();
74434
+ if (cut !== -1 && cut < sentCommits) {
74435
+ await rollBackTo(cut, "was refused by the server after the window (during persist)", true);
74436
+ return 0;
74437
+ }
74438
+ if (sentCommits < commits.length) {
74439
+ console.warn(
74440
+ `[SecureChannel] ${label}: only ${sentCommits}/${commits.length} commits were confirmed \u2014 requests stay PENDING for the next pull`
74441
+ );
74442
+ return 0;
74443
+ }
74444
+ if (!welcome) {
74445
+ console.warn(
74446
+ `[SecureChannel] ${label}: addMembers produced NO Welcome \u2014 ${targets.length} request(s) left pending rather than marked fulfilled`
74447
+ );
74448
+ return 0;
74449
+ }
74450
+ let fulfilled = 0;
74451
+ for (const t2 of targets) {
74452
+ const sent = await this._sendFrameConfirmed(
74453
+ {
74454
+ event: "mls_welcome",
74455
+ data: {
74456
+ target_device_id: t2.deviceId,
74457
+ group_id: mlsGroupId,
74458
+ ...welcomeFields,
74459
+ payload: Buffer.from(welcome).toString("hex")
74460
+ }
74461
+ },
74462
+ `${label} welcome\u2192${t2.deviceId.slice(0, 8)}`
74463
+ );
74464
+ if (!sent) {
74465
+ console.warn(
74466
+ `[SecureChannel] ${label}: Welcome for ${t2.deviceId.slice(0, 8)} was NOT written \u2014 leaving the request PENDING so the next pull retries it`
74467
+ );
74468
+ continue;
74469
+ }
74470
+ cut = cutFromRefusal();
74471
+ if (cut !== -1 && cut < sentCommits) {
74472
+ await rollBackTo(cut, "was refused by the server after the window (during the Welcome)", true);
74473
+ return fulfilled;
74474
+ }
74475
+ if (await this._postFulfilWelcome(mlsGroupId, t2.requestId, `${label} ${t2.deviceId.slice(0, 8)}`)) {
74476
+ fulfilled++;
74477
+ }
74291
74478
  }
74292
- if (await this._postFulfilWelcome(mlsGroupId, t2.requestId, `${label} ${t2.deviceId.slice(0, 8)}`)) {
74293
- fulfilled++;
74479
+ return fulfilled;
74480
+ } finally {
74481
+ this._commitInFlight.delete(mlsGroupId);
74482
+ for (const frame of inFlight.frames) {
74483
+ try {
74484
+ await this._handleMlsCommit(frame);
74485
+ } catch (err) {
74486
+ console.error(`[SecureChannel] ${label}: replaying a held MLS commit failed:`, err);
74487
+ }
74294
74488
  }
74295
74489
  }
74296
- return fulfilled;
74297
74490
  }
74298
74491
  /**
74299
74492
  * Resolve an MLS group id to the family that owns it.
@@ -74334,30 +74527,363 @@ ${baseText}`;
74334
74527
  return this._ws?.readyState === 1;
74335
74528
  }
74336
74529
  /** SHA-256 hex of a commit's bytes — its identity on the server (#1175/#1177). */
74530
+ /**
74531
+ * #1225: send ONE commit over the socket and wait for the server's verdict,
74532
+ * with the #1184 machinery — an in-flight record (so a `… commit rejected`
74533
+ * frame is correlated to it and a concurrent commit for the group is held
74534
+ * and replayed), the head poll, and rollback. The three A2A commit sites
74535
+ * (`a2a_participant_joined`, the `/distribute` WS fallback, `_foundA2AGroup`)
74536
+ * used a raw `ws.send` and treated "the socket took it" as "the server
74537
+ * stored it" — two of them then PERSISTED a state the server never saw.
74538
+ *
74539
+ * Returns the verdict. On `refused` / `unsent` the manager has been restored
74540
+ * to `stateBefore` and nothing must be persisted or minted from it.
74541
+ * `unverifiable` (head unreadable on every try AND no refusal) proceeds
74542
+ * UNCHECKED, the same deliberate choice as the fulfil site.
74543
+ */
74544
+ /**
74545
+ * #1225 (review F1): reserve the single in-flight slot for a group. The
74546
+ * record is what `_noteCommitRefusal` correlates a refusal to and what
74547
+ * `_handleMlsCommit` holds concurrent socket commits behind; a second run on
74548
+ * the same group would clobber both (measured: two overlapping joins → two
74549
+ * commits claiming one epoch, one Welcome instead of two, the manager AHEAD
74550
+ * of the server). Taken BEFORE the commit is generated; released by
74551
+ * `_releaseCommit` after the last step that depends on it.
74552
+ */
74553
+ /**
74554
+ * A pre-flight skip must not be a dead end. The skip happens because the
74555
+ * server's head is somewhere else; before this the group "caught up through
74556
+ * the normal divergence path", which only runs when a message for the group
74557
+ * arrives — so a group nobody writes to stayed diverged and its pending
74558
+ * Welcome requests were never fulfilled (named in PR #1227). When the server
74559
+ * is AHEAD, replay the missing commits now so the next pull can commit; when
74560
+ * WE are ahead there is nothing to replay (the fork-ahead direction, #972).
74561
+ */
74562
+ async _catchUpAfterPreflightSkip(mlsGroupId, serverEpoch, localEpoch, label) {
74563
+ if (serverEpoch < localEpoch) {
74564
+ console.warn(`[SecureChannel] ${label}: we are AHEAD of the server (${localEpoch} > ${serverEpoch}) \u2014 nothing to replay`);
74565
+ return;
74566
+ }
74567
+ const fam = this._resolveGroupFamily(mlsGroupId);
74568
+ if (!fam) return;
74569
+ try {
74570
+ const caughtUp = await this._resyncOnDivergence(fam.managerKey, mlsGroupId);
74571
+ console.log(`[SecureChannel] ${label}: catch-up after pre-flight skip ${caughtUp ? "replayed the missing commits" : "could not replay"}`);
74572
+ } catch (err) {
74573
+ console.warn(`[SecureChannel] ${label}: catch-up after pre-flight skip failed:`, err);
74574
+ }
74575
+ }
74576
+ _reserveCommit(groupId, label) {
74577
+ if (this._commitInFlight.has(groupId)) {
74578
+ console.warn(
74579
+ `[SecureChannel] ${label}: another commit for group ${groupId.slice(0, 8)} is in flight \u2014 NOT advancing the group (a Welcome can be requested again)`
74580
+ );
74581
+ return null;
74582
+ }
74583
+ const rec = { frames: [], sent: [] };
74584
+ this._commitInFlight.set(groupId, rec);
74585
+ return rec;
74586
+ }
74587
+ /** Release the slot taken by `_reserveCommit` and replay the socket commits held meanwhile. */
74588
+ async _releaseCommit(groupId, rec, label) {
74589
+ if (this._commitInFlight.get(groupId) === rec) this._commitInFlight.delete(groupId);
74590
+ for (const frame of rec.frames) {
74591
+ try {
74592
+ await this._handleMlsCommit(frame);
74593
+ } catch (err) {
74594
+ console.error(`[SecureChannel] ${label}: replaying a held MLS commit failed:`, err);
74595
+ }
74596
+ }
74597
+ }
74598
+ /**
74599
+ * #1225: send ONE commit over the socket and wait for the server's verdict
74600
+ * with the #1184 machinery (refusal frame correlated through the reserved
74601
+ * in-flight record, head poll). The three A2A commit sites
74602
+ * (`a2a_participant_joined`, the `/distribute` WS fallback, `_foundA2AGroup`)
74603
+ * used a raw `ws.send` and treated "the socket took it" as "the server
74604
+ * stored it" — two of them then PERSISTED a state the server never saw.
74605
+ *
74606
+ * Only `confirmed` proceeds. `unverifiable` (head unreadable on every try,
74607
+ * no refusal) is a ROLLBACK here, unlike the fulfil site: that site proceeds
74608
+ * UNCHECKED because 79 of 80 production rooms 403 the head read on an empty
74609
+ * roster; an A2A head read admits any active participant (observer
74610
+ * fallback), so five failed reads there mean an outage, not a routine 403.
74611
+ * On anything but `confirmed` the manager is back at `stateBefore`.
74612
+ */
74613
+ async _sendCommitConfirmed(opts) {
74614
+ const { mlsGroup, mlsGroupId, label, commit, epoch, parent: parent2, memberDeviceIds, stateBefore, inFlight } = opts;
74615
+ const ours = this._commitDigest(commit);
74616
+ inFlight.sent.push({ epoch, digest: ours, verified: false, stateBefore });
74617
+ const ok = await this._sendFrameConfirmed(
74618
+ {
74619
+ event: "mls_commit",
74620
+ data: {
74621
+ group_id: mlsGroupId,
74622
+ epoch,
74623
+ payload: Buffer.from(commit).toString("hex"),
74624
+ ...parent2 ? { parent_digest: parent2 } : {},
74625
+ ...memberDeviceIds && memberDeviceIds.length > 0 ? { member_device_ids: memberDeviceIds } : {}
74626
+ }
74627
+ },
74628
+ `${label} commit`
74629
+ );
74630
+ if (!ok) {
74631
+ inFlight.sent.pop();
74632
+ mlsGroup.importState(stateBefore);
74633
+ console.warn(
74634
+ `[SecureChannel] ${label}: commit at epoch ${epoch} was NOT written \u2014 rolled back to epoch ${Number(mlsGroup.epoch)}`
74635
+ );
74636
+ return "unsent";
74637
+ }
74638
+ const verdict = await this._confirmCommitAtHead(mlsGroupId, epoch, ours, inFlight, label);
74639
+ if (inFlight.refusal === void 0 && verdict === "confirmed") {
74640
+ inFlight.sent[inFlight.sent.length - 1].verified = true;
74641
+ return "confirmed";
74642
+ }
74643
+ const why = inFlight.refusal?.detail ?? verdict.toUpperCase();
74644
+ mlsGroup.importState(stateBefore);
74645
+ console.warn(
74646
+ `[SecureChannel] ${label}: commit at epoch ${epoch} was ${verdict === "unverifiable" ? "not verified (head unreadable)" : "refused by the server"} \u2014 "${why}" \u2014 rolled back to epoch ${Number(mlsGroup.epoch)}, nothing persisted`
74647
+ );
74648
+ return "refused";
74649
+ }
74337
74650
  _commitDigest(bytes) {
74338
74651
  return createHash("sha256").update(bytes).digest("hex");
74339
74652
  }
74340
74653
  /**
74341
74654
  * #1177: the digest of the commit at the server's head, or undefined.
74342
74655
  *
74343
- * Undefined covers "no head yet", "older backend", AND "could not read" —
74344
- * deliberately the same value. The commit is sent either way: the server
74345
- * skips the parent check for an ABSENT field and refuses a MALFORMED one,
74346
- * so a read failure must degrade to "unchecked", never to "blocked" (a
74347
- * rescue that cannot run is the 2026-09-04 lockout) and never to `null`.
74656
+ * Undefined covers "no head yet" (a pruned group: epoch known, digest NULL —
74657
+ * 29 in production) AND "could not read" — deliberately the same value. The
74658
+ * commit is sent either way: the server skips the parent check for an
74659
+ * ABSENT field and refuses a MALFORMED one, so a read failure must degrade
74660
+ * to "unchecked", never to "blocked" (a rescue that cannot run is the
74661
+ * 2026-09-04 lockout) and never to `null`.
74348
74662
  */
74349
74663
  async _readGroupHeadDigest(groupId) {
74664
+ const head = await this._readGroupHead(groupId);
74665
+ return head.ok ? head.digest : void 0;
74666
+ }
74667
+ /**
74668
+ * #1184: the server's head — its `current_epoch` and the digest of the commit
74669
+ * that produced it.
74670
+ *
74671
+ * - `{ ok: true, epoch, digest? }` the server described its head. `digest`
74672
+ * is absent on a pruned group (NULL
74673
+ * `last_commit_digest`).
74674
+ * - `{ ok: false, why }` the read failed: a non-2xx (the 403
74675
+ * `assert_group_member` returns on an
74676
+ * EMPTY roster — 79 of 80 production
74677
+ * rooms — or a 5xx), a timeout, or a body
74678
+ * with no `current_epoch`. Callers may
74679
+ * retry; a failure is never a verdict.
74680
+ *
74681
+ * `current_epoch` is a required field of `MLSGroupResponse`, so "200 with
74682
+ * no head" is not a shape any deployed backend produces; it is folded into
74683
+ * `ok: false` rather than given a branch of its own (the first cut of #1184
74684
+ * had one, and it was the only branch the suite exercised).
74685
+ */
74686
+ async _readGroupHead(groupId) {
74350
74687
  try {
74351
74688
  const res = await fetch(`${this.config.apiUrl}/api/v1/mls/groups/${groupId}`, {
74352
- headers: { Authorization: `Bearer ${this._deviceJwt}` }
74689
+ headers: { Authorization: `Bearer ${this._deviceJwt}` },
74690
+ // The confirm loop holds `_deliveryPulling`; a black-holed read must
74691
+ // not hold it for undici's 300 s default. A timeout is a failed read.
74692
+ signal: AbortSignal.timeout(_SecureChannel.HEAD_READ_TIMEOUT_MS)
74353
74693
  });
74354
- if (!res.ok) return void 0;
74694
+ if (!res.ok) return { ok: false, why: `HTTP ${res.status}` };
74355
74695
  const head = await res.json();
74356
- const d2 = head?.last_commit_digest;
74357
- return typeof d2 === "string" && /^[0-9a-f]{64}$/.test(d2) ? d2 : void 0;
74358
- } catch {
74359
- return void 0;
74696
+ if (typeof head?.current_epoch !== "number") return { ok: false, why: "no current_epoch in body" };
74697
+ const d2 = head.last_commit_digest;
74698
+ return { ok: true, epoch: head.current_epoch, digest: typeof d2 === "string" && /^[0-9a-f]{64}$/.test(d2) ? d2 : void 0 };
74699
+ } catch (err) {
74700
+ return { ok: false, why: err instanceof Error ? err.name : String(err) };
74701
+ }
74702
+ }
74703
+ /**
74704
+ * #1184: after each read of the head, how long to wait before the next.
74705
+ * The web's schedule (`lib/mls-commit-confirm.ts`) plus one immediate read:
74706
+ * five reads, ~3.75 s, no resend — an MLS commit cannot be regenerated. The
74707
+ * immediate read catches the common case where the server stored the frame
74708
+ * before the GET lands. Instance-level so tests can run the decision logic
74709
+ * without the clock; an empty schedule still reads once (`_confirmCommitAtHead`).
74710
+ */
74711
+ _commitConfirmDelaysMs = [0, 250, 500, 1e3, 2e3];
74712
+ static HEAD_READ_TIMEOUT_MS = 5e3;
74713
+ /**
74714
+ * #1184: groups with a commit of OURS between "built" and "confirmed or rolled
74715
+ * back". Each record holds the `mls_commit` frames the socket delivered for
74716
+ * that group meanwhile — before the confirm loop that window was
74717
+ * microseconds; now it is up to ~3.75 s, and a commit applied to our
74718
+ * unconfirmed local state would be lost by the rollback (in memory) after
74719
+ * being persisted (on disk). Held frames are replayed, in order, once the
74720
+ * group is back on a real state. The record also carries the server's
74721
+ * REFUSAL, when one arrives on the socket (`_noteCommitRefusal`), and a
74722
+ * `wake` the confirm loop installs so a refusal ends its wait at once.
74723
+ */
74724
+ _commitInFlight = /* @__PURE__ */ new Map();
74725
+ /** #1184: a pull was requested while one was running — run once more after. */
74726
+ _deliveryPullRequested = false;
74727
+ /**
74728
+ * #1184: an `error` frame naming a group we have a commit in flight for, with
74729
+ * a `… commit rejected` detail, is the server's refusal of one of those
74730
+ * commits — every rejection the `mls_commit` WS handler sends (malformed
74731
+ * parent, PARENT_MISMATCH, EPOCH_CONFLICT, not-a-member, no-participants)
74732
+ * ends with those words and carries `group_id` (pinned by
74733
+ * `tests/test_1184_commit_reject_frame_contract.py`). It is sent on OUR
74734
+ * socket after the server processed OUR frame, so it is deterministic in a
74735
+ * way the head poll is not, and it is the only refusal signal on a group
74736
+ * whose head we cannot read (403).
74737
+ *
74738
+ * WHICH commit, and what else the frame proves (re-review of #1227):
74739
+ * - EPOCH_CONFLICT carries `expected_epoch` (the epoch we claimed to build
74740
+ * on) and `current_epoch` (the server's); PARENT_MISMATCH carries
74741
+ * `current_epoch` (the head, which equalled our claim or the epoch check
74742
+ * would have fired first) and `head_digest`. The refused commit is the
74743
+ * one built on that epoch. A frame naming an epoch none of our in-flight
74744
+ * commits claimed is a refusal of some EARLIER attempt — ignored.
74745
+ * - the server's `current_epoch` bounds our earlier commits too: any of
74746
+ * ours ABOVE it did not land.
74747
+ * - `head_digest` at `current_epoch` is our commit at that epoch → that one
74748
+ * positively LANDED; someone else's → ours at that epoch did not.
74749
+ * - the un-numbered shapes refuse all of ours alike: the cut is the first
74750
+ * commit the head poll has not positively confirmed.
74751
+ * Recorded as the index of the first commit shown not to hold; the fulfil
74752
+ * path honours it whenever it arrives. A frame for a group with nothing in
74753
+ * flight is somebody else's business.
74754
+ */
74755
+ _noteCommitRefusal(payload) {
74756
+ const groupId = typeof payload?.group_id === "string" ? payload.group_id : void 0;
74757
+ const detail = typeof payload?.detail === "string" ? payload.detail : "";
74758
+ if (!groupId || !detail.endsWith("commit rejected")) return;
74759
+ const inFlight = this._commitInFlight.get(groupId);
74760
+ if (!inFlight) return;
74761
+ const num = (v2) => typeof v2 === "number" && Number.isFinite(v2) ? v2 : void 0;
74762
+ const current = num(payload.current_epoch);
74763
+ const claimed = num(payload.expected_epoch) ?? current;
74764
+ const headDigest = typeof payload.head_digest === "string" ? payload.head_digest : void 0;
74765
+ const sent = inFlight.sent;
74766
+ const tag = `[SecureChannel] refusal for ${groupId.slice(0, 8)} ("${detail}")`;
74767
+ let from = -1;
74768
+ if (claimed !== void 0) {
74769
+ from = sent.findIndex((c2) => c2.epoch === claimed + 1);
74770
+ if (from === -1) {
74771
+ console.warn(`${tag} names epoch ${claimed + 1}, which no commit in flight claimed \u2014 an earlier attempt's; ignored`);
74772
+ return;
74773
+ }
74774
+ }
74775
+ if (current !== void 0) {
74776
+ const above = sent.findIndex((c2) => c2.epoch > current && !c2.verified);
74777
+ if (above !== -1 && (from === -1 || above < from)) from = above;
74778
+ }
74779
+ if (headDigest !== void 0 && current !== void 0) {
74780
+ const at = sent.findIndex((c2) => c2.epoch === current);
74781
+ if (at !== -1) {
74782
+ if (sent[at].digest === headDigest) {
74783
+ sent[at].verified = true;
74784
+ } else if (from === -1 || at < from) {
74785
+ from = at;
74786
+ }
74787
+ }
74788
+ }
74789
+ if (from === -1) from = sent.findIndex((c2) => !c2.verified);
74790
+ if (from === -1 || sent[from].verified) {
74791
+ console.warn(`${tag} contradicts a head read that showed our commit \u2014 stale; ignored`);
74792
+ return;
74793
+ }
74794
+ if (inFlight.refusal !== void 0 && inFlight.refusal.from <= from) return;
74795
+ inFlight.refusal = { detail, from };
74796
+ inFlight.wake?.();
74797
+ }
74798
+ /**
74799
+ * #1184: a commit is CONFIRMED when the server's head shows OUR digest at OUR
74800
+ * epoch — never when the socket took the bytes (#1015 covers only that). The
74801
+ * server sends no ack for a stored commit, so, like the web, we read the
74802
+ * head back; but it DOES send a refusal on the socket (`_noteCommitRefusal`),
74803
+ * and that is checked first, before every read and during every wait.
74804
+ *
74805
+ * confirmed head.epoch === ours && head.digest === ours
74806
+ * conflict the server refused ours on the socket, OR the head is at
74807
+ * our epoch with ANOTHER digest, OR already past us
74808
+ * unconfirmed the head was READ (at least once) but never reached our
74809
+ * epoch within the window — the server described its state
74810
+ * and ours is not in it
74811
+ * unverifiable the head could not be read on ANY try (403 on an empty
74812
+ * roster, an outage) and no refusal arrived — or it is at
74813
+ * our epoch but keeps no digest (a pruned group). Nothing
74814
+ * can be decided on; the caller proceeds UNCHECKED (#1177's
74815
+ * rule: a rescue that cannot run is the 2026-09-04 lockout)
74816
+ *
74817
+ * The first cut returned `unverifiable` only for a body with no
74818
+ * `current_epoch`, which no deployed backend produces, and rolled back on
74819
+ * every real read failure — the opposite of the rule it stated.
74820
+ */
74821
+ async _confirmCommitAtHead(groupId, epoch, ours, inFlight, label) {
74822
+ const refused = () => {
74823
+ if (inFlight.refusal === void 0) return void 0;
74824
+ console.warn(`[SecureChannel] ${label}: server refused our commit at epoch ${epoch} \u2014 "${inFlight.refusal.detail}"`);
74825
+ return "conflict";
74826
+ };
74827
+ let lastSeen;
74828
+ let described = false;
74829
+ let lastFailure = "";
74830
+ const delays = this._commitConfirmDelaysMs.length > 0 ? this._commitConfirmDelaysMs : [0];
74831
+ for (const delay of delays) {
74832
+ if (delay > 0) {
74833
+ await new Promise((resolve3) => {
74834
+ const timer = setTimeout(() => {
74835
+ inFlight.wake = void 0;
74836
+ resolve3();
74837
+ }, delay);
74838
+ inFlight.wake = () => {
74839
+ clearTimeout(timer);
74840
+ inFlight.wake = void 0;
74841
+ resolve3();
74842
+ };
74843
+ });
74844
+ }
74845
+ const early = refused();
74846
+ if (early) return early;
74847
+ const head = await this._readGroupHead(groupId);
74848
+ const duringRead = refused();
74849
+ if (duringRead) return duringRead;
74850
+ if (!head.ok) {
74851
+ lastFailure = head.why;
74852
+ continue;
74853
+ }
74854
+ described = true;
74855
+ lastSeen = head.epoch;
74856
+ if (head.epoch === epoch) {
74857
+ if (head.digest === ours) return "confirmed";
74858
+ if (head.digest === void 0) return "unverifiable";
74859
+ console.warn(
74860
+ `[SecureChannel] ${label}: head at epoch ${epoch} is NOT our commit (${head.digest.slice(0, 12)}\u2026 vs ours ${ours.slice(0, 12)}\u2026) \u2014 ours was refused`
74861
+ );
74862
+ return "conflict";
74863
+ }
74864
+ if (head.epoch > epoch) {
74865
+ console.warn(`[SecureChannel] ${label}: head already at epoch ${head.epoch}, past our ${epoch} \u2014 ours was refused`);
74866
+ return "conflict";
74867
+ }
74868
+ }
74869
+ const late2 = refused();
74870
+ if (late2) return late2;
74871
+ if (!described && !this._wsIsOpen()) {
74872
+ console.warn(
74873
+ `[SecureChannel] ${label}: the socket closed during the confirm window and the head could not be read \u2014 a refusal of commit at epoch ${epoch} may have been lost; treating as refused`
74874
+ );
74875
+ return "unconfirmed";
74876
+ }
74877
+ if (!described) {
74878
+ console.warn(
74879
+ `[SecureChannel] ${label}: could not read the head on any of ${delays.length} tries (last: ${lastFailure}) and the server sent no refusal \u2014 cannot verify commit at epoch ${epoch}`
74880
+ );
74881
+ return "unverifiable";
74360
74882
  }
74883
+ console.warn(
74884
+ `[SecureChannel] ${label}: commit at epoch ${epoch} never appeared at the head (last seen ${lastSeen}) \u2014 treating as refused`
74885
+ );
74886
+ return "unconfirmed";
74361
74887
  }
74362
74888
  /**
74363
74889
  * #1015: write one frame and report whether the socket took it.
@@ -74879,7 +75405,11 @@ ${baseText}`;
74879
75405
  * Called on WS connect, on mls_delivery ping, and every 30s heartbeat.
74880
75406
  */
74881
75407
  async _pullDeliveryQueue() {
74882
- if (!this._deviceId || !this._deviceJwt || this._deliveryPulling) return;
75408
+ if (!this._deviceId || !this._deviceJwt) return;
75409
+ if (this._deliveryPulling) {
75410
+ this._deliveryPullRequested = true;
75411
+ return;
75412
+ }
74883
75413
  this._deliveryPulling = true;
74884
75414
  try {
74885
75415
  const res = await fetch(
@@ -75070,7 +75600,9 @@ ${baseText}`;
75070
75600
  mlsGroupId: roomState.mlsGroupId,
75071
75601
  label: `room ${roomId.slice(0, 8)}`,
75072
75602
  welcomeFields: { room_id: roomId },
75073
- targets: roomTargets
75603
+ targets: roomTargets,
75604
+ memberDeviceIds: (roomState.members ?? []).map((m2) => m2.deviceId)
75605
+ // #1226
75074
75606
  });
75075
75607
  if (done > 0) {
75076
75608
  console.log(
@@ -75125,7 +75657,9 @@ ${baseText}`;
75125
75657
  mlsGroupId: entry.mlsGroupId,
75126
75658
  label: `shared group ${gid.slice(0, 8)}`,
75127
75659
  welcomeFields: { conversation_group_id: gid },
75128
- targets: sharedTargets
75660
+ targets: sharedTargets,
75661
+ memberDeviceIds: entry.memberDeviceIds ?? []
75662
+ // #1226
75129
75663
  });
75130
75664
  if (done > 0) {
75131
75665
  console.log(`[SecureChannel] Batched Welcome for ${done}/${validReqs.length} devices in shared group ${gid.slice(0, 8)} (epoch=${mlsGroup.epoch})`);
@@ -75139,7 +75673,9 @@ ${baseText}`;
75139
75673
  mlsGroupId: entry.mlsGroupId,
75140
75674
  label: `shared group ${gid.slice(0, 8)}`,
75141
75675
  welcomeFields: { conversation_group_id: gid },
75142
- targets: [target]
75676
+ targets: [target],
75677
+ memberDeviceIds: entry.memberDeviceIds ?? []
75678
+ // #1226
75143
75679
  });
75144
75680
  if (done > 0) {
75145
75681
  console.log(`[SecureChannel] Individual Welcome for ${target.deviceId.slice(0, 8)} in shared group ${gid.slice(0, 8)}`);
@@ -75186,7 +75722,11 @@ ${baseText}`;
75186
75722
  requestId: req.id,
75187
75723
  deviceId: req.requesting_device_id,
75188
75724
  keyPackage: memberKp
75189
- }]
75725
+ }],
75726
+ // #1226: a 1:1 roster is [self, owner] — the web's shape too.
75727
+ memberDeviceIds: [this._persisted?.sessions?.[convId]?.ownerDeviceId].filter(
75728
+ (d2) => typeof d2 === "string"
75729
+ )
75190
75730
  });
75191
75731
  if (done > 0) {
75192
75732
  console.log(`[SecureChannel] Fulfilled Welcome for ${req.requesting_device_id.slice(0, 8)} in conv ${convId.slice(0, 8)}`);
@@ -75244,192 +75784,8 @@ ${baseText}`;
75244
75784
  console.log(`[SecureChannel] ${pending.length} pending A2A Welcome requests for channel ${chId.slice(0, 8)}`);
75245
75785
  for (const req of pending) {
75246
75786
  try {
75247
- const kpRes = await fetch(
75248
- `${this.config.apiUrl}/api/v1/mls/key-packages/batch?device_ids=${req.requesting_device_id}`,
75249
- { headers: { Authorization: `Bearer ${this._deviceJwt}` } }
75250
- );
75251
- if (!kpRes.ok) continue;
75252
- const kpData = await kpRes.json();
75253
- const kpHex = kpData[req.requesting_device_id];
75254
- if (!kpHex) continue;
75255
- const kpBytes = new Uint8Array(Buffer.from(kpHex, "hex"));
75256
- const memberKp = MLSGroupManager.deserializeKeyPackage(kpBytes);
75257
- console.log(`[SecureChannel] A2A addMembers for ${req.requesting_device_id.slice(0, 8)} group=${chState.mlsGroupId.slice(0, 8)} epoch=${mlsGroup.epoch}`);
75258
- let parentDigest = await this._readGroupHeadDigest(chState.mlsGroupId);
75259
- let commit;
75260
- let welcome;
75261
- try {
75262
- const result = await mlsGroup.addMembers([memberKp]);
75263
- commit = result.commit;
75264
- welcome = result.welcome;
75265
- } catch (addErr) {
75266
- const errMsg = addErr instanceof Error ? addErr.message : String(addErr);
75267
- if (errMsg.includes("already in the group")) {
75268
- console.log(`[SecureChannel] A2A member already in group \u2014 removing and re-adding ${req.requesting_device_id.slice(0, 8)}`);
75269
- const preRemoveState = JSON.stringify(mlsGroup.exportState());
75270
- let removeCommitPublished = false;
75271
- try {
75272
- const identityBytes = new TextEncoder().encode(req.requesting_device_id);
75273
- const leafIndex = mlsGroup.memberLeafIndex(identityBytes);
75274
- if (leafIndex === null) {
75275
- console.log(`[SecureChannel] A2A re-add: no leaf index for ${req.requesting_device_id.slice(0, 8)} \u2014 cannot remove`);
75276
- continue;
75277
- }
75278
- const removeResult = await mlsGroup.removeMember(leafIndex);
75279
- try {
75280
- const removeResp = await fetch(
75281
- `${this.config.apiUrl}/api/v1/mls/groups/${chState.mlsGroupId}/distribute`,
75282
- {
75283
- method: "POST",
75284
- headers: { Authorization: `Bearer ${this._deviceJwt}`, "Content-Type": "application/json" },
75285
- body: JSON.stringify({
75286
- group_id: chState.mlsGroupId,
75287
- device_id: this._deviceId,
75288
- commit: Buffer.from(removeResult.commit).toString("hex"),
75289
- commit_epoch: Number(mlsGroup.epoch),
75290
- ...parentDigest ? { parent_digest: parentDigest } : {}
75291
- })
75292
- }
75293
- );
75294
- if (removeResp.ok) {
75295
- removeCommitPublished = true;
75296
- parentDigest = this._commitDigest(removeResult.commit);
75297
- } else {
75298
- const detail = await removeResp.text().catch(() => "");
75299
- console.warn(`[SecureChannel] A2A remove-commit distribute failed (${removeResp.status}): ${detail}`);
75300
- }
75301
- } catch (distErr) {
75302
- console.warn(`[SecureChannel] A2A remove-commit distribute network error:`, distErr);
75303
- }
75304
- if (!removeCommitPublished) {
75305
- mlsGroup.importState(JSON.parse(preRemoveState));
75306
- continue;
75307
- }
75308
- const kpRes2 = await fetch(
75309
- `${this.config.apiUrl}/api/v1/mls/key-packages/batch?device_ids=${req.requesting_device_id}`,
75310
- { headers: { Authorization: `Bearer ${this._deviceJwt}` } }
75311
- );
75312
- if (kpRes2.ok) {
75313
- const kpData2 = await kpRes2.json();
75314
- const kpHex2 = kpData2[req.requesting_device_id];
75315
- if (kpHex2) {
75316
- const kpBytes2 = new Uint8Array(Buffer.from(kpHex2, "hex"));
75317
- const memberKp2 = MLSGroupManager.deserializeKeyPackage(kpBytes2);
75318
- const result2 = await mlsGroup.addMembers([memberKp2]);
75319
- commit = result2.commit;
75320
- welcome = result2.welcome;
75321
- } else {
75322
- console.log(`[SecureChannel] A2A re-add: no fresh KeyPackage for ${req.requesting_device_id.slice(0, 8)}`);
75323
- continue;
75324
- }
75325
- } else {
75326
- console.log(`[SecureChannel] A2A re-add: KeyPackage fetch failed (${kpRes2.status})`);
75327
- continue;
75328
- }
75329
- } catch (readdErr) {
75330
- console.log(`[SecureChannel] A2A remove+re-add FAILED: ${readdErr instanceof Error ? readdErr.message : String(readdErr)}`);
75331
- if (!removeCommitPublished) {
75332
- try {
75333
- mlsGroup.importState(JSON.parse(preRemoveState));
75334
- } catch {
75335
- }
75336
- }
75337
- continue;
75338
- }
75339
- } else {
75340
- console.log(`[SecureChannel] A2A addMembers FAILED: ${errMsg}`);
75341
- continue;
75342
- }
75343
- }
75344
- if (!welcome) {
75345
- console.warn(`[SecureChannel] A2A addMembers produced no Welcome for ${req.requesting_device_id.slice(0, 8)}`);
75346
- continue;
75347
- }
75348
- const commitHex = Buffer.from(commit).toString("hex");
75349
- const welcomeHex = Buffer.from(welcome).toString("hex");
75350
- let distributed = false;
75351
- const updatedMembers = [this._deviceId, req.requesting_device_id];
75352
- try {
75353
- const distResp = await fetch(
75354
- `${this.config.apiUrl}/api/v1/mls/groups/${chState.mlsGroupId}/distribute`,
75355
- {
75356
- method: "POST",
75357
- headers: { Authorization: `Bearer ${this._deviceJwt}`, "Content-Type": "application/json" },
75358
- body: JSON.stringify({
75359
- group_id: chState.mlsGroupId,
75360
- device_id: this._deviceId,
75361
- commit: commitHex,
75362
- commit_epoch: Number(mlsGroup.epoch),
75363
- welcomes: [{ target_device_id: req.requesting_device_id, payload: welcomeHex }],
75364
- member_device_ids: updatedMembers,
75365
- ...parentDigest ? { parent_digest: parentDigest } : {}
75366
- })
75367
- }
75368
- );
75369
- if (distResp.ok) {
75370
- distributed = true;
75371
- } else {
75372
- const detail = await distResp.text().catch(() => "");
75373
- console.warn(`[SecureChannel] A2A distribute failed (${distResp.status}): ${detail}`);
75374
- }
75375
- } catch (distErr) {
75376
- console.warn(`[SecureChannel] A2A distribute network error:`, distErr);
75377
- }
75378
- if (!distributed && this._wsIsOpen()) {
75379
- const a2aLabel = `A2A ${chId.slice(0, 8)}`;
75380
- const commitSent = await this._sendFrameConfirmed({
75381
- event: "mls_commit",
75382
- data: {
75383
- group_id: chState.mlsGroupId,
75384
- epoch: Number(mlsGroup.epoch),
75385
- payload: commitHex,
75386
- ...parentDigest ? { parent_digest: parentDigest } : {}
75387
- }
75388
- }, `${a2aLabel} commit`);
75389
- const welcomeSent = commitSent && await this._sendFrameConfirmed({
75390
- event: "mls_welcome",
75391
- data: {
75392
- target_device_id: req.requesting_device_id,
75393
- group_id: chState.mlsGroupId,
75394
- a2a_channel_id: chId,
75395
- payload: welcomeHex
75396
- }
75397
- }, `${a2aLabel} welcome`);
75398
- if (!welcomeSent) {
75399
- console.warn(`[SecureChannel] ${a2aLabel}: Welcome frames were NOT written \u2014 leaving the request pending`);
75400
- continue;
75401
- }
75402
- try {
75403
- await fetch(
75404
- `${this.config.apiUrl}/api/v1/mls/groups/${chState.mlsGroupId}/distribute`,
75405
- {
75406
- method: "POST",
75407
- headers: { Authorization: `Bearer ${this._deviceJwt}`, "Content-Type": "application/json" },
75408
- body: JSON.stringify({
75409
- group_id: chState.mlsGroupId,
75410
- device_id: this._deviceId,
75411
- commit_epoch: Number(mlsGroup.epoch),
75412
- member_device_ids: updatedMembers
75413
- })
75414
- }
75415
- );
75416
- } catch {
75417
- }
75418
- distributed = true;
75419
- }
75420
- if (!distributed) {
75421
- console.warn(`[SecureChannel] Cannot deliver Welcome \u2014 HTTP distribute and WS both unavailable`);
75422
- continue;
75423
- }
75424
- const a2aFulfilled = await this._postFulfilWelcome(
75425
- chState.mlsGroupId,
75426
- req.id,
75427
- `A2A ${chId.slice(0, 8)}`
75428
- );
75429
- await saveMlsState(this.config.dataDir, chState.mlsGroupId, JSON.stringify(mlsGroup.exportState()));
75430
- if (a2aFulfilled) {
75431
- console.log(`[SecureChannel] Fulfilled A2A Welcome for ${req.requesting_device_id.slice(0, 8)} in channel ${chId.slice(0, 8)}`);
75432
- }
75787
+ const next = await this._fulfilA2AWelcome(chId, chState, mlsGroup, req);
75788
+ if (next === "stop") break;
75433
75789
  } catch (fulfillErr) {
75434
75790
  console.log(`[SecureChannel] A2A Welcome fulfill failed: ${fulfillErr instanceof Error ? fulfillErr.message : String(fulfillErr)}`);
75435
75791
  }
@@ -75441,6 +75797,10 @@ ${baseText}`;
75441
75797
  console.warn("[SecureChannel] Delivery pull error:", err);
75442
75798
  } finally {
75443
75799
  this._deliveryPulling = false;
75800
+ if (this._deliveryPullRequested) {
75801
+ this._deliveryPullRequested = false;
75802
+ void this._pullDeliveryQueue().catch((err) => console.warn("[SecureChannel] Coalesced delivery pull error:", err));
75803
+ }
75444
75804
  }
75445
75805
  }
75446
75806
  /**
@@ -75524,7 +75884,7 @@ ${baseText}`;
75524
75884
  try {
75525
75885
  const parsed = JSON.parse(plaintext);
75526
75886
  messageType = parsed.type || "message";
75527
- messageText = parsed.text || plaintext;
75887
+ messageText = parsed.text ?? plaintext;
75528
75888
  } catch {
75529
75889
  messageType = "message";
75530
75890
  messageText = plaintext;
@@ -75696,6 +76056,227 @@ ${baseText}`;
75696
76056
  }
75697
76057
  }
75698
76058
  }
76059
+ /**
76060
+ * #1225: fulfil ONE pending A2A Welcome request — the `/distribute` path.
76061
+ * Extracted from the delivery pull so the in-flight slot (review F1) is held
76062
+ * for exactly this request and released on every exit. Returns "stop" when
76063
+ * nothing else on this channel can succeed this pull (review F2).
76064
+ */
76065
+ async _fulfilA2AWelcome(chId, chState, mlsGroup, req) {
76066
+ const a2aLabel = `A2A ${chId.slice(0, 8)}`;
76067
+ const preHead = await this._readGroupHead(chState.mlsGroupId);
76068
+ if (preHead.ok && preHead.epoch !== Number(mlsGroup.epoch)) {
76069
+ console.warn(
76070
+ `[SecureChannel] ${a2aLabel}: server head is at epoch ${preHead.epoch}, we are at ${Number(mlsGroup.epoch)} \u2014 NOT committing (it would be refused); resync catches up first`
76071
+ );
76072
+ await this._catchUpAfterPreflightSkip(chState.mlsGroupId, preHead.epoch, Number(mlsGroup.epoch), a2aLabel);
76073
+ return "stop";
76074
+ }
76075
+ const inFlight = this._reserveCommit(chState.mlsGroupId, a2aLabel);
76076
+ if (!inFlight) return "stop";
76077
+ try {
76078
+ const kpRes = await fetch(
76079
+ `${this.config.apiUrl}/api/v1/mls/key-packages/batch?device_ids=${req.requesting_device_id}`,
76080
+ { headers: { Authorization: `Bearer ${this._deviceJwt}` } }
76081
+ );
76082
+ if (!kpRes.ok) return "next";
76083
+ const kpData = await kpRes.json();
76084
+ const kpHex = kpData[req.requesting_device_id];
76085
+ if (!kpHex) return "next";
76086
+ const kpBytes = new Uint8Array(Buffer.from(kpHex, "hex"));
76087
+ const memberKp = MLSGroupManager.deserializeKeyPackage(kpBytes);
76088
+ console.log(`[SecureChannel] A2A addMembers for ${req.requesting_device_id.slice(0, 8)} group=${chState.mlsGroupId.slice(0, 8)} epoch=${mlsGroup.epoch}`);
76089
+ let parentDigest = preHead.ok ? preHead.digest : void 0;
76090
+ let commit;
76091
+ let welcome;
76092
+ let preAddState = mlsGroup.exportState();
76093
+ try {
76094
+ const result = await mlsGroup.addMembers([memberKp]);
76095
+ commit = result.commit;
76096
+ welcome = result.welcome;
76097
+ } catch (addErr) {
76098
+ const errMsg = addErr instanceof Error ? addErr.message : String(addErr);
76099
+ if (errMsg.includes("already in the group")) {
76100
+ console.log(`[SecureChannel] A2A member already in group \u2014 removing and re-adding ${req.requesting_device_id.slice(0, 8)}`);
76101
+ const preRemoveState = JSON.stringify(mlsGroup.exportState());
76102
+ let removeCommitPublished = false;
76103
+ try {
76104
+ const identityBytes = new TextEncoder().encode(req.requesting_device_id);
76105
+ const leafIndex = mlsGroup.memberLeafIndex(identityBytes);
76106
+ if (leafIndex === null) {
76107
+ console.log(`[SecureChannel] A2A re-add: no leaf index for ${req.requesting_device_id.slice(0, 8)} \u2014 cannot remove`);
76108
+ return "next";
76109
+ }
76110
+ const removeResult = await mlsGroup.removeMember(leafIndex);
76111
+ try {
76112
+ const removeResp = await fetch(
76113
+ `${this.config.apiUrl}/api/v1/mls/groups/${chState.mlsGroupId}/distribute`,
76114
+ {
76115
+ method: "POST",
76116
+ headers: { Authorization: `Bearer ${this._deviceJwt}`, "Content-Type": "application/json" },
76117
+ body: JSON.stringify({
76118
+ group_id: chState.mlsGroupId,
76119
+ device_id: this._deviceId,
76120
+ commit: Buffer.from(removeResult.commit).toString("hex"),
76121
+ commit_epoch: Number(mlsGroup.epoch),
76122
+ expected_epoch: Number(mlsGroup.epoch) - 1,
76123
+ // #1225: exact-step, like the WS path
76124
+ ...parentDigest ? { parent_digest: parentDigest } : {}
76125
+ })
76126
+ }
76127
+ );
76128
+ if (removeResp.ok) {
76129
+ removeCommitPublished = true;
76130
+ await saveMlsState(this.config.dataDir, chState.mlsGroupId, JSON.stringify(mlsGroup.exportState()));
76131
+ parentDigest = this._commitDigest(removeResult.commit);
76132
+ } else {
76133
+ const detail = await removeResp.text().catch(() => "");
76134
+ console.warn(`[SecureChannel] A2A remove-commit distribute failed (${removeResp.status}): ${detail}`);
76135
+ }
76136
+ } catch (distErr) {
76137
+ console.warn(`[SecureChannel] A2A remove-commit distribute network error:`, distErr);
76138
+ }
76139
+ if (!removeCommitPublished) {
76140
+ mlsGroup.importState(JSON.parse(preRemoveState));
76141
+ return "next";
76142
+ }
76143
+ const kpRes2 = await fetch(
76144
+ `${this.config.apiUrl}/api/v1/mls/key-packages/batch?device_ids=${req.requesting_device_id}`,
76145
+ { headers: { Authorization: `Bearer ${this._deviceJwt}` } }
76146
+ );
76147
+ if (kpRes2.ok) {
76148
+ const kpData2 = await kpRes2.json();
76149
+ const kpHex2 = kpData2[req.requesting_device_id];
76150
+ if (kpHex2) {
76151
+ const kpBytes2 = new Uint8Array(Buffer.from(kpHex2, "hex"));
76152
+ const memberKp2 = MLSGroupManager.deserializeKeyPackage(kpBytes2);
76153
+ preAddState = mlsGroup.exportState();
76154
+ const result2 = await mlsGroup.addMembers([memberKp2]);
76155
+ commit = result2.commit;
76156
+ welcome = result2.welcome;
76157
+ } else {
76158
+ console.log(`[SecureChannel] A2A re-add: no fresh KeyPackage for ${req.requesting_device_id.slice(0, 8)}`);
76159
+ return "next";
76160
+ }
76161
+ } else {
76162
+ console.log(`[SecureChannel] A2A re-add: KeyPackage fetch failed (${kpRes2.status})`);
76163
+ return "next";
76164
+ }
76165
+ } catch (readdErr) {
76166
+ console.log(`[SecureChannel] A2A remove+re-add FAILED: ${readdErr instanceof Error ? readdErr.message : String(readdErr)}`);
76167
+ if (!removeCommitPublished) {
76168
+ try {
76169
+ mlsGroup.importState(JSON.parse(preRemoveState));
76170
+ } catch {
76171
+ }
76172
+ }
76173
+ return "next";
76174
+ }
76175
+ } else {
76176
+ console.log(`[SecureChannel] A2A addMembers FAILED: ${errMsg}`);
76177
+ return "next";
76178
+ }
76179
+ }
76180
+ if (!welcome) {
76181
+ console.warn(`[SecureChannel] A2A addMembers produced no Welcome for ${req.requesting_device_id.slice(0, 8)}`);
76182
+ return "next";
76183
+ }
76184
+ const commitHex = Buffer.from(commit).toString("hex");
76185
+ const welcomeHex = Buffer.from(welcome).toString("hex");
76186
+ let distributed = false;
76187
+ let transportFailed = false;
76188
+ const updatedMembers = this._commitRoster([this._deviceId], [req.requesting_device_id]);
76189
+ try {
76190
+ const distResp = await fetch(
76191
+ `${this.config.apiUrl}/api/v1/mls/groups/${chState.mlsGroupId}/distribute`,
76192
+ {
76193
+ method: "POST",
76194
+ headers: { Authorization: `Bearer ${this._deviceJwt}`, "Content-Type": "application/json" },
76195
+ body: JSON.stringify({
76196
+ group_id: chState.mlsGroupId,
76197
+ device_id: this._deviceId,
76198
+ commit: commitHex,
76199
+ commit_epoch: Number(mlsGroup.epoch),
76200
+ welcomes: [{ target_device_id: req.requesting_device_id, payload: welcomeHex }],
76201
+ member_device_ids: updatedMembers,
76202
+ expected_epoch: Number(mlsGroup.epoch) - 1,
76203
+ // #1225: exact-step, like the WS path
76204
+ ...parentDigest ? { parent_digest: parentDigest } : {}
76205
+ })
76206
+ }
76207
+ );
76208
+ if (distResp.ok) {
76209
+ distributed = true;
76210
+ } else {
76211
+ const detail = await distResp.text().catch(() => "");
76212
+ console.warn(`[SecureChannel] A2A distribute failed (${distResp.status}): ${detail}`);
76213
+ if (distResp.status >= 500) {
76214
+ transportFailed = true;
76215
+ } else if (distResp.status === 409) {
76216
+ mlsGroup.importState(preAddState);
76217
+ console.warn(
76218
+ `[SecureChannel] ${a2aLabel}: add commit lost the race (409) \u2014 rolled back to epoch ${Number(mlsGroup.epoch)}, request stays pending`
76219
+ );
76220
+ return "next";
76221
+ } else {
76222
+ mlsGroup.importState(preAddState);
76223
+ console.error(
76224
+ `[SecureChannel] ${a2aLabel}: add commit refused with ${distResp.status} \u2014 rolled back to epoch ${Number(mlsGroup.epoch)}; not retrying this channel this pull`
76225
+ );
76226
+ return "stop";
76227
+ }
76228
+ }
76229
+ } catch (distErr) {
76230
+ console.warn(`[SecureChannel] A2A distribute network error:`, distErr);
76231
+ transportFailed = true;
76232
+ }
76233
+ if (!distributed && transportFailed && this._wsIsOpen()) {
76234
+ const verdict = await this._sendCommitConfirmed({
76235
+ mlsGroup,
76236
+ mlsGroupId: chState.mlsGroupId,
76237
+ label: a2aLabel,
76238
+ commit,
76239
+ epoch: Number(mlsGroup.epoch),
76240
+ parent: parentDigest,
76241
+ memberDeviceIds: updatedMembers,
76242
+ stateBefore: preAddState,
76243
+ inFlight
76244
+ });
76245
+ if (verdict !== "confirmed") return "next";
76246
+ const welcomeSent = await this._sendFrameConfirmed({
76247
+ event: "mls_welcome",
76248
+ data: {
76249
+ target_device_id: req.requesting_device_id,
76250
+ group_id: chState.mlsGroupId,
76251
+ a2a_channel_id: chId,
76252
+ payload: welcomeHex
76253
+ }
76254
+ }, `${a2aLabel} welcome`);
76255
+ if (!welcomeSent) {
76256
+ await saveMlsState(this.config.dataDir, chState.mlsGroupId, JSON.stringify(mlsGroup.exportState()));
76257
+ console.warn(`[SecureChannel] ${a2aLabel}: Welcome frame was NOT written \u2014 leaving the request pending`);
76258
+ return "next";
76259
+ }
76260
+ distributed = true;
76261
+ }
76262
+ if (!distributed) {
76263
+ console.warn(`[SecureChannel] Cannot deliver Welcome \u2014 HTTP distribute and WS both unavailable`);
76264
+ return "next";
76265
+ }
76266
+ const a2aFulfilled = await this._postFulfilWelcome(
76267
+ chState.mlsGroupId,
76268
+ req.id,
76269
+ `A2A ${chId.slice(0, 8)}`
76270
+ );
76271
+ await saveMlsState(this.config.dataDir, chState.mlsGroupId, JSON.stringify(mlsGroup.exportState()));
76272
+ if (a2aFulfilled) {
76273
+ console.log(`[SecureChannel] Fulfilled A2A Welcome for ${req.requesting_device_id.slice(0, 8)} in channel ${chId.slice(0, 8)}`);
76274
+ }
76275
+ return "next";
76276
+ } finally {
76277
+ await this._releaseCommit(chState.mlsGroupId, inFlight, a2aLabel);
76278
+ }
76279
+ }
75699
76280
  /**
75700
76281
  * Found the MLS group for an A2A channel as the creator agent.
75701
76282
  * Calls /mls/a2a/{channelId}/init, creates the local MLS group, and sends
@@ -75738,48 +76319,77 @@ ${baseText}`;
75738
76319
  await saveMlsState(this.config.dataDir, mlsData.group_id, JSON.stringify(mgr.exportState()));
75739
76320
  for (const p2 of participants) {
75740
76321
  if (p2.status !== "active" && p2.status !== "invited") continue;
76322
+ const a2aLabel = `A2A ${channelId.slice(0, 8)} found\u2192${p2.hubAddress}`;
75741
76323
  try {
75742
- const kpRes = await fetch(
75743
- `${this.config.apiUrl}/api/v1/mls/key-packages?hub_id=${encodeURIComponent(p2.hubId)}`,
75744
- { headers: { Authorization: `Bearer ${this._deviceJwt}` } }
75745
- );
75746
- if (kpRes.ok) {
75747
- const kpData = await kpRes.json();
75748
- if (kpData.key_package) {
75749
- const kpBytes = new Uint8Array(Buffer.from(kpData.key_package, "hex"));
75750
- const memberKp = MLSGroupManager.deserializeKeyPackage(kpBytes);
75751
- const { commit, welcome } = await mgr.addMembers([memberKp]);
75752
- if (welcome && this._ws) {
75753
- this._ws.send(JSON.stringify({
75754
- event: "mls_commit",
76324
+ if (!p2.deviceId) {
76325
+ console.warn(
76326
+ `[SecureChannel] ${a2aLabel}: no deviceId \u2014 NOT adding (a Welcome could not be addressed). Backend a2a_channel_approved payload may be missing device_id for this participant.`
76327
+ );
76328
+ continue;
76329
+ }
76330
+ if (!this._wsIsOpen()) {
76331
+ console.warn(`[SecureChannel] ${a2aLabel}: socket is not OPEN \u2014 NOT advancing the group`);
76332
+ continue;
76333
+ }
76334
+ const head = await this._readGroupHead(mlsData.group_id);
76335
+ if (head.ok && head.epoch !== Number(mgr.epoch)) {
76336
+ console.warn(`[SecureChannel] ${a2aLabel}: server head is at epoch ${head.epoch}, we are at ${Number(mgr.epoch)} \u2014 NOT committing`);
76337
+ await this._catchUpAfterPreflightSkip(mlsData.group_id, head.epoch, Number(mgr.epoch), a2aLabel);
76338
+ continue;
76339
+ }
76340
+ const inFlight = this._reserveCommit(mlsData.group_id, a2aLabel);
76341
+ if (!inFlight) continue;
76342
+ try {
76343
+ const kpRes = await fetch(
76344
+ `${this.config.apiUrl}/api/v1/mls/key-packages?hub_id=${encodeURIComponent(p2.hubId)}`,
76345
+ { headers: { Authorization: `Bearer ${this._deviceJwt}` } }
76346
+ );
76347
+ if (kpRes.ok) {
76348
+ const kpData = await kpRes.json();
76349
+ if (kpData.key_package) {
76350
+ const kpBytes = new Uint8Array(Buffer.from(kpData.key_package, "hex"));
76351
+ const memberKp = MLSGroupManager.deserializeKeyPackage(kpBytes);
76352
+ const stateBefore = mgr.exportState();
76353
+ const { commit, welcome } = await mgr.addMembers([memberKp]);
76354
+ const committedEpoch = Number(mgr.epoch);
76355
+ if (!welcome) {
76356
+ mgr.importState(stateBefore);
76357
+ console.warn(`[SecureChannel] ${a2aLabel}: addMembers produced no Welcome \u2014 rolled back`);
76358
+ continue;
76359
+ }
76360
+ const verdict = await this._sendCommitConfirmed({
76361
+ mlsGroup: mgr,
76362
+ mlsGroupId: mlsData.group_id,
76363
+ label: a2aLabel,
76364
+ commit,
76365
+ epoch: committedEpoch,
76366
+ parent: head.ok ? head.digest : void 0,
76367
+ memberDeviceIds: this._commitRoster(participants.map((q22) => q22.deviceId), [p2.deviceId]),
76368
+ stateBefore,
76369
+ inFlight
76370
+ });
76371
+ if (verdict !== "confirmed") continue;
76372
+ const sent = await this._sendFrameConfirmed({
76373
+ event: "mls_welcome",
75755
76374
  data: {
75756
76375
  group_id: mlsData.group_id,
75757
- epoch: Number(mgr.epoch),
75758
- payload: Buffer.from(commit).toString("hex")
76376
+ payload: Buffer.from(welcome).toString("hex"),
76377
+ target_device_id: p2.deviceId,
76378
+ a2a_channel_id: channelId,
76379
+ target_hub_id: p2.hubId
76380
+ // observability only — the backend ignores it
75759
76381
  }
75760
- }));
75761
- if (!p2.deviceId) {
75762
- console.warn(
75763
- `[SecureChannel] Skipping MLS Welcome to ${p2.hubAddress} for A2A ${channelId.slice(0, 8)}: no deviceId. Backend a2a_channel_approved payload may be missing device_id for this participant.`
75764
- );
75765
- } else {
75766
- this._ws.send(JSON.stringify({
75767
- event: "mls_welcome",
75768
- data: {
75769
- group_id: mlsData.group_id,
75770
- payload: Buffer.from(welcome).toString("hex"),
75771
- target_device_id: p2.deviceId,
75772
- a2a_channel_id: channelId,
75773
- // Kept for observability — backend ignores but it's
75774
- // useful in logs/wire captures to correlate to a hub.
75775
- target_hub_id: p2.hubId
75776
- }
75777
- }));
76382
+ }, `${a2aLabel} welcome`);
76383
+ await saveMlsState(this.config.dataDir, mlsData.group_id, JSON.stringify(mgr.exportState()));
76384
+ if (sent) {
75778
76385
  console.log(`[SecureChannel] Sent MLS Welcome to ${p2.hubAddress} (device ${p2.deviceId.slice(0, 8)}) for A2A ${channelId.slice(0, 8)}`);
76386
+ } else {
76387
+ console.warn(`[SecureChannel] ${a2aLabel}: Welcome was NOT written \u2014 the participant will request one`);
75779
76388
  }
75780
76389
  }
75781
- await saveMlsState(this.config.dataDir, mlsData.group_id, JSON.stringify(mgr.exportState()));
75782
76390
  }
76391
+ } finally {
76392
+ await this._releaseCommit(mlsData.group_id, inFlight, a2aLabel);
75783
76393
  }
75784
76394
  } catch (kpErr) {
75785
76395
  console.warn(`[SecureChannel] Failed to Welcome participant ${p2.hubAddress}:`, kpErr);
@@ -75873,7 +76483,7 @@ ${baseText}`;
75873
76483
  try {
75874
76484
  const parsed = JSON.parse(plaintext);
75875
76485
  messageType = parsed.type || "message";
75876
- messageText = parsed.text || plaintext;
76486
+ messageText = parsed.text ?? plaintext;
75877
76487
  } catch {
75878
76488
  messageType = "message";
75879
76489
  messageText = plaintext;
@@ -76158,7 +76768,7 @@ ${baseText}`;
76158
76768
  try {
76159
76769
  const parsed = JSON.parse(plaintext);
76160
76770
  messageType = parsed.type || "message";
76161
- messageText = parsed.text || plaintext;
76771
+ messageText = parsed.text ?? plaintext;
76162
76772
  } catch {
76163
76773
  messageType = "message";
76164
76774
  messageText = plaintext;
@@ -76594,13 +77204,6 @@ ${actionsText}`);
76594
77204
  init_openclaw_compat();
76595
77205
  }
76596
77206
  });
76597
- init_room_protocol = __esm2({
76598
- "src/room-protocol.ts"() {
76599
- "use strict";
76600
- MENTION_RE = /@([\w-]+)/gi;
76601
- BROADCAST_NAMES = /* @__PURE__ */ new Set(["all", "everyone"]);
76602
- }
76603
- });
76604
77207
  init_llm_response_parser = __esm2({
76605
77208
  "src/llm-response-parser.ts"() {
76606
77209
  "use strict";
@@ -97856,7 +98459,7 @@ data:
97856
98459
  await init_skill_telemetry();
97857
98460
  await init_policy_enforcer();
97858
98461
  init_room_protocol();
97859
- VERSION = true ? "0.23.39" : "0.0.0-dev";
98462
+ VERSION = true ? "0.23.41" : "0.0.0-dev";
97860
98463
  }
97861
98464
  });
97862
98465
  await init_index();
@@ -99239,7 +99842,7 @@ function mkdir700(path2) {
99239
99842
  chmodSync5(path2, 448);
99240
99843
  }
99241
99844
  function packageVersion() {
99242
- return true ? "0.1.15" : void 0;
99845
+ return true ? "0.1.17" : void 0;
99243
99846
  }
99244
99847
  function helpText(version2) {
99245
99848
  return [