@canonmsg/agent-sdk 12.5.1 → 13.0.2
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 +57 -24
- package/dist/chunks/{chunk-C2WJT3VX.js → chunk-5WPAB5MB.js} +1047 -856
- package/dist/chunks/{chunk-3KFVBOVI.js → chunk-DNHJGAEL.js} +1115 -1020
- package/dist/chunks/chunk-DTAWEQCM.js +406 -0
- package/dist/chunks/{chunk-TFSURFF6.js → chunk-TYZT346O.js} +1720 -4006
- package/dist/chunks/chunk-YHDDOADK.js +118 -0
- package/dist/chunks/chunk-YJUPKU47.js +1490 -0
- package/dist/chunks/{mls-crypto-Y44BOSEM.js → mls-crypto-NZVMSUFU.js} +2 -1
- package/dist/cli.js +86 -48
- package/dist/drivers/acp.d.ts +56 -0
- package/dist/drivers/codex.d.ts +1 -2
- package/dist/drivers/subprocess.d.ts +38 -0
- package/dist/index.d.ts +4 -2
- package/dist/index.js +12 -3
- package/dist/internal/backend-contracts/endpointContentPolicy.d.ts +2 -2
- package/dist/internal/backend-contracts/index.d.ts +1 -0
- package/dist/internal/backend-contracts/loggedOutSessions.d.ts +7 -0
- package/dist/internal/backend-contracts/mlsDevCredential.d.ts +3 -0
- 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 +6 -0
- package/dist/internal/endpoint/mls-dev-client.d.ts +2 -0
- package/dist/internal/endpoint/mls-dev-groups.d.ts +12 -0
- package/dist/internal/endpoint/mls-dev-text.d.ts +32 -0
- package/dist/internal/endpoint/node-mls-custody-protocol.d.ts +5 -0
- package/dist/internal/endpoint/node.d.ts +1 -0
- package/dist/internal/endpoint/transport.d.ts +1 -1
- package/dist/media.js +4 -2
- package/dist/mls/darwin-arm64/canon-mls-custody +0 -0
- package/dist/mls/linux-arm64/canon-mls-custody +0 -0
- package/dist/mls/linux-x64/canon-mls-custody +0 -0
- package/dist/mls/wasm/canon_mls_wasm.js +363 -0
- package/dist/mls/wasm/canon_mls_wasm_bg.wasm +0 -0
- package/dist/mls-wasm-host.js +258 -0
- package/dist/runner/mls-bundled.d.ts +15 -2
- package/dist/runner/mls-managed.d.ts +6 -3
- package/dist/runner/mls.d.ts +4 -7
- package/dist/runner/runner.d.ts +9 -12
- package/dist/runner/types.d.ts +37 -1
- 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
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
import {
|
|
2
|
+
WASM_HOST_CONFIGURE,
|
|
3
|
+
WASM_HOST_PROVISION,
|
|
4
|
+
keyFile
|
|
5
|
+
} from "./chunks/chunk-DTAWEQCM.js";
|
|
6
|
+
import {
|
|
7
|
+
MAX_INPUT,
|
|
8
|
+
NodeMlsDevCustodyError,
|
|
9
|
+
parseFrame,
|
|
10
|
+
rejected,
|
|
11
|
+
requireValue
|
|
12
|
+
} from "./chunks/chunk-YJUPKU47.js";
|
|
13
|
+
import {
|
|
14
|
+
__name,
|
|
15
|
+
canonicalEndpointJson,
|
|
16
|
+
isMlsDevReceiptClockSkew,
|
|
17
|
+
parseMlsDevDeviceContext
|
|
18
|
+
} from "./chunks/chunk-5WPAB5MB.js";
|
|
19
|
+
|
|
20
|
+
// src/mls-wasm-host.ts
|
|
21
|
+
import { createHash } from "node:crypto";
|
|
22
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
23
|
+
|
|
24
|
+
// ../endpoint/src/mls-browser-custody.ts
|
|
25
|
+
var AAD_DOMAIN = "canon.mls.browser-state.v1";
|
|
26
|
+
var encoder = new TextEncoder();
|
|
27
|
+
var decoder = new TextDecoder();
|
|
28
|
+
var MAX_PAYLOAD_BYTES = 4 * 1024 * 1024;
|
|
29
|
+
var refused = /* @__PURE__ */ __name(() => {
|
|
30
|
+
throw new Error("CANON_MLS_CUSTODY_REFUSED");
|
|
31
|
+
}, "refused");
|
|
32
|
+
function toBase64(bytes) {
|
|
33
|
+
let text = "";
|
|
34
|
+
for (let at = 0; at < bytes.length; at += 32768) text += String.fromCharCode(...bytes.subarray(at, at + 32768));
|
|
35
|
+
return btoa(text);
|
|
36
|
+
}
|
|
37
|
+
__name(toBase64, "toBase64");
|
|
38
|
+
function fromBase64(text) {
|
|
39
|
+
const binary = atob(text), bytes = new Uint8Array(binary.length);
|
|
40
|
+
for (let at = 0; at < binary.length; at += 1) bytes[at] = binary.charCodeAt(at);
|
|
41
|
+
return bytes;
|
|
42
|
+
}
|
|
43
|
+
__name(fromBase64, "fromBase64");
|
|
44
|
+
function parsedContext(json) {
|
|
45
|
+
const { revision, ...context } = JSON.parse(json);
|
|
46
|
+
if (typeof revision !== "number" || !Number.isSafeInteger(revision) || revision < 0) refused();
|
|
47
|
+
return { contextId: canonicalEndpointJson(context), context, revision };
|
|
48
|
+
}
|
|
49
|
+
__name(parsedContext, "parsedContext");
|
|
50
|
+
function createMlsBrowserCustody(options) {
|
|
51
|
+
const { engine, wrappingKey } = options;
|
|
52
|
+
const clock = options.clock ?? Date.now;
|
|
53
|
+
const issuerKeys = encoder.encode(JSON.stringify(options.issuerKeys)), receiptKeys = encoder.encode(JSON.stringify(options.receiptKeys));
|
|
54
|
+
const aad = /* @__PURE__ */ __name((purpose, context, binding) => encoder.encode(canonicalEndpointJson({ domain: AAD_DOMAIN, purpose, context, binding })), "aad");
|
|
55
|
+
async function wrap(key, additionalData, clear) {
|
|
56
|
+
const iv = crypto.getRandomValues(new Uint8Array(12));
|
|
57
|
+
const sealed = new Uint8Array(await crypto.subtle.encrypt({ name: "AES-GCM", iv, additionalData }, key, clear));
|
|
58
|
+
const framed = new Uint8Array(12 + sealed.length);
|
|
59
|
+
framed.set(iv);
|
|
60
|
+
framed.set(sealed, 12);
|
|
61
|
+
return `v1:${toBase64(framed)}`;
|
|
62
|
+
}
|
|
63
|
+
__name(wrap, "wrap");
|
|
64
|
+
async function unwrap(key, additionalData, wrapped) {
|
|
65
|
+
if (!wrapped.startsWith("v1:")) refused();
|
|
66
|
+
const framed = fromBase64(wrapped.slice(3));
|
|
67
|
+
if (framed.length < 28) refused();
|
|
68
|
+
return new Uint8Array(await crypto.subtle.decrypt({ name: "AES-GCM", iv: framed.subarray(0, 12), additionalData }, key, framed.subarray(12)));
|
|
69
|
+
}
|
|
70
|
+
__name(unwrap, "unwrap");
|
|
71
|
+
function answer2(bytes) {
|
|
72
|
+
if (bytes.length < 4) refused();
|
|
73
|
+
const length = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength).getUint32(0);
|
|
74
|
+
if (length === 0) return { skew: decoder.decode(bytes.subarray(4)) };
|
|
75
|
+
if (length > MAX_PAYLOAD_BYTES || length > bytes.length - 4) refused();
|
|
76
|
+
return { payload: decoder.decode(bytes.subarray(4, 4 + length)), clear: bytes.slice(4 + length) };
|
|
77
|
+
}
|
|
78
|
+
__name(answer2, "answer");
|
|
79
|
+
async function finish(bytes, seal) {
|
|
80
|
+
try {
|
|
81
|
+
const result = answer2(bytes);
|
|
82
|
+
if ("skew" in result) return result.skew;
|
|
83
|
+
try {
|
|
84
|
+
return `{"wrappedState":${JSON.stringify(await seal(result.clear, result.payload))},"payload":${result.payload}}`;
|
|
85
|
+
} finally {
|
|
86
|
+
result.clear.fill(0);
|
|
87
|
+
}
|
|
88
|
+
} finally {
|
|
89
|
+
bytes.fill(0);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
__name(finish, "finish");
|
|
93
|
+
return Object.freeze({
|
|
94
|
+
async initializeDevSigning(contextJson) {
|
|
95
|
+
await wrappingKey(parsedContext(contextJson).contextId);
|
|
96
|
+
},
|
|
97
|
+
async prepareDevSigning(contextJson, commandJson, wrappedState) {
|
|
98
|
+
const { contextId, context, revision } = parsedContext(contextJson), key = await wrappingKey(contextId);
|
|
99
|
+
const clear = wrappedState === null ? void 0 : await unwrap(key, aad("identity", context, { revision }), wrappedState);
|
|
100
|
+
try {
|
|
101
|
+
const prepared2 = engine.prepare_signing(encoder.encode(contextJson), encoder.encode(commandJson), clear, clock());
|
|
102
|
+
return await finish(prepared2, (next) => wrap(key, aad("identity", context, { revision: revision + 1 }), next));
|
|
103
|
+
} finally {
|
|
104
|
+
clear?.fill(0);
|
|
105
|
+
}
|
|
106
|
+
},
|
|
107
|
+
async prepareDevKeyPackage(contextJson, commandJson, wrappedIdentityState) {
|
|
108
|
+
const { contextId, context, revision } = parsedContext(contextJson), key = await wrappingKey(contextId);
|
|
109
|
+
const identity2 = await unwrap(key, aad("identity", context, { revision }), wrappedIdentityState);
|
|
110
|
+
try {
|
|
111
|
+
const prepared2 = engine.prepare_key_package(encoder.encode(contextJson), encoder.encode(commandJson), identity2, issuerKeys, clock());
|
|
112
|
+
return await finish(prepared2, (next, payload) => {
|
|
113
|
+
const { keyPackageId, deviceId } = JSON.parse(payload);
|
|
114
|
+
return wrap(key, aad("key-package", context, { version: 1, keyPackageId, deviceId }), next);
|
|
115
|
+
});
|
|
116
|
+
} finally {
|
|
117
|
+
identity2.fill(0);
|
|
118
|
+
}
|
|
119
|
+
},
|
|
120
|
+
async prepareDevGroup(identityContextJson, groupContextJson, commandJson, wrappedIdentityState, wrappedGroupState, wrappedKeyPackageState) {
|
|
121
|
+
const { contextId, context, revision } = parsedContext(identityContextJson), key = await wrappingKey(contextId);
|
|
122
|
+
const group = JSON.parse(groupContextJson);
|
|
123
|
+
const clear = [];
|
|
124
|
+
const open = /* @__PURE__ */ __name(async (additionalData, wrapped) => {
|
|
125
|
+
const opened = await unwrap(key, additionalData, wrapped);
|
|
126
|
+
clear.push(opened);
|
|
127
|
+
return opened;
|
|
128
|
+
}, "open");
|
|
129
|
+
try {
|
|
130
|
+
const identity2 = await open(aad("identity", context, { revision }), wrappedIdentityState);
|
|
131
|
+
const groupState = wrappedGroupState === null ? void 0 : await open(aad("group", context, group), wrappedGroupState);
|
|
132
|
+
let keyPackageState;
|
|
133
|
+
if (wrappedKeyPackageState !== null) {
|
|
134
|
+
const { keyPackageId } = JSON.parse(commandJson);
|
|
135
|
+
keyPackageState = await open(aad("key-package", context, { version: 1, keyPackageId, deviceId: group.deviceId }), wrappedKeyPackageState);
|
|
136
|
+
}
|
|
137
|
+
const prepared2 = engine.prepare_group(
|
|
138
|
+
encoder.encode(identityContextJson),
|
|
139
|
+
encoder.encode(groupContextJson),
|
|
140
|
+
encoder.encode(commandJson),
|
|
141
|
+
identity2,
|
|
142
|
+
groupState,
|
|
143
|
+
keyPackageState,
|
|
144
|
+
issuerKeys,
|
|
145
|
+
receiptKeys,
|
|
146
|
+
clock()
|
|
147
|
+
);
|
|
148
|
+
return await finish(prepared2, (next) => wrap(key, aad("group", context, { ...group, revision: group.revision + 1 }), next));
|
|
149
|
+
} finally {
|
|
150
|
+
clear.forEach((bytes) => bytes.fill(0));
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
__name(createMlsBrowserCustody, "createMlsBrowserCustody");
|
|
156
|
+
|
|
157
|
+
// src/mls-wasm-host.ts
|
|
158
|
+
var NULL = Buffer.from('{"version":1,"ok":true,"value":null}');
|
|
159
|
+
var REJECTED = Buffer.from('{"version":1,"ok":false,"error":"MLS_NODE_CUSTODY_REJECTED"}');
|
|
160
|
+
var sha256 = /* @__PURE__ */ __name((bytes) => createHash("sha256").update(bytes).digest("hex"), "sha256");
|
|
161
|
+
var prepared = /* @__PURE__ */ __name((value) => Buffer.from(isMlsDevReceiptClockSkew(JSON.parse(value)) ? value : `{"version":1,"ok":true,"value":${value}}`), "prepared");
|
|
162
|
+
var configuration;
|
|
163
|
+
var custody;
|
|
164
|
+
var wrapping;
|
|
165
|
+
async function load() {
|
|
166
|
+
requireValue(existsSync(configuration.keyPath));
|
|
167
|
+
const bytes = keyFile(configuration.keyPath);
|
|
168
|
+
try {
|
|
169
|
+
return await crypto.subtle.importKey("raw", bytes, "AES-GCM", false, ["encrypt", "decrypt"]);
|
|
170
|
+
} finally {
|
|
171
|
+
bytes.fill(0);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
__name(load, "load");
|
|
175
|
+
async function configure(value) {
|
|
176
|
+
const wasm = readFileSync(value.wasmPath), glue = readFileSync(value.gluePath);
|
|
177
|
+
requireValue(sha256(wasm) === value.wasmSha256 && sha256(glue) === value.glueSha256);
|
|
178
|
+
const engine = await import(`data:text/javascript;base64,${glue.toString("base64")}`);
|
|
179
|
+
engine.initSync({ module: wasm });
|
|
180
|
+
configuration = value;
|
|
181
|
+
custody = createMlsBrowserCustody({
|
|
182
|
+
engine,
|
|
183
|
+
issuerKeys: value.issuerKeys,
|
|
184
|
+
receiptKeys: value.receiptKeys,
|
|
185
|
+
wrappingKey: /* @__PURE__ */ __name(() => wrapping ??= load().catch((error) => {
|
|
186
|
+
wrapping = void 0;
|
|
187
|
+
throw error;
|
|
188
|
+
}), "wrappingKey")
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
__name(configure, "configure");
|
|
192
|
+
var identity = /* @__PURE__ */ __name((value) => {
|
|
193
|
+
const { revision: _revision, ...device } = parseMlsDevDeviceContext(value);
|
|
194
|
+
return JSON.stringify(device);
|
|
195
|
+
}, "identity");
|
|
196
|
+
function own(value) {
|
|
197
|
+
requireValue(typeof value === "string" && configuration);
|
|
198
|
+
requireValue(identity(JSON.parse(value)) === identity({ ...configuration.context, revision: 0 }));
|
|
199
|
+
return value;
|
|
200
|
+
}
|
|
201
|
+
__name(own, "own");
|
|
202
|
+
async function answer(operation, values) {
|
|
203
|
+
if (operation === WASM_HOST_CONFIGURE) {
|
|
204
|
+
requireValue(!custody && values.length === 1);
|
|
205
|
+
await configure(JSON.parse(values[0]));
|
|
206
|
+
return NULL;
|
|
207
|
+
}
|
|
208
|
+
const context = own(values[0]);
|
|
209
|
+
if (operation === WASM_HOST_PROVISION) {
|
|
210
|
+
requireValue(values.length === 1 && !existsSync(configuration.keyPath));
|
|
211
|
+
keyFile(configuration.keyPath).fill(0);
|
|
212
|
+
return NULL;
|
|
213
|
+
}
|
|
214
|
+
if (operation === 1 && values.length === 1) {
|
|
215
|
+
await custody.initializeDevSigning(context);
|
|
216
|
+
return NULL;
|
|
217
|
+
}
|
|
218
|
+
if (operation === 2 && values.length === 3) return prepared(await custody.prepareDevSigning(context, values[1], values[2]));
|
|
219
|
+
if (operation === 3 && values.length === 3) return prepared(await custody.prepareDevKeyPackage(context, values[1], values[2]));
|
|
220
|
+
if (operation === 4 && values.length === 6) {
|
|
221
|
+
return prepared(await custody.prepareDevGroup(context, values[1], values[2], values[3], values[4], values[5]));
|
|
222
|
+
}
|
|
223
|
+
return rejected();
|
|
224
|
+
}
|
|
225
|
+
__name(answer, "answer");
|
|
226
|
+
async function* frames(input) {
|
|
227
|
+
let buffered = Buffer.alloc(0);
|
|
228
|
+
for await (const chunk of input) {
|
|
229
|
+
buffered = Buffer.concat([buffered, chunk]);
|
|
230
|
+
while (buffered.length >= 4) {
|
|
231
|
+
const length = buffered.readUInt32BE(0);
|
|
232
|
+
requireValue(length <= MAX_INPUT);
|
|
233
|
+
if (buffered.length < 4 + length) break;
|
|
234
|
+
yield buffered.subarray(4, 4 + length);
|
|
235
|
+
buffered = buffered.subarray(4 + length);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
__name(frames, "frames");
|
|
240
|
+
function send(message) {
|
|
241
|
+
const length = Buffer.alloc(4);
|
|
242
|
+
length.writeUInt32BE(message.length);
|
|
243
|
+
return new Promise((sent) => {
|
|
244
|
+
process.stdout.write(Buffer.concat([length, message]), () => sent());
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
__name(send, "send");
|
|
248
|
+
for (const signal of ["SIGINT", "SIGTERM", "SIGBREAK", "SIGHUP"]) process.on(signal, () => void 0);
|
|
249
|
+
for await (const message of frames(process.stdin)) {
|
|
250
|
+
try {
|
|
251
|
+
const { operation, values } = parseFrame(message);
|
|
252
|
+
await send(await answer(operation, values));
|
|
253
|
+
} catch (error) {
|
|
254
|
+
if (!custody || !(error instanceof NodeMlsDevCustodyError) && error.message !== "CANON_MLS_CUSTODY_REFUSED") process.exit(70);
|
|
255
|
+
await send(REJECTED);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
process.exit(0);
|
|
@@ -16,10 +16,23 @@ export declare const MLS_ENVIRONMENT_PINS: Readonly<Record<MlsEnvironmentId, {
|
|
|
16
16
|
}>>;
|
|
17
17
|
/**
|
|
18
18
|
* The custody helper this package ships for this host (`dist/mls/<platform>/canon-mls-custody`), checked against its
|
|
19
|
-
* pin, or undefined where it ships none: an Intel Mac, musl,
|
|
20
|
-
* file that is there but differs from its pin is refused.
|
|
19
|
+
* pin, or undefined where it ships none that runs: an Intel Mac, Windows, musl, an old glibc, or a build of this package
|
|
20
|
+
* without helpers. A file that is there but differs from its pin is refused.
|
|
21
21
|
*/
|
|
22
22
|
export declare function bundledMlsCustodyHelper(): {
|
|
23
23
|
path: string;
|
|
24
24
|
sha256: string;
|
|
25
25
|
} | undefined;
|
|
26
|
+
export interface BundledMlsWasmCore {
|
|
27
|
+
/** The process that runs the core (dist/mls-wasm-host.js). */
|
|
28
|
+
hostPath: string;
|
|
29
|
+
wasmPath: string;
|
|
30
|
+
wasmSha256: string;
|
|
31
|
+
gluePath: string;
|
|
32
|
+
glueSha256: string;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* The WASM core this package ships for hosts with no helper (`dist/mls/wasm/`), checked against its pins, or undefined in a
|
|
36
|
+
* build of this package without it. A file that is there but differs from its pin is refused.
|
|
37
|
+
*/
|
|
38
|
+
export declare function bundledMlsWasmCore(): BundledMlsWasmCore | undefined;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type MlsDevDeviceContext, type MlsEnvironmentId } from '../internal/backend-contracts/index.js';
|
|
2
|
+
import { type BundledMlsWasmCore } from './mls-bundled.js';
|
|
2
3
|
import type { RunnerMlsOptions } from './mls.js';
|
|
3
4
|
export interface ManagedMlsDeviceOptions {
|
|
4
5
|
/** The runner's home (CANON_HOME, or a relay installation's home). */
|
|
@@ -9,6 +10,8 @@ export interface ManagedMlsDeviceOptions {
|
|
|
9
10
|
path: string;
|
|
10
11
|
sha256: string;
|
|
11
12
|
};
|
|
13
|
+
/** The WASM core rather than a helper, as on a host with none. */
|
|
14
|
+
wasm?: BundledMlsWasmCore;
|
|
12
15
|
/** Other public pins than this package's for the environment. */
|
|
13
16
|
pins?: {
|
|
14
17
|
issuerKeys: ReadonlyArray<{
|
|
@@ -24,13 +27,13 @@ export interface ManagedMlsDeviceOptions {
|
|
|
24
27
|
}
|
|
25
28
|
export type ManagedMlsDevice = RunnerMlsOptions & {
|
|
26
29
|
provision(context: MlsDevDeviceContext): Promise<void>;
|
|
27
|
-
/** Whether this context's
|
|
30
|
+
/** Whether this context's device was provisioned here. Throws, saying why, if it was made in the way this host cannot open. */
|
|
28
31
|
provisioned(context: MlsDevDeviceContext): boolean;
|
|
29
32
|
close(): Promise<void>;
|
|
30
33
|
};
|
|
31
34
|
/**
|
|
32
|
-
* A runner's device where it is given none (`createRunner` and the CLI alike): managed custody
|
|
33
|
-
*
|
|
35
|
+
* A runner's device where it is given none (`createRunner` and the CLI alike): managed custody as `custodyFor` chooses, or
|
|
36
|
+
* undefined where this package ships neither helper nor core, or the environment has no MLS.
|
|
34
37
|
*/
|
|
35
38
|
export declare function defaultMlsDevice(home: string, environmentId: string): ManagedMlsDevice | undefined;
|
|
36
39
|
export declare function managedMlsDevice(options: ManagedMlsDeviceOptions): ManagedMlsDevice;
|
package/dist/runner/mls.d.ts
CHANGED
|
@@ -27,12 +27,12 @@ export declare function mlsAudiencePrincipals(value: MlsDevCurrentAudience): str
|
|
|
27
27
|
*/
|
|
28
28
|
export declare function mlsAudienceEpoch(value: MlsDevCurrentAudience): string;
|
|
29
29
|
/** What an encrypted room's text message is to the runner; Canon never learns it (`MlsIds.text`). */
|
|
30
|
-
export type MlsTextKind = 'final' | 'failed' | 'restart' | 'said' | 'item';
|
|
30
|
+
export type MlsTextKind = 'final' | 'failed' | 'restart' | 'said' | 'item' | 'part';
|
|
31
31
|
/**
|
|
32
32
|
* §6 ids. Canon sees the message id of everything the runner sends to an encrypted room, and a card's request id (its
|
|
33
33
|
* public binding), so these are keyed with a secret only this installation holds (`privateKey`): HMAC-SHA256 over
|
|
34
34
|
* domain-separated inputs, cut to 128 bits. Canon can test no guess of the text, question or turn an id names, and every
|
|
35
|
-
* text message (a final, a failure or restart notice, a `communicate` send, an item) has the same shape, `msg-<32 hex>`, so none
|
|
35
|
+
* text message (a final, a part of one, a failure or restart notice, a `communicate` send, an item) has the same shape, `msg-<32 hex>`, so none
|
|
36
36
|
* says what kind it is. The same inputs give the same id after a restart, so journal replay, a repeated `communicate`,
|
|
37
37
|
* a re-asked card and a restart notice keep theirs.
|
|
38
38
|
*/
|
|
@@ -46,14 +46,11 @@ export interface MlsIds {
|
|
|
46
46
|
export declare const MLS_ID_KEY_BYTES = 32;
|
|
47
47
|
export declare function createMlsIds(key: Uint8Array): MlsIds;
|
|
48
48
|
/**
|
|
49
|
-
* Encrypted rooms are for
|
|
50
|
-
*
|
|
49
|
+
* Encrypted rooms are for an environment with an MLS profile. Canon decides who joins a room under the invitation policies,
|
|
50
|
+
* as for plaintext rooms, and a closed agent's text needs authority for each reply (`replyGrant` in runner.ts).
|
|
51
51
|
*/
|
|
52
52
|
export declare function requireMlsSupport(input: {
|
|
53
53
|
environmentId: string;
|
|
54
|
-
agent: {
|
|
55
|
-
outboundPolicy: string;
|
|
56
|
-
};
|
|
57
54
|
}): void;
|
|
58
55
|
/**
|
|
59
56
|
* Whether `text` @mentions any of `names` (an agent's display name, id or aliases). Encrypted text carries no `mentions[]`,
|
package/dist/runner/runner.d.ts
CHANGED
|
@@ -33,11 +33,9 @@ export interface RunnerOptions {
|
|
|
33
33
|
*/
|
|
34
34
|
mls?: RunnerMlsOptions | false;
|
|
35
35
|
/**
|
|
36
|
-
* Relay mode, the class-C relay (spec §11.4):
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
* the runtime descriptor and heartbeat, so the apps show a relayed turn working and offer Stop. It needs the agent's MLS
|
|
40
|
-
* device.
|
|
36
|
+
* Relay mode, the class-C relay (spec §11.4): what lets a `remote` driver (the connector) into encrypted rooms, and
|
|
37
|
+
* `relayRead`, `relaySend` and `relayStart` serve the relay channel. It publishes the runtime descriptor and heartbeat,
|
|
38
|
+
* so the apps show a relayed turn working and offer Stop. It needs the agent's MLS device.
|
|
41
39
|
*/
|
|
42
40
|
relay?: boolean;
|
|
43
41
|
/** The engine's installation: the relay's reserved one, from its configuration. A store opened under another refuses to start. */
|
|
@@ -45,18 +43,17 @@ export interface RunnerOptions {
|
|
|
45
43
|
/**
|
|
46
44
|
* Team rooms: agents the owner trusts. A message from one of them counts as the owner's for tool permissions (the
|
|
47
45
|
* drivers' guards read `TurnInputMessage.trustedAgent`) only in a room that holds no one but this agent, its owner and
|
|
48
|
-
* listed agents, all of which Canon confirms at the start of the turn are active agents of this agent's own owner
|
|
49
|
-
*
|
|
50
|
-
* encrypted room never with a relay device in the group. A `remote` runtime has no tools on this host: a relay ignores it.
|
|
46
|
+
* listed agents, all of which Canon confirms at the start of the turn are active agents of this agent's own owner, and
|
|
47
|
+
* never with a relay device in the group. A `remote` runtime has no tools on this host: a relay ignores it.
|
|
51
48
|
*/
|
|
52
49
|
trustedAgents?: string[];
|
|
53
50
|
/**
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
*
|
|
51
|
+
* How many messages from other agents that @mention this agent a room may wake it with before a person writes there
|
|
52
|
+
* again (default 6; 0 never wakes on an agent). Past the cap they are settled with no turn, logged once. The room's own
|
|
53
|
+
* rules can stop them sooner (`RoomRules`).
|
|
57
54
|
*/
|
|
58
55
|
agentTurnCap?: number;
|
|
59
|
-
/**
|
|
56
|
+
/** Names besides the agent's display name and id that @mention it, such as a role (`@engineer`). */
|
|
60
57
|
mentionAliases?: string[];
|
|
61
58
|
/** How long an aborted turn may take to settle before its session is reset (default 10 s). */
|
|
62
59
|
abortGraceMs?: number;
|
package/dist/runner/types.d.ts
CHANGED
|
@@ -5,7 +5,7 @@ import type { MediaAttachment } from '../internal/core/index.js';
|
|
|
5
5
|
* the claude and codex drivers pass the conformance suite.
|
|
6
6
|
*/
|
|
7
7
|
export interface RuntimeDriver {
|
|
8
|
-
readonly name: 'code' | 'claude' | 'codex' | 'connector';
|
|
8
|
+
readonly name: 'code' | 'claude' | 'codex' | 'acp' | 'connector';
|
|
9
9
|
/** Input to the generated runtime descriptor; never a static table. */
|
|
10
10
|
readonly capabilities: DriverCapabilities;
|
|
11
11
|
runTurn(turn: DriverTurn): Promise<DriverResult>;
|
|
@@ -71,6 +71,11 @@ export interface DriverCapabilities {
|
|
|
71
71
|
* picked up.
|
|
72
72
|
*/
|
|
73
73
|
remote?: boolean;
|
|
74
|
+
/**
|
|
75
|
+
* The runtime takes only its owner's messages, so the recent history a `none` session carries holds only those and the
|
|
76
|
+
* agent's own. Otherwise a message refused at the door would reach the runtime in the owner's next turn, with the owner's tools.
|
|
77
|
+
*/
|
|
78
|
+
ownerHistoryOnly?: boolean;
|
|
74
79
|
/** Across rooms; runner default 8. */
|
|
75
80
|
maxConcurrentTurns?: number;
|
|
76
81
|
}
|
|
@@ -196,6 +201,37 @@ export interface TurnInput {
|
|
|
196
201
|
readonly errand?: ErrandFacts;
|
|
197
202
|
/** Open errands that brought the agent into this conversation (§12.6), also at the head of `prompt`. */
|
|
198
203
|
readonly errands?: ReadonlyArray<ErrandFacts>;
|
|
204
|
+
/** An encrypted room's turn: the room as Canon's records give it, also at the head of `prompt`. A relay's turn has none. */
|
|
205
|
+
readonly roomFacts?: RoomFacts;
|
|
206
|
+
}
|
|
207
|
+
/** What a turn knows about its encrypted room. Names, name and topic are what people typed: data, never instructions. */
|
|
208
|
+
export interface RoomFacts {
|
|
209
|
+
readonly name?: string;
|
|
210
|
+
readonly topic?: string;
|
|
211
|
+
readonly owner: {
|
|
212
|
+
readonly id: string;
|
|
213
|
+
readonly name?: string;
|
|
214
|
+
};
|
|
215
|
+
readonly members: ReadonlyArray<{
|
|
216
|
+
readonly id: string;
|
|
217
|
+
readonly name?: string;
|
|
218
|
+
readonly type: 'human' | 'ai_agent';
|
|
219
|
+
readonly isOwner: boolean;
|
|
220
|
+
readonly isYou: boolean;
|
|
221
|
+
}>;
|
|
222
|
+
/** Absent only when this Canon does not send them yet. */
|
|
223
|
+
readonly rules?: RoomRules;
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* How the agent takes part in this room, resolved from its owner's defaults and the room's settings. Any admin of the room
|
|
227
|
+
* can set them, so they are never the owner's orders. The runner enforces all but `style`, which it tells the agent.
|
|
228
|
+
*/
|
|
229
|
+
export interface RoomRules {
|
|
230
|
+
readonly style: 'natural' | 'collaborative' | 'mention-first' | 'approval-gated' | 'handoff-only' | 'observer';
|
|
231
|
+
readonly requireMentionForGroupReplies: boolean;
|
|
232
|
+
readonly allowAgentToAgent: boolean;
|
|
233
|
+
readonly allowLongRunningCollaboration: boolean;
|
|
234
|
+
readonly maxConsecutiveAgentTurns: number | null;
|
|
199
235
|
}
|
|
200
236
|
/** An errand as a turn sees it: why the owner sent the agent, what to do, the recipient and the origin conversation. */
|
|
201
237
|
export interface ErrandFacts {
|
package/package.json
CHANGED
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
import type { CanonClient } from './client.js';
|
|
2
|
-
import type { CanonUnifiedDiff } from './types.js';
|
|
3
|
-
import type { ApprovalConfig, ApprovalNativeRequestMetadata, ApprovalResult, ApprovalRequestCategory, ApprovalRequestDetail, ApprovalRisk } from './approval-types.js';
|
|
4
|
-
import { RuntimeRequestManager, type RuntimeRequestOptions } from './runtime-request-manager.js';
|
|
5
|
-
interface ApprovalRequestOpts {
|
|
6
|
-
riskLevel?: 'normal' | 'destructive';
|
|
7
|
-
risk?: ApprovalRisk;
|
|
8
|
-
category?: ApprovalRequestCategory;
|
|
9
|
-
runtimeId?: string;
|
|
10
|
-
turnId?: string;
|
|
11
|
-
native?: ApprovalNativeRequestMetadata;
|
|
12
|
-
toolSummary?: string;
|
|
13
|
-
details?: ApprovalRequestDetail[];
|
|
14
|
-
/** File-change preview rendered inline on the approval card (size-disciplined automatically). */
|
|
15
|
-
diff?: CanonUnifiedDiff;
|
|
16
|
-
ignoreSessionRules?: boolean;
|
|
17
|
-
allowSessionRule?: boolean;
|
|
18
|
-
/** Human conversation member who triggered this turn and should answer. */
|
|
19
|
-
responseUserId?: string;
|
|
20
|
-
}
|
|
21
|
-
/**
|
|
22
|
-
* Platform-agnostic approval protocol for Canon.
|
|
23
|
-
*
|
|
24
|
-
* Now a thin `RuntimeRequestManager` subclass: the pending map, deadline timers,
|
|
25
|
-
* the hybrid poll-plus-push loop, and cancellation live in the generalized
|
|
26
|
-
* engine; approval-specific behaviour (session-rule fast path, redacted request
|
|
27
|
-
* metadata, owner-authored outcome messages, and session-rule storage) is
|
|
28
|
-
* expressed as a single `KindDescriptor`. Behaviour is preserved verbatim.
|
|
29
|
-
*/
|
|
30
|
-
export declare class ApprovalManager extends RuntimeRequestManager {
|
|
31
|
-
private config;
|
|
32
|
-
constructor(client: CanonClient, agentId: string, ownerId: string, config?: Partial<ApprovalConfig>);
|
|
33
|
-
/**
|
|
34
|
-
* Request approval for a tool call.
|
|
35
|
-
*
|
|
36
|
-
* 1. Checks session rules — may resolve immediately.
|
|
37
|
-
* 2. Sends a message with approval card metadata.
|
|
38
|
-
* 3. Waits for the owner's reply (or timeout).
|
|
39
|
-
*/
|
|
40
|
-
requestApproval(conversationId: string, toolName: string, toolInput: Record<string, unknown>, opts?: ApprovalRequestOpts, runtimeOptions?: RuntimeRequestOptions): Promise<ApprovalResult>;
|
|
41
|
-
private buildApprovalDescriptor;
|
|
42
|
-
}
|
|
43
|
-
export {};
|