@canonmsg/agent-sdk 12.4.1 → 12.6.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.
Files changed (40) hide show
  1. package/README.md +37 -6
  2. package/dist/chunks/{chunk-6PKXASSK.js → chunk-5J4YPLNW.js} +394 -1687
  3. package/dist/chunks/{chunk-NXL4C2R6.js → chunk-DAJ7L7TN.js} +954 -429
  4. package/dist/chunks/chunk-FVYKLH6I.js +1475 -0
  5. package/dist/chunks/chunk-HH7KR22P.js +406 -0
  6. package/dist/chunks/chunk-QK37HAHC.js +118 -0
  7. package/dist/chunks/{chunk-GWQF4WFT.js → chunk-S7BV74DN.js} +734 -812
  8. package/dist/chunks/{mls-crypto-LGPCXM3K.js → mls-crypto-4DZT2UFH.js} +2 -1
  9. package/dist/cli.js +70 -32
  10. package/dist/drivers/acp.d.ts +56 -0
  11. package/dist/drivers/codex.d.ts +1 -2
  12. package/dist/drivers/connector.d.ts +5 -4
  13. package/dist/drivers/subprocess.d.ts +35 -0
  14. package/dist/index.d.ts +3 -1
  15. package/dist/index.js +12 -3
  16. package/dist/internal/backend-contracts/connectorRelay.d.ts +34 -0
  17. package/dist/internal/backend-contracts/endpoint.d.ts +1 -1
  18. package/dist/internal/backend-contracts/endpointContentPolicy.d.ts +2 -2
  19. package/dist/internal/backend-contracts/index.d.ts +1 -0
  20. package/dist/internal/backend-contracts/loggedOutSessions.d.ts +7 -0
  21. package/dist/internal/backend-contracts/mlsDevCredential.d.ts +3 -0
  22. package/dist/internal/endpoint/engine.d.ts +8 -1
  23. package/dist/internal/endpoint/mls-dev-client.d.ts +11 -0
  24. package/dist/internal/endpoint/mls-dev-text.d.ts +3 -0
  25. package/dist/internal/endpoint/node-mls-custody-protocol.d.ts +5 -0
  26. package/dist/internal/endpoint/node.d.ts +1 -0
  27. package/dist/media.js +4 -2
  28. package/dist/mls/darwin-arm64/canon-mls-custody +0 -0
  29. package/dist/mls/linux-arm64/canon-mls-custody +0 -0
  30. package/dist/mls/linux-x64/canon-mls-custody +0 -0
  31. package/dist/mls/wasm/canon_mls_wasm.js +363 -0
  32. package/dist/mls/wasm/canon_mls_wasm_bg.wasm +0 -0
  33. package/dist/mls-wasm-host.js +258 -0
  34. package/dist/runner/mls-bundled.d.ts +15 -2
  35. package/dist/runner/mls-managed.d.ts +10 -2
  36. package/dist/runner/mls.d.ts +7 -1
  37. package/dist/runner/relay.d.ts +3 -3
  38. package/dist/runner/runner.d.ts +18 -7
  39. package/dist/runner/types.d.ts +12 -4
  40. package/package.json +1 -1
@@ -2,7 +2,8 @@ import {
2
2
  createMlsDevCredentialVerifier,
3
3
  mlsDevDeviceHttpConfiguration,
4
4
  parseMlsDevClientConfiguration
5
- } from "./chunk-GWQF4WFT.js";
5
+ } from "./chunk-QK37HAHC.js";
6
+ import "./chunk-S7BV74DN.js";
6
7
  export {
7
8
  createMlsDevCredentialVerifier,
8
9
  mlsDevDeviceHttpConfiguration,
package/dist/cli.js CHANGED
@@ -1,30 +1,33 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  MLS_SETUP_ACTIONS,
4
- assertPrivateHome,
4
+ acpDriver,
5
5
  awaitErrand,
6
- bundledMlsCustodyHelper,
6
+ checkAcp,
7
7
  checkCodex,
8
8
  claudeDriver,
9
9
  codeDriver,
10
10
  codexDriver,
11
11
  connectorDriver,
12
12
  createRunner,
13
+ defaultMlsDevice,
13
14
  errandJournal,
14
15
  errandsDirectory,
15
16
  loadClaudeQuery,
16
17
  loadInstructions,
17
18
  loadMlsDevConfiguration,
18
19
  loadRelayConfiguration,
19
- managedMlsDevice,
20
20
  queueErrand,
21
21
  readRelayCredential,
22
22
  relayRunnerHome,
23
23
  runMlsDevSetup,
24
- runnerLockName,
25
24
  startRelayChannel,
26
25
  writeRelayCredential
27
- } from "./chunks/chunk-NXL4C2R6.js";
26
+ } from "./chunks/chunk-DAJ7L7TN.js";
27
+ import {
28
+ assertPrivateHome,
29
+ runnerLockName
30
+ } from "./chunks/chunk-HH7KR22P.js";
28
31
  import {
29
32
  CanonApiError,
30
33
  CanonClient,
@@ -35,12 +38,13 @@ import {
35
38
  resolveCanonRuntimeConnection,
36
39
  runCli,
37
40
  verifyCanonRuntimeConnection
38
- } from "./chunks/chunk-6PKXASSK.js";
41
+ } from "./chunks/chunk-5J4YPLNW.js";
42
+ import "./chunks/chunk-FVYKLH6I.js";
43
+ import "./chunks/chunk-QK37HAHC.js";
39
44
  import {
40
45
  __name,
41
- isCanonClientVersion,
42
- isMlsEnvironmentId
43
- } from "./chunks/chunk-GWQF4WFT.js";
46
+ isCanonClientVersion
47
+ } from "./chunks/chunk-S7BV74DN.js";
44
48
 
45
49
  // src/cli.ts
