@canonmsg/agent-sdk 12.3.1 → 12.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -3,6 +3,7 @@ import {
3
3
  BoundedFileReadError,
4
4
  CanonClient,
5
5
  MAX_CANON_MEDIA_BYTES,
6
+ NodeMlsDevCustodyError,
6
7
  RUNTIME_NEW_SESSION_ACTION,
7
8
  RUNTIME_STOP_ACTION,
8
9
  RUNTIME_STOP_AND_DROP_ACTION,
@@ -29,7 +30,7 @@ import {
29
30
  sendMessageWithRetryChunked,
30
31
  splitTextByUtf8Bytes,
31
32
  verifyCanonRuntimeConnection
32
- } from "./chunk-SKFI2MJA.js";
33
+ } from "./chunk-6PKXASSK.js";
33
34
  import {
34
35
  CONNECTOR_RELAY_ANSWER_WITHIN_MS,
35
36
  CONNECTOR_RELAY_INSTALLATION_PATTERN,
@@ -58,7 +59,7 @@ import {
58
59
  parseMlsDevGroupHead,
59
60
  parseMlsDevGroupRoster,
60
61
  resolveAllowedUploadMimeType
61
- } from "./chunk-5MI6Q3VS.js";
62
+ } from "./chunk-GWQF4WFT.js";
62
63
 
63
64
  // src/runner/types.ts
64
65
  var DriverSessionError = class extends Error {
@@ -464,6 +465,8 @@ var GUEST_ITEMS = /* @__PURE__ */ new Set([
464
465
  "subAgentActivity",
465
466
  "sleep"
466
467
  ]);
468
+ var OWN_LISTINGS = /* @__PURE__ */ new Set(["list_mcp_resources", "list_mcp_resource_templates"]);
469
+ var GUEST = "Only your owner can let you use tools in this conversation, and this turn holds a message from someone else: you cannot read files, run commands or use any tool but the canon MCP server's. Answer from the conversation and what you already know, and say so when something needs your owner.";
467
470
  var RISKY = /\brm\s+-[a-z]*[rf]|\bgit\s+(reset\s+--hard|push\s+(-f|--force))|\bdrop\s+(table|database)\b|\bmkfs\b|\bdd\s+if=/i;
468
471
  var IMAGE = /\.(png|jpe?g|gif|webp)$/i;
469
472
  var isRecord2 = /* @__PURE__ */ __name((value) => typeof value === "object" && value !== null && !Array.isArray(value), "isRecord");
@@ -750,10 +753,11 @@ function codexDriver(options) {
750
753
  settle(new Error(`Codex ended the turn ${ended.status === "interrupted" ? "interrupted" : `with an error${detail ? `: ${oneLine2(detail, 300)}` : ""}`}`));
751
754
  }, "conclude");
752
755
  const guard = /* @__PURE__ */ __name((item) => {
753
- if (!guest || GUEST_ITEMS.has(text(item.type)) || item.type === "mcpToolCall" && item.server === CANON) return;
756
+ if (!guest || GUEST_ITEMS.has(text(item.type)) || item.type === "mcpToolCall" && (item.server === CANON || item.server === "codex" && OWN_LISTINGS.has(text(item.tool)))) return;
754
757
  interrupt();
755
758
  entry.close();
756
- settle(new DriverSessionError(`Codex started ${text(item.type) || "an unknown item"} in a turn holding someone other than the owner's message`));
759
+ const what = item.type === "mcpToolCall" ? `mcpToolCall (${describe(item)})` : text(item.type) || "an unknown item";
760
+ settle(new DriverSessionError(`Codex started ${what} in a turn holding someone other than the owner's message`));
757
761
  }, "guard");
