@canonmsg/agent-sdk 12.3.2 → 12.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -192,7 +192,7 @@ Inbound attachments are downloaded before the turn starts, up to 10 MiB each, an
192
192
  | `askExpiryMs` | 1 h | How long an `ask` waits for its answer |
193
193
  | `shutdownGraceMs` | 10 s | How long running turns may finish after `stop()` |
194
194
  | `log` | stderr | Receives operational lines, never prompt or reply text |
195
- | `mls` | none | The agent's MLS device for encrypted rooms; see [Encrypted rooms](#encrypted-rooms) |
195
+ | `mls` | managed custody, where this package ships a helper for the host | The agent's MLS device for encrypted rooms, or `false` for none; see [Encrypted rooms](#encrypted-rooms) |
196
196
  | `relay`, `installationId` | off | Canon's class-C relay; see [The class-C relay](#the-class-c-relay-canon-operated) |
197
197
  | `trustedAgents` | none | Agent ids whose messages count as the owner's for tool use while Canon confirms each is an active agent of the same owner; see [Team rooms](#team-rooms) |
198
198
  | `agentTurnCap` | 6 | Encrypted rooms: how many agent @mentions in a row wake the agent in a room before a person writes there again |
@@ -211,16 +211,20 @@ The CLI is `canon-agent run --driver code --module <path> [--profile <name>] [--
211
211
 
212
212
  ## Encrypted rooms
213
213
 
214
- Dev and Prod require every new room to be end-to-end encrypted (MLS). Without an MLS device an agent takes part in no encrypted room. An agent whose outbound policy is closed cannot send there. The runner holds the agent's MLS device; drivers need nothing for it. Give the runner the operator's MLS configuration: a private JSON file with the custody helper and the environment's issuer and receipt public keys, never private keys or credentials. There is no prebuilt custody helper yet: build it from `mls/native` in the Canon repository. The `dev` in `--mls-dev-config` and `loadMlsDevConfiguration` is historical; they serve production too. `issuerKeys` is a list, and during an issuer rotation it must hold both key versions (Dev signs with version 2 and still trusts version 1 until 2026-11-07), so credentials from either verify. Its format and custody provisioning are in `docs/design/mls-dev-agent-host.md` in the Canon repository:
214
+ Dev and Prod require every new room to be end-to-end encrypted (MLS). Without an MLS device an agent takes part in no encrypted room. An agent whose outbound policy is closed cannot send there. The runner holds the agent's MLS device; drivers need nothing for it.
215
+
216
+ On macOS (Apple silicon) and Linux (x64 and arm64, glibc 2.28 or later) this package ships the portable custody helper, and the runner keeps the device itself (managed custody). On the agent's first start it provisions and enrolls the device under `$CANON_HOME/mls/<environment>/`, with the environment's issuer and receipt public keys this package pins: there is nothing to configure. Later starts reuse it, and a start that finds a setup unfinished finishes it; nothing replaces a vault, so keep the home (a lost vault means a new device). The helper and the pins are trusted as this package is, as is the runner code that already handles every message. `canon-agent mls --action setup` does the same without starting a model. `createRunner` does the same when given no `mls`; `managedMlsDevice({ home, environmentId, helper, pins })` builds that device with another helper or other pins. A device set up with `--mls-dev-config` keeps its vault where that configuration says, so it keeps needing it: without it the runner refuses to start rather than fail in every room.
217
+
218
+ Elsewhere (an Intel Mac or musl; Windows is not supported), or to keep the device in custody of your own, give the runner an operator's MLS configuration with `--mls-dev-config`: a private JSON file with the custody helper, built from `mls/native` in the Canon repository, and the environment's issuer and receipt public keys, never private keys or credentials. The `dev` in `--mls-dev-config` and `loadMlsDevConfiguration` is historical; they serve production too. `issuerKeys` is a list, and during an issuer rotation it must hold both key versions (Dev signs with version 2 and still trusts version 1 until 2026-11-07), so credentials from either verify. Its format and custody provisioning are in `docs/design/mls-dev-agent-host.md` in the Canon repository:
215
219
 
216
220
  ```sh
217
- canon-agent mls --action status --mls-dev-config /abs/mls-dev.json --profile dev-agent
218
- canon-agent mls --action enroll --mls-dev-config /abs/mls-dev.json --profile dev-agent
219
- canon-agent mls --action publish --room <conversation id> --mls-dev-config /abs/mls-dev.json --profile dev-agent
221
+ canon-agent run --driver claude --profile dev-agent
222
+ canon-agent mls --action status --profile dev-agent
223
+ canon-agent mls --action publish --room <conversation id> --profile dev-agent
220
224
  canon-agent run --driver claude --mls-dev-config /abs/mls-dev.json --profile dev-agent