46
50
  import { realpathSync } from "node:fs";
@@ -69,6 +73,8 @@ Usage:
69
73
  canon-agent run --driver codex [--cwd <dir>] [--model <id>] [--sandbox <mode>]
70
74
  [--approval-policy <policy>] [--codex-path <path>] [--profile <name>]
71
75
  [--environment <id>]
76
+ canon-agent run --driver acp [--cwd <dir>] [--guest-mode <id> | --unguarded-guests]
77
+ [--profile <name>] [--environment <id>] -- <agent command> [args]
72
78
  canon-agent run --driver connector --relay-config <path> [--mls-dev-config <path>]
73
79
  [--profile <name>] [--environment <id>]
74
80
  canon-agent mls --action <action> [--mls-dev-config <path>] [--room <id>]
@@ -83,17 +89,18 @@ Usage:
83
89
  canon-agent errands [--follow] [--cancel <errand id>] (--agent <id> | --profile <name>)
84
90
  [--environment <id>]
85
91
 
86
- Encrypted rooms need the agent's MLS device. On macOS (Apple silicon) and Linux
87
- (x64, arm64) the runner keeps it in $CANON_HOME with the custody helper this package
88
- ships, and sets it up on the agent's first start: nothing to configure. Every run
89
- command also takes --mls-dev-config <path>, an operator's own custody instead. With
90
- neither, the agent refuses encrypted rooms, and where Canon encrypts every new
91
- conversation it does not start.
92
+ Encrypted rooms need the agent's MLS device. The runner keeps it in $CANON_HOME and sets
93
+ it up on the agent's first start: nothing to configure. On macOS (Apple silicon) and
94
+ glibc Linux (x64, arm64) it uses the custody helper this package ships; on Windows,
95
+ Intel Macs and musl Linux, this package's WASM core. Every run command also takes
96
+ --mls-dev-config <path>, an operator's own custody instead. With none of these, the
97
+ agent refuses encrypted rooms, and where Canon encrypts every new conversation it does
98
+ not start.
92
99
 
