@canonmsg/agent-sdk 11.2.1 → 12.1.0

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 (181) hide show
  1. package/README.md +248 -523
  2. package/dist/chunks/chunk-7N6OPPW4.js +5430 -0
  3. package/dist/chunks/chunk-N722GWCP.js +4897 -0
  4. package/dist/chunks/chunk-QZ4PPRQW.js +14247 -0
  5. package/dist/chunks/mls-crypto-LMNQZOS6.js +10 -0
  6. package/dist/cli.js +572 -0
  7. package/dist/drivers/claude.d.ts +42 -0
  8. package/dist/drivers/code.d.ts +18 -0
  9. package/dist/drivers/codex.d.ts +46 -0
  10. package/dist/drivers/connector.d.ts +34 -0
  11. package/dist/index.d.ts +20 -25
  12. package/dist/index.js +91 -13
  13. package/dist/internal/backend-contracts/agentBehaviorPolicy.d.ts +122 -0
  14. package/dist/internal/backend-contracts/agentDirectory.d.ts +32 -0
  15. package/dist/internal/backend-contracts/agentOrigin.d.ts +23 -0
  16. package/dist/internal/backend-contracts/agentSearch.d.ts +17 -0
  17. package/dist/internal/backend-contracts/api-origins.generated.d.ts +4 -0
  18. package/dist/internal/backend-contracts/apiErrors.d.ts +1 -0
  19. package/dist/internal/backend-contracts/artifactActions.d.ts +8 -0
  20. package/dist/internal/backend-contracts/artifacts.d.ts +386 -0
  21. package/dist/internal/backend-contracts/clientFloor.d.ts +41 -0
  22. package/dist/internal/backend-contracts/communication.d.ts +146 -0
  23. package/dist/internal/backend-contracts/connectorRelay.d.ts +179 -0
  24. package/dist/internal/backend-contracts/conversationMembership.d.ts +28 -0
  25. package/dist/internal/backend-contracts/deliveryObservation.d.ts +23 -0
  26. package/dist/internal/backend-contracts/diffRedaction.d.ts +6 -0
  27. package/dist/internal/backend-contracts/endpoint-unread.d.ts +4 -0
  28. package/dist/internal/backend-contracts/endpoint.d.ts +120 -0
  29. package/dist/internal/backend-contracts/endpointContentPolicy.d.ts +48 -0
  30. package/dist/internal/backend-contracts/environment.d.ts +20 -0
  31. package/dist/internal/backend-contracts/externalInbound.d.ts +24 -0
  32. package/dist/internal/backend-contracts/firestoreValues.d.ts +9 -0
  33. package/dist/internal/backend-contracts/index.d.ts +54 -0
  34. package/dist/internal/backend-contracts/media-url.d.ts +20 -0
  35. package/dist/internal/backend-contracts/media.d.ts +51 -0
  36. package/dist/internal/backend-contracts/mediaDelivery.d.ts +46 -0
  37. package/dist/internal/backend-contracts/mediaTransferProtocol.d.ts +17 -0
  38. package/dist/internal/backend-contracts/membershipChange.d.ts +21 -0
  39. package/dist/internal/backend-contracts/message.d.ts +86 -0
  40. package/dist/internal/backend-contracts/messageText.d.ts +6 -0
  41. package/dist/internal/backend-contracts/mlsDevApplication.d.ts +161 -0
  42. package/dist/internal/backend-contracts/mlsDevCredential.d.ts +78 -0
  43. package/dist/internal/backend-contracts/mlsDevCredentialReconciliation.d.ts +45 -0
  44. package/dist/internal/backend-contracts/mlsDevDeviceCustody.d.ts +52 -0
  45. package/dist/internal/backend-contracts/mlsDevEnrollment.d.ts +75 -0
  46. package/dist/internal/backend-contracts/mlsDevFeedReceipt.d.ts +43 -0
  47. package/dist/internal/backend-contracts/mlsDevGroup.d.ts +321 -0
  48. package/dist/internal/backend-contracts/mlsDevGroupFeed.d.ts +51 -0
  49. package/dist/internal/backend-contracts/mlsDevInteraction.d.ts +159 -0
  50. package/dist/internal/backend-contracts/mlsDevKeyPackage.d.ts +42 -0
  51. package/dist/internal/backend-contracts/mlsDevKeyPackages.d.ts +55 -0
  52. package/dist/internal/backend-contracts/mlsEnvironment.d.ts +35 -0
  53. package/dist/internal/backend-contracts/mlsPrincipalAlias.d.ts +23 -0
  54. package/dist/internal/backend-contracts/moderation.d.ts +7 -0
  55. package/dist/internal/backend-contracts/plaintextProcessors.d.ts +63 -0
  56. package/dist/internal/backend-contracts/preparedContent.d.ts +54 -0
  57. package/dist/internal/backend-contracts/replyAuthority.d.ts +14 -0
  58. package/dist/internal/backend-contracts/runtimeCardFields.d.ts +66 -0
  59. package/dist/internal/backend-contracts/runtimeCardStorage.d.ts +3 -0
  60. package/dist/internal/backend-contracts/runtimeDescriptor.d.ts +88 -0
  61. package/dist/internal/backend-contracts/selfContext.d.ts +9 -0
  62. package/dist/internal/backend-contracts/turnProtocol.d.ts +43 -0
  63. package/dist/internal/backend-contracts/upload-mime-types.d.ts +17 -0
  64. package/dist/internal/backend-contracts/verbContract.d.ts +856 -0
  65. package/dist/internal/backend-contracts/verbSchemas.d.ts +1717 -0
  66. package/dist/internal/backend-contracts/verbWire.d.ts +493 -0
  67. package/dist/internal/backend-contracts/webAuth.d.ts +8 -0
  68. package/dist/internal/core/agent-profiles.d.ts +65 -0
  69. package/dist/internal/core/approval-format.d.ts +22 -0
  70. package/dist/internal/core/approval-manager.d.ts +43 -0
  71. package/dist/internal/core/approval-types.d.ts +73 -0
  72. package/dist/internal/core/base-url.d.ts +1 -0
  73. package/dist/internal/core/cli-metadata.d.ts +13 -0
  74. package/dist/internal/core/client.d.ts +432 -0
  75. package/dist/internal/core/completed-output-journal.d.ts +39 -0
  76. package/dist/internal/core/constants.d.ts +1 -0
  77. package/dist/internal/core/endpoint-client.d.ts +56 -0
  78. package/dist/internal/core/execution-environment-mode.d.ts +6 -0
  79. package/dist/internal/core/gifs.d.ts +29 -0
  80. package/dist/internal/core/host-runtime.d.ts +53 -0
  81. package/dist/internal/core/index.d.ts +68 -0
  82. package/dist/internal/core/node-media.d.ts +6 -0
  83. package/dist/internal/core/policy.d.ts +2 -0
  84. package/dist/internal/core/registration-profile.d.ts +10 -0
  85. package/dist/internal/core/registration-session.d.ts +55 -0
  86. package/dist/internal/core/registration.d.ts +5 -0
  87. package/dist/internal/core/retry.d.ts +113 -0
  88. package/dist/internal/core/rtdb-rest.d.ts +141 -0
  89. package/dist/internal/core/runtime-cards.d.ts +286 -0
  90. package/dist/internal/core/runtime-descriptor.d.ts +16 -0
  91. package/dist/internal/core/runtime-endpoints.d.ts +26 -0
  92. package/dist/internal/core/runtime-heartbeat.d.ts +29 -0
  93. package/dist/internal/core/runtime-presentation.d.ts +9 -0
  94. package/dist/internal/core/runtime-request-manager.d.ts +283 -0
  95. package/dist/internal/core/runtime-state-publisher.d.ts +42 -0
  96. package/dist/internal/core/self-context.d.ts +22 -0
  97. package/dist/internal/core/silent-turn.d.ts +49 -0
  98. package/dist/internal/core/stream.d.ts +78 -0
  99. package/dist/internal/core/turn-protocol.d.ts +28 -0
  100. package/dist/internal/core/turn-verbosity.d.ts +85 -0
  101. package/dist/internal/core/types.d.ts +964 -0
  102. package/dist/internal/core/unified-diff.d.ts +35 -0
  103. package/dist/internal/endpoint/artifact-images.d.ts +60 -0
  104. package/dist/internal/endpoint/artifact-reducer.d.ts +35 -0
  105. package/dist/internal/endpoint/artifact-views.d.ts +113 -0
  106. package/dist/internal/endpoint/artifacts.d.ts +75 -0
  107. package/dist/internal/endpoint/delivery.d.ts +47 -0
  108. package/dist/internal/endpoint/engine.d.ts +249 -0
  109. package/dist/internal/endpoint/ephemeral.d.ts +8 -0
  110. package/dist/internal/endpoint/events.d.ts +32 -0
  111. package/dist/internal/endpoint/external-inbound.d.ts +36 -0
  112. package/dist/internal/endpoint/handoff.d.ts +50 -0
  113. package/dist/internal/endpoint/history.d.ts +22 -0
  114. package/dist/internal/endpoint/image-generation-errors.d.ts +2 -0
  115. package/dist/internal/endpoint/inbound.d.ts +29 -0
  116. package/dist/internal/endpoint/index.d.ts +32 -0
  117. package/dist/internal/endpoint/media.d.ts +177 -0
  118. package/dist/internal/endpoint/migration.d.ts +50 -0
  119. package/dist/internal/endpoint/mls-crypto.d.ts +32 -0
  120. package/dist/internal/endpoint/mls-dev-client.d.ts +52 -0
  121. package/dist/internal/endpoint/mls-dev-device.d.ts +69 -0
  122. package/dist/internal/endpoint/mls-dev-groups.d.ts +174 -0
  123. package/dist/internal/endpoint/mls-dev-interactions.d.ts +99 -0
  124. package/dist/internal/endpoint/mls-dev-key-package-service.d.ts +46 -0
  125. package/dist/internal/endpoint/mls-dev-key-packages.d.ts +62 -0
  126. package/dist/internal/endpoint/mls-dev-text.d.ts +67 -0
  127. package/dist/internal/endpoint/mls-dev-transport.d.ts +38 -0
  128. package/dist/internal/endpoint/node-mls-custody-protocol.d.ts +29 -0
  129. package/dist/internal/endpoint/node-mls-custody.d.ts +15 -0
  130. package/dist/internal/endpoint/node.d.ts +7 -0
  131. package/dist/internal/endpoint/observe-query.d.ts +17 -0
  132. package/dist/internal/endpoint/portable-node-mls-custody.d.ts +23 -0
  133. package/dist/internal/endpoint/queue.d.ts +22 -0
  134. package/dist/internal/endpoint/recovery.d.ts +27 -0
  135. package/dist/internal/endpoint/retention.d.ts +7 -0
  136. package/dist/internal/endpoint/store.d.ts +22 -0
  137. package/dist/internal/endpoint/sync.d.ts +33 -0
  138. package/dist/internal/endpoint/transient-publisher.d.ts +8 -0
  139. package/dist/internal/endpoint/transport.d.ts +63 -0
  140. package/dist/media.d.ts +1 -1
  141. package/dist/media.js +42 -541
  142. package/dist/runner/errands.d.ts +118 -0
  143. package/dist/runner/mls.d.ts +97 -0
  144. package/dist/runner/relay.d.ts +87 -0
  145. package/dist/runner/runner.d.ts +98 -0
  146. package/dist/runner/types.d.ts +215 -0
  147. package/package.json +18 -5
  148. package/dist/attached-session.d.ts +0 -58
  149. package/dist/attached-session.js +0 -638
  150. package/dist/bounded-file-read.d.ts +0 -14
  151. package/dist/bounded-file-read.js +0 -34
  152. package/dist/canon-agent.d.ts +0 -236
  153. package/dist/canon-agent.js +0 -2531
  154. package/dist/debouncer.d.ts +0 -18
  155. package/dist/debouncer.js +0 -133
  156. package/dist/inbound-handoff.d.ts +0 -6
  157. package/dist/inbound-handoff.js +0 -17
  158. package/dist/managed-session-host.d.ts +0 -27
  159. package/dist/managed-session-host.js +0 -195
  160. package/dist/managed-session-setup.d.ts +0 -99
  161. package/dist/managed-session-setup.js +0 -234
  162. package/dist/realtime.d.ts +0 -62
  163. package/dist/realtime.js +0 -193
  164. package/dist/runtime-card.d.ts +0 -48
  165. package/dist/runtime-card.js +0 -64
  166. package/dist/session-manager.d.ts +0 -77
  167. package/dist/session-manager.js +0 -285
  168. package/dist/turn-streaming-request.d.ts +0 -17
  169. package/dist/turn-streaming-request.js +0 -22
  170. package/dist/turn-verbosity-option.d.ts +0 -33
  171. package/dist/turn-verbosity-option.js +0 -23
  172. package/dist/types.d.ts +0 -387
  173. package/dist/types.js +0 -1
  174. package/dist/work-session-coordination.d.ts +0 -21
  175. package/dist/work-session-coordination.js +0 -53
  176. package/dist/work-session-host.d.ts +0 -31
  177. package/dist/work-session-host.js +0 -617
  178. package/dist/work-session-interactions.d.ts +0 -12
  179. package/dist/work-session-interactions.js +0 -173
  180. package/dist/work-session-state.d.ts +0 -36
  181. package/dist/work-session-state.js +0 -62
