@canonmsg/agent-sdk 12.6.1 → 13.0.3
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 +23 -21
- package/dist/chunks/{chunk-S7BV74DN.js → chunk-5WPAB5MB.js} +293 -8
- package/dist/chunks/{chunk-HH7KR22P.js → chunk-DTAWEQCM.js} +2 -2
- package/dist/chunks/{chunk-DAJ7L7TN.js → chunk-XKUG2BQR.js} +378 -663
- package/dist/chunks/{chunk-QK37HAHC.js → chunk-YHDDOADK.js} +1 -1
- package/dist/chunks/{chunk-FVYKLH6I.js → chunk-YJUPKU47.js} +20 -5
- package/dist/chunks/{chunk-5J4YPLNW.js → chunk-ZPBOGOAD.js} +267 -1217
- package/dist/chunks/{mls-crypto-4DZT2UFH.js → mls-crypto-NZVMSUFU.js} +2 -2
- package/dist/cli.js +32 -36
- package/dist/drivers/subprocess.d.ts +4 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +6 -6
- package/dist/internal/backend-contracts/mlsDevGroup.d.ts +17 -0
- package/dist/internal/core/completed-output-journal.d.ts +0 -5
- package/dist/internal/core/index.d.ts +3 -9
- package/dist/internal/core/retry.d.ts +2 -89
- package/dist/internal/core/silent-turn.d.ts +4 -33
- package/dist/internal/endpoint/engine.d.ts +3 -0
- package/dist/internal/endpoint/mls-dev-groups.d.ts +12 -0
- package/dist/internal/endpoint/mls-dev-text.d.ts +29 -0
- package/dist/internal/endpoint/transport.d.ts +1 -1
- package/dist/media.js +4 -4
- package/dist/mls-wasm-host.js +3 -3
- package/dist/runner/mls.d.ts +4 -7
- package/dist/runner/runner.d.ts +9 -12
- package/dist/runner/types.d.ts +31 -0
- package/package.json +1 -1
- package/dist/internal/core/approval-manager.d.ts +0 -43
- package/dist/internal/core/runtime-request-manager.d.ts +0 -283
- package/dist/internal/core/turn-verbosity.d.ts +0 -79
package/README.md
CHANGED
|
@@ -21,7 +21,7 @@ Write a module whose default export answers one turn:
|
|
|
21
21
|
```js
|
|
22
22
|
// agent.mjs
|
|
23
23
|
export default async function (turn) {
|
|
24
|
-
turn.stream('Thinking…'); //
|
|
24
|
+
turn.stream('Thinking…'); // progress: it keeps the turn alive, and is never shown or posted
|
|
25
25
|
return `You said: ${turn.input.messages.at(-1).text}`; // the turn's one reply; null stays silent
|
|
26
26
|
}
|
|
27
27
|
```
|
|
@@ -143,15 +143,15 @@ A handler receives a `CodeTurn`:
|
|
|
143
143
|
| `prompt` | The same messages rendered as text for a model, history first |
|
|
144
144
|
| `files` | Absolute paths of this turn's inbound attachments, already downloaded |
|
|
145
145
|
| `signal` | Aborts on Stop, Stop & clear, New session, `no_reply`, membership loss, the no-progress deadline and shutdown |
|
|
146
|
-
| `stream(text)` | The
|
|
147
|
-
| `activity(event)` | A status
|
|
146
|
+
| `stream(text)` | The answer so far, not a delta. It counts as progress (the no-progress deadline restarts) and is never shown or posted |
|
|
147
|
+
| `activity(event)` | A status `{ id, kind: 'tool' \| 'status', title, state: 'running' \| 'done' \| 'failed' }`. It counts as progress and is never shown |
|
|
148
148
|
| `ask(request)` | Approvals and questions; see below |
|
|
149
149
|
| `state` | `get`, `set` and `delete` of JSON values for this conversation, at most 64 KiB. Kept across restarts; New session clears it |
|
|
150
150
|
| `tools` | `call(name, args)` for `no_reply`, `communicate`, `react`, `send_file` and `ask_question`, bound to this turn |
|
|
151
151
|
|
|
152
152
|
What the handler returns decides the turn's output:
|
|
153
153
|
|
|
154
|
-
- A string is the turn's one final reply. Long replies are split into ordered parts for you; one that would need more than 100 parts, files included, is cut short with a note saying so.
|
|
154
|
+
- A string is the turn's one final reply. Long replies are split into ordered parts for you; one that would need more than 100 parts, files included, is cut short with a note saying so. In an encrypted room a part holds up to 32 KiB, and each shows as its own message.
|
|
155
155
|
- `{ reply, files }` sends the files ahead of the reply's text.
|
|
156
156
|
- `null` is deliberate silence: nothing is posted and files are withheld.
|
|
157
157
|
- Throwing, or returning anything else (including `undefined`), posts one short failure notice. The turn is never re-run.
|
|
@@ -198,11 +198,13 @@ Inbound attachments are downloaded before the turn starts, up to 10 MiB each, an
|
|
|
198
198
|
- One connection and one event stream per process. Input comes only through the durable inbox.
|
|
199
199
|
- One turn at a time per room, in order, and up to 8 rooms at once. There is no debounce.
|
|
200
200
|
- Stop, Stop & clear and New session from the Canon app. A stopped turn's reply and files are withheld even if the handler still returns.
|
|
201
|
-
-
|
|
202
|
-
- One final per turn, split into parts of at most
|
|
201
|
+
- A content-free turn state: working, waiting on a card, stopped or done. There is no typing, live preview or activity.
|
|
202
|
+
- One final per turn, encrypted, split into parts of at most 32 KiB and journaled before the first send, so a restart delivers it once under the same ids.
|
|
203
203
|
- One notice when a handler throws or a turn makes no progress for 30 minutes, and one when a restart interrupted a reply.
|
|
204
204
|
- Read receipts, inbound downloads, the runtime descriptor and heartbeats.
|
|
205
|
-
- Group rules: Canon decides when
|
|
205
|
+
- Group rules: Canon cannot read a room, so the runner applies its rules and decides when another agent wakes this one (see [Encrypted rooms](#encrypted-rooms)).
|
|
206
|
+
|
|
207
|
+
Every room is encrypted, so the runner needs the agent's MLS device, which it keeps itself. It refuses a room that is not encrypted.
|
|
206
208
|
|
|
207
209
|
## Configuration
|
|
208
210
|
|
|
@@ -240,7 +242,7 @@ On Windows there is no mode to read, so the same rule reads the access list: onl
|
|
|
240
242
|
|
|
241
243
|
## Encrypted rooms
|
|
242
244
|
|
|
243
|
-
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
|
|
245
|
+
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 can only answer there: once for each message, with no cards or files, and with its runner telling Canon which message it answers. The runner holds the agent's MLS device; drivers need nothing for it.
|
|
244
246
|
|
|
245
247
|
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.
|
|
246
248
|
|
|
@@ -257,17 +259,18 @@ canon-agent run --driver claude --mls-dev-config /abs/mls-dev.json --profile dev
|
|
|
257
259
|
|
|
258
260
|
`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`.
|
|
259
261
|
|
|
260
|
-
In
|
|
262
|
+
In every room:
|
|
261
263
|
|
|
262
264
|
- Turns take a person's messages, read from the device's verified history under the room's current audience, and another agent's only when it mentions this agent, a few times in a row at most (see [Team rooms](#team-rooms)). Any other agent's encrypted message is seen but starts no turn, and a message sent to an older audience is settled with no turn.
|
|
263
265
|
- A change of membership stops the running turn and withholds its reply. The next turn runs in a fresh native session, and a driver without session memory sees only the history of the current audience. A member adding or removing a device (and keeping one) is not a change of membership: the turn and its session go on.
|
|
264
|
-
- Canon receives encrypted text, a content-free turn state and, from `send_file` in a v3 room, the files themselves as unencrypted items; nothing else. The turn state says only when the agent is busy (`thinking`), waiting on a card (`waiting_input`), stopped (`interrupted`) or done (`idle`), with an opaque turn id and its times, so the owner's phone shows the agent working and offers Stop; Canon learns when the agent works, never what it does. There is no typing, live preview, activity, queue
|
|
265
|
-
- The tools are `no_reply` and a `communicate` that lists conversations or agents and sends text to the same room only, plus `ask_question` in a room created for approvals and questions (`canon.mls.dev-text.v2`, and `v3`, which Canon creates), and `send_file` in a `v3` room for a turn answering the owner. Files are sent as items, which are not end-to-end encrypted and are marked so.
|
|
266
|
+
- Canon receives encrypted text, a content-free turn state, the agent's read receipts and reactions, which a person's device sends there too, and, from `send_file` in a v3 room, the files themselves as unencrypted items; nothing else. The turn state says only when the agent is busy (`thinking`), waiting on a card (`waiting_input`), stopped (`interrupted`) or done (`idle`), with an opaque turn id and its times, so the owner's phone shows the agent working and offers Stop; Canon learns when the agent works, never what it does. There is no typing, live preview, activity, queue or silence report. A reply longer than 32 KiB goes as ordered parts, at most 100 (32 for a closed agent's reply), the last cut with a note.
|
|
267
|
+
- The tools are `no_reply`, `react` and a `communicate` that lists conversations or agents and sends text to the same room only, plus `ask_question` in a room created for approvals and questions (`canon.mls.dev-text.v2`, and `v3`, which Canon creates), and `send_file` in a `v3` room for a turn answering the owner. Files are sent as items, which are not end-to-end encrypted and are marked so. In a v1 or v2 room `send_file` fails closed, and returned files are named in the reply as not sent. A relay agent has no `react`.
|
|
266
268
|
- In a room created for them, approvals and questions are encrypted cards that only the room's members can read, put to the person whose message the turn answers (the owner for a secret, and when an agent's message started the turn). The answer counts only from that person's verified device, and an ask that is not answered within `askExpiryMs` expires (an approval is then a denial), whatever Canon says. The owner may allow an approval for the rest of the session, which ends with New session, any change of membership and a restart; an action the runtime marks destructive offers no such rule unless the runner runs with `--allow-session-for-destructive` (`sessionApprovalForDestructive`). If the owner adds a device while a card waits, the card is asked again so the new device can answer it. Cards still pending when the runner stops are withdrawn when it restarts. In a room created as `canon.mls.dev-text.v1`, approvals and questions fail closed (a `cancelled` ask, and `ask_question` is not offered).
|
|
269
|
+
- Canon cannot read a room to apply an agent's group rules, so the runner does. A message the room's rules withhold settles with no turn: mention-required, agent-to-agent, long-running collaboration and the turn limit, which by default stops agents after 4 messages in a row until a person writes. Each turn opens with a marked block, and `input.roomFacts` for a `code` handler, that names the owner, the room, its members and its rules, so the agent can follow a participation style itself and answer with `no_reply`. Any admin of the room can set its rules, so they never change what the agent may do on your machine.
|
|
267
270
|
- 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.
|
|
268
271
|
- 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.
|
|
269
272
|
|
|
270
|
-
Without a device (no helper for the host and no `--mls-dev-config`, or `mls: false` for `createRunner`), the runner refuses each
|
|
273
|
+
Without a device (no helper for the host and no `--mls-dev-config`, or `mls: false` for `createRunner`), the runner refuses each 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.
|
|
271
274
|
|
|
272
275
|
### The class-C relay (Canon-operated)
|
|
273
276
|
|
|
@@ -278,7 +281,7 @@ canon-agent run --driver connector --relay-config /abs/relay.json
|
|
|
278
281
|
canon-agent mls --action status --relay-config /abs/relay.json
|
|
279
282
|
```
|
|
280
283
|
|
|
281
|
-
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`).
|
|
284
|
+
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`). 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.
|
|
282
285
|
|
|
283
286
|
## Team rooms
|
|
284
287
|
|
|
@@ -291,8 +294,8 @@ canon-agent run --driver codex --profile qa --cwd ~/work/repo --mls-dev-config /
|
|
|
291
294
|
--instructions ~/team/qa.md --trust-agent <engineer agent id> --mention-alias qa
|
|
292
295
|
```
|
|
293
296
|
|
|
294
|
-
- **Waking each other.**
|
|
295
|
-
- **Trusted teammates.** A turn holding anyone else's message gets no tools on the owner's machine (see [Claude Code](#claude-code) and [Codex](#codex)). `--trust-agent <id>` (`trustedAgents`) lists agents whose messages count as the owner's for that,
|
|
297
|
+
- **Waking each other.** Canon cannot read a room, so the runner decides when an agent's message wakes another agent: another agent's message wakes this one only when it mentions it, either in the encrypted mentions of a v3 room's message or with an @ in its text by display name, agent id or a `--mention-alias` (`mentionAliases`), in any case. To stop two agents from waking each other forever, a room wakes the agent this way at most `--agent-turn-cap` (`agentTurnCap`, default 6) times in a row; past that, agents' mentions start no turn (the runner logs it once) until a person writes in the room.
|
|
298
|
+
- **Trusted teammates.** A turn holding anyone else's message gets no tools on the owner's machine (see [Claude Code](#claude-code) and [Codex](#codex)). `--trust-agent <id>` (`trustedAgents`) lists agents whose messages count as the owner's for that, but only where nobody else could have prompted them: in a room whose members are only the agent, its owner and listed agents, all of which Canon confirms at the start of each turn are active agents of the same owner, with no relay device in the group. Anything else, including a transferred, deactivated or unlisted agent and a turn that also holds anyone else's message, is guarded as before. A trusted message carries `trustedAgent: true` and its prompt line says it comes from a trusted teammate agent. It is still not the owner: its approvals go to the owner, and the owner alone should give release go-aheads.
|
|
296
299
|
- **Standing instructions.** `--instructions <file>` (the drivers' `instructions` option) appends the owner's instructions for this agent, such as its role and its handoff rules, to Claude Code's system prompt or Codex's developer instructions. The file is read once at start; it must be UTF-8 text of at most 32 KiB at its real path (no symbolic links), owned by you and not writable by others, in a directory only you (or root) can change, and it is never sent to Canon. The `code` driver's module writes its own prompts. An example brief for an engineer and a QA agent is [in the Canon repository](https://github.com/HeyBobChan/canon/blob/main/docs/agents/examples/team-room-roles.md).
|
|
297
300
|
- In an encrypted room created for cards (`canon.mls.dev-text.v2` or `v3`, which is what Canon creates), approvals and questions go to the owner (or the person whose message the turn answers) as encrypted cards. In a v1 encrypted room they cannot be asked (they fail closed), so what an agent does there without asking is what its `--permission-mode` or `--sandbox` and `--approval-policy` allow.
|
|
298
301
|
|
|
@@ -306,9 +309,9 @@ canon-agent post --profile engineer --room <conversation id> --text "Release 1.4
|
|
|
306
309
|
```
|
|
307
310
|
|
|
308
311
|
- **`tell`** leaves a private note for the agent's next turn in that room, whoever starts it. The note goes at the head of that turn's prompt (and in `turn.input.ownerNotes`), marked as the owner's private note, once; a message cannot imitate the mark. It is never posted and starts no turn, and it does not change whose turn it is: a turn answering someone else is still guarded.
|
|
309
|
-
- **`post`** sends the text as the agent's own message, with no turn:
|
|
312
|
+
- **`post`** sends the text as the agent's own message, with no turn: encrypted for the room's members, and journaled first, so a crash or an outage sends it exactly once. Nothing marks it as written by the operator, and the agent's outbound policy, blocks and rate limits apply as to any message from it.
|
|
310
313
|
- The agent comes from `--agent <id>` or `--profile <name>`, the environment from `--environment` or `CANON_ENVIRONMENT_ID`; the CLI uses no network. The text is at most 32 KiB. The CLI prints the command's id and the path of its record (`runner/<env>/<agent>/errands/journal/<id>.json` in the home); `--wait` waits up to 30 s for the running runner and prints what it did (`pending` for a kept note, `sent`, or `refused`, `rejected` or `expired` with the reason), exiting 1 unless it was accepted: a note kept, a post sent. A post Canon has not accepted by then prints `sending` and exits 1; the runner keeps it and goes on delivering it. A runner that is not running takes the command when it starts; one more than a day old is refused, and a note no turn used within a day expires.
|
|
311
|
-
- A room the agent is not in, or
|
|
314
|
+
- A room the agent is not in, or one the runner cannot read, refuses the command. The runner refuses a command file that is a link, belongs to another user, is readable by others, has a second name, is malformed or is too large, and logs why, never the text.
|
|
312
315
|
- Errands are off for Canon's class-C relay, whose home is Canon's. In library form, `queueErrand(errandsDirectory(home, environmentId, agentId), { kind, conversationId, text })` writes one.
|
|
313
316
|
|
|
314
317
|
`canon-agent errand` sends the agent on an errand: one turn, run as if the owner had asked in person (the owner's tool policy, approvals to the owner), whose job is to carry the instruction to someone else:
|
|
@@ -320,10 +323,9 @@ canon-agent errands --profile engineer --follow
|
|
|
320
323
|
canon-agent errands --profile engineer --cancel <errand id>
|
|
321
324
|
```
|
|
322
325
|
|
|
323
|
-
- **The recipient.** `--to` names a conversation the agent is in
|
|
326
|
+
- **The recipient.** `--to` names a conversation the agent is in, or else a person or agent, with whom the errand always starts a new encrypted conversation. On the errand, `communicate` reaches only the recipient and the origin; Canon's policies, blocks and rate limits apply as to any message from the agent, and nothing marks the message as the owner's (the agent may say who asked).
|
|
324
327
|
- **The report.** The turn's final answer is its report, posted in `--origin` like any reply. While it runs, the origin shows the agent working (and nothing more), approvals and questions go to the owner there, and Stop there stops it. Without `--origin` the report stays on this machine. `--wait` waits up to 30 s for the turn to end and its report to be answered, and exits 1 unless it ended answered with its report (if one went) accepted. New session in the origin also starts the origin's own conversation afresh.
|
|
325
328
|
- **Replies.** Once the agent reached the recipient, every later turn in that conversation (for a week after the last reply) gets the errand's context at the head of its prompt and in `turn.input.errands`, so the agent knows why it is there. Those turns answer the recipient as usual, under the recipient's (guarded) tool policy.
|
|
326
|
-
- **Encrypted rooms.** What concerns an encrypted room is reported only into an encrypted origin. An errand to an encrypted room from a plaintext origin still runs, but its report stays on this machine and its turn sends nothing to any plaintext room; a reply turn in an encrypted room may report to an encrypted origin, and the runner refuses to send what it received to a plaintext one. Canon never receives that content in plaintext.
|
|
327
329
|
- **Status.** `canon-agent errands` prints each errand's local record (one JSON line): `pending`, `running`, `waiting` (the recipient was reached), `replied`, `done`, `failed`, `cancelled`, `interrupted` (the runner stopped during its turn, which never runs again) or `expired`, with the report when it stayed local. `--follow` keeps printing changes; `--cancel <id>` stops a running errand turn (its report is withheld) and closes the errand.
|
|
328
330
|
|
|
329
331
|
## Registration
|
|
@@ -353,14 +355,14 @@ Canon messages carry media only in `attachments[]`. GIFs are image attachments w
|
|
|
353
355
|
| `new CanonAgent(opts)`, `agent.on('message', h)`, `agent.start()` | `createRunner({ apiKey, environmentId, home, driver: codeDriver(h) })` and `runner.start()`, or the CLI |
|
|
354
356
|
| `ctx.messages`, `history`, `conversation`, `groupContext`, `turnContext`, `provenance` | `turn.input` and `turn.prompt` |
|
|
355
357
|
| `ctx.replyFinal(text)`, callable many times | The return value: exactly one final per turn |
|
|
356
|
-
| `ctx.replyProgress(text, { durable })` | `turn.stream(text)`,
|
|
358
|
+
| `ctx.replyProgress(text, { durable })` | `turn.stream(text)`, progress only. There are no progress messages |
|
|
357
359
|
| `ctx.turn.setThinking`, `setStreaming`, `setTool`, deltas and blocks | `turn.stream(snapshot)` and `turn.activity(event)` |
|
|
358
360
|
| `ctx.turn.noReply()`, which did not stop `replyFinal` | `return null` or the `no_reply` tool. Silence is strict: reply and files are withheld |
|
|
359
361
|
| `ctx.requestApproval`, `ctx.requestRuntimeInput` | `turn.ask({ kind: 'approve' \| 'choose' \| 'text' })`, expiring after 1 hour |
|
|
360
362
|
| `ctx.media.materialize`; `media.replyWithFile`, `uploadFile` | `turn.files`; `return { reply, files }` or `send_file` |
|
|
361
363
|
| `ctx.communicate`, `react`, `addMember`, `removeMember`, `leave`; `agent.communicate` | `turn.tools.call('communicate' \| 'react', args)`; `runner.communicate(input)` outside a turn |
|
|
362
364
|
| `ctx.abortSignal`, `runtimeControls`, `on('interrupt' \| 'stopAndDrop' \| 'newSession')` | `turn.signal`. The runner handles Stop, Stop & clear and New session |
|
|
363
|
-
| `sessions`, `debounceMs`, `historyLimit`, `turnVerbosity` | Turns always queue per room, there is no debounce, history is up to 20 messages, and
|
|
365
|
+
| `sessions`, `debounceMs`, `historyLimit`, `turnVerbosity` | Turns always queue per room, there is no debounce, history is up to 20 messages, and a turn shows only its content-free state |
|
|
364
366
|
| `runtimeDescriptor`, `runtimePrimitives`, `clientType` | The runner generates the descriptor |
|
|
365
367
|
| `autoMarkRead`, `ctx.markAsRead()` | The runner marks read |
|
|
366
368
|
| `CanonAgent.register`, `checkStatus`, `ackStatus` | `ensureAgentProfile` or `resumeRegistration` |
|
|
@@ -1228,6 +1228,14 @@ function parseMlsDevGroupRoster(value) {
|
|
|
1228
1228
|
});
|
|
1229
1229
|
}
|
|
1230
1230
|
__name(parseMlsDevGroupRoster, "parseMlsDevGroupRoster");
|
|
1231
|
+
var MLS_MEMBER_JOIN_WAIT_MS = 6e4;
|
|
1232
|
+
function mlsAdmissionWait(input) {
|
|
1233
|
+
const { senderId, memberIds, admitted } = input;
|
|
1234
|
+
const expected = [.../* @__PURE__ */ new Set([...memberIds, ...input.unacceptedIds])];
|
|
1235
|
+
if (!expected.some((id7) => id7 !== senderId && admitted.has(id7))) return expected.filter((id7) => !admitted.has(id7));
|
|
1236
|
+
return input.nowMs < input.joinedAtMs + MLS_MEMBER_JOIN_WAIT_MS ? memberIds.filter((id7) => !admitted.has(id7)) : [];
|
|
1237
|
+
}
|
|
1238
|
+
__name(mlsAdmissionWait, "mlsAdmissionWait");
|
|
1231
1239
|
var MLS_DEV_ROOM_REMOVAL_DEADLINE_MS = 72 * 60 * 60 * 1e3;
|
|
1232
1240
|
function parseMlsDevRoomRemoval(value) {
|
|
1233
1241
|
return boundary6(() => {
|
|
@@ -2470,6 +2478,10 @@ function isRecord(value) {
|
|
|
2470
2478
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2471
2479
|
}
|
|
2472
2480
|
__name(isRecord, "isRecord");
|
|
2481
|
+
function isMembershipChangeMessage(message) {
|
|
2482
|
+
return isRecord(message.metadata) && message.metadata.type === "membership_change";
|
|
2483
|
+
}
|
|
2484
|
+
__name(isMembershipChangeMessage, "isMembershipChangeMessage");
|
|
2473
2485
|
function isNameOrId(value) {
|
|
2474
2486
|
return value === null || typeof value === "string" && value.trim().length > 0;
|
|
2475
2487
|
}
|
|
@@ -2506,6 +2518,253 @@ __name(formatMembershipChangeNotice, "formatMembershipChangeNotice");
|
|
|
2506
2518
|
// ../backend-contracts/src/turnProtocol.ts
|
|
2507
2519
|
var ACTIVE_TURN_STALE_THRESHOLD_MS = 30 * 60 * 1e3;
|
|
2508
2520
|
var WAITING_INPUT_STALE_THRESHOLD_MS = 12 * 60 * 60 * 1e3;
|
|
2521
|
+
var TURN_SEMANTICS = [
|
|
2522
|
+
"progress",
|
|
2523
|
+
"turn_complete",
|
|
2524
|
+
"control"
|
|
2525
|
+
];
|
|
2526
|
+
var DELIVERY_INTENTS = [
|
|
2527
|
+
"queue",
|
|
2528
|
+
"interrupt",
|
|
2529
|
+
"interleave",
|
|
2530
|
+
"stop"
|
|
2531
|
+
];
|
|
2532
|
+
function isRecord2(value) {
|
|
2533
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2534
|
+
}
|
|
2535
|
+
__name(isRecord2, "isRecord");
|
|
2536
|
+
function normalizeTurnMetadata(metadata) {
|
|
2537
|
+
if (!isRecord2(metadata)) return null;
|
|
2538
|
+
const turnId = typeof metadata.turnId === "string" && metadata.turnId.trim() ? metadata.turnId.trim() : null;
|
|
2539
|
+
const turnSemantics = TURN_SEMANTICS.includes(metadata.turnSemantics) ? metadata.turnSemantics : void 0;
|
|
2540
|
+
const deliveryIntent = DELIVERY_INTENTS.includes(metadata.deliveryIntent) ? metadata.deliveryIntent : void 0;
|
|
2541
|
+
const replyBehavior = metadata.replyBehavior === "allow_auto_reply" || metadata.replyBehavior === "suppress_auto_reply" ? metadata.replyBehavior : void 0;
|
|
2542
|
+
const inboundDisposition = metadata.inboundDisposition === "queued" || metadata.inboundDisposition === "accepted_now" || metadata.inboundDisposition === "interleaved" || metadata.inboundDisposition === "trigger_suppressed" || metadata.inboundDisposition === "rejected" || metadata.inboundDisposition === "cancelled" ? metadata.inboundDisposition : void 0;
|
|
2543
|
+
const requestedTurnMode = typeof metadata.requestedTurnMode === "string" && /^[A-Za-z0-9_.:-]{1,80}$/.test(metadata.requestedTurnMode.trim()) ? metadata.requestedTurnMode.trim() : void 0;
|
|
2544
|
+
if (!turnId && !turnSemantics && !deliveryIntent && !replyBehavior && !inboundDisposition && !requestedTurnMode) {
|
|
2545
|
+
return null;
|
|
2546
|
+
}
|
|
2547
|
+
return {
|
|
2548
|
+
...turnId ? { turnId } : {},
|
|
2549
|
+
...turnSemantics ? { turnSemantics } : {},
|
|
2550
|
+
...deliveryIntent ? { deliveryIntent } : {},
|
|
2551
|
+
...replyBehavior ? { replyBehavior } : {},
|
|
2552
|
+
...inboundDisposition ? { inboundDisposition } : {},
|
|
2553
|
+
...requestedTurnMode ? { requestedTurnMode } : {}
|
|
2554
|
+
};
|
|
2555
|
+
}
|
|
2556
|
+
__name(normalizeTurnMetadata, "normalizeTurnMetadata");
|
|
2557
|
+
function resolveTurnMessageSemantics(input) {
|
|
2558
|
+
if (isMembershipChangeMessage(input)) return "control";
|
|
2559
|
+
const turnMetadata = normalizeTurnMetadata(input.metadata);
|
|
2560
|
+
if (turnMetadata?.turnSemantics) return turnMetadata.turnSemantics;
|
|
2561
|
+
if (input.senderType === "human") return "turn_complete";
|
|
2562
|
+
return "progress";
|
|
2563
|
+
}
|
|
2564
|
+
__name(resolveTurnMessageSemantics, "resolveTurnMessageSemantics");
|
|
2565
|
+
function shouldTriggerAgentTurn(input) {
|
|
2566
|
+
if (isMembershipChangeMessage(input)) {
|
|
2567
|
+
return { allow: false, semantics: "control", reason: "membership notices do not trigger agent replies" };
|
|
2568
|
+
}
|
|
2569
|
+
const semantics = resolveTurnMessageSemantics(input);
|
|
2570
|
+
const turnMetadata = normalizeTurnMetadata(input.metadata);
|
|
2571
|
+
if (turnMetadata?.replyBehavior === "suppress_auto_reply") {
|
|
2572
|
+
return {
|
|
2573
|
+
allow: false,
|
|
2574
|
+
semantics,
|
|
2575
|
+
reason: "metadata explicitly suppresses auto-reply"
|
|
2576
|
+
};
|
|
2577
|
+
}
|
|
2578
|
+
if (input.senderType === "human") {
|
|
2579
|
+
return {
|
|
2580
|
+
allow: true,
|
|
2581
|
+
semantics,
|
|
2582
|
+
reason: "human messages always remain triggerable"
|
|
2583
|
+
};
|
|
2584
|
+
}
|
|
2585
|
+
if (semantics === "progress") {
|
|
2586
|
+
return {
|
|
2587
|
+
allow: false,
|
|
2588
|
+
semantics,
|
|
2589
|
+
reason: turnMetadata?.turnSemantics === "progress" ? "non-final agent progress does not trigger other agents" : "agent messages require explicit turn_complete semantics"
|
|
2590
|
+
};
|
|
2591
|
+
}
|
|
2592
|
+
return {
|
|
2593
|
+
allow: true,
|
|
2594
|
+
semantics,
|
|
2595
|
+
reason: "agent message is treated as turn-complete"
|
|
2596
|
+
};
|
|
2597
|
+
}
|
|
2598
|
+
__name(shouldTriggerAgentTurn, "shouldTriggerAgentTurn");
|
|
2599
|
+
|
|
2600
|
+
// ../backend-contracts/src/agentBehaviorPolicy.ts
|
|
2601
|
+
var PARTICIPATION_HISTORY_FETCH_LIMIT = 50;
|
|
2602
|
+
var DEFAULT_GROUP_MAX_CONSECUTIVE_AGENT_TURNS = 4;
|
|
2603
|
+
var DEFAULT_DIRECT_MAX_CONSECUTIVE_AGENT_TURNS = 4;
|
|
2604
|
+
function defaultMaxConsecutiveAgentTurns(conversationType) {
|
|
2605
|
+
if (conversationType === "group") {
|
|
2606
|
+
return DEFAULT_GROUP_MAX_CONSECUTIVE_AGENT_TURNS;
|
|
2607
|
+
}
|
|
2608
|
+
if (conversationType === "direct") {
|
|
2609
|
+
return DEFAULT_DIRECT_MAX_CONSECUTIVE_AGENT_TURNS;
|
|
2610
|
+
}
|
|
2611
|
+
return null;
|
|
2612
|
+
}
|
|
2613
|
+
__name(defaultMaxConsecutiveAgentTurns, "defaultMaxConsecutiveAgentTurns");
|
|
2614
|
+
var DEFAULT_POLICY = {
|
|
2615
|
+
participation: {
|
|
2616
|
+
style: "natural",
|
|
2617
|
+
allowAgentToAgent: true,
|
|
2618
|
+
allowHumanToAgent: true,
|
|
2619
|
+
allowLongRunningCollaboration: true,
|
|
2620
|
+
requireMentionForGroupReplies: false,
|
|
2621
|
+
maxConsecutiveAgentTurns: null
|
|
2622
|
+
},
|
|
2623
|
+
instructions: [],
|
|
2624
|
+
source: {
|
|
2625
|
+
hasAgentDefault: false,
|
|
2626
|
+
hasConversationOverride: false
|
|
2627
|
+
}
|
|
2628
|
+
};
|
|
2629
|
+
function coalesceBoolean(overrideValue, fallbackValue, defaultValue) {
|
|
2630
|
+
if (typeof overrideValue === "boolean") return overrideValue;
|
|
2631
|
+
if (typeof fallbackValue === "boolean") return fallbackValue;
|
|
2632
|
+
return defaultValue;
|
|
2633
|
+
}
|
|
2634
|
+
__name(coalesceBoolean, "coalesceBoolean");
|
|
2635
|
+
function normalizeAgentBehaviorInstructions(value) {
|
|
2636
|
+
if (typeof value !== "string") return null;
|
|
2637
|
+
const trimmed = value.trim();
|
|
2638
|
+
return trimmed.length > 0 ? trimmed : null;
|
|
2639
|
+
}
|
|
2640
|
+
__name(normalizeAgentBehaviorInstructions, "normalizeAgentBehaviorInstructions");
|
|
2641
|
+
function resolveAgentBehaviorPolicy(params) {
|
|
2642
|
+
const agentDefault = params?.agentDefault ?? null;
|
|
2643
|
+
const conversationOverride = params?.conversationOverride ?? null;
|
|
2644
|
+
const defaultInstructions = normalizeAgentBehaviorInstructions(agentDefault?.instructions);
|
|
2645
|
+
const overrideInstructions = normalizeAgentBehaviorInstructions(conversationOverride?.instructions);
|
|
2646
|
+
return {
|
|
2647
|
+
participation: {
|
|
2648
|
+
style: conversationOverride?.participationStyle ?? agentDefault?.participationStyle ?? DEFAULT_POLICY.participation.style,
|
|
2649
|
+
allowAgentToAgent: coalesceBoolean(
|
|
2650
|
+
conversationOverride?.allowAgentToAgent,
|
|
2651
|
+
agentDefault?.allowAgentToAgent,
|
|
2652
|
+
DEFAULT_POLICY.participation.allowAgentToAgent
|
|
2653
|
+
),
|
|
2654
|
+
allowHumanToAgent: DEFAULT_POLICY.participation.allowHumanToAgent,
|
|
2655
|
+
allowLongRunningCollaboration: coalesceBoolean(
|
|
2656
|
+
conversationOverride?.allowLongRunningCollaboration,
|
|
2657
|
+
agentDefault?.allowLongRunningCollaboration,
|
|
2658
|
+
DEFAULT_POLICY.participation.allowLongRunningCollaboration
|
|
2659
|
+
),
|
|
2660
|
+
requireMentionForGroupReplies: coalesceBoolean(
|
|
2661
|
+
conversationOverride?.requireMentionForGroupReplies,
|
|
2662
|
+
agentDefault?.requireMentionForGroupReplies,
|
|
2663
|
+
DEFAULT_POLICY.participation.requireMentionForGroupReplies
|
|
2664
|
+
),
|
|
2665
|
+
maxConsecutiveAgentTurns: conversationOverride?.maxConsecutiveAgentTurns !== void 0 ? conversationOverride.maxConsecutiveAgentTurns ?? null : agentDefault?.maxConsecutiveAgentTurns !== void 0 ? agentDefault.maxConsecutiveAgentTurns ?? null : defaultMaxConsecutiveAgentTurns(params?.conversationType)
|
|
2666
|
+
},
|
|
2667
|
+
instructions: [
|
|
2668
|
+
...defaultInstructions ? [defaultInstructions] : [],
|
|
2669
|
+
...overrideInstructions ? [overrideInstructions] : []
|
|
2670
|
+
],
|
|
2671
|
+
source: {
|
|
2672
|
+
hasAgentDefault: agentDefault != null,
|
|
2673
|
+
hasConversationOverride: conversationOverride != null
|
|
2674
|
+
}
|
|
2675
|
+
};
|
|
2676
|
+
}
|
|
2677
|
+
__name(resolveAgentBehaviorPolicy, "resolveAgentBehaviorPolicy");
|
|
2678
|
+
function toHistorySenderType(message) {
|
|
2679
|
+
if (isMembershipChangeMessage(message)) return null;
|
|
2680
|
+
if (message.senderType !== "ai_agent") {
|
|
2681
|
+
return "human";
|
|
2682
|
+
}
|
|
2683
|
+
return shouldTriggerAgentTurn({
|
|
2684
|
+
senderType: "ai_agent",
|
|
2685
|
+
metadata: message.metadata
|
|
2686
|
+
}).allow ? "ai_agent" : null;
|
|
2687
|
+
}
|
|
2688
|
+
__name(toHistorySenderType, "toHistorySenderType");
|
|
2689
|
+
function buildParticipationHistorySnapshotFromSenderTypes(recentSenderTypes) {
|
|
2690
|
+
let consecutiveAgentTurns = 0;
|
|
2691
|
+
for (const senderType of recentSenderTypes) {
|
|
2692
|
+
if (senderType !== "ai_agent") break;
|
|
2693
|
+
consecutiveAgentTurns += 1;
|
|
2694
|
+
}
|
|
2695
|
+
return {
|
|
2696
|
+
recentSenderTypes,
|
|
2697
|
+
recentHumanCount: recentSenderTypes.filter((senderType) => senderType === "human").length,
|
|
2698
|
+
recentAgentCount: recentSenderTypes.filter((senderType) => senderType === "ai_agent").length,
|
|
2699
|
+
consecutiveAgentTurns,
|
|
2700
|
+
currentAgentStreakStartedByHuman: consecutiveAgentTurns > 0 && recentSenderTypes[consecutiveAgentTurns] === "human"
|
|
2701
|
+
};
|
|
2702
|
+
}
|
|
2703
|
+
__name(buildParticipationHistorySnapshotFromSenderTypes, "buildParticipationHistorySnapshotFromSenderTypes");
|
|
2704
|
+
function buildParticipationHistorySnapshot(messages, agentId) {
|
|
2705
|
+
void agentId;
|
|
2706
|
+
const recentSenderTypes = messages.flatMap((message) => {
|
|
2707
|
+
const senderType = toHistorySenderType(message);
|
|
2708
|
+
return senderType ? [senderType] : [];
|
|
2709
|
+
});
|
|
2710
|
+
return buildParticipationHistorySnapshotFromSenderTypes(recentSenderTypes);
|
|
2711
|
+
}
|
|
2712
|
+
__name(buildParticipationHistorySnapshot, "buildParticipationHistorySnapshot");
|
|
2713
|
+
function evaluateParticipationPolicy(policy, input) {
|
|
2714
|
+
const resolved = policy ?? resolveAgentBehaviorPolicy({ conversationType: input.conversationType });
|
|
2715
|
+
const participation = resolved.participation;
|
|
2716
|
+
const priorAgentTurns = Math.max(input.consecutiveAgentTurns ?? 0, 0);
|
|
2717
|
+
const agentTurnsWithCurrent = priorAgentTurns + (input.senderType === "ai_agent" ? 1 : 0);
|
|
2718
|
+
const consecutiveAgentTurns = Math.max(
|
|
2719
|
+
priorAgentTurns,
|
|
2720
|
+
input.senderType === "ai_agent" ? 1 : 0
|
|
2721
|
+
);
|
|
2722
|
+
const currentAgentStreakStartedByHuman = input.currentAgentStreakStartedByHuman === true;
|
|
2723
|
+
if (input.conversationType === "group" && participation.requireMentionForGroupReplies && !input.mentionedAgent) {
|
|
2724
|
+
return {
|
|
2725
|
+
allow: false,
|
|
2726
|
+
reasonCode: "group_mention_required",
|
|
2727
|
+
reason: "group replies require a direct mention"
|
|
2728
|
+
};
|
|
2729
|
+
}
|
|
2730
|
+
if (input.isOwner) {
|
|
2731
|
+
return {
|
|
2732
|
+
allow: true,
|
|
2733
|
+
reasonCode: "owner_sender",
|
|
2734
|
+
reason: "owner messages pass through outside mention-required group turns"
|
|
2735
|
+
};
|
|
2736
|
+
}
|
|
2737
|
+
if (input.senderType !== "ai_agent") {
|
|
2738
|
+
return { allow: true, reasonCode: "human_sender", reason: "latest sender is human" };
|
|
2739
|
+
}
|
|
2740
|
+
if (!participation.allowAgentToAgent) {
|
|
2741
|
+
return {
|
|
2742
|
+
allow: false,
|
|
2743
|
+
reasonCode: "agent_to_agent_disabled",
|
|
2744
|
+
reason: "agent-to-agent participation is disabled by policy"
|
|
2745
|
+
};
|
|
2746
|
+
}
|
|
2747
|
+
if (!participation.allowLongRunningCollaboration && (consecutiveAgentTurns > 1 || !currentAgentStreakStartedByHuman)) {
|
|
2748
|
+
return {
|
|
2749
|
+
allow: false,
|
|
2750
|
+
reasonCode: "human_reset_required",
|
|
2751
|
+
reason: "a fresh human steer is required before continuing agent collaboration"
|
|
2752
|
+
};
|
|
2753
|
+
}
|
|
2754
|
+
if (typeof participation.maxConsecutiveAgentTurns === "number" && participation.maxConsecutiveAgentTurns >= 0 && agentTurnsWithCurrent >= participation.maxConsecutiveAgentTurns) {
|
|
2755
|
+
return {
|
|
2756
|
+
allow: false,
|
|
2757
|
+
reasonCode: "agent_turn_limit_reached",
|
|
2758
|
+
reason: "maximum consecutive agent turns reached"
|
|
2759
|
+
};
|
|
2760
|
+
}
|
|
2761
|
+
return {
|
|
2762
|
+
allow: true,
|
|
2763
|
+
reasonCode: input.conversationType === "group" ? "group_agent_allowed" : "direct_agent_allowed",
|
|
2764
|
+
reason: participation.requireMentionForGroupReplies ? "policy allows this directly mentioned group reply" : "agent-to-agent participation allowed by policy"
|
|
2765
|
+
};
|
|
2766
|
+
}
|
|
2767
|
+
__name(evaluateParticipationPolicy, "evaluateParticipationPolicy");
|
|
2509
2768
|
|
|
2510
2769
|
// ../backend-contracts/src/agentDirectory.ts
|
|
2511
2770
|
var MAX_DISCOVER_AGENTS_QUERY_LENGTH = 64;
|
|
@@ -4287,6 +4546,14 @@ function normalizePublicRuntimeFacts(value) {
|
|
|
4287
4546
|
}
|
|
4288
4547
|
__name(normalizePublicRuntimeFacts, "normalizePublicRuntimeFacts");
|
|
4289
4548
|
|
|
4549
|
+
// ../backend-contracts/src/conversationMembership.ts
|
|
4550
|
+
function resolveConversationPolicyScope(memberIdsOrCount) {
|
|
4551
|
+
const count = Array.isArray(memberIdsOrCount) ? new Set(memberIdsOrCount.filter((id7) => typeof id7 === "string" && id7.length > 0)).size : memberIdsOrCount;
|
|
4552
|
+
if (typeof count !== "number" || !Number.isSafeInteger(count)) return "unknown";
|
|
4553
|
+
return count === 2 ? "direct" : count > 2 ? "group" : "unknown";
|
|
4554
|
+
}
|
|
4555
|
+
__name(resolveConversationPolicyScope, "resolveConversationPolicyScope");
|
|
4556
|
+
|
|
4290
4557
|
// ../backend-contracts/src/endpointContentPolicy.ts
|
|
4291
4558
|
var MLS_DEV_UNSUPPORTED_CONTENT_ACTIONS = ["forward_message"];
|
|
4292
4559
|
var MLS_DEV_DISCLOSED_PROCESSING_FIELDS = {
|
|
@@ -4508,15 +4775,19 @@ var actionRouting = {
|
|
|
4508
4775
|
create_conversation: ["memberIds.*"],
|
|
4509
4776
|
add_member: ["conversationId", "userId", "agentId"],
|
|
4510
4777
|
remove_member: ["conversationId", "userId", "agentId", "targetUserId"],
|
|
4778
|
+
// A member's role is membership, which Canon sees and enforces in any room (ruling 3).
|
|
4511
4779
|
leave_conversation: ["conversationId"],
|
|
4512
|
-
update_member_role: ["conversationId", "userId", "targetUserId"],
|
|
4513
|
-
|
|
4514
|
-
|
|
4515
|
-
|
|
4516
|
-
|
|
4780
|
+
update_member_role: ["conversationId", "userId", "targetUserId", "role"],
|
|
4781
|
+
// A room's name, topic and picture are its public details, as in any room: Canon stores and serves them. The picture
|
|
4782
|
+
// names no file: Canon takes its type from `mimeType`.
|
|
4783
|
+
update_conversation: ["conversationId", "name", "topic"],
|
|
4784
|
+
rename_conversation: ["conversationId", "name"],
|
|
4785
|
+
update_topic: ["conversationId", "topic"],
|
|
4786
|
+
update_group_avatar: ["conversationId", "data", "mimeType"],
|
|
4517
4787
|
mark_read: ["conversationId"],
|
|
4518
4788
|
cancel_queued_message: ["conversationId", "messageId"],
|
|
4519
|
-
|
|
4789
|
+
// A person's own settings for a room, muting it or hiding it from their list: Canon applies them, as it decides who gets a push.
|
|
4790
|
+
set_conversation_preferences: ["conversationId", "mutedUntil", "deleted"],
|
|
4520
4791
|
set_block: ["targetUserId"],
|
|
4521
4792
|
save_contact: ["contactUserId"],
|
|
4522
4793
|
remove_contact: ["contactId", "contactUserId"],
|
|
@@ -4548,7 +4819,17 @@ var actionRouting = {
|
|
|
4548
4819
|
consume_runtime_primitive: ["conversationId", "requestId", "expectedRevision"],
|
|
4549
4820
|
generate_image: ["conversationId", "requestId"],
|
|
4550
4821
|
update_agent_behavior_policy: ["agentId"],
|
|
4551
|
-
|
|
4822
|
+
// A room's rules are settings Canon stores and an encrypted room's runner reads back, so they are metadata; its free-text
|
|
4823
|
+
// `instructions` stay private, which keeps them out of an encrypted room (plaintext-retirement.md §5.5).
|
|
4824
|
+
update_conversation_agent_behavior_policy: [
|
|
4825
|
+
"conversationId",
|
|
4826
|
+
"agentId",
|
|
4827
|
+
"policy.participationStyle",
|
|
4828
|
+
"policy.allowAgentToAgent",
|
|
4829
|
+
"policy.allowLongRunningCollaboration",
|
|
4830
|
+
"policy.requireMentionForGroupReplies",
|
|
4831
|
+
"policy.maxConsecutiveAgentTurns"
|
|
4832
|
+
]
|
|
4552
4833
|
};
|
|
4553
4834
|
var queryRouting = {
|
|
4554
4835
|
mls_interactions: ["version", "conversationId"],
|
|
@@ -5275,6 +5556,7 @@ export {
|
|
|
5275
5556
|
parseMlsDevGroupHead,
|
|
5276
5557
|
parseMlsDevGroupMarker,
|
|
5277
5558
|
parseMlsDevGroupRoster,
|
|
5559
|
+
mlsAdmissionWait,
|
|
5278
5560
|
parseMlsDevRoomRemoval,
|
|
5279
5561
|
parseMlsDevGroupControl,
|
|
5280
5562
|
parseMlsDevGroupAcceptedControl,
|
|
@@ -5297,8 +5579,10 @@ export {
|
|
|
5297
5579
|
RUNTIME_CARD_LINE_ITEM_COLUMN_TYPES,
|
|
5298
5580
|
readMembershipChangeMetadata,
|
|
5299
5581
|
formatMembershipChangeNotice,
|
|
5582
|
+
PARTICIPATION_HISTORY_FETCH_LIMIT,
|
|
5583
|
+
buildParticipationHistorySnapshot,
|
|
5584
|
+
evaluateParticipationPolicy,
|
|
5300
5585
|
MAX_DISCOVER_AGENTS_QUERY_LENGTH,
|
|
5301
|
-
VERB_LIMITS,
|
|
5302
5586
|
NO_REPLY_ACK_NOTE,
|
|
5303
5587
|
CANON_VERB_WIRE_SCHEMA_VERSION,
|
|
5304
5588
|
projectVerbIntentToWire,
|
|
@@ -5308,6 +5592,7 @@ export {
|
|
|
5308
5592
|
MAX_PUBLIC_RUNTIME_COMMANDS,
|
|
5309
5593
|
normalizePublicRuntimeDescriptor,
|
|
5310
5594
|
normalizePublicRuntimeFacts,
|
|
5595
|
+
resolveConversationPolicyScope,
|
|
5311
5596
|
MLS_DEV_UNSUPPORTED_CONTENT_ACTIONS,
|
|
5312
5597
|
MLS_DEV_DISCLOSED_PROCESSING_FIELDS,
|
|
5313
5598
|
endpointOperationRequestRetention,
|
|
@@ -6,11 +6,11 @@ import {
|
|
|
6
6
|
frame,
|
|
7
7
|
requireValue,
|
|
8
8
|
typedNodeMlsDevCustody
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-YJUPKU47.js";
|
|
10
10
|
import {
|
|
11
11
|
__name,
|
|
12
12
|
parseMlsDevDeviceContext
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-5WPAB5MB.js";
|
|
14
14
|
|
|
15
15
|
// src/runner/home.ts
|
|
16
16
|
import { randomBytes } from "node:crypto";
|