93
- Team rooms (an owner's agents working together): claude and codex also take
100
+ Team rooms (an owner's agents working together): claude, codex and acp also take
94
101
  [--instructions <file>] [--trust-agent <agentId>]...; every run command takes
95
102
  [--agent-turn-cap <n>] [--mention-alias <name>]..., which apply in encrypted rooms.
96
- code, claude and codex take [--allow-session-for-destructive] for encrypted cards.
103
+ code, claude, codex and acp take [--allow-session-for-destructive] for encrypted cards.
97
104
 
98
105
  Errands: tell, post and errand direct a running agent from the machine it runs on:
99
106
  whoever can write $CANON_HOME commands it, and the CLI itself uses no network. tell
@@ -107,10 +114,12 @@ local (canon-agent errands). Canon's policies, blocks and rate limits apply as t
107
114
  message from the agent.
108
115
 
109
116
  Flags:
110
- --driver <name> The runtime driver: "code", "claude", "codex" or "connector".
117
+ --driver <name> The runtime driver: "code", "claude", "codex", "acp" or
118
+ "connector". acp runs any agent that speaks the Agent Client
119
+ Protocol over stdio, named after --: -- goose acp.
111
120
  --module <path> code: an ES module whose default export answers a turn.
112
121
  Its directory is one the agent may send files from.
113
- --cwd <dir> claude, codex: the directory the runtime works in (default:
122
+ --cwd <dir> claude, codex, acp: the directory the runtime works in (default:
114
123
  the current one). The agent may send files from it.
115
124
  --model <id> claude, codex: the model (default: the runtime's).
116
125
  --permission-mode <m> claude: "default" puts each tool call Claude Code would ask
@@ -126,12 +135,20 @@ Flags:
126
135
  Turns holding anyone else's message are always read-only,
127
136
  approve nothing and get only Canon's tools.
128
137
  --codex-path <path> codex: the Codex CLI (default: codex on the PATH).
129
- --instructions <file> claude, codex: standing instructions from the owner (a role,
130
- say), appended to the runtime's system or developer
131
- instructions. A UTF-8 file of at most 32 KiB, owned by you
138
+ --guest-mode <id> acp: the agent's own read-only session mode, for turns holding
139
+ anyone else's message, such as chat on Goose. Without this
140
+ the agent is not started for such a turn, and the person
141
+ is told it takes only its owner's messages.
142
+ --unguarded-guests acp: run such turns under the agent's own policy instead. The
143
+ driver only refuses permission requests and stops a turn
144
+ if the agent reports a machine tool, so an agent that runs
145
+ tools unasked runs them on this machine for anyone.
146
+ --instructions <file> claude, codex, acp: standing instructions from the owner (a role,
147
+ say), added to the runtime's system or developer
148
+ instructions, or to a session's first prompt. A UTF-8 file of at most 32 KiB, owned by you
132
149
  and not writable by others, read once at start; never sent
133
150
  to Canon.
134
- --trust-agent <id> claude, codex: an agent of the same owner whose messages count
151
+ --trust-agent <id> claude, codex, acp: an agent of the same owner whose messages count
135
152
  as the owner's for tool use (repeat for several), only in a
136
153
  room whose other members are all listed agents that Canon
137
154
  confirms at each turn as active agents of this agent's
@@ -194,15 +211,17 @@ Flags:
194
211
  Environment:
195
212
  CANON_API_KEY The agent's key (or use --profile).
196
213
  CANON_ENVIRONMENT_ID Required; a profile from another environment is refused.
197
- CANON_HOME Agent state, default ~/.canon. It must be private (0700).
214
+ CANON_HOME Agent state, default ~/.canon. It must be private (0700; on
215
+ Windows, only you, SYSTEM and Administrators in its access list).
198
216
  ANTHROPIC_API_KEY claude: use this API key instead of a Claude subscription
199
217
  login. The claude driver needs @anthropic-ai/claude-agent-sdk.
200
218
  OPENAI_API_KEY codex: use this API key instead of Codex's own login, unless
201
219
  Codex is configured for another model provider.
202
220
 
203
221
  SIGTERM or Ctrl-C stops taking messages, lets running turns finish for 10 seconds,
204
- then stops them.`;
205
- var AVAILABLE = ["code", "claude", "codex", "connector"];
222
+ then stops them. Windows has no SIGTERM: use Ctrl-C or Ctrl-Break, which is what a
223
+ service wrapper such as NSSM sends. Closing the console gives far less time.`;
224
+ var AVAILABLE = ["code", "claude", "codex", "acp", "connector"];
206
225
  var PERMISSION_MODES = ["default", "acceptEdits", "bypassPermissions"];
207
226
  var SANDBOXES = ["workspace-write", "read-only", "danger-full-access"];
208
227
  var APPROVAL_POLICIES = ["on-request", "untrusted", "never"];
@@ -227,7 +246,7 @@ async function main() {
227
246
  throw new UsageError(error instanceof Error ? error.message : String(error));
228
247
  }
229
248
  const { values, positionals } = parsed;
230
- if (!["run", "mls", "tell", "post", "errand", "errands"].includes(positionals[0] ?? "") || positionals.length > 1) {
249
+ if (!["run", "mls", "tell", "post", "errand", "errands"].includes(positionals[0] ?? "") || positionals.length > 1 && positionals[0] !== "run") {
231
250
  throw new UsageError('Expected the "run", "mls", "tell", "post", "errand" or "errands" command.');
232
251
  }
233
252
  if (positionals[0] === "mls") return mlsSetup(values);
@@ -239,9 +258,13 @@ async function main() {
239
258
  }
240
259
  if (values.driver === "code" && !values.module) throw new UsageError("--driver code needs --module <path>.");
241
260
  if (values.driver === "connector" !== !!values["relay-config"]) throw new UsageError("--relay-config goes with --driver connector, which needs it.");
242
- if (!["claude", "codex"].includes(values.driver) && (values.instructions !== void 0 || values["trust-agent"]?.length)) {
243
- throw new UsageError("--instructions and --trust-agent go with --driver claude or codex.");
261
+ if (!["claude", "codex", "acp"].includes(values.driver) && (values.instructions !== void 0 || values["trust-agent"]?.length)) {
262
+ throw new UsageError("--instructions and --trust-agent go with --driver claude, codex or acp.");
244
263
  }
264
+ const [agentCommand, ...agentArgs] = positionals.slice(1);
265
+ if (values.driver === "acp" !== !!agentCommand) throw new UsageError("--driver acp needs its agent's command after --, and only it takes one: canon-agent run --driver acp -- goose acp.");
266
+ if (values.driver !== "acp" && (values["guest-mode"] !== void 0 || values["unguarded-guests"])) throw new UsageError("--guest-mode and --unguarded-guests go with --driver acp.");
267
+ if (values["guest-mode"] !== void 0 && values["unguarded-guests"]) throw new UsageError("--guest-mode and --unguarded-guests are alternatives.");
245
268
  const team = teamOptions(values);
246
269
  if (values.driver === "connector" && values["allow-session-for-destructive"]) throw new UsageError("--allow-session-for-destructive goes with a driver that asks.");
247
270
  if (values.driver === "connector") return runRelay(values, team);
@@ -284,6 +307,20 @@ async function main() {
284
307
  ...approvalPolicy ? { approvalPolicy } : {},
285
308
  ...instructions ? { instructions } : {}
286
309
  });
310
+ } else if (values.driver === "acp") {
311
+ const cwd = resolve(values.cwd ?? ".");
312
+ const agent = await checkAcp({ command: agentCommand, args: agentArgs, cwd });
313
+ console.error(`canon-agent: ${agent.name}${agent.version ? ` ${agent.version}` : ""} speaks the Agent Client Protocol; ${agent.sessionMemory === "native" ? "it keeps each room's session" : "it keeps no session, so prompts carry recent history"}.`);
314
+ driver = acpDriver({
315
+ command: agentCommand,
316
+ args: agentArgs,
317
+ cwd,
318
+ sessionMemory: agent.sessionMemory,
319
+ ...values["guest-mode"] ? { guestMode: values["guest-mode"] } : {},
320
+ ...values["unguarded-guests"] ? { unguardedGuests: true } : {},
321
+ ...instructions ? { instructions } : {}
322
+ });
323
+ if (!values["guest-mode"] && !values["unguarded-guests"]) console.error("canon-agent: it takes only its owner's messages. To let others reach it, give --guest-mode with a read-only mode of the agent's own.");
287
324
  } else {
288
325
  const modulePath = realpathSync(resolve(values.module));
289
326
  const handler = (await import(pathToFileURL(modulePath).href)).default;
@@ -427,6 +464,8 @@ function parse() {
427
464
  sandbox: { type: "string" },
428
465
  "approval-policy": { type: "string" },
429
466
  "codex-path": { type: "string" },
467
+ "guest-mode": { type: "string" },
468
+ "unguarded-guests": { type: "boolean" },
430
469
  "mls-dev-config": { type: "string" },
431
470
  action: { type: "string" },
432
471
  room: { type: "string" },
@@ -464,9 +503,7 @@ async function settings(values, { key = true } = {}) {
464
503
  __name(settings, "settings");
465
504
  async function mlsDevice(values, environmentId, home) {
466
505
  if (values["mls-dev-config"]) return loadMlsDevConfiguration(values["mls-dev-config"], { environmentId });
467
- if (!isMlsEnvironmentId(environmentId)) return void 0;
468
- const helper = bundledMlsCustodyHelper();
469
- return helper ? managedMlsDevice({ home, environmentId, helper }) : void 0;
506
+ return defaultMlsDevice(home, environmentId);
470
507
  }
471
508
  __name(mlsDevice, "mlsDevice");
472
509
  async function mlsSetup(values) {
@@ -577,6 +614,7 @@ function stopOnSignals(stop) {
577
614
  }, "onSignal");
578
615
  process.on("SIGTERM", onSignal);
579
616
  process.on("SIGINT", onSignal);
617
+ if (process.platform === "win32") for (const signal of ["SIGBREAK", "SIGHUP"]) process.on(signal, onSignal);
580
618
  }
581
619
  __name(stopOnSignals, "stopOnSignals");
582
620
  runCli(import.meta.url, main, (error) => {
@@ -0,0 +1,56 @@
1
+ import { type DriverCapabilities, type RuntimeDriver } from '../runner/types.js';
2
+ export interface AcpDriverOptions {
3
+ /** The agent's executable, e.g. `goose`, found on the PATH. */
4
+ command: string;
5
+ /** Its arguments, e.g. `['acp']`. */
6
+ args?: string[];
7
+ /** The directory the agent works in. The agent may also send files from it. */
8
+ cwd: string;
9
+ /**
10
+ * `native` when the agent keeps a session across its processes; `none` (the default) makes every prompt carry the recent
11
+ * history. `checkAcp` reports which one an agent is.
12
+ */
13
+ sessionMemory?: DriverCapabilities['sessionMemory'];
14
+ /**
15
+ * What a turn holding a message from anyone but the owner or a confirmed trusted agent of the owner does. By default the
16
+ * agent is not started and the person is told it takes only its owner's messages, because nothing here can limit what
17
+ * an arbitrary agent runs. `guestMode` runs it in that session mode instead: a read-only mode of the agent's own, such
18
+ * as `chat` on Goose. Such a turn fails if the agent refuses the mode.
19
+ */
20
+ guestMode?: string;
21
+ /**
22
+ * Run such a turn under the agent's own policy, with no mode. The driver then only refuses permission requests and stops
23
+ * the turn when the agent reports a machine tool running: an agent that does neither runs what the message asks, on
24
+ * this machine. Only for an agent whose developer has limited it some other way.
25
+ */
26
+ unguardedGuests?: boolean;
27
+ /**
28
+ * The owner's standing instructions for this agent (its role, say), added to the first prompt of a session after Canon's
29
+ * own framing, since the protocol has no system prompt. At most 32 KiB; the CLI reads them from `--instructions <file>`.
30
+ */
31
+ instructions?: string;
32
+ /** How long a cancelled agent may take to stop, and an agent with its input closed to exit (default 5 s). */
33
+ interruptGraceMs?: number;
34
+ }
35
+ /** What `initialize` told the driver about an agent. */
36
+ export interface AcpAgent {
37
+ name: string;
38
+ version: string;
39
+ sessionMemory: DriverCapabilities['sessionMemory'];
40
+ }
41
+ /** Checks the agent starts and speaks the protocol; the CLI calls it at startup so a wrong command fails at once. */
42
+ export declare function checkAcp({ command, args, cwd, timeoutMs }: Pick<AcpDriverOptions, 'command' | 'args' | 'cwd'> & {
43
+ timeoutMs?: number;
44
+ }): Promise<AcpAgent>;
45
+ /**
46
+ * Runs an agent that speaks the Agent Client Protocol (v1, JSON-RPC over stdio), which most coding agents do. Each turn is
47
+ * one process that restores the room's session, whose id the runner keeps in the driver state, and exits after the turn:
48
+ * Canon's tool URL and token change per turn, and a session takes its MCP servers only when opened. Stop cancels the
49
+ * native turn and closes a process that does not stop. A room's next turn starts only after the previous process has exited.
50
+ *
51
+ * Unlike the claude and codex drivers, this one cannot configure a sandbox: what runs on the machine is the agent's own
52
+ * policy, which its developer sets. So a turn holding anyone else's message runs only if the owner chose how (`guestMode`
53
+ * or `unguardedGuests`), approves nothing, and stops when the agent reports a machine tool running or leaves that mode.
54
+ * Only the mode is a real limit, since the agent enforces it; the rest depends on what the agent reports.
55
+ */
56
+ export declare function acpDriver(options: AcpDriverOptions): RuntimeDriver;
@@ -1,4 +1,5 @@
1
1
  import { type DriverTurn, type RuntimeDriver } from '../runner/types.js';
2
+ import { type Json } from './subprocess.js';
2
3
  export interface CodexDriverOptions {
3
4
  /** The directory Codex works in. The agent may also send files from it. */
4
5
  cwd: string;
@@ -22,7 +23,6 @@ export interface CodexDriverOptions {
22
23
  /** How long an interrupted Codex may take to stop before its process is closed (default 5 s). */
23
24
  interruptGraceMs?: number;
24
25
  }
25
- type Json = Record<string, unknown>;
26
26
  /** Checks the Codex CLI runs at all; the CLI calls it at startup so a missing install fails at once. */
27
27
  export declare function checkCodex(codexPath?: string): string;
28
28
  /**
@@ -43,4 +43,3 @@ export declare function answerCodexRequest(turn: DriverTurn, method: string, par
43
43
  * does not stop. A room's next turn starts only after the previous process has exited.
44
44
  */
45
45
  export declare function codexDriver(options: CodexDriverOptions): RuntimeDriver;
46
- export {};
@@ -10,7 +10,7 @@ export interface ConnectorInbox {
10
10
  /**
11
11
  * The first claim gets `runAllowed` and the turn's input, once Canon confirms the agent may still act on it (its
12
12
  * membership and the room's audience, read now, never from a hint); a repeat gets its state, never the input again.
13
- * A claim that cannot be confirmed by `deadline` (epoch ms) is refused as busy and the input stays pending.
13
+ * A claim not confirmed by `deadline` (epoch ms) is busy; retry the same request id to finish that confirmation.
14
14
  */
15
15
  claim(inputId: string, requestId: string, options?: {
16
16
  deadline?: number;
@@ -27,8 +27,9 @@ export interface ConnectorDriver extends RuntimeDriver {
27
27
  /**
28
28
  * The `connector` driver: the runtime is an assistant its provider runs (ChatGPT, Claude.ai and the like), reached
29
29
  * through Canon's remote MCP connector, so a turn waits, as one pending input, for the assistant to claim it and answer
30
- * through the connector's pickup tools (spec §11.3). It keeps no timer: an assistant that never answers is the runner's
31
- * no-progress deadline, which a claim restarts. It never calls Canon itself; the relay channel calls its inbox, and the
32
- * runner confirms each claim with Canon.
30
+ * through the connector's pickup tools (spec §11.3). Such an assistant acts only when its user prompts it, so an input
31
+ * waits as long as it takes: the runner submits it, and starts its no-progress deadline, only at the claim. The driver
32
+ * keeps no timer. It never calls Canon itself; the relay channel calls its inbox, and the runner confirms (and submits)
33
+ * each claim with Canon.
33
34
  */
34
35
  export declare function connectorDriver(): ConnectorDriver;
@@ -0,0 +1,35 @@
1
+ import type { spawn } from 'node:child_process';
2
+ import type { AskRequest, DriverTurn } from '../runner/types.js';
3
+ /** What the drivers of runtimes that run as a child process share. */
4
+ export type Json = Record<string, unknown>;
5
+ export declare const isRecord: (value: unknown) => value is Json;
6
+ export declare const text: (value: unknown) => string;
7
+ export declare const oneLine: (value: string, max: number) => string;
8
+ export declare const RISKY: RegExp;
9
+ /**
10
+ * A turn holding a message from anyone but the owner, or an agent the runner confirmed as the owner's trusted teammate,
11
+ * is someone else's: coalesced follow-ups count too.
12
+ */
13
+ export declare const guestTurn: (turn: DriverTurn) => boolean;
14
+ /**
15
+ * RUN-25: one native approval request becomes one `approve` ask. The runner answers a repeated ask id once, so the id also
16
+ * carries what is asked: two requests on one item never share an answer.
17
+ */
18
+ export declare function approve(turn: DriverTurn, request: Omit<Extract<AskRequest, {
19
+ kind: 'approve';
20
+ }>, 'kind'>): Promise<"stopped" | "once" | "session" | "denied">;
21
+ export interface StdioRpcOptions {
22
+ /** The runtime's name, for a refusal that carries no message. */
23
+ name: string;
24
+ /** Sent on every message: `{ jsonrpc: '2.0' }` where the runtime's protocol requires it. */
25
+ envelope?: Json;
26
+ }
27
+ /** JSON-RPC over a child's stdio: one line per message. A refusal rejects with the runtime's numeric `code`. */
28
+ export declare function stdioRpc(child: ReturnType<typeof spawn>, handlers: {
29
+ notify(method: string, params: Json): void;
30
+ request(method: string, params: Json, id: unknown): Promise<unknown>;
31
+ }, { name, envelope }: StdioRpcOptions): {
32
+ call: (method: string, params: Json) => Promise<Json>;
33
+ notify: (method: string, params?: Json) => void;
34
+ fail(error: Error): void;
35
+ };
package/dist/index.d.ts CHANGED
@@ -3,7 +3,7 @@ export type { Runner, RunnerOptions } from './runner/runner.js';
3
3
  export { ConnectorRelayError, DriverSessionError } from './runner/types.js';
4
4
  export { loadMlsDevConfiguration, MLS_SETUP_ACTIONS, mlsDevRoomPackageId, runMlsDevSetup } from './runner/mls.js';
5
5
  export type { MlsSetupAction, RunnerMlsOptions } from './runner/mls.js';
6
- export { bundledMlsCustodyHelper, MLS_ENVIRONMENT_PINS } from './runner/mls-bundled.js';
6
+ export { bundledMlsCustodyHelper, bundledMlsWasmCore, MLS_ENVIRONMENT_PINS, type BundledMlsWasmCore } from './runner/mls-bundled.js';
7
7
  export { managedMlsDevice, type ManagedMlsDevice, type ManagedMlsDeviceOptions } from './runner/mls-managed.js';
8
8
  export type { ActivityEvent, AskAnswer, AskRequest, DriverCapabilities, DriverResult, DriverState, DriverTurn, ErrandFacts, RuntimeDriver, ToolDefinition, ToolResult, TurnInput, TurnInputMessage, TurnTools, } from './runner/types.js';
9
9
  export { errandJournal, errandsDirectory, queueErrand } from './runner/errands.js';
@@ -14,6 +14,8 @@ export { claudeDriver, loadClaudeQuery } from './drivers/claude.js';
14
14
  export type { ClaudeDriverOptions, ClaudeQuery } from './drivers/claude.js';
15
15
  export { checkCodex, codexDriver } from './drivers/codex.js';
16
16
  export type { CodexDriverOptions } from './drivers/codex.js';
17
+ export { acpDriver, checkAcp } from './drivers/acp.js';
18
+ export type { AcpAgent, AcpDriverOptions } from './drivers/acp.js';
17
19
  export { connectorDriver } from './drivers/connector.js';
18
20
  export type { ConnectorDriver, ConnectorInbox } from './drivers/connector.js';
19
21
  export { loadRelayConfiguration, startRelayChannel } from './runner/relay.js';
package/dist/index.js CHANGED
@@ -3,7 +3,10 @@ import {
3
3
  DriverSessionError,
4
4
  MLS_ENVIRONMENT_PINS,
5
5
  MLS_SETUP_ACTIONS,
6
+ acpDriver,
6
7
  bundledMlsCustodyHelper,
8
+ bundledMlsWasmCore,
9
+ checkAcp,
7
10
  checkCodex,
8
11
  claudeDriver,
9
12
  codeDriver,
@@ -21,7 +24,8 @@ import {
21
24
  runMlsDevSetup,
22
25
  runtimeDescriptor,
23
26
  startRelayChannel
24
- } from "./chunks/chunk-NXL4C2R6.js";
27
+ } from "./chunks/chunk-DAJ7L7TN.js";
28
+ import "./chunks/chunk-HH7KR22P.js";
25
29
  import {
26
30
  CanonApiError,
27
31
  DEFAULT_ANTHROPIC_REQUEST_HEADROOM_BYTES,
@@ -46,8 +50,10 @@ import {
46
50
  toAnthropicImageBlock,
47
51
  toAnthropicImageBlocksWithinBudget,
48
52
  uploadMediaFile
49
- } from "./chunks/chunk-6PKXASSK.js";
50
- import "./chunks/chunk-GWQF4WFT.js";
53
+ } from "./chunks/chunk-5J4YPLNW.js";
54
+ import "./chunks/chunk-FVYKLH6I.js";
55
+ import "./chunks/chunk-QK37HAHC.js";
56
+ import "./chunks/chunk-S7BV74DN.js";
51
57
  export {
52
58
  CanonApiError,
53
59
  ConnectorRelayError,
@@ -60,7 +66,10 @@ export {
60
66
  MAX_CANON_MEDIA_BYTES,
61
67
  MLS_ENVIRONMENT_PINS,
62
68
  MLS_SETUP_ACTIONS,
69
+ acpDriver,
63
70
  bundledMlsCustodyHelper,
71
+ bundledMlsWasmCore,
72
+ checkAcp,
64
73
  checkCodex,
65
74
  claudeDriver,
66
75
  clearPendingRegistration,
@@ -1,3 +1,4 @@
1
+ import type { DirectConversationSelection } from './communication.js';
1
2
  /**
2
3
  * The class-C relay (docs/design/agent-runner.md §11): a Canon-operated process that relays a connector agent's
3
4
  * encrypted rooms, holding an MLS device for the agent and decrypting and re-encrypting messages for the assistant (which
@@ -108,6 +109,14 @@ export type ConnectorRelayRequest = {
108
109
  messageId: string;
109
110
  text: string;
110
111
  };
112
+ } | {
113
+ op: 'start';
114
+ args: {
115
+ targetUserId: string;
116
+ messageId: string;
117
+ text: string;
118
+ selection?: DirectConversationSelection;
119
+ };
111
120
  } | {
112
121
  op: 'credential';
113
122
  args: ConnectorRelayCredentialDelivery;
@@ -172,6 +181,31 @@ export interface ConnectorRelaySent {
172
181
  historyMessageId: string;
173
182
  status: 'sent' | 'pending';
174
183
  }
184
+ /**
185
+ * A start (§11.5): the relay's device founds (or finds) the encrypted room and sends the opening line itself, as any
186
+ * device does with a withheld opener. `messageId` is the start's identity, as for a send; `historyMessageId` is the
187
+ * opening line's id in the room. `starting`: the outcome is unresolved, including whether local recording completed;
188
+ * retry with the same `messageId` until a terminal answer. Canon's answers otherwise: `messaged` (the line is going into `conversationId`),
189
+ * `requested` or `pending` (it goes once the request is approved), `blocked` or `unavailable`.
190
+ */
191
+ export type ConnectorRelayStarted = {
192
+ messageId: string;
193
+ historyMessageId: string;
194
+ } & ({
195
+ status: 'messaged';
196
+ conversationId: string;
197
+ created?: boolean;
198
+ reused?: boolean;
199
+ } | {
200
+ status: 'requested' | 'pending';
201
+ requestId: string;
202
+ conversationId?: string;
203
+ } | {
204
+ status: 'starting';
205
+ } | {
206
+ status: 'blocked' | 'unavailable';
207
+ reason: string;
208
+ });
175
209
  export declare class ConnectorRelayRequestError extends Error {
176
210
  name: string;
177
211
  }
@@ -97,7 +97,7 @@ export interface EndpointCapabilities {
97
97
  actions: string[];
98
98
  queries: string[];
99
99
  maintenance: boolean;
100
- /** Only for a principal eligible for MLS here: how new rooms are born (functions `rollout.ts`, `CANON_MLS_DEV_ROOMS`). */
100
+ /** Only while MLS is on here, where every valid principal is eligible: how new rooms are born (functions `devActivation.ts`). */
101
101
  mls?: {
102
102
  eligible: true;
103
103
  roomMode: 'default';
@@ -14,8 +14,8 @@ export declare const MLS_DEV_UNSUPPORTED_CONTENT_ACTIONS: readonly ["forward_mes
14
14
  * them through, and any other field still keeps the operation out. */
15
15
  export declare const MLS_DEV_DISCLOSED_PROCESSING_FIELDS: Readonly<Record<string, readonly string[]>>;
16
16
  export declare const ENDPOINT_CONTENT_ACTIONS: readonly ["mls_application", "mls_group_control", "initialize_endpoint", "append_artifact_command", "retain_artifact_asset", "sync_phone_contacts", "update_agent_behavior_policy", "update_conversation_agent_behavior_policy", "send_message", "delete_message", "forward_message", "react", "verb_execute", "send_contextual_message", "create_interaction", "consume_interaction", "start_conversation", "create_conversation", "add_member", "remove_member", "leave_conversation", "update_member_role", "update_conversation", "rename_conversation", "update_topic", "update_group_avatar", "mark_read", "cancel_queued_message", "set_conversation_preferences", "set_block", "save_contact", "remove_contact", "respond_interaction", "session_control", "approve_contact_request", "reject_contact_request", "cancel_direct_request", "update_message_disposition", "create_upload", "finalize_upload", "delete_upload", "retry_video_processing", "stage_media_message", "cancel_media_message", "renew_media_message_upload", "finalize_memo_stream", "abort_memo_stream", "create_voice_session", "join_voice_session", "decline_voice_session", "end_voice_session", "consume_runtime_signal", "consume_runtime_primitive", "generate_image"];
17
- export declare const ENDPOINT_CONTENT_FUNCTION_QUERIES: readonly ["mls_group_feed", "mls_group_controls", "mls_group_admissions", "mls_interactions", "list_artifacts", "get_artifact_log", "get_artifact_asset", "discover_agents", "get_unread_summary", "peek_runtime_signal", "list_runtime_primitives", "get_agent_behavior_policy", "get_conversation_agent_behavior_policy", "get_ephemeral", "list_voice_sessions", "list_contact_requests", "list_conversations", "list_messages", "get_messages", "admission_fences", "list_contacts", "get_contact", "get_members", "resolve_admission", "resolve_group_admission", "get_runtime_card_state", "conversation", "conversation_changes", "get_upload", "get_media_message", "get_voice_session", "get_image_generation", "get_image_generation_status", "same_owner_agents"];
18
- export declare const ENDPOINT_CONTENT_QUERIES: readonly ["mls_group_feed", "mls_group_controls", "mls_group_admissions", "mls_interactions", "list_artifacts", "get_artifact_log", "get_artifact_asset", "discover_agents", "get_unread_summary", "peek_runtime_signal", "list_runtime_primitives", "get_agent_behavior_policy", "get_conversation_agent_behavior_policy", "get_ephemeral", "list_voice_sessions", "list_contact_requests", "list_conversations", "list_messages", "get_messages", "admission_fences", "list_contacts", "get_contact", "get_members", "resolve_admission", "resolve_group_admission", "get_runtime_card_state", "conversation", "conversation_changes", "get_upload", "get_media_message", "get_voice_session", "get_image_generation", "get_image_generation_status", "same_owner_agents", "admit_messages"];
17
+ export declare const ENDPOINT_CONTENT_FUNCTION_QUERIES: readonly ["mls_group_feed", "mls_group_controls", "mls_group_admissions", "mls_interactions", "mls_devices", "list_artifacts", "get_artifact_log", "get_artifact_asset", "discover_agents", "get_unread_summary", "peek_runtime_signal", "list_runtime_primitives", "get_agent_behavior_policy", "get_conversation_agent_behavior_policy", "get_ephemeral", "list_voice_sessions", "list_contact_requests", "list_conversations", "list_messages", "get_messages", "admission_fences", "list_contacts", "get_contact", "get_members", "resolve_admission", "resolve_group_admission", "get_runtime_card_state", "conversation", "conversation_changes", "get_upload", "get_media_message", "get_voice_session", "get_image_generation", "get_image_generation_status", "same_owner_agents"];
18
+ export declare const ENDPOINT_CONTENT_QUERIES: readonly ["mls_group_feed", "mls_group_controls", "mls_group_admissions", "mls_interactions", "mls_devices", "list_artifacts", "get_artifact_log", "get_artifact_asset", "discover_agents", "get_unread_summary", "peek_runtime_signal", "list_runtime_primitives", "get_agent_behavior_policy", "get_conversation_agent_behavior_policy", "get_ephemeral", "list_voice_sessions", "list_contact_requests", "list_conversations", "list_messages", "get_messages", "admission_fences", "list_contacts", "get_contact", "get_members", "resolve_admission", "resolve_group_admission", "get_runtime_card_state", "conversation", "conversation_changes", "get_upload", "get_media_message", "get_voice_session", "get_image_generation", "get_image_generation_status", "same_owner_agents", "admit_messages"];
19
19
  export declare const ENDPOINT_CONTENT_EPHEMERALS: readonly ["prewarm_agents", "create_memo_stream", "publish_presence", "publish_typing", "publish_streaming", "publish_turn", "publish_runtime_status", "publish_session_state", "publish_runtime_info", "publish_runtime_activity", "append_memo_chunk"];
20
20
  export declare const ENDPOINT_CONTENT_EPHEMERAL_FAMILIES: readonly ["typing", "streaming", "turn-state", "agent-session", "runtime-card-state", "memo-streams", "runtime-info", "runtime-activity", "runtime-suppression", "runtime-silence", "runtime-attention", "call-attention", "agent-runtime", "presence"];
21
21
  export declare const ENDPOINT_CONTENT_EVENTS: readonly ["connected", "heartbeat", "agent.context", "replay.expired", "error", "participation.suppressed", "artifact.changed", "message.created", "message.snapshot", "message.updated", "message.deleted", "message.media_terminal", "conversation.updated", "conversation.changed", "conversation.removed", "conversations.changed", "contacts.changed", "contact.added", "contact.updated", "contact.removed", "presence", "typing", "runtime.updated", "runtime.control", "turn.updated", "voice.session.started", "voice.session.ended", ...string[]];
@@ -41,6 +41,7 @@ export * from './artifacts.js';
41
41
  export * from './artifactActions.js';
42
42
  export * from './mlsEnvironment.js';
43
43
  export * from './mlsDevEnrollment.js';
44
+ export * from './loggedOutSessions.js';
44
45
  export * from './mlsDevCredential.js';
45
46
  export * from './mlsDevCredentialReconciliation.js';
46
47
  export * from './mlsDevDeviceCustody.js';
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Firebase sessions a device log-out ended (web-mls-device.md, decision 10). Firebase can only revoke every session of an
3
+ * account, so Canon names each ended one by its sign-in time, which a token refresh keeps, and refuses it.
4
+ */
5
+ export declare const LOGGED_OUT_SESSIONS_DOCUMENT = "sessions";
6
+ export declare const SESSION_LOGGED_OUT = "SESSION_LOGGED_OUT";
7
+ export declare function isLoggedOutSession(record: unknown, authTime: number): boolean;
@@ -1,6 +1,9 @@
1
1
  import { type MlsDevEnrollmentDevice } from './mlsDevEnrollment.js';
2
2
  import { type MlsEnvironmentId, type MlsIssuerId } from './mlsEnvironment.js';
3
+ /** The lifetime the issuer gives every device. */
3
4
  export declare const MLS_DEV_CREDENTIAL_TTL_MS = 86400000;
5
+ /** The other lifetime verifiers accept, for a person's device only. Nothing issues it yet (mls-production-cutover.md §6). */
6
+ export declare const MLS_DEV_CREDENTIAL_LONG_TTL_MS: number;
4
7
  export declare const MLS_DEV_CREDENTIAL_CHALLENGE_TTL_MS = 300000;
5
8
  export declare const MLS_DEV_CREDENTIAL_MAX_BYTES = 8192;
6
9
  export interface MlsDevCredentialClaims {
@@ -67,6 +67,8 @@ export interface EndpointOperation {
67
67
  preparedContent?: PreparedContent;
68
68
  /** Authored-input collision check, never recorded for memory-only answers. */
69
69
  inputFingerprint?: string;
70
+ /** Local-only binding of a withheld start's text; retained after the opener is sent or its payload is erased. */
71
+ openerFingerprint?: string;
70
72
  receipt?: EndpointOperationReceipt;
71
73
  attempts: number;
72
74
  attempted: boolean;
@@ -130,6 +132,9 @@ export interface EndpointOptions {
130
132
  autoDrain?: boolean;
131
133
  /** Explicit synthetic Dev device identity operations, using this endpoint's store and lifecycle. */
132
134
  mlsDevDevice?: MlsDevDeviceClientOptions;
135
+ /** A person's inbox rows show each encrypted room's last message from this device's copy. Never set for an agent: an
136
+ * inbox read can serve a turn from someone outside that room. */
137
+ localInboxPreviews?: boolean;
133
138
  }
134
139
  export type EndpointContentMode = 'plaintext' | 'mls-dev-text' | 'mls-required';
135
140
  export interface Endpoint {
@@ -196,7 +201,9 @@ export interface Endpoint {
196
201
  text: string;
197
202
  requestId?: string | null;
198
203
  }): Promise<void>;
199
- /** Persist the opening line before starting a room; recover the start through its durable receipt. */
204
+ /**
205
+ * Persist the opening line before starting a room; recover the start through its durable receipt.
206
+ */
200
207
  startWithheldOpener(input: Omit<StartDirectInput, 'opener'> & {
201
208
  messageId: string;
202
209
  text: string;
@@ -17,6 +17,11 @@ export interface MlsDevDeviceClientOptions {
17
17
  /** Explicit synthetic Dev operations. No timers, eager enrollment or room authority. */
18
18
  export interface MlsDevDeviceClient {
19
19
  read(): Promise<MlsDevDeviceView>;
20
+ /** Read only committed local device state, without discovering or waiting for its remote alias.
21
+ * Returns undefined when no local device exists. Used to capture the device before logout erases it. */
22
+ read(options: {
23
+ localOnly: true;
24
+ }): Promise<MlsDevDeviceView | undefined>;
20
25
  /** Ensure local registration; an existing view is not an online status check. */
21
26
  enroll(): Promise<MlsDevDeviceView>;
22
27
  renew(): Promise<MlsDevDeviceView>;
@@ -28,6 +33,12 @@ export interface MlsDevDeviceClient {
28
33
  * current credential is valid, a failed renewal returns it and is retried
29
34
  * after a minute. Never creates an unrequested enrollment. */
30
35
  ensureReady(): Promise<MlsDevDeviceView>;
36
+ /** Retire this registered device at Canon for good (`POST /revoke`): it can no longer authenticate, renew or be offered
37
+ * to a room, and readiness refuses it. A device Canon never registered has
38
+ * nothing to revoke; a revoked one is returned as it is. Rooms keep its leaf until Canon can remove devices (step 5c). */
39
+ revoke(): Promise<MlsDevDeviceView>;
40
+ /** Logs out another of this account's devices (its device list); this one leaves through `revoke`. */
41
+ revokeOther(deviceId: string): Promise<void>;
31
42
  }
32
43
  /** Whether readiness would renew this registered device: it has no credential, or less than half its lifetime remains. */
33
44
  export declare function isMlsDevCredentialRenewalDue(view: MlsDevDeviceView, now: number): boolean;
@@ -40,6 +40,9 @@ export declare function putMlsDevTextMessage(tx: EndpointStoreTransaction, value
40
40
  /** What a v3 reply quotes from this device's copy of the message it answers, trimmed to the payload's bound: a text, or
41
41
  * an item's caption or else what it is. Nothing for a card, a message deleted for everyone, or one not held here. */
42
42
  export declare function mlsDevReplyQuote(value: MlsDevLocalTextMessage | undefined, maximum: number): string | undefined;
43
+ /** An inbox page with each last message this device holds in place of Canon's stub. The page is for this person's own
44
+ * screen: a row then carries a room's plaintext, so it never reaches an HTTP request or an agent's turn. */
45
+ export declare function overlayMlsDevInboxPreviews<T>(tx: EndpointStoreTransaction, page: T): Promise<T>;
43
46
  /** `account` opens the stored sender alias to its account (mls-dev-groups.md, erasure privacy). */
44
47
  export declare function projectMlsDevTextMessage(value: MlsDevLocalTextMessage, account: MlsDevAccountResolver): Record<string, unknown>;
45
48
  /** Opens a stored principal (an alias, with the type its credential gave) to its account. */