@@ -0,0 +1,118 @@
1
+ /** `tell` and `post` (§12.3, §12.4), `errand` (§12.6) and `cancel`, which ends an errand. */
2
+ export declare const ERRAND_KINDS: readonly ["tell", "post", "errand", "cancel"];
3
+ export type ErrandKind = (typeof ERRAND_KINDS)[number];
4
+ /** At most what one encrypted message holds (§6), so a post is never cut. */
5
+ export declare const ERRAND_TEXT_MAX_BYTES: number;
6
+ /** An errand's why, at most. */
7
+ export declare const ERRAND_WHY_MAX_BYTES: number;
8
+ /** A command not taken within a day is refused, and a note no turn used within a day expires. */
9
+ export declare const ERRAND_TTL_MS: number;
10
+ /** An errand stays open for replies this long after the agent reached its recipient, or after the last reply. */
11
+ export declare const ERRAND_OPEN_MS: number;
12
+ /** What the operator asks for: a note or a post for a room, an errand, or the end of an errand. */
13
+ export type ErrandInput = {
14
+ kind: 'tell' | 'post';
15
+ conversationId: string;
16
+ text: string;
17
+ }
18
+ /** `to`: a room the agent is in, or a person or agent to start a new conversation with; `text`: the instruction. */
19
+ | {
20
+ kind: 'errand';
21
+ to: string;
22
+ why: string;
23
+ text: string;
24
+ origin?: string;
25
+ } | {
26
+ kind: 'cancel';
27
+ errand: string;
28
+ };
29
+ /** One command, as the CLI writes it into the inbox. */
30
+ export type ErrandCommand = ErrandInput & {
31
+ version: 1;
32
+ id: string;
33
+ createdAt: string;
34
+ };
35
+ /**
36
+ * What the runner did with a command. A note: `pending` until its room's next turn marks it `used`. A post: `sending`,
37
+ * then `sent`. An errand: `pending`, `running` (its turn), then `waiting` once the agent reached the recipient, `replied`
38
+ * when a turn ran there on a reply, `done` when its turn ended without reaching them, `failed`, `cancelled` (Stop in the
39
+ * origin, or `cancel`), `interrupted` (the runner stopped during its turn, which never runs again) or `expired`. A cancel
40
+ * is `applied`. Any command: `refused` (valid, but the runner could not apply it), `rejected` (not a valid command file),
41
+ * `expired` (too old).
42
+ */
43
+ export type ErrandStatus = 'pending' | 'used' | 'sending' | 'sent' | 'running' | 'waiting' | 'replied' | 'done' | 'failed' | 'cancelled' | 'interrupted' | 'applied' | 'refused' | 'rejected' | 'expired';
44
+ export interface ErrandRecord {
45
+ version: 1;
46
+ id: string;
47
+ status: ErrandStatus;
48
+ updatedAt: string;
49
+ kind?: ErrandKind;
50
+ createdAt?: string;
51
+ reason?: string;
52
+ /** A note's or a post's room; an errand's once the agent reached its recipient there. */
53
+ conversationId?: string;
54
+ /** The installation scope it was taken under (§5): a note or an errand is acted on only under the same one. */
55
+ scope?: string;
56
+ /** A pending note's text, dropped once the note leaves `pending`; an errand's instruction. */
57
+ text?: string;
58
+ /** The turn that used a note; an errand's turn. */
59
+ turnId?: string;
60
+ /** An errand: its recipient (a room, or a person or agent to start a conversation with), why, and origin room. */
61
+ to?: string;
62
+ target?: 'room' | 'contact';
63
+ why?: string;
64
+ origin?: string;
65
+ /** An errand: when its turn reached the recipient, and the last reply there. */
66
+ reachedAt?: string;
67
+ repliedAt?: string;
68
+ /** An errand: its report kept here, when it may not go to the origin (§12.7), and how the one sent there went. */
69
+ report?: string;
70
+ reported?: 'sending' | 'sent' | 'refused';
71
+ /** A cancel: the errand it ends. */
72
+ errand?: string;
73
+ }
74
+ /** An agent's errands in the home: `runner/<env>/<agentId>/errands/` (spec §5). */
75
+ export declare function errandsDirectory(home: string, environmentId: string, agentId: string): string;
76
+ /** The inbox directory, created private if missing; one that is not this uid's private directory is refused. */
77
+ export declare function errandInbox(directory: string): string;
78
+ /** The operator's side (the CLI, or a library caller): one durable command file in the inbox. Throws on an invalid command. */
79
+ export declare function queueErrand(directory: string, input: ErrandInput): ErrandCommand;
80
+ /** The runner's records, one file per command taken (§12.2). */
81
+ export declare function errandJournal(directory: string): {
82
+ has: (id: string) => boolean;
83
+ get: (id: string) => ErrandRecord | undefined;
84
+ put: (record: ErrandRecord) => void;
85
+ list(): ErrandRecord[];
86
+ };
87
+ export type ErrandJournal = ReturnType<typeof errandJournal>;
88
+ /**
89
+ * `--wait` (§12.1): the command's record once the runner has answered it, or as it stands after `ms`, and whether it was
90
+ * accepted. A post (or an errand's report) Canon has not answered stays `sending` and is not accepted, though the runner
91
+ * keeps it and goes on delivering it: it may yet be refused.
92
+ */
93
+ export declare function awaitErrand(directory: string, command: Pick<ErrandCommand, 'id' | 'kind'>, ms: number): Promise<{
94
+ record: ErrandRecord | undefined;
95
+ accepted: boolean;
96
+ }>;
97
+ export interface ErrandHandler {
98
+ /** The command was taken already (its record or its journaled output exists): its file is only removed. */
99
+ taken(id: string): boolean;
100
+ /**
101
+ * The command, or why its file is refused. True once its effect (or refusal) is journaled, so the file may go; false
102
+ * leaves it for the next scan. A throw is logged once per command and reason, and also leaves it.
103
+ */
104
+ take(id: string, command: ErrandCommand | {
105
+ problem: string;
106
+ }): Promise<boolean>;
107
+ }
108
+ /**
109
+ * §12.1: watches the inbox and scans it every `intervalMs` as a backstop (and at once), one scan at a time, files in
110
+ * name order. A file is removed only after the handler has journaled what it did with it.
111
+ */
112
+ export declare function watchErrandInbox(directory: string, handler: ErrandHandler, options: {
113
+ log: (line: string) => void;
114
+ intervalMs?: number;
115
+ }): {
116
+ close(): Promise<void>;
117
+ };
118
+ export type ErrandInbox = ReturnType<typeof watchErrandInbox>;
@@ -0,0 +1,97 @@
1
+ import { type CanonEndpoint, type CanonMlsDevOptions, type MlsDevCurrentAudience } from '../internal/core/index.js';
2
+ import { type MlsDevDeviceContext } from '../internal/backend-contracts/index.js';
3
+ import type { ToolDefinition } from './types.js';
4
+ /** What `createRunner({ mls })` takes: issuer pins and custody, plus the custody's own shutdown. */
5
+ export type RunnerMlsOptions = CanonMlsDevOptions & {
6
+ close?(): Promise<void>;
7
+ };
8
+ /** A closed copy of the public audience (no provider input or key material), in a fixed field order. */
9
+ export declare function snapshotMlsAudience(value: unknown): MlsDevCurrentAudience;
10
+ /**
11
+ * The principals who can read the room, each with its admission (an agent also with its ownership version), sorted: the
12
+ * set the server's device-only re-ask rule compares (encrypted-interactions.md §4), never the devices themselves. A
13
+ * principal reading through a device Canon operates (a class-C relay, §11) counts apart, so a relay device joining or
14
+ * leaving is never a device-only change.
15
+ */
16
+ export declare function mlsAudiencePrincipals(value: MlsDevCurrentAudience): string[];
17
+ /**
18
+ * The audience's epoch: who can read the room, which is its principals with their admissions, the membership revision
19
+ * and this device's own place in the group. A device-only change (a member adds or removes a device and keeps one) is a
20
+ * re-key, not an audience change (§4), so the group head and the device set are left out, as is the feed cursor, which
21
+ * ordinary text moves.
22
+ */
23
+ export declare function mlsAudienceEpoch(value: MlsDevCurrentAudience): string;
24
+ /** What an encrypted room's text message is to the runner; Canon never learns it (`MlsIds.text`). */
25
+ export type MlsTextKind = 'final' | 'failed' | 'restart' | 'said' | 'item';
26
+ /**
27
+ * §6 ids. Canon sees the message id of everything the runner sends to an encrypted room, and a card's request id (its
28
+ * public binding), so these are keyed with a secret only this installation holds (`privateKey`): HMAC-SHA256 over
29
+ * domain-separated inputs, cut to 128 bits. Canon can test no guess of the text, question or turn an id names, and every
30
+ * text message (a final, a failure or restart notice, a `communicate` send, an item) has the same shape, `msg-<32 hex>`, so none
31
+ * says what kind it is. The same inputs give the same id after a restart, so journal replay, a repeated `communicate`,
32
+ * a re-asked card and a restart notice keep theirs.
33
+ */
34
+ export interface MlsIds {
35
+ /** A text message's id: `parts` name it (the turn, or the turn and a `communicate` send's text). */
36
+ text(kind: MlsTextKind, conversationId: string, ...parts: string[]): string;
37
+ /** A card's request id, `ask-<32 hex>` (`MLS_DEV_INTERACTION_ID_PATTERN`), for the runtime's ask `askId` in turn `turnId`. */
38
+ request(conversationId: string, turnId: string, askId: string): string;
39
+ }
40
+ /** The installation's id key: random bytes, created once under CANON_HOME. */
41
+ export declare const MLS_ID_KEY_BYTES = 32;
42
+ export declare function createMlsIds(key: Uint8Array): MlsIds;
43
+ /**
44
+ * Encrypted rooms are for agents whose outbound communication is not closed, in an environment with an MLS profile. Canon
45
+ * decides who joins a room under the invitation policies, as for plaintext rooms, and whether that environment runs MLS.
46
+ */
47
+ export declare function requireMlsSupport(input: {
48
+ environmentId: string;
49
+ agent: {
50
+ outboundPolicy: string;
51
+ };
52
+ }): void;
53
+ /**
54
+ * Whether `text` @mentions any of `names` (an agent's display name, id or aliases). Encrypted text carries no `mentions[]`,
55
+ * so this is how one agent addresses another there: `@`, not preceded by a letter, digit, `_` or `@` (so no address
56
+ * like `ops@example.com`), then the name in any case, with any run of whitespace for a space, not followed by a letter,
57
+ * digit, mark or `_` (`@Cipher A` does not mention `Cipher AB`).
58
+ */
59
+ export declare function mentionsAgent(text: string, names: ReadonlyArray<string | undefined>): boolean;
60
+ /** An encrypted turn's `communicate`: listing, and text to its own room, which the runner encrypts. */
61
+ export declare const ENCRYPTED_COMMUNICATE: ToolDefinition;
62
+ /**
63
+ * The operator's MLS configuration file: public issuer pins and the custody helper's identity, never credentials or
64
+ * private keys. A canonical, current-user-owned regular file, not group- or other-writable, at most 64 KiB, read once.
65
+ * Its configuration must be for the environment the runner runs in.
66
+ */
67
+ export declare function loadMlsDevConfiguration(filePath: string, input: {
68
+ environmentId: string;
69
+ }): Promise<RunnerMlsOptions & {
70
+ provision?: (context: MlsDevDeviceContext) => Promise<void>;
71
+ close(): Promise<void>;
72
+ }>;
73
+ export declare const MLS_SETUP_ACTIONS: readonly ["status", "provision", "enroll", "renew", "publish", "refresh"];
74
+ export type MlsSetupAction = typeof MLS_SETUP_ACTIONS[number];
75
+ /** The KeyPackage an agent publishes for one room: stable for its installation, so a repeated publish is the same intent. */
76
+ export declare function mlsDevRoomPackageId(identity: {
77
+ environmentId: string;
78
+ principalId: string;
79
+ installationId: string;
80
+ ownershipVersion?: number;
81
+ }, room: string): string;
82
+ /**
83
+ * One operator step for the agent's device (`canon-agent mls`), on the same engine store the runner uses. It never
84
+ * starts a model, and prints only public device state: no credentials, proofs, envelopes or history.
85
+ */
86
+ export declare function runMlsDevSetup(endpoint: CanonEndpoint, action: MlsSetupAction, room?: string, provision?: (context: MlsDevDeviceContext) => Promise<void>): Promise<{
87
+ conversationId?: string | undefined;
88
+ version: number;
89
+ action: "status" | "publish" | "enroll" | "renew" | "provision" | "refresh";
90
+ environmentId: "canon-dev-v1" | "canon-prod-v1";
91
+ principalId: string;
92
+ installationId: string;
93
+ ownershipVersion: number | undefined;
94
+ status: "absent" | "identity_committed" | "registered" | "credential_committed" | "revoked";
95
+ deviceId: string | null;
96
+ credentialGeneration: number;
97
+ }>;
@@ -0,0 +1,87 @@
1
+ import { type ConnectorRelayCredentialDelivery } from '../internal/backend-contracts/index.js';
2
+ import type { ConnectorInbox } from '../drivers/connector.js';
3
+ import type { Runner } from './runner.js';
4
+ /** What the channel serves: the relay's runner (history and sends) and its connector driver's inbox (pickup). */
5
+ export interface RelayTarget {
6
+ runner: Pick<Runner, 'relayRead' | 'relaySend'>;
7
+ inbox: ConnectorInbox;
8
+ }
9
+ /** Canon's credential for this relay, as delivered: the connection it serves is the delivering request's. */
10
+ export type RelayCredentialDelivery = ConnectorRelayCredentialDelivery & {
11
+ grantId: string;
12
+ };
13
+ export interface RelayChannelOptions {
14
+ /** The runner and inbox to serve from the start; `serve` sets or replaces them. Until then only `credential` is answered. */
15
+ runner?: Pick<Runner, 'relayRead' | 'relaySend'>;
16
+ inbox?: ConnectorInbox;
17
+ /** The relayed agent: requests naming another agent are refused. */
18
+ agentId: string;
19
+ /**
20
+ * The connector connection served: requests naming another are refused. A credential delivery (the owner's approval)
21
+ * names its own connection and moves the relay to it.
22
+ */
23
+ grantId?: string;
24
+ /** The Standard Webhooks secret (`whsec_…`) Functions signs with: this agent's alone. */
25
+ secret: string;
26
+ /**
27
+ * Canon delivers the relay's credential here when the owner approves, or re-approves, the relay; the channel confirms
28
+ * the delivery once this resolves, so it should keep the credential durably first. Without it, deliveries are refused.
29
+ */
30
+ onCredential?: (delivery: RelayCredentialDelivery) => void | Promise<void>;
31
+ /** Default 127.0.0.1 and a random port. */
32
+ host?: string;
33
+ port?: number;
34
+ /** Never receives message text. */
35
+ log?: (line: string) => void;
36
+ /** Test seam: the clock signatures are checked against. */
37
+ now?: () => number;
38
+ /** Test seam: how long an exchange may take before it is answered (default `CONNECTOR_RELAY_ANSWER_WITHIN_MS`). */
39
+ answerWithinMs?: number;
40
+ }
41
+ export interface RelayChannel {
42
+ readonly url: string;
43
+ /** Serves this runner and inbox for `grantId` (default: the connection already served), or nothing (`undefined`). */
44
+ serve(target: RelayTarget | undefined, grantId?: string): void;
45
+ close(): Promise<void>;
46
+ }
47
+ export declare function relaySecretKey(secret: string): Buffer;
48
+ /** The headers Functions sends (as the `standardwebhooks` library signs them); the tests' stand-in for Functions uses it. */
49
+ export declare function signRelayRequest(secret: string, id: string, body: string, nowMs?: number): Record<string, string>;
50
+ /** Starts the channel. Every request is authenticated before its body is parsed; refusals carry no detail. */
51
+ export declare function startRelayChannel(options: RelayChannelOptions): Promise<RelayChannel>;
52
+ export interface RelayConfiguration {
53
+ environmentId: string;
54
+ agentId: string;
55
+ listen: {
56
+ host: string;
57
+ port: number;
58
+ };
59
+ secret: string;
60
+ }
61
+ /**
62
+ * The operator's relay configuration (`--relay-config`): which agent this process relays, where the channel listens, and
63
+ * the path of the channel secret, itself a private file. The connection, installation and credential come from Canon,
64
+ * with the credential, when the owner approves the relay.
65
+ */
66
+ export declare function loadRelayConfiguration(path: string, input: {
67
+ environmentId: string;
68
+ }): Promise<RelayConfiguration>;
69
+ /** The relay's credential as it keeps it: `<CANON_HOME>/relay/credential.json`, 0600, written whole or not at all. */
70
+ export interface StoredRelayCredential {
71
+ version: 1;
72
+ environmentId: string;
73
+ agentId: string;
74
+ grantId: string;
75
+ installationId: string;
76
+ generation: number;
77
+ credential: string;
78
+ }
79
+ /** The credential Canon delivered to this relay, if any; one for another agent or environment is refused. */
80
+ export declare function readRelayCredential(home: string, relay: Pick<RelayConfiguration, 'environmentId' | 'agentId'>): Promise<StoredRelayCredential | undefined>;
81
+ /** Keeps a delivered credential durably (fsync, then rename), private to this user, before the delivery is confirmed. */
82
+ export declare function writeRelayCredential(home: string, relay: Pick<RelayConfiguration, 'environmentId' | 'agentId'>, delivery: RelayCredentialDelivery): StoredRelayCredential;
83
+ /**
84
+ * Where the runner for one relay installation keeps its state (engine store, journal, lock): a private directory per
85
+ * installation, so a new approval (a new installation and device) starts clean beside the old one.
86
+ */
87
+ export declare function relayRunnerHome(home: string, installationId: string): string;
@@ -0,0 +1,98 @@
1
+ import { type ConnectorRelayPage, type ConnectorRelaySent } from '../internal/backend-contracts/index.js';
2
+ import { type CanonEndpoint, type CanonRuntimeDescriptor, type CommunicateInput, type CommunicateResult } from '../internal/core/index.js';
3
+ import { type RunnerMlsOptions } from './mls.js';
4
+ import { type RuntimeDriver } from './types.js';
5
+ export interface RunnerOptions {
6
+ /** Canon refused this runner for good (its client floor, mls-cutover-operator.md §2 15b); the CLI stops and exits. */
7
+ onFatal?(error: Error): void;
8
+ /** How the runner names itself to Canon's client floor (`X-Canon-Client`); the CLI passes agent-sdk's version. */
9
+ client?: {
10
+ kind: 'runner';
11
+ version: string;
12
+ };
13
+ apiKey: string;
14
+ environmentId: string;
15
+ /** CANON_HOME: a private 0700 directory, checked before anything is opened. */
16
+ home: string;
17
+ driver: RuntimeDriver;
18
+ /** Test seam: an injected endpoint engine. Production never sets it. */
19
+ endpoint?: CanonEndpoint;
20
+ /** Test seam: HTTP outside the endpoint protocol (health, identity). Production never sets it. */
21
+ fetch?: typeof fetch;
22
+ /** Test-only origin overrides (the CLI's CANON_BASE_URL and CANON_STREAM_URL). */
23
+ baseUrl?: string;
24
+ streamUrl?: string;
25
+ /**
26
+ * The agent's MLS device (Dev only, spec §6): issuer pins and custody, from `loadMlsDevConfiguration`. With it the
27
+ * runner takes part in encrypted rooms; without it each encrypted room is refused, once, in the log. An injected
28
+ * `endpoint` that already has an MLS device counts as configured. `close` runs last on shutdown.
29
+ */
30
+ mls?: RunnerMlsOptions;
31
+ /**
32
+ * Relay mode, the class-C relay (spec §11.4): only encrypted rooms are served, which is what lets a `remote` driver
33
+ * (the connector) into them. Plaintext input is cancelled with no turn (the connector's own installation serves it),
34
+ * control hints are read only for encrypted rooms, no runtime descriptor is published, and `relayRead` and `relaySend`
35
+ * serve the relay channel. It needs the agent's MLS device.
36
+ */
37
+ relay?: boolean;
38
+ /** The engine's installation: the relay's reserved one, from its configuration. A store opened under another refuses to start. */
39
+ installationId?: string;
40
+ /**
41
+ * Team rooms: agents the owner trusts. A message from one of them counts as the owner's for tool permissions (the
42
+ * drivers' guards read `TurnInputMessage.trustedAgent`) only in a room that holds no one but this agent, its owner and
43
+ * listed agents, all of which Canon confirms at the start of the turn are active agents of this agent's own owner; in
44
+ * a plaintext room only for a final its runner marked as answering the owner or confirmed teammates alone, and in an
45
+ * encrypted room never with a relay device in the group. A `remote` runtime has no tools on this host: a relay ignores it.
46
+ */
47
+ trustedAgents?: string[];
48
+ /**
49
+ * Encrypted rooms (§6): how many messages from other agents that @mention this agent a room may wake it with before a
50
+ * person's message is admitted there again (default 6; 0 never wakes on an agent). Past the cap they are settled with
51
+ * no turn, logged once. Plaintext rooms are Canon's to dispatch (RUN-29).
52
+ */
53
+ agentTurnCap?: number;
54
+ /** Encrypted rooms: names besides the agent's display name and id that @mention it, such as a role (`@engineer`). */
55
+ mentionAliases?: string[];
56
+ /** How long an aborted turn may take to settle before its session is reset (default 10 s). */
57
+ abortGraceMs?: number;
58
+ /** A turn with no text or activity for this long fails (default 30 min); the clock waits while an `ask` is pending. */
59
+ noProgressMs?: number;
60
+ /** How long an `ask` waits for its answer before it expires (default 1 h; an expired approval is a denial). */
61
+ askExpiryMs?: number;
62
+ /**
63
+ * Encrypted rooms (encrypted-interactions.md §3): whether an approval the runtime's harness marked `risk: 'destructive'`
64
+ * may offer the owner a session rule. Off by default, so each such action asks; other approvals keep session rules.
65
+ */
66
+ sessionApprovalForDestructive?: boolean;
67
+ /** How long running turns may finish on shutdown before they are aborted (default 10 s). */
68
+ shutdownGraceMs?: number;
69
+ /** Never receives prompt or reply text. */
70
+ log?: (line: string) => void;
71
+ }
72
+ export interface Runner {
73
+ /** Connects and starts taking input; resolves once listening is set up. */
74
+ start(): Promise<void>;
75
+ /** Stops intake, releases unsubmitted input, lets running turns finish within the shutdown grace. */
76
+ stop(): Promise<void>;
77
+ /** A proactive send outside any turn (cron): the `communicate` tool's handler, under the runner's identity. */
78
+ communicate(input: CommunicateInput): Promise<CommunicateResult>;
79
+ /**
80
+ * Relay mode (§11.6): an encrypted room's history as this device verified it and Canon accepted it, newest first, cut
81
+ * to the channel's response budget. `deadline` (epoch ms, default the channel's answer budget) bounds the read.
82
+ */
83
+ relayRead(conversationId: string, page?: {
84
+ limit?: number;
85
+ before?: string;
86
+ deadline?: number;
87
+ }): Promise<ConnectorRelayPage>;
88
+ /**
89
+ * Relay mode (§11.6): encrypted text to a room outside any turn, journaled first; the same `messageId` is the same
90
+ * message. It answers by `deadline`: `pending` only once journaled, a busy refusal (nothing journaled) before that.
91
+ */
92
+ relaySend(conversationId: string, messageId: string, text: string, options?: {
93
+ deadline?: number;
94
+ }): Promise<ConnectorRelaySent>;
95
+ }
96
+ /** RUN-27: generated once from the driver's declarations; no static capability table. */
97
+ export declare function runtimeDescriptor(driver: RuntimeDriver): CanonRuntimeDescriptor;
98
+ export declare function createRunner(options: RunnerOptions): Promise<Runner>;
@@ -0,0 +1,215 @@
1
+ import type { MediaAttachment } from '../internal/core/index.js';
2
+ /**
3
+ * A runtime adapter: it only runs turns. Canon semantics (inbox, queue, Stop,
4
+ * turn identity, delivery, descriptor) belong to the runner. Experimental until
5
+ * the claude and codex drivers pass the conformance suite.
6
+ */
7
+ export interface RuntimeDriver {
8
+ readonly name: 'code' | 'claude' | 'codex' | 'hermes' | 'connector';
9
+ /** Input to the generated runtime descriptor; never a static table. */
10
+ readonly capabilities: DriverCapabilities;
11
+ runTurn(turn: DriverTurn): Promise<DriverResult>;
12
+ /** Discard the native session, even mid-turn. */
13
+ reset?(session: string): Promise<void>;
14
+ /** Process shutdown only. */
15
+ close?(): Promise<void>;
16
+ }
17
+ export interface DriverTurn {
18
+ /** Native-session key: the conversation id. */
19
+ session: string;
20
+ /** Canon-rendered input for the model. */
21
+ prompt: string;
22
+ /** The same facts, structured and read-only; no Canon handle. */
23
+ input: TurnInput;
24
+ /** Absolute paths of this turn's materialized inbound attachments. */
25
+ files: string[];
26
+ /** The only control: Stop, no_reply, deadline, membership loss, shutdown. */
27
+ signal: AbortSignal;
28
+ /** The full visible answer so far (never a delta); preview only, never durable. */
29
+ onText(snapshot: string): void;
30
+ /** Minimal "working on…" status; replaces the row with the same id. */
31
+ onActivity(event: ActivityEvent): void;
32
+ ask(request: AskRequest): Promise<AskAnswer>;
33
+ /** Persisted by the runner, scoped to (installation, driver, session). */
34
+ state: DriverState;
35
+ /** Runner tools bound to this turn by a per-turn token. */
36
+ tools: TurnTools;
37
+ /**
38
+ * For a `remote` runtime, before its input first leaves this host (the connector's claim): the runner reads the
39
+ * agent's membership, and an encrypted room's audience, from Canon now and counts the pickup as progress (RUN-12).
40
+ * It rejects, and ends the turn, when the agent may no longer act on this input; a transient failure only rejects.
41
+ */
42
+ confirmPickup(): Promise<void>;
43
+ }
44
+ /** `reply: null` is deliberate silence. */
45
+ export interface DriverResult {
46
+ reply: string | null;
47
+ files?: string[];
48
+ }
49
+ export interface DriverCapabilities {
50
+ /** 'none': every prompt carries the recent history window. */
51
+ sessionMemory: 'native' | 'none';
52
+ ask: ReadonlyArray<'approve' | 'choose' | 'text'>;
53
+ streaming: boolean;
54
+ activity: boolean;
55
+ /** The runtime can call TurnTools; required for model-driven no_reply. */
56
+ tools: boolean;
57
+ /**
58
+ * The runtime has a question tool of its own that the driver maps to `ask` (Claude Code's AskUserQuestion). Without
59
+ * one, a driver declaring `choose` and `text` gets the runner's `ask_question` tool, so a runtime never has two.
60
+ */
61
+ nativeQuestions?: boolean;
62
+ outboundFiles: {
63
+ roots: string[];
64
+ } | false;
65
+ /**
66
+ * The runtime runs off the runner's host (the class-C connector, spec §11.3): it gets no local files (inbound
67
+ * attachments are named in the prompt, never materialized), and since its plaintext leaves this host, the runner
68
+ * takes it into encrypted rooms only in relay mode, whose installation discloses that.
69
+ */
70
+ remote?: boolean;
71
+ /** Across rooms; runner default 8. */
72
+ maxConcurrentTurns?: number;
73
+ }
74
+ export type ActivityEvent = {
75
+ id: string;
76
+ kind: 'tool' | 'status';
77
+ title: string;
78
+ state: 'running' | 'done' | 'failed';
79
+ };
80
+ export type AskRequest =
81
+ /**
82
+ * `scopeKey`: what a session approval covers, e.g. the exact call (a tool and its whole input). Asks with the same key
83
+ * share the owner's session rule; without one, a rule covers only this exact title. A key of letters, digits, `_`, `:`
84
+ * and `-` (up to 64) is kept as the card's tool name and compared without regard to case; any other key is hashed.
85
+ */
86
+ {
87
+ kind: 'approve';
88
+ id: string;
89
+ title: string;
90
+ detail?: string;
91
+ risk?: 'normal' | 'destructive';
92
+ allowSession?: boolean;
93
+ scopeKey?: string;
94
+ } | {
95
+ kind: 'choose';
96
+ id: string;
97
+ question: string;
98
+ options: Array<{
99
+ id: string;
100
+ label: string;
101
+ }>;
102
+ multiple?: boolean;
103
+ }
104
+ /** `secret`: owner only, the value is never posted. */
105
+ | {
106
+ kind: 'text';
107
+ id: string;
108
+ question: string;
109
+ secret?: boolean;
110
+ };
111
+ export type AskAnswer = {
112
+ status: 'answered';
113
+ by: string;
114
+ allow?: boolean;
115
+ scope?: 'once' | 'session';
116
+ choices?: string[];
117
+ text?: string;
118
+ } | {
119
+ status: 'expired' | 'cancelled';
120
+ };
121
+ /** JSON only, at most 64 KiB per session; durable before the turn's inputs settle. */
122
+ export interface DriverState {
123
+ get<T>(key: string): T | undefined;
124
+ set(key: string, value: unknown): void;
125
+ delete(key: string): void;
126
+ }
127
+ export interface ToolDefinition {
128
+ name: string;
129
+ description: string;
130
+ inputSchema: Record<string, unknown>;
131
+ }
132
+ export interface ToolResult {
133
+ content: Array<{
134
+ type: 'text';
135
+ text: string;
136
+ }>;
137
+ isError?: boolean;
138
+ }
139
+ export interface TurnTools {
140
+ /** 128-bit, valid only while this turn is open. */
141
+ token: string;
142
+ /** http://127.0.0.1:<port>/mcp/t/<token> (MCP Streamable HTTP). */
143
+ url: string;
144
+ /** no_reply, communicate, react, send_file (with outbound roots) and ask_question (without native questions). */
145
+ definitions: ToolDefinition[];
146
+ /** The same handlers, in process. */
147
+ call(name: string, args: unknown): Promise<ToolResult>;
148
+ }
149
+ export interface TurnInputMessage {
150
+ readonly id: string;
151
+ readonly senderId: string;
152
+ readonly senderName?: string;
153
+ readonly senderType: 'human' | 'ai_agent';
154
+ readonly isOwner: boolean;
155
+ /**
156
+ * Set only on a message from an agent the owner listed as trusted (`RunnerOptions.trustedAgents`), in a room of the
157
+ * owner's agents that Canon confirmed at this turn's start (see `trustedAgents` for the whole rule). Drivers treat such
158
+ * a message as the owner's for tool permissions; it is never the owner (`isOwner` stays false), and approvals still go
159
+ * to the owner.
160
+ */
161
+ readonly trustedAgent?: true;
162
+ readonly text: string;
163
+ readonly contentType: string;
164
+ readonly attachments: ReadonlyArray<MediaAttachment>;
165
+ readonly mentions: ReadonlyArray<string>;
166
+ readonly replyTo?: string;
167
+ readonly createdAt: string;
168
+ }
169
+ export interface TurnInput {
170
+ readonly conversationId: string;
171
+ readonly room: 'direct' | 'group';
172
+ /** Sender of the newest input (the turn's trigger). */
173
+ readonly sender: {
174
+ readonly id: string;
175
+ readonly name?: string;
176
+ readonly type: 'human' | 'ai_agent';
177
+ readonly isOwner: boolean;
178
+ readonly trustedAgent?: true;
179
+ };
180
+ /** This turn's inputs, oldest first. */
181
+ readonly messages: ReadonlyArray<TurnInputMessage>;
182
+ /** For drivers without session memory: up to 20 earlier messages since the agent's admission, oldest first. */
183
+ readonly history: ReadonlyArray<TurnInputMessage>;
184
+ /**
185
+ * The owner's private notes for this turn (`canon-agent tell`, spec §12.3), oldest first, also at the head of `prompt`:
186
+ * context from the machine the runner runs on, never a message in the conversation. They do not change whose turn it is.
187
+ */
188
+ readonly ownerNotes?: ReadonlyArray<string>;
189
+ /**
190
+ * An errand turn (`canon-agent errand`, spec §12.6): the owner's errand, also in `prompt`. Its one message is the owner's,
191
+ * written by the runner; its final is the report to the owner.
192
+ */
193
+ readonly errand?: ErrandFacts;
194
+ /** Open errands that brought the agent into this conversation (§12.6), also at the head of `prompt`. */
195
+ readonly errands?: ReadonlyArray<ErrandFacts>;
196
+ }
197
+ /** An errand as a turn sees it: why the owner sent the agent, what to do, the recipient and the origin conversation. */
198
+ export interface ErrandFacts {
199
+ readonly id: string;
200
+ readonly why: string;
201
+ readonly instruction: string;
202
+ readonly to: string;
203
+ readonly origin?: string;
204
+ }
205
+ /** Thrown by a driver whose native session is unusable: the turn fails and the runner resets the session. */
206
+ export declare class DriverSessionError extends Error {
207
+ name: string;
208
+ }
209
+ /** A relay channel refusal (spec §11.6): the channel answers Functions with this status and code. */
210
+ export declare class ConnectorRelayError extends Error {
211
+ readonly status: 400 | 403 | 404 | 409 | 503;
212
+ readonly code: string;
213
+ name: string;
214
+ constructor(status: 400 | 403 | 404 | 409 | 503, code: string, message: string);
215
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@canonmsg/agent-sdk",
3
- "version": "11.2.1",
3
+ "version": "12.1.0",
4
4
  "description": "Canon Agent SDK — build AI agents that participate in Canon conversations",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -15,20 +15,32 @@
15
15
  "types": "./dist/media.d.ts"
16
16
  }