758
762
  const link = rpc(child, {
759
763
  notify(method, params) {
@@ -813,6 +817,7 @@ function codexDriver(options) {
813
817
  }
814
818
  if (turn.signal.aborted) return finish();
815
819
  const started = await link.call("turn/start", { threadId, input: [
820
+ ...guest ? [{ type: "text", text: GUEST, text_elements: [] }] : [],
816
821
  { type: "text", text: turn.prompt, text_elements: [] },
817
822
  // Images go to the model as images; every attachment's path is also in the prompt for Codex to read.
818
823
  ...turn.files.filter((file) => IMAGE.test(file)).map((path) => ({ type: "localImage", path }))
@@ -1485,7 +1490,7 @@ async function loadMlsDevConfiguration(filePath, input) {
1485
1490
  return { configuration, custody: custody2, close: /* @__PURE__ */ __name(() => custody2.close(), "close") };
1486
1491
  }
1487
1492
  __name(loadMlsDevConfiguration, "loadMlsDevConfiguration");
1488
- var MLS_SETUP_ACTIONS = ["status", "provision", "enroll", "renew", "publish", "refresh"];
1493
+ var MLS_SETUP_ACTIONS = ["status", "setup", "provision", "enroll", "renew", "publish", "refresh"];
1489
1494
  function mlsDevRoomPackageId(identity, room) {
1490
1495
  const hex = createHash4("sha256").update(JSON.stringify([
1491
1496
  "canon.mls.dev-agent-room-package.v1",
@@ -1498,25 +1503,37 @@ function mlsDevRoomPackageId(identity, room) {
1498
1503
  return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-4${hex.slice(13, 16)}-8${hex.slice(17, 20)}-${hex.slice(20, 32)}`;
1499
1504
  }
1500
1505
  __name(mlsDevRoomPackageId, "mlsDevRoomPackageId");
1501
- async function runMlsDevSetup(endpoint, action, room, provision) {
1506
+ async function runMlsDevSetup(endpoint, action, room, provision, provisioned) {
1502
1507
  const identity = await endpoint.ready();
1503
- if (!isMlsEnvironmentId(identity.environmentId) || !endpoint.mlsDevDevice || !endpoint.mlsDevKeyPackages || !endpoint.mlsDevGroups) throw new Error("MLS_DEV_SETUP_UNAVAILABLE");
1508
+ const { environmentId } = identity, devices = endpoint.mlsDevDevice;
1509
+ if (!isMlsEnvironmentId(environmentId) || !devices || !endpoint.mlsDevKeyPackages || !endpoint.mlsDevGroups) throw new Error("MLS_DEV_SETUP_UNAVAILABLE");
1504
1510
  if ((action === "publish" || action === "refresh") !== !!room) throw new Error("MLS_DEV_SETUP_ARGUMENTS_INVALID");
1505
- if (action === "provision") {
1511
+ const enroll = /* @__PURE__ */ __name(async () => {
1512
+ await devices.enroll();
1513
+ if ((await devices.read()).status !== "credential_committed") await devices.renew();
1514
+ }, "enroll");
1515
+ const provisionDevice = /* @__PURE__ */ __name(async (unlessProvisioned) => {
1506
1516
  if (!provision || identity.ownershipVersion === void 0) throw new Error("MLS_DEV_SETUP_UNAVAILABLE");
1507
- const device2 = await endpoint.mlsDevDevice.read();
1517
+ const device2 = await devices.read();
1508
1518
  if (device2.registration || device2.status !== "absent") throw new Error("MLS_DEV_SETUP_ALREADY_PROVISIONED");
1509
- await provision({
1519
+ const context = {
1510
1520
  version: 1,
1511
- environmentId: identity.environmentId,
1521
+ environmentId,
1512
1522
  installationId: identity.installationId,
1513
1523
  principal: { type: "ai_agent", id: await endpoint.mlsPrincipalId(), ownershipVersion: identity.ownershipVersion },
1514
1524
  revision: 0
1515
- });
1516
- } else if (action === "enroll") {
1517
- await endpoint.mlsDevDevice.enroll();
1518
- if ((await endpoint.mlsDevDevice.read()).status !== "credential_committed") await endpoint.mlsDevDevice.renew();
1519
- } else if (action === "renew") await endpoint.mlsDevDevice.renew();
1525
+ };
1526
+ if (!unlessProvisioned || !provisioned?.(context)) await provision(context);
1527
+ }, "provisionDevice");
1528
+ if (action === "setup") {
1529
+ const { status } = await devices.read();
1530
+ if (status === "revoked") throw new Error("MLS_DEV_SETUP_REVOKED");
1531
+ if (status === "absent") await provisionDevice(true);
1532
+ if (status === "registered") await devices.ensureReady();
1533
+ else if (status !== "credential_committed") await enroll();
1534
+ } else if (action === "provision") await provisionDevice(false);
1535
+ else if (action === "enroll") await enroll();
1536
+ else if (action === "renew") await devices.renew();
1520
1537
  else if (action === "publish") {
1521
1538
  const keyPackageId = mlsDevRoomPackageId(identity, room);
1522
1539
  await endpoint.mlsDevKeyPackages.prepare({ keyPackageId });
@@ -1525,7 +1542,7 @@ async function runMlsDevSetup(endpoint, action, room, provision) {
1525
1542
  const group = await endpoint.mlsDevGroups.refresh(room);
1526
1543
  if (group?.status !== "active" || await endpoint.mlsDevGroups.readRecoveryFailure(room)) throw new Error("MLS_DEV_SETUP_ROOM_NOT_READY");
1527
1544
  }
1528
- const device = await endpoint.mlsDevDevice.read();
1545
+ const device = await devices.read();
1529
1546
  return {
1530
1547
  version: 1,
1531
1548
  action,
@@ -1541,12 +1558,238 @@ async function runMlsDevSetup(endpoint, action, room, provision) {
1541
1558
  }
1542
1559
  __name(runMlsDevSetup, "runMlsDevSetup");
1543
1560
 
1561
+ // src/runner/mls-bundled.ts
1562
+ import { createHash as createHash5 } from "node:crypto";
1563
+ import { existsSync, readFileSync as readFileSync2 } from "node:fs";
1564
+ import { dirname as dirname4, join as join2 } from "node:path";
1565
+ import { fileURLToPath } from "node:url";
1566
+
1567
+ // mls-custody.json
1568
+ var mls_custody_default = {
1569
+ schemaVersion: 1,
1570
+ release: "mls-custody-cadb937436ce",
1571
+ protocolVersion: 1,
1572
+ rustVersion: "rustc 1.91.0 (f8297e351 2025-10-28)",
1573
+ sourceHashes: {
1574
+ "mls/native/Cargo.toml": "f93b48329951d5a11ef6d5830ff7c375cb847543bdb1c98a6fd8a8d02026db89",
1575
+ "mls/native/Cargo.lock": "9c2e9a3bb0f79c14540942c091124cba4aa262ee14cef2cdd5490653cd4656f3",
1576
+ "mls/native/src/lib.rs": "8e586bfaedc5fe376febbda78c32f8383aaa2ac92868000dd4c84ca083731571",
1577
+ "mls/native/src/bin/canon-mls-custody.rs": "3ab54102bfe315e425b6c3cf0aa7feb66e44d047e135336b0291305507062459",
1578
+ "mls/native/src/portable/mod.rs": "3005b60f7777a841347421ad96eb631b72063e85cebe96afd1c11fef095b0e34",
1579
+ "mls/native/src/portable/json.rs": "d4e68556a051ffe9008686036597b1fd8239ae66fad67bd848c3f0e16bf8ccaf",
1580
+ "mls/native/src/portable/vault.rs": "14b9ff1e10ec4058e39a46511a489c7e82a9836e4175a768ac5d4b69c1dba8e5",
1581
+ "mls/native/src/portable/tests.rs": "7eadba2a49ed044470bb567d9b93fba44b64dc117b1da50a22187cff3dccb64e",
1582
+ "mls/native/portable/build.mjs": "84c422a8a8aa6a7ec112a358bdeef080201aa4ea9a91f6d5726b05f46fa2a10c",
1583
+ "mls/core/open.rs": "8cdc03882f3b84dca72ab06afd437f162de01a0fe21327b019f17867dd59f5d0",
1584
+ "mls/core/protocol.rs": "7f3e021d6e4f1cc130b5dd3c98e0b0fa23edf813c72802c3fa3f8fb9e431c947",
1585
+ "mls/core/fault_storage.rs": "fdd6672a21f6ffee657142755637824541a5090b4f8a5fb4902f482556fb2793",
1586
+ "mls/core/mls_environment.rs": "390d11ee82ef8b108a7fffc7bb193ac7f3e470aa14e9643d029007aaaa29c4e4",
1587
+ "mls/core/dev_identity.rs": "97df6bdb9daab66f0e3d4ca3b49bf9954fa06ac032c927d9ae6c83509b0166bd",
1588
+ "mls/core/dev_key_package.rs": "3e7011fe8ab3044d80d84d04944068dbcf683141fcaba8a7a00bd2cfd103de72",
1589
+ "mls/core/dev_application.rs": "fafe245ace38e384da265bab2c12fef6b77c86b2c20477d00563ab891a54f94b",
1590
+ "mls/core/dev_group.rs": "1d519a5d15394bbc163aef00e0ddacb9bb87631beaf8a13e52c76d628749152b",
1591
+ "packages/test-fixtures/src/mlsDevEnrollmentProofV1.json": "50d7896e0a3a25b420412e84484e64f1e49480a3f298ee2d125f2f9188382810",
1592
+ "packages/test-fixtures/src/mlsDevCredentialV1.json": "24cd23af794b8a6e35caf050995020c6584b806de060969db4536f44b1188780",
1593
+ "packages/test-fixtures/src/mlsDevCredentialReconciliationV1.json": "bd4bbb3a2e5dc7921867a2e5936d1da77d536740b4360012c1f741f9caed2826",
1594
+ "packages/test-fixtures/src/mlsDevGroupV1.json": "10b7c0da00cfd3ecfb3c9e7f0118bc574be613cc82d97268766ba271c464ca9d",
1595
+ "packages/test-fixtures/src/mlsDevApplicationV1.json": "77c05e728acb7793a70c03a6857bc8b0e522e586b117241432460eec05ae3673",
1596
+ "packages/test-fixtures/src/mlsDevInteractionV1.json": "045ee34e1b5e126b67da94705b3f03a1d265774d7889ec3f4f272dd06ecef676"
1597
+ },
1598
+ platforms: {
1599
+ "darwin-arm64": {
1600
+ target: "aarch64-apple-darwin",
1601
+ sha256: "918ae350f1d7bd339c4eee548f6668943217f5fa66022bb93f41a02992dd296c",
1602
+ bytes: 2932976,
1603
+ unitTestsPassed: 95,
1604
+ commit: "cdc6ce8fd9d7a47bc5c796d19a72828e4cc9db06",
1605
+ builder: {
1606
+ macos: "26.3.1",
1607
+ sdk: "26.4",
1608
+ linker: "@(#)PROGRAM:ld PROJECT:ld-1266.8"
1609
+ }
1610
+ },
1611
+ "linux-x64": {
1612
+ target: "x86_64-unknown-linux-gnu",
1613
+ sha256: "1c5a12d0213c53bdf59b5f36a7390a4b4c17f8a53c95e760ad8cb4905297d256",
1614
+ bytes: 3389816,
1615
+ unitTestsPassed: 95,
1616
+ commit: "cdc6ce8fd9d7a47bc5c796d19a72828e4cc9db06",
1617
+ builder: {
1618
+ image: "quay.io/pypa/manylinux_2_28_x86_64@sha256:e0adf18521ad35192df733a6c62d35df7801957d24e0470238aacecfab2188a3",
1619
+ dockerfileSha256: "fe8d4895b8bd304954441fd27c6273c8c00baa10a1ad912d3c3851b193b8ad75"
1620
+ }
1621
+ },
1622
+ "linux-arm64": {
1623
+ target: "aarch64-unknown-linux-gnu",
1624
+ sha256: "371c5754cf0c437f561e8c49b3ea654c037311b951e11b1c5a29fb6b91656619",
1625
+ bytes: 3234232,
1626
+ unitTestsPassed: 95,
1627
+ commit: "cdc6ce8fd9d7a47bc5c796d19a72828e4cc9db06",
1628
+ builder: {
1629
+ image: "quay.io/pypa/manylinux_2_28_aarch64@sha256:a3e02308c822fb6ce0d12248b0c941817ba5e1a1bc9e7bb5929f29c50eaa40a9",
1630
+ dockerfileSha256: "fe8d4895b8bd304954441fd27c6273c8c00baa10a1ad912d3c3851b193b8ad75"
1631
+ }
1632
+ }
1633
+ }
1634
+ };
1635
+
1636
+ // src/runner/mls-bundled.ts
1637
+ var MLS_ENVIRONMENT_PINS = {
1638
+ "canon-prod-v1": {
1639
+ issuerKeys: [{
1640
+ keyVersion: "projects/canonmail-prod/locations/europe-west1/keyRings/canon-mls/cryptoKeys/device-issuer/cryptoKeyVersions/1",
1641
+ signaturePublicKey: "AYLEqY7gKOhQvRALb-31lz82XinehW2_gl0jKC-G8Pk"
1642
+ }],
1643
+ receiptKeys: [{
1644
+ keyVersion: "projects/canonmail-prod/locations/europe-west1/keyRings/canon-mls/cryptoKeys/feed-receipt/cryptoKeyVersions/1",
1645
+ signaturePublicKey: "sRi4mvMbFTL-LKzXilIJedefSqtqrF8Q-deM2AUSt40"
1646
+ }]
1647
+ },
1648
+ "canon-dev-v1": {
1649
+ // Dev signs with version 2 and still trusts version 1 until 2026-11-07.
1650
+ issuerKeys: [
1651
+ {
1652
+ keyVersion: "projects/canonmail-dev/locations/europe-west1/keyRings/canon-mls/cryptoKeys/device-issuer/cryptoKeyVersions/1",
1653
+ signaturePublicKey: "Hm1ULJGsF9MIQbHYIWmpkK3j2dPcFzeJ71xbYVsPZNs"
1654
+ },
1655
+ {
1656
+ keyVersion: "projects/canonmail-dev/locations/europe-west1/keyRings/canon-mls/cryptoKeys/device-issuer/cryptoKeyVersions/2",
1657
+ signaturePublicKey: "G2fwJdPQXP6Ar68LJveXQ_WAOtdtcsiyKdeEVb-9Gcw"
1658
+ }
1659
+ ],
1660
+ receiptKeys: [{
1661
+ keyVersion: "projects/canonmail-dev/locations/europe-west1/keyRings/canon-mls/cryptoKeys/feed-receipt/cryptoKeyVersions/1",
1662
+ signaturePublicKey: "LSRMjRHH73Q4ExPFSTaU_KTvr8wQ4Cp9vtaBBrVZvig"
1663
+ }]
1664
+ }
1665
+ };
1666
+ function packageRoot() {
1667
+ for (let directory = dirname4(fileURLToPath(import.meta.url)); ; directory = dirname4(directory)) {
1668
+ const manifest = join2(directory, "package.json");
1669
+ if (existsSync(manifest) && JSON.parse(readFileSync2(manifest, "utf8")).name === "@canonmsg/agent-sdk") return directory;
1670
+ if (dirname4(directory) === directory) return void 0;
1671
+ }
1672
+ }
1673
+ __name(packageRoot, "packageRoot");
1674
+ function bundledMlsCustodyHelper() {
1675
+ const platform = `${process.platform}-${process.arch}`;
1676
+ const pin = mls_custody_default.platforms[platform];
1677
+ const root = pin && packageRoot();
1678
+ const path = root && join2(root, "dist", "mls", platform, "canon-mls-custody");
1679
+ if (!pin || !path || !existsSync(path)) return void 0;
1680
+ if (createHash5("sha256").update(readFileSync2(path)).digest("hex") !== pin.sha256) {
1681
+ throw new Error(`${path} is not the custody helper this package pins; reinstall @canonmsg/agent-sdk.`);
1682
+ }
1683
+ return { path, sha256: pin.sha256 };
1684
+ }
1685
+ __name(bundledMlsCustodyHelper, "bundledMlsCustodyHelper");
1686
+
1687
+ // src/runner/mls-managed.ts
1688
+ import { createHash as createHash6 } from "node:crypto";
1689
+ import { chmodSync, copyFileSync, existsSync as existsSync2, mkdirSync as mkdirSync3, readFileSync as readFileSync3, renameSync as renameSync2 } from "node:fs";
1690
+ import { dirname as dirname5, join as join3 } from "node:path";
1691
+ var sha256 = /* @__PURE__ */ __name((bytes) => createHash6("sha256").update(bytes).digest("hex"), "sha256");
1692
+ function managedMlsDevice(options) {
1693
+ const { environmentId } = options;
1694
+ const helper = options.helper ?? bundledMlsCustodyHelper();
1695
+ if (!helper) {
1696
+ throw new Error(`@canonmsg/agent-sdk ships no MLS custody helper for ${process.platform}-${process.arch}: build one from mls/native in the Canon repository and give the runner --mls-dev-config <path> (docs/design/mls-dev-agent-host.md).`);
1697
+ }
1698
+ const pins = options.pins ?? MLS_ENVIRONMENT_PINS[environmentId];
1699
+ const configuration = parseMlsDevClientConfiguration({
1700
+ version: 1,
1701
+ environmentId,
1702
+ enabled: true,
1703
+ issuerKeys: pins.issuerKeys,
1704
+ receiptKeys: pins.receiptKeys
1705
+ });
1706
+ const root = join3(options.home, "mls", environmentId);
1707
+ const directory = /* @__PURE__ */ __name((context) => join3(root, sha256(Buffer.from(JSON.stringify([
1708
+ context.installationId,
1709
+ context.principal.type,
1710
+ context.principal.id,
1711
+ "ownershipVersion" in context.principal ? context.principal.ownershipVersion : null
1712
+ ]))).slice(0, 32)), "directory");
1713
+ const vault = /* @__PURE__ */ __name((context) => join3(directory(context), "vault", "vault.bin"), "vault");
1714
+ let executable;
1715
+ function install() {
1716
+ const target = join3(root, "bin", helper.sha256, "canon-mls-custody");
1717
+ if (existsSync2(target) && sha256(readFileSync3(target)) === helper.sha256) return target;
1718
+ mkdirSync3(dirname5(target), { recursive: true, mode: 448 });
1719
+ const temporary = `${target}.${process.pid}.tmp`;
1720
+ copyFileSync(helper.path, temporary);
1721
+ chmodSync(temporary, 448);
1722
+ if (sha256(readFileSync3(temporary)) !== helper.sha256) throw new Error(`${helper.path} is not the pinned custody helper`);
1723
+ renameSync2(temporary, target);
1724
+ return target;
1725
+ }
1726
+ __name(install, "install");
1727
+ const devices = /* @__PURE__ */ new Map();
1728
+ function device(context, provisioning) {
1729
+ const key = directory(context);
1730
+ let found = devices.get(key);
1731
+ if (!found) {
1732
+ found = (async () => {
1733
+ if (!provisioning && !existsSync2(vault(context))) throw new NodeMlsDevCustodyError();
1734
+ mkdirSync3(dirname5(vault(context)), { recursive: true, mode: 448 });
1735
+ const unlock = join3(key, "unlock");
1736
+ if (provisioning) privateKey(unlock, 32);
1737
+ const text3 = `${JSON.stringify({
1738
+ version: 1,
1739
+ environmentId,
1740
+ context: {
1741
+ version: 1,
1742
+ environmentId: context.environmentId,
1743
+ principal: context.principal,
1744
+ installationId: context.installationId
1745
+ },
1746
+ vaultPath: vault(context),
1747
+ issuerKeys: configuration.issuerKeys,
1748
+ receiptKeys: configuration.receiptKeys
1749
+ }, null, 2)}
1750
+ `;
1751
+ const file = join3(key, "native.json");
1752
+ if (!existsSync2(file) || readFileSync3(file, "utf8") !== text3) writeDurable(file, text3);
1753
+ return createPortableNodeMlsDevCustody({
1754
+ executablePath: executable ??= install(),
1755
+ executableSha256: helper.sha256,
1756
+ configurationPath: file,
1757
+ configurationSha256: sha256(Buffer.from(text3)),
1758
+ secretSource: { kind: "managed-file", path: unlock },
1759
+ ...options.timeoutMs === void 0 ? {} : { timeoutMs: options.timeoutMs }
1760
+ });
1761
+ })();
1762
+ devices.set(key, found);
1763
+ found.catch(() => {
1764
+ if (devices.get(key) === found) devices.delete(key);
1765
+ });
1766
+ }
1767
+ return found;
1768
+ }
1769
+ __name(device, "device");
1770
+ const custody2 = {
1771
+ prepareProtected: /* @__PURE__ */ __name((context, ...rest) => device(context, false).then((open4) => open4.prepareProtected(context, ...rest)), "prepareProtected"),
1772
+ prepareProtectedKeyPackage: /* @__PURE__ */ __name((context, ...rest) => device(context, false).then((open4) => open4.prepareProtectedKeyPackage(context, ...rest)), "prepareProtectedKeyPackage"),
1773
+ prepareProtectedGroup: /* @__PURE__ */ __name((identity, ...rest) => device(identity, false).then((open4) => open4.prepareProtectedGroup(identity, ...rest)), "prepareProtectedGroup")
1774
+ };
1775
+ return {
1776
+ configuration,
1777
+ custody: custody2,
1778
+ provision: /* @__PURE__ */ __name((context) => device(context, true).then((open4) => open4.provision(context)), "provision"),
1779
+ provisioned: /* @__PURE__ */ __name((context) => existsSync2(vault(context)), "provisioned"),
1780
+ async close() {
1781
+ await Promise.allSettled([...devices.values()].map((open4) => open4.then((custody3) => custody3.close())));
1782
+ }
1783
+ };
1784
+ }
1785
+ __name(managedMlsDevice, "managedMlsDevice");
1786
+
1544
1787
  // src/runner/relay.ts
1545
1788
  import { createHmac as createHmac2, timingSafeEqual } from "node:crypto";
1546
- import { closeSync as closeSync3, constants as constants5, fsyncSync as fsyncSync2, mkdirSync as mkdirSync3, openSync as openSync3, renameSync as renameSync2, writeSync as writeSync2 } from "node:fs";
1789
+ import { closeSync as closeSync3, constants as constants5, fsyncSync as fsyncSync2, mkdirSync as mkdirSync4, openSync as openSync3, renameSync as renameSync3, writeSync as writeSync2 } from "node:fs";
1547
1790
  import { lstat as lstat2, open as open3, realpath as realpath4 } from "node:fs/promises";
1548
1791
  import { createServer } from "node:http";
1549
- import { isAbsolute as isAbsolute2, join as join2, resolve as resolve2 } from "node:path";
1792
+ import { isAbsolute as isAbsolute2, join as join4, resolve as resolve2 } from "node:path";
1550
1793
  var TOLERANCE_S = 300;
1551
1794
  function relaySecretKey(secret) {
1552
1795
  if (typeof secret !== "string" || !/^whsec_[A-Za-z0-9+/]+={0,2}$/.test(secret)) throw new Error("The relay channel secret must be a whsec_ value");
@@ -1715,8 +1958,8 @@ async function loadRelayConfiguration(path, input) {
1715
1958
  return { environmentId: value.environmentId, agentId: value.agentId, listen: { host: listen.host, port: listen.port }, secret };
1716
1959
  }
1717
1960
  __name(loadRelayConfiguration, "loadRelayConfiguration");
1718
- var relayDirectory = /* @__PURE__ */ __name((home) => join2(home, "relay"), "relayDirectory");
1719
- var credentialFile = /* @__PURE__ */ __name((home) => join2(relayDirectory(home), "credential.json"), "credentialFile");
1961
+ var relayDirectory = /* @__PURE__ */ __name((home) => join4(home, "relay"), "relayDirectory");
1962
+ var credentialFile = /* @__PURE__ */ __name((home) => join4(relayDirectory(home), "credential.json"), "credentialFile");
1720
1963
  async function readRelayCredential(home, relay) {
1721
1964
  const path = credentialFile(home);
1722
1965
  if (!await lstat2(path).then(() => true, () => false)) return void 0;
@@ -1742,7 +1985,7 @@ function writeRelayCredential(home, relay, delivery) {
1742
1985
  generation: delivery.generation,
1743
1986
  credential: delivery.credential
1744
1987
  };
1745
- mkdirSync3(relayDirectory(home), { recursive: true, mode: 448 });
1988
+ mkdirSync4(relayDirectory(home), { recursive: true, mode: 448 });
1746
1989
  const file = credentialFile(home);
1747
1990
  const temporary = `${file}.${process.pid}.tmp`;
1748
1991
  const fd = openSync3(temporary, "w", 384);
@@ -1752,23 +1995,23 @@ function writeRelayCredential(home, relay, delivery) {
1752
1995
  } finally {
1753
1996
  closeSync3(fd);
1754
1997
  }
1755
- renameSync2(temporary, file);
1998
+ renameSync3(temporary, file);
1756
1999
  return value;
1757
2000
  }
1758
2001
  __name(writeRelayCredential, "writeRelayCredential");
1759
2002
  function relayRunnerHome(home, installationId) {
1760
2003
  if (!CONNECTOR_RELAY_INSTALLATION_PATTERN.test(installationId)) throw new Error("Invalid relay installation");
1761
- const directory = join2(relayDirectory(home), installationId.slice(CONNECTOR_RELAY_INSTALLATION_PREFIX.length));
1762
- mkdirSync3(directory, { recursive: true, mode: 448 });
2004
+ const directory = join4(relayDirectory(home), installationId.slice(CONNECTOR_RELAY_INSTALLATION_PREFIX.length));
2005
+ mkdirSync4(directory, { recursive: true, mode: 448 });
1763
2006
  return directory;
1764
2007
  }
1765
2008
  __name(relayRunnerHome, "relayRunnerHome");
1766
2009
 
1767
2010
  // src/runner/runner.ts
1768
- import { createHash as createHash6 } from "node:crypto";
1769
- import { readFileSync as readFileSync3, rmSync as rmSync3 } from "node:fs";
2011
+ import { createHash as createHash8 } from "node:crypto";
2012
+ import { readFileSync as readFileSync5, rmSync as rmSync3 } from "node:fs";
1770
2013
  import { realpath as realpath5, stat as stat2 } from "node:fs/promises";
1771
- import { basename as basename3, isAbsolute as isAbsolute3, join as join4, sep } from "node:path";
2014
+ import { basename as basename3, isAbsolute as isAbsolute3, join as join6, sep } from "node:path";
1772
2015
 
1773
2016
  // ../agent-tools/src/communication-tool.ts
1774
2017
  var CANON_COMMUNICATE_TOOL_NAME = "communicate";
@@ -2262,10 +2505,10 @@ function createLivePublisher(options) {
2262
2505
  __name(createLivePublisher, "createLivePublisher");
2263
2506
 
2264
2507
  // src/runner/mls-ask.ts
2265
- import { createHash as createHash5 } from "node:crypto";
2266
- import { readdirSync as readdirSync2, readFileSync as readFileSync2, rmSync as rmSync2 } from "node:fs";
2267
- import { join as join3 } from "node:path";
2268
- var sha256 = /* @__PURE__ */ __name((value) => createHash5("sha256").update(value).digest("hex"), "sha256");
2508
+ import { createHash as createHash7 } from "node:crypto";
2509
+ import { readdirSync as readdirSync2, readFileSync as readFileSync4, rmSync as rmSync2 } from "node:fs";
2510
+ import { join as join5 } from "node:path";
2511
+ var sha2562 = /* @__PURE__ */ __name((value) => createHash7("sha256").update(value).digest("hex"), "sha256");
2269
2512
  var pause = /* @__PURE__ */ __name((ms) => new Promise((resolve3) => {
2270
2513
  setTimeout(resolve3, ms).unref?.();
2271
2514
  }), "pause");
@@ -2319,10 +2562,10 @@ function createMlsAsks(options, approvalScope2) {
2319
2562
  });
2320
2563
  }
2321
2564
  });
2322
- const file = /* @__PURE__ */ __name((conversationId, requestId) => join3(options.directory, `${sha256(JSON.stringify([conversationId, requestId])).slice(0, 32)}.json`), "file");
2565
+ const file = /* @__PURE__ */ __name((conversationId, requestId) => join5(options.directory, `${sha2562(JSON.stringify([conversationId, requestId])).slice(0, 32)}.json`), "file");
2323
2566
  const save = /* @__PURE__ */ __name((journal) => writeDurable(file(journal.conversationId, journal.requestId), JSON.stringify(journal)), "save");
2324
2567
  const forget = /* @__PURE__ */ __name((journal) => rmSync2(file(journal.conversationId, journal.requestId), { force: true }), "forget");
2325
- const operationOf = /* @__PURE__ */ __name((conversationId, messageId) => `runner-mls-ask:${sha256(JSON.stringify([conversationId, messageId])).slice(0, 48)}`, "operationOf");
2568
+ const operationOf = /* @__PURE__ */ __name((conversationId, messageId) => `runner-mls-ask:${sha2562(JSON.stringify([conversationId, messageId])).slice(0, 48)}`, "operationOf");
2326
2569
  const withdrawalOf = /* @__PURE__ */ __name((journal) => operationOf(journal.conversationId, `${journal.requestId}:withdraw`), "withdrawalOf");
2327
2570
  const audienceChanged = /* @__PURE__ */ __name((error) => codeOf(error) === "MLS_DEV_AUDIENCE_CHANGED", "audienceChanged");
2328
2571
  const sameHead = /* @__PURE__ */ __name((a, b) => JSON.stringify(a.head) === JSON.stringify(b.head), "sameHead");
@@ -2663,11 +2906,11 @@ function createMlsAsks(options, approvalScope2) {
2663
2906
  for (const name of names) {
2664
2907
  let journal;
2665
2908
  try {
2666
- journal = JSON.parse(readFileSync2(join3(options.directory, name), "utf8"));
2909
+ journal = JSON.parse(readFileSync4(join5(options.directory, name), "utf8"));
2667
2910
  if (journal.version !== 1 || typeof journal.conversationId !== "string" || typeof journal.requestId !== "string" || !Array.isArray(journal.sends) || journal.sends.some((sent) => typeof sent?.operationId !== "string" || typeof sent.messageId !== "string")) throw new Error();
2668
2911
  for (const sent of journal.sends) sent.audience = snapshotMlsAudience(sent.audience);
2669
2912
  } catch {
2670
- rmSync2(join3(options.directory, name), { force: true });
2913
+ rmSync2(join5(options.directory, name), { force: true });
2671
2914
  continue;
2672
2915
  }
2673
2916
  try {
@@ -2825,7 +3068,7 @@ function answerText(answer) {
2825
3068
  return answer.text?.trim() ? answer.text : "They answered with an empty message.";
2826
3069
  }
2827
3070
  __name(answerText, "answerText");
2828
- var sha2562 = /* @__PURE__ */ __name((value) => createHash6("sha256").update(value).digest("hex"), "sha256");
3071
+ var sha2563 = /* @__PURE__ */ __name((value) => createHash8("sha256").update(value).digest("hex"), "sha256");
2829
3072
  var toolText = /* @__PURE__ */ __name((text3, isError = false) => ({ content: [{ type: "text", text: text3 }], ...isError ? { isError } : {} }), "toolText");
2830
3073
  var turnEnded2 = /* @__PURE__ */ __name(() => new DOMException("This turn has ended", "AbortError"), "turnEnded");
2831
3074
  function within(promise, ms) {
@@ -2841,9 +3084,10 @@ var pause2 = /* @__PURE__ */ __name((ms) => new Promise((resolve3) => {
2841
3084
  }), "pause");
2842
3085
  var audienceOf = /* @__PURE__ */ __name((room) => room?.memberIds ? { memberIds: [...room.memberIds], ...typeof room.membershipRevision === "number" ? { membershipRevision: room.membershipRevision } : {} } : null, "audienceOf");
2843
3086
  var inert = /* @__PURE__ */ __name((value) => value.replace(/\s+/g, " ").trim(), "inert");
3087
+ var OWNER_MARK = " [your owner]";
2844
3088
  var TRUSTED_MARK = " [trusted teammate agent]";
2845
3089
  var speaker = /* @__PURE__ */ __name((name) => inert(name).replace(/\[/g, "(").replace(/\]/g, ")"), "speaker");
2846
- var defused = /* @__PURE__ */ __name((text3) => text3.replace(/\[(\s*trusted\s+teammate\s+agent\s*)\]/gi, "($1)").replace(/\[(\s*(?:end\s+of\s+)?owner['\u2019]s\s+(?:private\s+note|errand)\s*)\]/gi, "($1)"), "defused");
3090
+ var defused = /* @__PURE__ */ __name((text3) => text3.replace(/\[(\s*(?:trusted\s+teammate\s+agent|your\s+owner)\s*)\]/gi, "($1)").replace(/\[(\s*(?:end\s+of\s+)?owner['\u2019]s\s+(?:private\s+note|errand)\s*)\]/gi, "($1)"), "defused");
2847
3091
  var NOTE_OPEN = "[owner's private note] Your owner left this for you on the machine you run on. It is not a message in this conversation: no one in it sees it, and it was not posted.";
2848
3092
  var NOTE_CLOSE = "[end of owner's private note]";
2849
3093
  var ERRAND_OPEN = "[owner's errand]";
@@ -2858,8 +3102,8 @@ var errandCommunicate = /* @__PURE__ */ __name((contact) => ({
2858
3102
  properties: { action: { const: "start_conversation" }, principalId: { type: "string", minLength: 1 }, text: { type: "string", minLength: 1 } }
2859
3103
  }] : []] }
2860
3104
  }), "errandCommunicate");
2861
- var promptLine = /* @__PURE__ */ __name((message2, name, media, trusted = false) => [
2862
- `${speaker(name)}${trusted ? TRUSTED_MARK : ""}: ${defused(renderCanonHostInboundContent(message2, media?.files))}`,
3105
+ var promptLine = /* @__PURE__ */ __name((message2, name, media, mark = "") => [
3106
+ `${speaker(name)}${mark}: ${defused(renderCanonHostInboundContent(message2, media?.files))}`,
2863
3107
  ...(media?.failed ?? []).map((label) => `[Attachment "${defused(inert(label))}" could not be downloaded]`)
2864
3108
  ].join("\n"), "promptLine");
2865
3109
  var inputMessage = /* @__PURE__ */ __name((message2, trusted = false) => Object.freeze({
@@ -2896,7 +3140,7 @@ function approvalScope(request) {
2896
3140
  if (request.scopeKey !== void 0 && /^[A-Za-z0-9_:-]{1,64}$/.test(request.scopeKey)) return request.scopeKey;
2897
3141
  const key = request.scopeKey ?? request.title;
2898
3142
  const slug = key.toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "").slice(0, 40) || "approval";
2899
- return `${slug}-${sha2562(JSON.stringify([request.scopeKey === void 0 ? "title" : "scope", key])).slice(0, 16)}`;
3143
+ return `${slug}-${sha2563(JSON.stringify([request.scopeKey === void 0 ? "title" : "scope", key])).slice(0, 16)}`;
2900
3144
  }
2901
3145
  __name(approvalScope, "approvalScope");
2902
3146
  var MAX_GROUP_PARTS = 100;
@@ -3053,7 +3297,7 @@ async function createRunner(options) {
3053
3297
  const dispose = /* @__PURE__ */ __name((inputs, kind, reason) => Promise.allSettled(inputs.map((input) => input.handle[kind](reason))).then(() => void 0), "dispose");
3054
3298
  const settle = /* @__PURE__ */ __name(async (ids, reason) => (await Promise.allSettled(ids.map((id) => endpoint.setInboundState(id, "settled", reason)))).every(({ status }) => status === "fulfilled"), "settle");
3055
3299
  const queued = /* @__PURE__ */ __name((turn) => turn.inputs.filter((input) => input.handle.phase === "queued"), "queued");
3056
- const turnId = /* @__PURE__ */ __name((roomId, inputs) => `turn-${sha2562(JSON.stringify([roomId, ...inputs.map((input) => [input.id, input.generation])])).slice(0, 32)}`, "turnId");
3300
+ const turnId = /* @__PURE__ */ __name((roomId, inputs) => `turn-${sha2563(JSON.stringify([roomId, ...inputs.map((input) => [input.id, input.generation])])).slice(0, 32)}`, "turnId");
3057
3301
  const livePublisher = /* @__PURE__ */ __name((current, turn, mode) => createLivePublisher({
3058
3302
  client,
3059
3303
  conversationId: current.id,
@@ -3069,7 +3313,7 @@ async function createRunner(options) {
3069
3313
  current.lane = current.lane.then(publish2).catch(() => void 0);
3070
3314
  }, "enqueue")
3071
3315
  }), "livePublisher");
3072
- const stateFile = /* @__PURE__ */ __name((session) => join4(home, "runner", options.environmentId, agentId, "state", driver.name, `${sha2562(JSON.stringify([scope, session]))}.json`), "stateFile");
3316
+ const stateFile = /* @__PURE__ */ __name((session) => join6(home, "runner", options.environmentId, agentId, "state", driver.name, `${sha2563(JSON.stringify([scope, session]))}.json`), "stateFile");
3073
3317
  const resetSession = /* @__PURE__ */ __name(async (session) => {
3074
3318
  rmSync3(stateFile(session), { force: true });
3075
3319
  mlsAsks?.endSession(session);
@@ -3189,6 +3433,7 @@ ${error instanceof Error ? error.message : String(error)}`;
3189
3433
  const verified = {
3190
3434
  id: message2.id,
3191
3435
  senderId: message2.senderId,
3436
+ ...message2.senderName ? { senderName: message2.senderName } : {},
3192
3437
  senderType: fromAgent ? "ai_agent" : "human",
3193
3438
  isOwner: !fromAgent && message2.senderId === ownerId,
3194
3439
  text: message2.text ?? void 0,
@@ -3399,7 +3644,7 @@ ${error instanceof Error ? error.message : String(error)}`;
3399
3644
  return check;
3400
3645
  }
3401
3646
  __name(reaudience, "reaudience");
3402
- const inboundMedia = /* @__PURE__ */ __name((conversationId) => ({ agentId: "inbound", conversationId, rootDir: join4(home, "runner", options.environmentId, agentId) }), "inboundMedia");
3647
+ const inboundMedia = /* @__PURE__ */ __name((conversationId) => ({ agentId: "inbound", conversationId, rootDir: join6(home, "runner", options.environmentId, agentId) }), "inboundMedia");
3403
3648
  async function materialize(current, turn) {
3404
3649
  if (driver.capabilities.remote) return;
3405
3650
  for (const { payload: { message: message2 } } of turn.inputs) {
@@ -3450,7 +3695,7 @@ ${error instanceof Error ? error.message : String(error)}`;
3450
3695
  ask_question: /* @__PURE__ */ __name(async (args) => {
3451
3696
  const question = questionOf(args);
3452
3697
  if (typeof question === "string") return { result: toolText(question, true) };
3453
- const key = sha2562(JSON.stringify(question));
3698
+ const key = sha2563(JSON.stringify(question));
3454
3699
  const waiting = questions.get(key);
3455
3700
  const entry = waiting?.waiting ? waiting : { id: `canon:ask_question:${asked += 1}`, waiting: true };
3456
3701
  questions.set(key, entry);
@@ -3768,7 +4013,7 @@ ${JSON.stringify(result, null, 2)}`);
3768
4013
  messageId: `${root}-part-${index + 1}`,
3769
4014
  metadata: buildChunkedMessagePartMetadata({ groupId: root, metadata: options2.metadata, index: index + 1, count: bodies.length })
3770
4015
  } }));
3771
- return client.prepareMessageGroup(record.conversationId, root, parts, { signal, ...replaces ? { operationId: sha2562(JSON.stringify([root, ...attachments.map((attachment) => attachment.uploadId)])), replacesOperationId: replaces } : {} }).then((group2) => ({ ...group2, count: parts.length }));
4016
+ return client.prepareMessageGroup(record.conversationId, root, parts, { signal, ...replaces ? { operationId: sha2563(JSON.stringify([root, ...attachments.map((attachment) => attachment.uploadId)])), replacesOperationId: replaces } : {} }).then((group2) => ({ ...group2, count: parts.length }));
3772
4017
  }, "register");
3773
4018
  const original = await register(record.attachments);
3774
4019
  let group = original;
@@ -3891,7 +4136,7 @@ ${JSON.stringify(result, null, 2)}`);
3891
4136
  return { items, refused: refused2 };
3892
4137
  }
3893
4138
  __name(admitItems, "admitItems");
3894
- const encryptedOperation = /* @__PURE__ */ __name((audience, messageId) => `runner-mls:${sha2562(JSON.stringify([audience.conversationId, messageId])).slice(0, 48)}`, "encryptedOperation");
4139
+ const encryptedOperation = /* @__PURE__ */ __name((audience, messageId) => `runner-mls:${sha2563(JSON.stringify([audience.conversationId, messageId])).slice(0, 48)}`, "encryptedOperation");
3895
4140
  function forgetGuard(audience, messageId, sent) {
3896
4141
  if (sent === "pending") return;
3897
4142
  const operationId = encryptedOperation(audience, messageId);
@@ -3919,7 +4164,7 @@ ${JSON.stringify(result, null, 2)}`);
3919
4164
  ...turn.contexts.length ? { errands: Object.freeze(turn.contexts.map(({ record }) => errandFacts(record))) } : {},
3920
4165
  ...turn.errand ? { errand: errandFacts(turn.errand.record) } : {}
3921
4166
  });
3922
- const line = /* @__PURE__ */ __name((message2, trusted = false) => promptLine(message2, message2.senderId === agentId ? "You" : message2.senderName ?? message2.senderId, turn.media.get(message2.id), trusted), "line");
4167
+ const line = /* @__PURE__ */ __name((message2, mark = "") => promptLine(message2, message2.senderId === agentId ? "You" : message2.senderName ?? message2.senderId, turn.media.get(message2.id), mark), "line");
3923
4168
  const open4 = /* @__PURE__ */ __name(() => turn.open && !turn.controller.signal.aborted, "open");
3924
4169
  const live = /* @__PURE__ */ __name(() => open4() && !turn.silenced, "live");
3925
4170
  return {
@@ -3929,7 +4174,7 @@ ${JSON.stringify(result, null, 2)}`);
3929
4174
  ...turn.notes.flatMap((note) => [NOTE_OPEN, defused(note.text), NOTE_CLOSE, ""]),
3930
4175
  ...turn.contexts.flatMap((context) => [...contextPrompt(context, !!turn.mls), ""]),
3931
4176
  ...history.length ? ["Earlier in this conversation (context, not new requests):", ...history.map((message2) => line(message2)), "", "New messages:"] : turn.notes.length || turn.contexts.length ? ["New messages:"] : [],
3932
- ...turn.inputs.map(({ payload: { message: message2 } }) => line(message2, trustedSender(message2)))
4177
+ ...turn.inputs.map(({ payload: { message: message2 } }) => line(message2, message2.isOwner === true ? OWNER_MARK : trustedSender(message2) ? TRUSTED_MARK : ""))
3933
4178
  ].join("\n"),
3934
4179
  input,
3935
4180
  files: turn.inputs.flatMap(({ payload: { message: message2 } }) => turn.media.get(message2.id)?.files.map((file) => file.path) ?? []),
@@ -3989,7 +4234,7 @@ ${JSON.stringify(result, null, 2)}`);
3989
4234
  }));
3990
4235
  const secret = request.kind === "text" && request.secret === true;
3991
4236
  const responder = secret || sender.type !== "human" ? ownerId : sender.id;
3992
- const requestId = turn.mls ? keyed().request(roomId, turn.id, request.id) : `ask-${sha2562(JSON.stringify([turn.id, request.id])).slice(0, 32)}`;
4237
+ const requestId = turn.mls ? keyed().request(roomId, turn.id, request.id) : `ask-${sha2563(JSON.stringify([turn.id, request.id])).slice(0, 32)}`;
3993
4238
  const expiresAt = Date.now() + askExpiryMs;
3994
4239
  if (turn.mls) {
3995
4240
  return await mlsAsks.ask({
@@ -4061,7 +4306,7 @@ ${JSON.stringify(result, null, 2)}`);
4061
4306
  const file = stateFile(session);
4062
4307
  let data = {};
4063
4308
  try {
4064
- data = JSON.parse(readFileSync3(file, "utf8"));
4309
+ data = JSON.parse(readFileSync5(file, "utf8"));
4065
4310
  } catch {
4066
4311
  }
4067
4312
  const save = /* @__PURE__ */ __name((next) => {
@@ -4278,7 +4523,7 @@ ${JSON.stringify(result, null, 2)}`);
4278
4523
  log(`Errand ${id} (tell) kept for the next turn in ${conversationId}.`);
4279
4524
  return true;
4280
4525
  }
4281
- const turnId2 = `turn-${sha2562(JSON.stringify(["canon.runner.errand", id])).slice(0, 32)}`;
4526
+ const turnId2 = `turn-${sha2563(JSON.stringify(["canon.runner.errand", id])).slice(0, 32)}`;
4282
4527
  const record = {
4283
4528
  version: 1,
4284
4529
  turnId: turnId2,
@@ -4347,7 +4592,7 @@ ${JSON.stringify(result, null, 2)}`);
4347
4592
  pump();
4348
4593
  }
4349
4594
  __name(enqueueErrand, "enqueueErrand");
4350
- const errandTurnId = /* @__PURE__ */ __name((id) => `turn-${sha2562(JSON.stringify(["canon.runner.errand-turn", id])).slice(0, 32)}`, "errandTurnId");
4595
+ const errandTurnId = /* @__PURE__ */ __name((id) => `turn-${sha2563(JSON.stringify(["canon.runner.errand-turn", id])).slice(0, 32)}`, "errandTurnId");
4351
4596
  const errandFacts = /* @__PURE__ */ __name((record) => Object.freeze({
4352
4597
  id: record.id,
4353
4598
  why: record.why,
@@ -4683,7 +4928,7 @@ ${JSON.stringify(result, null, 2)}`);
4683
4928
  client = new CanonClient(options.apiKey, connection.apiBaseUrl, {
4684
4929
  environmentId: connection.environmentId,
4685
4930
  streamUrl: connection.streamUrl,
4686
- stateDirectory: join4(home, "endpoint"),
4931
+ stateDirectory: join6(home, "endpoint"),
4687
4932
  ...options.client ? { client: options.client } : {},
4688
4933
  // Below Canon's client floor nothing can work, wherever the refusal surfaces: a query, an operation or the stream.
4689
4934
  ...options.onFatal ? { onClientUpdateRequired: /* @__PURE__ */ __name((error) => options.onFatal(error), "onClientUpdateRequired") } : {},
@@ -4704,7 +4949,14 @@ ${JSON.stringify(result, null, 2)}`);
4704
4949
  if (options.wake !== void 0) await client.configureWake(options.wake);
4705
4950
  mlsOn = !!(endpoint.mlsDevDevice && endpoint.mlsDevGroups);
4706
4951
  if (!mlsOn && (await endpoint.capabilities().catch(() => null))?.mls?.roomMode === "default") {
4707
- throw new Error("Canon encrypts every new conversation here, and this runner has no MLS device: run it with --mls-dev-config <path> (set one up with canon-agent mls).");
4952
+ throw new Error("Canon encrypts every new conversation here, and this runner has no MLS device: give it mls (managedMlsDevice, or an operator's configuration: --mls-dev-config <path>).");
4953
+ }
4954
+ if (mlsOn && options.mls?.provisioned && (await endpoint.mlsDevDevice.read()).status !== "credential_committed") {
4955
+ log("Setting up this agent's MLS device");
4956
+ const device = await runMlsDevSetup(endpoint, "setup", void 0, options.mls.provision, options.mls.provisioned).catch((error) => {
4957
+ throw new Error(`Could not set up this agent's MLS device: ${error instanceof Error ? error.message : String(error)}`);
4958
+ });
4959
+ log(`MLS device ${device.deviceId} is ${device.status}`);
4708
4960
  }
4709
4961
  if (mlsOn) await endpoint.mlsDevDevice.ensureReady().catch(warn("MLS device not ready; encrypted rooms wait for it"));
4710
4962
  const keepPool = /* @__PURE__ */ __name(() => endpoint.mlsDevDevice.read().then(async (view) => {
@@ -4721,23 +4973,23 @@ ${JSON.stringify(result, null, 2)}`);
4721
4973
  }
4722
4974
  scope = JSON.stringify([identity.environmentId, agentId, identity.ownershipVersion ?? 0, identity.installationId]);
4723
4975
  if (mlsOn) {
4724
- mlsIds = createMlsIds(privateKey(join4(home, "runner", options.environmentId, agentId, "mls-ids", `${sha2562(scope).slice(0, 32)}.key`), MLS_ID_KEY_BYTES));
4976
+ mlsIds = createMlsIds(privateKey(join6(home, "runner", options.environmentId, agentId, "mls-ids", `${sha2563(scope).slice(0, 32)}.key`), MLS_ID_KEY_BYTES));
4725
4977
  mlsAsks = createMlsAsks({
4726
4978
  endpoint,
4727
4979
  agentId,
4728
- directory: join4(home, "runner", options.environmentId, agentId, "asks", sha2562(scope).slice(0, 32)),
4980
+ directory: join6(home, "runner", options.environmentId, agentId, "asks", sha2563(scope).slice(0, 32)),
4729
4981
  assertAudience,
4730
4982
  sessionForDestructive: options.sessionApprovalForDestructive === true,
4731
4983
  log
4732
4984
  }, approvalScope);
4733
4985
  }
4734
- journal = createCompletedOutputJournal(join4(home, "runner", options.environmentId, agentId, "finals"), scope, {
4986
+ journal = createCompletedOutputJournal(join6(home, "runner", options.environmentId, agentId, "finals"), scope, {
4735
4987
  label: "agent runner",
4736
4988
  validate: /* @__PURE__ */ __name((record) => typeof record.text === "string" && typeof record.options?.messageId === "string" && Array.isArray(record.inputs) && typeof record.notice === "boolean" && (record.attachments === void 0 || Array.isArray(record.attachments)) && (record.flows === void 0 || Array.isArray(record.flows) && record.flows.length === record.attachments?.length) && (record.mls === void 0 || !!record.mls && typeof record.mls === "object") && (record.errand === void 0 || typeof record.errand === "string"), "validate")
4737
4989
  });
4738
4990
  if (!options.relay && !driver.capabilities.remote) {
4739
4991
  errands = errandJournal(errandsDirectory(home, options.environmentId, agentId));
4740
- errandScope = sha2562(scope).slice(0, 32);
4992
+ errandScope = sha2563(scope).slice(0, 32);
4741
4993
  const pending = errands.list().filter((record) => record.kind === "tell" && record.status === "pending").sort((a, b) => (a.createdAt ?? "").localeCompare(b.createdAt ?? "") || a.id.localeCompare(b.id));
4742
4994
  for (const note of pending) {
4743
4995
  if (note.scope === errandScope) notes.set(note.conversationId, [...notes.get(note.conversationId) ?? [], note]);
@@ -4858,7 +5110,7 @@ ${JSON.stringify(result, null, 2)}`);
4858
5110
  throw new ConnectorRelayError(400, "CONNECTOR_RELAY_INVALID", "A send needs a messageId and 1 byte to 32 KiB of text");
4859
5111
  }
4860
5112
  const deadline = options2.deadline ?? Date.now() + CONNECTOR_RELAY_ANSWER_WITHIN_MS;
4861
- const id = `relay-send-${sha2562(JSON.stringify([conversationId, messageId])).slice(0, 32)}`;
5113
+ const id = `relay-send-${sha2563(JSON.stringify([conversationId, messageId])).slice(0, 32)}`;
4862
5114
  let entry = relaySends.get(id);
4863
5115
  if (!entry) relaySends.set(id, entry = prepareRelaySend(conversationId, id, text3, deadline));
4864
5116
  const prepared = await beforeDeadline(entry.ready, deadline);
@@ -4903,6 +5155,9 @@ export {
4903
5155
  MLS_SETUP_ACTIONS,
4904
5156
  mlsDevRoomPackageId,
4905
5157
  runMlsDevSetup,
5158
+ MLS_ENVIRONMENT_PINS,
5159
+ bundledMlsCustodyHelper,
5160
+ managedMlsDevice,
4906
5161
  startRelayChannel,
4907
5162
  loadRelayConfiguration,
4908
5163
  readRelayCredential,