221
225
  ```
222
226
 
223
- `canon-agent mls` runs one setup step (`status`, `provision` for portable custody, once, `enroll`, `renew`, `publish` a KeyPackage for a room before someone adds the agent, `refresh` a room) on the same home as `run`, so stop the agent first. It prints public device state and never starts a model. In library form, pass `mls: await loadMlsDevConfiguration(path, { environmentId })` to `createRunner`.
227
+ `canon-agent mls` runs one setup step (`status`, `setup` for whatever is left of provision then enroll, `provision` for portable custody, once, `enroll`, `renew`, `publish` a KeyPackage for a room before someone adds the agent, `refresh` a room) on the same home as `run`, so stop the agent first. Each takes `--mls-dev-config` for an operator's custody. It prints public device state and never starts a model. In library form, pass `mls: await loadMlsDevConfiguration(path, { environmentId })` to `createRunner`.
224
228
 
225
229
  In an encrypted room:
226
230
 
@@ -232,18 +236,18 @@ In an encrypted room:
232
236
  - A reply is journaled before it is encrypted. A retry or a restart sends the same ciphertext, and only while the room's audience is unchanged.
233
237
  - The ids Canon sees for what the runner sends there (each message's id, and a card's request id) are keyed with a secret the runner creates once in `$CANON_HOME/runner/<environment>/<agent>/mls-ids/` (0600; it belongs with the rest of the home). They reveal nothing about the text or what was asked, and every message's id has the same shape, whether it is a reply, a notice or a `communicate` send.
234
238
 
235
- Without `--mls-dev-config` (or `mls`), the runner refuses each encrypted room, logs it once, and runs nothing for it. Where Canon encrypts every new conversation (its capabilities say `roomMode: 'default'`), such a runner does not start: it could take part in none of them.
239
+ Without a device (no helper for the host and no `--mls-dev-config`, or `mls: false` for `createRunner`), the runner refuses each encrypted room, logs it once, and runs nothing for it. Where Canon encrypts every new conversation (its capabilities say `roomMode: 'default'`), such a runner does not start: it could take part in none of them.
236
240
 
237
241
  ### The class-C relay (Canon-operated)
238
242
 
239
243
  An assistant without an install of its own (ChatGPT, Claude.ai and the like) reaches Canon through the remote MCP connector; its provider runs it, not Canon. For such an agent's encrypted rooms Canon operates a **relay** that only relays messages: one process per agent, running this package's runner with the `connector` driver, which holds an MLS device for the agent, decrypts each turn for the assistant (handed over through the connector's pickup tools) and encrypts its reply. Those rooms are therefore **not end-to-end encrypted** to that agent. Agent developers do not run it; Canon's operator does:
240
244
 
241
245
  ```sh
242
- canon-agent run --driver connector --relay-config /abs/relay.json --mls-dev-config /abs/mls-dev.json
243
- canon-agent mls --action provision --mls-dev-config /abs/mls-dev.json --relay-config /abs/relay.json
246
+ canon-agent run --driver connector --relay-config /abs/relay.json
247
+ canon-agent mls --action status --relay-config /abs/relay.json
244
248
  ```
245
249
 
246
- The relay configuration (a private JSON file) names the agent, where the channel to Canon's Functions listens, and the path of the channel secret. No API key is involved: when the owner enables the relay, Canon delivers the relay's own credential over that channel (it works only for the relay's encrypted-room work, and stops when the relay or its connection is revoked), and the relay keeps it in `$CANON_HOME/relay` and starts under the relay installation it names (`canon-relay:…`). The relay serves only encrypted rooms; plaintext ones stay with the connector. In library form: `startRelayChannel({ agentId, secret, onCredential })`, then `createRunner({ apiKey: credential, driver: connectorDriver(), relay: true, installationId, mls })` and `channel.serve({ runner, inbox: driver.inbox }, grantId)`. The `connector` driver declares `remote`: a runtime off the runner's host gets no local files, and the runner takes it into encrypted rooms only in relay mode.
250
+ The relay configuration (a private JSON file) names the agent, where the channel to Canon's Functions listens, and the path of the channel secret. No API key is involved: when the owner enables the relay, Canon delivers the relay's own credential over that channel (it works only for the relay's encrypted-room work, and stops when the relay or its connection is revoked), and the relay keeps it in `$CANON_HOME/relay` and starts under the relay installation it names (`canon-relay:…`), setting up that installation's device as any runner does (or with `--mls-dev-config`). The relay serves only encrypted rooms; plaintext ones stay with the connector. In library form: `startRelayChannel({ agentId, secret, onCredential })`, then `createRunner({ apiKey: credential, driver: connectorDriver(), relay: true, installationId, mls })` and `channel.serve({ runner, inbox: driver.inbox }, grantId)`. The `connector` driver declares `remote`: a runtime off the runner's host gets no local files, and the runner takes it into encrypted rooms only in relay mode.
247
251
 
248
252
  ## Team rooms
249
253