17
17
  },
18
+ "bin": {
19
+ "canon-agent": "dist/cli.js"
20
+ },
18
21
  "files": [
19
22
  "dist"
20
23
  ],
21
24
  "scripts": {
22
- "build": "node -e \"require('fs').rmSync('dist',{recursive:true,force:true})\" && tsc",
23
- "dev": "tsc --watch",
24
- "test": "vitest run",
25
+ "build": "node ../../scripts/build-agent-sdk.mjs",
26
+ "dev": "tsc --watch --noEmit",
25
27
  "prepack": "npm run build"
26
28
  },
27
29
  "engines": {
28
30
  "node": ">=22.22.3"
29
31
  },
30
32
  "dependencies": {
31
- "@canonmsg/core": "^13.4.0"
33
+ "@modelcontextprotocol/sdk": "^1.30.0",
34
+ "@noble/curves": "2.4.0",
35
+ "chess.js": "1.4.0"
36
+ },
37
+ "peerDependencies": {
38
+ "@anthropic-ai/claude-agent-sdk": "^0.3.289"
39
+ },
40
+ "peerDependenciesMeta": {
41
+ "@anthropic-ai/claude-agent-sdk": {
42
+ "optional": true
43
+ }
32
44
  },
33
45
  "repository": {
34
46
  "type": "git",
@@ -39,6 +51,7 @@
39
51
  "access": "public"
40
52
  },
41
53
  "devDependencies": {
54
+ "@anthropic-ai/claude-agent-sdk": "0.3.289",
42
55
  "@types/node": "^22.0.0",
43
56
  "typescript": "~5.7.0",
44
57
  "vitest": "^4.1.8"