@stelis/agent-q-core 0.0.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 (47) hide show
  1. package/README.md +144 -0
  2. package/dist/adapter-internal.d.ts +5 -0
  3. package/dist/adapter-internal.js +6 -0
  4. package/dist/adapter-internal.js.map +1 -0
  5. package/dist/config.d.ts +74 -0
  6. package/dist/config.js +489 -0
  7. package/dist/config.js.map +1 -0
  8. package/dist/core.d.ts +320 -0
  9. package/dist/core.js +840 -0
  10. package/dist/core.js.map +1 -0
  11. package/dist/device.d.ts +55 -0
  12. package/dist/device.js +23 -0
  13. package/dist/device.js.map +1 -0
  14. package/dist/errors.d.ts +6 -0
  15. package/dist/errors.js +20 -0
  16. package/dist/errors.js.map +1 -0
  17. package/dist/host-output-schema.d.ts +2437 -0
  18. package/dist/host-output-schema.js +655 -0
  19. package/dist/host-output-schema.js.map +1 -0
  20. package/dist/protocol-error.d.ts +4 -0
  21. package/dist/protocol-error.js +9 -0
  22. package/dist/protocol-error.js.map +1 -0
  23. package/dist/protocol-management-primitives.d.ts +27 -0
  24. package/dist/protocol-management-primitives.js +51 -0
  25. package/dist/protocol-management-primitives.js.map +1 -0
  26. package/dist/protocol-primitives.d.ts +53 -0
  27. package/dist/protocol-primitives.js +331 -0
  28. package/dist/protocol-primitives.js.map +1 -0
  29. package/dist/protocol.d.ts +207 -0
  30. package/dist/protocol.js +897 -0
  31. package/dist/protocol.js.map +1 -0
  32. package/dist/provider-protocol.d.ts +262 -0
  33. package/dist/provider-protocol.js +637 -0
  34. package/dist/provider-protocol.js.map +1 -0
  35. package/dist/public-error.d.ts +9 -0
  36. package/dist/public-error.js +79 -0
  37. package/dist/public-error.js.map +1 -0
  38. package/dist/safe-text.d.ts +31 -0
  39. package/dist/safe-text.js +143 -0
  40. package/dist/safe-text.js.map +1 -0
  41. package/dist/transport-invariants.d.ts +18 -0
  42. package/dist/transport-invariants.js +24 -0
  43. package/dist/transport-invariants.js.map +1 -0
  44. package/dist/usb.d.ts +76 -0
  45. package/dist/usb.js +454 -0
  46. package/dist/usb.js.map +1 -0
  47. package/package.json +58 -0
@@ -0,0 +1,897 @@
1
+ import { IDENTIFICATION_CODE_PATTERN, MAX_FIRMWARE_NAME_LENGTH, MAX_FIRMWARE_VERSION_LENGTH, MAX_HARDWARE_ID_LENGTH, isDeviceState, isClientName, isProvisioningState, isSafeDeviceId, isSafeRequestId, isSessionId, sanitizeDisplayText, } from "./safe-text.js";
2
+ import { makeConnectRequest as makeProviderConnectRequest, makeDisconnectRequest as makeProviderDisconnectRequest, makeGetAccountsRequest as makeProviderGetAccountsRequest, makeGetCapabilitiesRequest as makeProviderGetCapabilitiesRequest, parseProviderProtocolResponse as parseProviderProtocolResponseCore, validateSignPersonalMessageParams as validateProviderSignPersonalMessageParams, validateSignPersonalMessageInput as validateProviderSignPersonalMessageInput, validateSignTransactionParams as validateProviderSignTransactionParams, validateSignTransactionInput as validateProviderSignTransactionInput, } from "./provider-protocol.js";
3
+ import { ProtocolError } from "./protocol-error.js";
4
+ import { AGENT_Q_POLICY_SCHEMA, APPROVAL_HISTORY_HIGHEST_ACTIONS, APPROVAL_HISTORY_POLICY_UPDATE_RESULTS, APPROVAL_HISTORY_REASON_CODE_PATTERN, APPROVAL_HISTORY_RULE_REF_PATTERN, MAX_APPROVAL_HISTORY_RECORDS, MAX_POLICY_CHAIN_ID_LENGTH, MAX_POLICY_CRITERION_VALUES, MAX_POLICY_FIELD_ID_LENGTH, MAX_POLICY_METHOD_LENGTH, MAX_POLICY_RULE_CRITERIA, MAX_POLICY_RULE_COUNT, MAX_POLICY_RULE_ID_LENGTH, MAX_POLICY_UPDATE_REQUEST_JSON_BYTES, MAX_POLICY_VALUE_LENGTH, POLICY_ACTIONS, POLICY_FIELD_ID_PATTERN, POLICY_ID_PATTERN, POLICY_IDENTIFIER_PATTERN, POLICY_OPERATORS, POLICY_PROPOSE_RESULT_STATUSES, POLICY_RULE_ID_PATTERN, SIGNING_HISTORY_RECORD_KINDS, SIGNING_HISTORY_TERMINAL_RESULTS, SIGN_CHAIN_PATTERN, SIGN_METHOD_PATTERN, UINT_DECIMAL_STRING_PATTERN, } from "./protocol-management-primitives.js";
5
+ import { ED25519_PUBLIC_KEY_BASE64_PATTERN, FORBIDDEN_SECRET_FIELD_NAMES, MAX_ACCOUNTS_PER_RESPONSE, MAX_CAPABILITY_ACCOUNTS_PER_CHAIN, MAX_CAPABILITY_CHAINS, MAX_PROTOCOL_RESPONSE_LINE_BYTES, MAX_RAW_PROTOCOL_JSON_BYTES, MAX_SESSION_TTL_MS, MAX_SIGN_RESULT_PAYLOAD_BASE64_CHARS, MAX_SIGNING_CAPABILITIES, MAX_SUI_SIGN_PERSONAL_MESSAGE_BASE64_CHARS, MAX_SUI_SIGN_PERSONAL_MESSAGE_BYTES, MAX_SUI_SIGN_TRANSACTION_TX_BYTES, MAX_SUI_SIGN_TRANSACTION_TX_BYTES_BASE64_CHARS, PROTOCOL_VERSION, SIGN_RESULT_ERROR_MESSAGES, SUI_ADDRESS_PATTERN, SUI_CHAIN_ID, SUI_DERIVATION_PATH, SUI_ED25519_SIGNATURE_BASE64_PATTERN, SUI_SIGN_PERSONAL_MESSAGE_METHOD, SUI_SIGN_TRANSACTION_METHOD, SUI_SIGN_TRANSACTION_NETWORKS, UNSUPPORTED_METHOD_MESSAGE, consumeProtocolResponseChunk, createRequestId, hasOnlyObjectKeys, hasSecretPayloadKey, isRecord, isSuiAddressForPublicKey, randomBytesPortable, utf8ByteLength, } from "./protocol-primitives.js";
6
+ // These boundary functions are defined once in safe-text.ts (the single source of
7
+ // truth) and re-exported here because protocol.ts is the wire-ingress boundary
8
+ // that applies them; existing importers and tests resolve them via protocol.ts.
9
+ export { isClientName, isSafeDeviceId, isSafeRequestId, isSessionId, sanitizeDisplayText };
10
+ export { AGENT_Q_POLICY_SCHEMA, APPROVAL_HISTORY_HIGHEST_ACTIONS, APPROVAL_HISTORY_POLICY_UPDATE_RESULTS, APPROVAL_HISTORY_REASON_CODE_PATTERN, APPROVAL_HISTORY_RULE_REF_PATTERN, ED25519_PUBLIC_KEY_BASE64_PATTERN, FORBIDDEN_SECRET_FIELD_NAMES, MAX_ACCOUNTS_PER_RESPONSE, MAX_APPROVAL_HISTORY_RECORDS, MAX_CAPABILITY_ACCOUNTS_PER_CHAIN, MAX_CAPABILITY_CHAINS, MAX_POLICY_CHAIN_ID_LENGTH, MAX_POLICY_CRITERION_VALUES, MAX_POLICY_FIELD_ID_LENGTH, MAX_POLICY_METHOD_LENGTH, MAX_POLICY_RULE_CRITERIA, MAX_POLICY_RULE_COUNT, MAX_POLICY_RULE_ID_LENGTH, MAX_POLICY_UPDATE_REQUEST_JSON_BYTES, MAX_POLICY_VALUE_LENGTH, MAX_PROTOCOL_RESPONSE_LINE_BYTES, MAX_RAW_PROTOCOL_JSON_BYTES, MAX_SESSION_TTL_MS, MAX_SIGN_RESULT_PAYLOAD_BASE64_CHARS, MAX_SIGNING_CAPABILITIES, MAX_SUI_SIGN_PERSONAL_MESSAGE_BASE64_CHARS, MAX_SUI_SIGN_PERSONAL_MESSAGE_BYTES, MAX_SUI_SIGN_TRANSACTION_TX_BYTES, MAX_SUI_SIGN_TRANSACTION_TX_BYTES_BASE64_CHARS, POLICY_ACTIONS, POLICY_FIELD_ID_PATTERN, POLICY_ID_PATTERN, POLICY_IDENTIFIER_PATTERN, POLICY_OPERATORS, POLICY_PROPOSE_RESULT_STATUSES, POLICY_RULE_ID_PATTERN, PROTOCOL_VERSION, SIGNING_HISTORY_RECORD_KINDS, SIGNING_HISTORY_TERMINAL_RESULTS, SIGN_CHAIN_PATTERN, SIGN_METHOD_PATTERN, SIGN_RESULT_ERROR_MESSAGES, SUI_ADDRESS_PATTERN, SUI_CHAIN_ID, SUI_DERIVATION_PATH, SUI_ED25519_SIGNATURE_BASE64_PATTERN, SUI_SIGN_PERSONAL_MESSAGE_METHOD, SUI_SIGN_TRANSACTION_METHOD, SUI_SIGN_TRANSACTION_NETWORKS, UINT_DECIMAL_STRING_PATTERN, UNSUPPORTED_METHOD_MESSAGE, consumeProtocolResponseChunk, createRequestId, isSuiAddressForPublicKey, };
11
+ export { ProtocolError };
12
+ // Signing request builders and route identification are owned by
13
+ // provider-protocol because provider adapters and the full protocol share the
14
+ // same top-level signing methods. The full protocol entrypoint re-exports them
15
+ // for compatibility instead of wrapping or reclassifying the route.
16
+ export { identifySignRoute, makeSignPersonalMessageRequest, makeSignTransactionRequest, } from "./provider-protocol.js";
17
+ export function makeGetStatusRequest(id = createRequestId()) {
18
+ validateRequestId(id);
19
+ return {
20
+ id,
21
+ version: PROTOCOL_VERSION,
22
+ type: "get_status",
23
+ };
24
+ }
25
+ export function makeIdentifyDeviceRequest(code, id = createRequestId()) {
26
+ validateRequestId(id);
27
+ if (!isIdentificationCode(code)) {
28
+ throw new ProtocolError("invalid_code", "Invalid identification code.");
29
+ }
30
+ return {
31
+ id,
32
+ version: PROTOCOL_VERSION,
33
+ type: "identify_device",
34
+ params: {
35
+ code,
36
+ },
37
+ };
38
+ }
39
+ export function createIdentificationCode() {
40
+ const bytes = randomBytesPortable(2);
41
+ return ((((bytes[0] ?? 0) << 8) | (bytes[1] ?? 0)) % 10000).toString().padStart(4, "0");
42
+ }
43
+ export const makeConnectRequest = makeProviderConnectRequest;
44
+ export const makeDisconnectRequest = makeProviderDisconnectRequest;
45
+ export const makeGetCapabilitiesRequest = makeProviderGetCapabilitiesRequest;
46
+ export const makeGetAccountsRequest = makeProviderGetAccountsRequest;
47
+ export function makePolicyGetRequest(sessionId, id = createRequestId()) {
48
+ validateRequestId(id);
49
+ if (!isSessionId(sessionId)) {
50
+ throw new ProtocolError("invalid_session", "Invalid sessionId.");
51
+ }
52
+ return {
53
+ id,
54
+ version: PROTOCOL_VERSION,
55
+ type: "policy_get",
56
+ sessionId,
57
+ };
58
+ }
59
+ export function makeGetApprovalHistoryRequest(sessionId, params = {}, id = createRequestId()) {
60
+ validateRequestId(id);
61
+ if (!isSessionId(sessionId)) {
62
+ throw new ProtocolError("invalid_session", "Invalid sessionId.");
63
+ }
64
+ const normalizedParams = validateApprovalHistoryInput(params);
65
+ return {
66
+ id,
67
+ version: PROTOCOL_VERSION,
68
+ type: "get_approval_history",
69
+ sessionId,
70
+ params: normalizedParams,
71
+ };
72
+ }
73
+ export function makePolicyProposeRequest(sessionId, policy, id = createRequestId()) {
74
+ validateRequestId(id);
75
+ if (!isSessionId(sessionId)) {
76
+ throw new ProtocolError("invalid_session", "Invalid sessionId.");
77
+ }
78
+ validatePolicyProposeInput(policy);
79
+ const request = {
80
+ id,
81
+ version: PROTOCOL_VERSION,
82
+ type: "policy_propose",
83
+ sessionId,
84
+ params: {
85
+ policy,
86
+ },
87
+ };
88
+ if (utf8ByteLength(JSON.stringify(request)) > MAX_POLICY_UPDATE_REQUEST_JSON_BYTES) {
89
+ throw new ProtocolError("invalid_params", "policy_propose request is too large for the runtime.");
90
+ }
91
+ return request;
92
+ }
93
+ export function validatePolicyProposeRequestInput(sessionId, policy) {
94
+ makePolicyProposeRequest(sessionId, policy, "req_000000000000000000000000");
95
+ }
96
+ export function validateSignRequestInput(chain, method, params, requestType = "sign") {
97
+ return validateProviderSignTransactionInput(chain, method, params, requestType);
98
+ }
99
+ export function validateSignTransactionParamsInput(params, requestType = "sign_transaction") {
100
+ return validateProviderSignTransactionParams(params, requestType);
101
+ }
102
+ export function validateSignPersonalMessageRequestInput(chain, method, params, requestType = "sign_personal_message") {
103
+ return validateProviderSignPersonalMessageInput(chain, method, params, requestType);
104
+ }
105
+ export function validateSignPersonalMessageParamsInput(params, requestType = "sign_personal_message") {
106
+ return validateProviderSignPersonalMessageParams(params, requestType);
107
+ }
108
+ export function validatePolicyProposeInput(policy) {
109
+ if (!isRecord(policy) || Array.isArray(policy)) {
110
+ throw new ProtocolError("invalid_params", "policy_propose policy must be an object.");
111
+ }
112
+ if (hasSecretPayloadKey(policy)) {
113
+ throw new ProtocolError("invalid_params", "policy_propose policy must not include secret material.");
114
+ }
115
+ let serialized;
116
+ try {
117
+ serialized = JSON.stringify(policy);
118
+ }
119
+ catch {
120
+ throw new ProtocolError("invalid_params", "policy_propose policy must be JSON serializable.");
121
+ }
122
+ if (serialized === undefined) {
123
+ throw new ProtocolError("invalid_params", "policy_propose policy must be JSON serializable.");
124
+ }
125
+ }
126
+ export function validateApprovalHistoryInput(params = {}) {
127
+ const limit = params.limit ?? MAX_APPROVAL_HISTORY_RECORDS;
128
+ if (!Number.isInteger(limit) || limit <= 0 || limit > MAX_APPROVAL_HISTORY_RECORDS) {
129
+ throw new ProtocolError("invalid_params", "approval history limit is invalid.");
130
+ }
131
+ if (params.beforeSeq !== undefined && !isUint64DecimalString(params.beforeSeq)) {
132
+ throw new ProtocolError("invalid_params", "approval history beforeSeq is invalid.");
133
+ }
134
+ return params.beforeSeq === undefined ? { limit } : { limit, beforeSeq: params.beforeSeq };
135
+ }
136
+ export function serializeRequest(request) {
137
+ return `${JSON.stringify(request)}\n`;
138
+ }
139
+ export function parseJsonLine(line) {
140
+ try {
141
+ return JSON.parse(line);
142
+ }
143
+ catch {
144
+ throw new ProtocolError("invalid_json", "Invalid JSON response.");
145
+ }
146
+ }
147
+ export function parseProtocolResponse(line, expectedId) {
148
+ const value = parseJsonLine(line);
149
+ if (!isRecord(value)) {
150
+ throw new ProtocolError("protocol_error", "Protocol response must be an object.");
151
+ }
152
+ if (value.version !== PROTOCOL_VERSION) {
153
+ throw new ProtocolError("incompatible_version", "Unsupported protocol response version.");
154
+ }
155
+ if (expectedId !== undefined && value.id !== expectedId) {
156
+ throw new ProtocolError("protocol_error", "Protocol response id does not match request id.");
157
+ }
158
+ if (isProviderProtocolResponseType(value.type)) {
159
+ return parseProviderProtocolResponse(line, expectedId);
160
+ }
161
+ if (value.type === "status") {
162
+ if (!hasOnlyObjectKeys(value, ["id", "version", "type", "device", "provisioning"])) {
163
+ throw new ProtocolError("protocol_error", "Status response contains unsupported fields.");
164
+ }
165
+ requireWireDeviceStatusShape(value.device, "Status response device object");
166
+ requireWireProvisioningStatusShape(value.provisioning, "Status response provisioning object");
167
+ const device = sanitizeDeviceStatus(value.device);
168
+ if (device === null) {
169
+ throw new ProtocolError("protocol_error", "Status response device object is malformed.");
170
+ }
171
+ const provisioning = sanitizeProvisioningStatus(value.provisioning);
172
+ if (provisioning === null) {
173
+ throw new ProtocolError("protocol_error", "Status response provisioning object is malformed.");
174
+ }
175
+ if (typeof value.id !== "string") {
176
+ throw new ProtocolError("protocol_error", "Status response id is malformed.");
177
+ }
178
+ return {
179
+ id: value.id,
180
+ version: PROTOCOL_VERSION,
181
+ type: "status",
182
+ device,
183
+ provisioning,
184
+ };
185
+ }
186
+ if (value.type === "identify_device_result") {
187
+ if (!hasOnlyObjectKeys(value, ["id", "version", "type", "status", "code", "device"])) {
188
+ throw new ProtocolError("protocol_error", "Identify response contains unsupported fields.");
189
+ }
190
+ requireWireDeviceStatusShape(value.device, "Identify response device object");
191
+ const device = sanitizeDeviceStatus(value.device);
192
+ if (device === null) {
193
+ throw new ProtocolError("protocol_error", "Identify response device object is malformed.");
194
+ }
195
+ if (typeof value.id !== "string" || value.status !== "displayed" || !isIdentificationCode(value.code)) {
196
+ throw new ProtocolError("protocol_error", "Identify response is malformed.");
197
+ }
198
+ return {
199
+ id: value.id,
200
+ version: PROTOCOL_VERSION,
201
+ type: "identify_device_result",
202
+ status: "displayed",
203
+ code: value.code,
204
+ device,
205
+ };
206
+ }
207
+ if (value.type === "policy") {
208
+ if (typeof value.id !== "string") {
209
+ throw new ProtocolError("protocol_error", "Policy response id is malformed.");
210
+ }
211
+ if (hasSecretPayloadKey(value)) {
212
+ throw new ProtocolError("protocol_error", "Policy response must not include secret material.");
213
+ }
214
+ if (!hasOnlyObjectKeys(value, ["id", "version", "type", "policy"])) {
215
+ throw new ProtocolError("protocol_error", "Policy response contains unsupported fields.");
216
+ }
217
+ const policy = sanitizeCurrentPolicyDocument(value.policy);
218
+ if (policy === null) {
219
+ throw new ProtocolError("protocol_error", "Policy response policy object is malformed.");
220
+ }
221
+ return {
222
+ id: value.id,
223
+ version: PROTOCOL_VERSION,
224
+ type: "policy",
225
+ policy,
226
+ };
227
+ }
228
+ if (value.type === "approval_history") {
229
+ if (typeof value.id !== "string") {
230
+ throw new ProtocolError("protocol_error", "Approval history response id is malformed.");
231
+ }
232
+ if (hasSecretPayloadKey(value)) {
233
+ throw new ProtocolError("protocol_error", "Approval history response must not include secret material.");
234
+ }
235
+ if (!hasOnlyObjectKeys(value, ["id", "version", "type", "records", "hasMore"])) {
236
+ throw new ProtocolError("protocol_error", "Approval history response contains unsupported fields.");
237
+ }
238
+ if (!Array.isArray(value.records) || value.records.length > MAX_APPROVAL_HISTORY_RECORDS) {
239
+ throw new ProtocolError("protocol_error", "Approval history response records are malformed.");
240
+ }
241
+ if (typeof value.hasMore !== "boolean") {
242
+ throw new ProtocolError("protocol_error", "Approval history response hasMore is malformed.");
243
+ }
244
+ return {
245
+ id: value.id,
246
+ version: PROTOCOL_VERSION,
247
+ type: "approval_history",
248
+ records: value.records.map((entry) => sanitizeApprovalHistoryRecord(entry)),
249
+ hasMore: value.hasMore,
250
+ };
251
+ }
252
+ if (value.type === "policy_propose_result") {
253
+ if (typeof value.id !== "string") {
254
+ throw new ProtocolError("protocol_error", "policy_propose_result id is malformed.");
255
+ }
256
+ if (hasSecretPayloadKey(value)) {
257
+ throw new ProtocolError("protocol_error", "policy_propose_result must not include secret material.");
258
+ }
259
+ if (!hasOnlyObjectKeys(value, ["id", "version", "type", "status", "reasonCode", "policy"])) {
260
+ throw new ProtocolError("protocol_error", "policy_propose_result contains unsupported fields.");
261
+ }
262
+ if (!POLICY_PROPOSE_RESULT_STATUSES.includes(value.status) ||
263
+ typeof value.reasonCode !== "string" ||
264
+ !APPROVAL_HISTORY_REASON_CODE_PATTERN.test(value.reasonCode)) {
265
+ throw new ProtocolError("protocol_error", "policy_propose_result is malformed.");
266
+ }
267
+ const policy = sanitizePolicyProposeResultPolicy(value.policy);
268
+ if (value.status === "invalid_policy") {
269
+ if (policy !== undefined) {
270
+ throw new ProtocolError("protocol_error", "Invalid policy result must not include policy metadata.");
271
+ }
272
+ }
273
+ else if (policy === undefined) {
274
+ throw new ProtocolError("protocol_error", "policy_propose_result policy metadata is malformed.");
275
+ }
276
+ return {
277
+ id: value.id,
278
+ version: PROTOCOL_VERSION,
279
+ type: "policy_propose_result",
280
+ status: value.status,
281
+ reasonCode: value.reasonCode,
282
+ ...(policy !== undefined ? { policy } : {}),
283
+ };
284
+ }
285
+ throw new ProtocolError("protocol_error", "Protocol response type is unsupported.");
286
+ }
287
+ export function parseProviderProtocolResponse(line, expectedId) {
288
+ return parseProviderProtocolResponseCore(line, expectedId);
289
+ }
290
+ function isProviderProtocolResponseType(value) {
291
+ return (value === "error" ||
292
+ value === "connect_result" ||
293
+ value === "disconnect_result" ||
294
+ value === "capabilities" ||
295
+ value === "accounts" ||
296
+ value === "sign_result");
297
+ }
298
+ export function assertStatusResponse(response) {
299
+ if (response.type === "error") {
300
+ throw new ProtocolError(response.error.code, response.error.message);
301
+ }
302
+ if (response.type !== "status") {
303
+ throw new ProtocolError("protocol_error", "Protocol response type is not status.");
304
+ }
305
+ return response;
306
+ }
307
+ export function assertIdentifyDeviceResponse(response) {
308
+ if (response.type === "error") {
309
+ throw new ProtocolError(response.error.code, response.error.message);
310
+ }
311
+ if (response.type !== "identify_device_result") {
312
+ throw new ProtocolError("protocol_error", "Protocol response type is not identify_device_result.");
313
+ }
314
+ return response;
315
+ }
316
+ export function assertConnectResponse(response) {
317
+ if (response.type === "error") {
318
+ throw new ProtocolError(response.error.code, response.error.message);
319
+ }
320
+ if (response.type !== "connect_result") {
321
+ throw new ProtocolError("protocol_error", "Protocol response type is not connect_result.");
322
+ }
323
+ return response;
324
+ }
325
+ export function assertDisconnectResponse(response) {
326
+ if (response.type === "error") {
327
+ throw new ProtocolError(response.error.code, response.error.message);
328
+ }
329
+ if (response.type !== "disconnect_result") {
330
+ throw new ProtocolError("protocol_error", "Protocol response type is not disconnect_result.");
331
+ }
332
+ return response;
333
+ }
334
+ export function assertCapabilitiesResponse(response) {
335
+ if (response.type === "error") {
336
+ throw new ProtocolError(response.error.code, response.error.message);
337
+ }
338
+ if (response.type !== "capabilities") {
339
+ throw new ProtocolError("protocol_error", "Protocol response type is not capabilities.");
340
+ }
341
+ return response;
342
+ }
343
+ export function assertAccountsResponse(response) {
344
+ if (response.type === "error") {
345
+ throw new ProtocolError(response.error.code, response.error.message);
346
+ }
347
+ if (response.type !== "accounts") {
348
+ throw new ProtocolError("protocol_error", "Protocol response type is not accounts.");
349
+ }
350
+ return response;
351
+ }
352
+ export function assertPolicyResponse(response) {
353
+ if (response.type === "error") {
354
+ throw new ProtocolError(response.error.code, response.error.message);
355
+ }
356
+ if (response.type !== "policy") {
357
+ throw new ProtocolError("protocol_error", "Protocol response type is not policy.");
358
+ }
359
+ return response;
360
+ }
361
+ export function assertApprovalHistoryResponse(response) {
362
+ if (response.type === "error") {
363
+ throw new ProtocolError(response.error.code, response.error.message);
364
+ }
365
+ if (response.type !== "approval_history") {
366
+ throw new ProtocolError("protocol_error", "Protocol response type is not approval_history.");
367
+ }
368
+ return response;
369
+ }
370
+ export function assertPolicyProposeResultResponse(response) {
371
+ if (response.type === "error") {
372
+ throw new ProtocolError(response.error.code, response.error.message);
373
+ }
374
+ if (response.type !== "policy_propose_result") {
375
+ throw new ProtocolError("protocol_error", "Protocol response type is not policy_propose_result.");
376
+ }
377
+ return response;
378
+ }
379
+ export function assertSignResultResponse(response) {
380
+ if (response.type === "error") {
381
+ throw new ProtocolError(response.error.code, response.error.message);
382
+ }
383
+ if (response.type !== "sign_result") {
384
+ throw new ProtocolError("protocol_error", "Protocol response type is not sign_result.");
385
+ }
386
+ return response;
387
+ }
388
+ // Reduce an untrusted wire or stored device object to a safe DeviceStatus, or
389
+ // null when its identity fields are unusable. deviceId and state are REJECTED
390
+ // when malformed (returns null); the display strings are sanitized to bounded
391
+ // printable ASCII. Shared by the wire boundary (above) and the disk boundary
392
+ // (config.ts) so both apply the identical policy.
393
+ export function sanitizeDeviceStatus(value) {
394
+ if (!isRecord(value) || !isSafeDeviceId(value.deviceId) || !isDeviceState(value.state)) {
395
+ return null;
396
+ }
397
+ return {
398
+ deviceId: value.deviceId,
399
+ state: value.state,
400
+ firmwareName: sanitizeDisplayText(value.firmwareName, MAX_FIRMWARE_NAME_LENGTH),
401
+ hardware: sanitizeDisplayText(value.hardware, MAX_HARDWARE_ID_LENGTH),
402
+ firmwareVersion: sanitizeDisplayText(value.firmwareVersion, MAX_FIRMWARE_VERSION_LENGTH),
403
+ };
404
+ }
405
+ export function sanitizeProvisioningStatus(value) {
406
+ if (!isRecord(value) || !isProvisioningState(value.state)) {
407
+ return null;
408
+ }
409
+ return { state: value.state };
410
+ }
411
+ export function isSignChain(value) {
412
+ return typeof value === "string" && SIGN_CHAIN_PATTERN.test(value);
413
+ }
414
+ export function isSignMethod(value) {
415
+ return typeof value === "string" && SIGN_METHOD_PATTERN.test(value);
416
+ }
417
+ export function isUint64DecimalString(value) {
418
+ if (typeof value !== "string" || !UINT_DECIMAL_STRING_PATTERN.test(value)) {
419
+ return false;
420
+ }
421
+ return value.length < UINT64_MAX_DECIMAL.length ||
422
+ (value.length === UINT64_MAX_DECIMAL.length && value <= UINT64_MAX_DECIMAL);
423
+ }
424
+ const UINT64_MAX_DECIMAL = "18446744073709551615";
425
+ function isBoundedPolicyString(value, maxLength) {
426
+ return typeof value === "string" && value.length > 0 && value.length <= maxLength;
427
+ }
428
+ function isPolicyIdentifierString(value, maxLength) {
429
+ return isBoundedPolicyString(value, maxLength) && POLICY_IDENTIFIER_PATTERN.test(value);
430
+ }
431
+ const COMMON_POLICY_FIELDS = {
432
+ "common.chain": { type: "string", allowEq: true, allowIn: true, allowLte: false },
433
+ "common.method": { type: "string", allowEq: true, allowIn: true, allowLte: false },
434
+ "common.intent": { type: "string", allowEq: true, allowIn: true, allowLte: false },
435
+ };
436
+ const SUI_SIGN_TRANSACTION_POLICY_FIELDS = {
437
+ "sui.command_shape": { type: "string", allowEq: true, allowIn: true, allowLte: false },
438
+ "sui.sender_address": { type: "string", allowEq: true, allowIn: true, allowLte: false },
439
+ "sui.gas_owner_address": { type: "string", allowEq: true, allowIn: true, allowLte: false },
440
+ "sui.command_count": { type: "u64_decimal", allowEq: true, allowIn: false, allowLte: true },
441
+ "sui.command0_kind": { type: "string", allowEq: true, allowIn: true, allowLte: false },
442
+ "sui.command1_kind": { type: "string", allowEq: true, allowIn: true, allowLte: false },
443
+ "sui.recipient_address": { type: "string", allowEq: true, allowIn: true, allowLte: false },
444
+ "sui.coin_type": { type: "string", allowEq: true, allowIn: true, allowLte: false },
445
+ "sui.amount_raw": { type: "u64_decimal", allowEq: true, allowIn: false, allowLte: true },
446
+ "sui.gas_budget": { type: "u64_decimal", allowEq: true, allowIn: false, allowLte: true },
447
+ "sui.gas_price": { type: "u64_decimal", allowEq: true, allowIn: false, allowLte: true },
448
+ };
449
+ function policyRuleHasSupportedMethod(rule) {
450
+ return rule.chain === SUI_CHAIN_ID && rule.method === SUI_SIGN_TRANSACTION_METHOD;
451
+ }
452
+ function findPolicyFieldDescriptor(rule, field) {
453
+ if (!policyRuleHasSupportedMethod(rule)) {
454
+ return null;
455
+ }
456
+ return COMMON_POLICY_FIELDS[field] ?? SUI_SIGN_TRANSACTION_POLICY_FIELDS[field] ?? null;
457
+ }
458
+ function policyOperatorAllowed(descriptor, op) {
459
+ switch (op) {
460
+ case "eq":
461
+ return descriptor.allowEq;
462
+ case "in":
463
+ return descriptor.allowIn;
464
+ case "lte":
465
+ return descriptor.allowLte;
466
+ }
467
+ }
468
+ function policyCriterionMatchesDescriptor(rule, criterion) {
469
+ const descriptor = findPolicyFieldDescriptor(rule, criterion.field);
470
+ if (descriptor === null || !policyOperatorAllowed(descriptor, criterion.op)) {
471
+ return false;
472
+ }
473
+ if (criterion.op === "in") {
474
+ if (criterion.values === undefined || criterion.value !== undefined) {
475
+ return false;
476
+ }
477
+ return descriptor.type !== "u64_decimal" ||
478
+ criterion.values.every((value) => isUint64DecimalString(value));
479
+ }
480
+ if (criterion.value === undefined || criterion.values !== undefined) {
481
+ return false;
482
+ }
483
+ return descriptor.type !== "u64_decimal" || isUint64DecimalString(criterion.value);
484
+ }
485
+ function policyCriterionEq(criterion, field, value) {
486
+ return criterion.field === field &&
487
+ criterion.op === "eq" &&
488
+ criterion.value === value;
489
+ }
490
+ function policyCriterionLte(criterion, field) {
491
+ return criterion.field === field &&
492
+ criterion.op === "lte" &&
493
+ criterion.value !== undefined &&
494
+ isUint64DecimalString(criterion.value);
495
+ }
496
+ function policyCriterionU64Eq(criterion, field, value) {
497
+ return policyCriterionEq(criterion, field, value) &&
498
+ criterion.value !== undefined &&
499
+ isUint64DecimalString(criterion.value);
500
+ }
501
+ function policyRecipientIsBounded(criterion) {
502
+ if (criterion.field !== "sui.recipient_address") {
503
+ return false;
504
+ }
505
+ if (criterion.op === "eq") {
506
+ return criterion.value !== undefined && criterion.value.length > 0;
507
+ }
508
+ return criterion.op === "in" &&
509
+ criterion.values !== undefined &&
510
+ criterion.values.length === 1;
511
+ }
512
+ function policySignRuleIsBounded(rule) {
513
+ if (rule.action !== "sign") {
514
+ return true;
515
+ }
516
+ if (rule.chain !== SUI_CHAIN_ID ||
517
+ rule.method !== SUI_SIGN_TRANSACTION_METHOD ||
518
+ rule.criteria.length === 0) {
519
+ return false;
520
+ }
521
+ let hasIntent = false;
522
+ let hasShape = false;
523
+ let hasAsset = false;
524
+ let hasCommandCount = false;
525
+ let hasCommand0Kind = false;
526
+ let hasCommand1Kind = false;
527
+ let hasRecipient = false;
528
+ let hasAmountBound = false;
529
+ let hasGasBudgetBound = false;
530
+ let hasGasPriceBound = false;
531
+ for (const criterion of rule.criteria) {
532
+ hasIntent = hasIntent || policyCriterionEq(criterion, "common.intent", "single_asset_transfer");
533
+ hasShape = hasShape || policyCriterionEq(criterion, "sui.command_shape", "restricted_transfer");
534
+ hasAsset = hasAsset || policyCriterionEq(criterion, "sui.coin_type", "0x2::sui::SUI");
535
+ hasCommandCount = hasCommandCount || policyCriterionU64Eq(criterion, "sui.command_count", "2");
536
+ hasCommand0Kind = hasCommand0Kind || policyCriterionEq(criterion, "sui.command0_kind", "split_coins");
537
+ hasCommand1Kind = hasCommand1Kind || policyCriterionEq(criterion, "sui.command1_kind", "transfer_objects");
538
+ hasRecipient = hasRecipient || policyRecipientIsBounded(criterion);
539
+ hasAmountBound = hasAmountBound || policyCriterionLte(criterion, "sui.amount_raw");
540
+ hasGasBudgetBound = hasGasBudgetBound || policyCriterionLte(criterion, "sui.gas_budget");
541
+ hasGasPriceBound = hasGasPriceBound || policyCriterionLte(criterion, "sui.gas_price");
542
+ }
543
+ return hasIntent &&
544
+ hasShape &&
545
+ hasAsset &&
546
+ hasCommandCount &&
547
+ hasCommand0Kind &&
548
+ hasCommand1Kind &&
549
+ hasRecipient &&
550
+ hasAmountBound &&
551
+ hasGasBudgetBound &&
552
+ hasGasPriceBound;
553
+ }
554
+ function sanitizePolicyCriterion(value) {
555
+ if (!isRecord(value)) {
556
+ return null;
557
+ }
558
+ if (hasSecretPayloadKey(value)) {
559
+ throw new ProtocolError("protocol_error", "Policy criterion must not include secret material.");
560
+ }
561
+ if (!isBoundedPolicyString(value.field, MAX_POLICY_FIELD_ID_LENGTH) ||
562
+ !POLICY_FIELD_ID_PATTERN.test(value.field) ||
563
+ typeof value.op !== "string" ||
564
+ !POLICY_OPERATORS.includes(value.op)) {
565
+ return null;
566
+ }
567
+ const op = value.op;
568
+ if (op === "in") {
569
+ if (!hasOnlyObjectKeys(value, ["field", "op", "values"])) {
570
+ throw new ProtocolError("protocol_error", "Policy criterion contains unsupported fields.");
571
+ }
572
+ if (!Array.isArray(value.values) ||
573
+ value.values.length === 0 ||
574
+ value.values.length > MAX_POLICY_CRITERION_VALUES ||
575
+ !value.values.every((item) => isBoundedPolicyString(item, MAX_POLICY_VALUE_LENGTH))) {
576
+ return null;
577
+ }
578
+ return {
579
+ field: value.field,
580
+ op,
581
+ values: [...value.values],
582
+ };
583
+ }
584
+ if (!hasOnlyObjectKeys(value, ["field", "op", "value"])) {
585
+ throw new ProtocolError("protocol_error", "Policy criterion contains unsupported fields.");
586
+ }
587
+ if (!isBoundedPolicyString(value.value, MAX_POLICY_VALUE_LENGTH)) {
588
+ return null;
589
+ }
590
+ return {
591
+ field: value.field,
592
+ op,
593
+ value: value.value,
594
+ };
595
+ }
596
+ function sanitizePolicyRule(value) {
597
+ if (!isRecord(value)) {
598
+ return null;
599
+ }
600
+ if (hasSecretPayloadKey(value)) {
601
+ throw new ProtocolError("protocol_error", "Policy rule must not include secret material.");
602
+ }
603
+ if (!hasOnlyObjectKeys(value, ["id", "chain", "method", "action", "criteria"])) {
604
+ throw new ProtocolError("protocol_error", "Policy rule contains unsupported fields.");
605
+ }
606
+ if (!isBoundedPolicyString(value.id, MAX_POLICY_RULE_ID_LENGTH) ||
607
+ !POLICY_RULE_ID_PATTERN.test(value.id) ||
608
+ !isPolicyIdentifierString(value.chain, MAX_POLICY_CHAIN_ID_LENGTH) ||
609
+ !isPolicyIdentifierString(value.method, MAX_POLICY_METHOD_LENGTH) ||
610
+ typeof value.action !== "string" ||
611
+ !POLICY_ACTIONS.includes(value.action) ||
612
+ !Array.isArray(value.criteria) ||
613
+ value.criteria.length > MAX_POLICY_RULE_CRITERIA) {
614
+ return null;
615
+ }
616
+ const criteria = value.criteria.map((criterion) => sanitizePolicyCriterion(criterion));
617
+ if (criteria.some((criterion) => criterion === null)) {
618
+ return null;
619
+ }
620
+ const rule = {
621
+ id: value.id,
622
+ chain: value.chain,
623
+ method: value.method,
624
+ action: value.action,
625
+ criteria: criteria,
626
+ };
627
+ if (!policyRuleHasSupportedMethod(rule) ||
628
+ !rule.criteria.every((criterion) => policyCriterionMatchesDescriptor(rule, criterion)) ||
629
+ !policySignRuleIsBounded(rule)) {
630
+ return null;
631
+ }
632
+ return rule;
633
+ }
634
+ export function sanitizeCurrentPolicyDocument(value) {
635
+ if (!isRecord(value)) {
636
+ return null;
637
+ }
638
+ if (hasSecretPayloadKey(value)) {
639
+ throw new ProtocolError("protocol_error", "Policy document must not include secret material.");
640
+ }
641
+ if (!hasOnlyObjectKeys(value, ["schema", "policyId", "defaultAction", "ruleCount", "rules"])) {
642
+ throw new ProtocolError("protocol_error", "Policy document contains unsupported fields.");
643
+ }
644
+ if (value.schema !== AGENT_Q_POLICY_SCHEMA ||
645
+ typeof value.policyId !== "string" ||
646
+ !POLICY_ID_PATTERN.test(value.policyId) ||
647
+ value.defaultAction !== "reject" ||
648
+ typeof value.ruleCount !== "number" ||
649
+ !Number.isInteger(value.ruleCount) ||
650
+ value.ruleCount < 0 ||
651
+ value.ruleCount > MAX_POLICY_RULE_COUNT ||
652
+ !Array.isArray(value.rules) ||
653
+ value.rules.length !== value.ruleCount) {
654
+ return null;
655
+ }
656
+ const rules = value.rules.map((rule) => sanitizePolicyRule(rule));
657
+ if (rules.some((rule) => rule === null)) {
658
+ return null;
659
+ }
660
+ const signRuleCount = rules.filter((rule) => rule?.action === "sign").length;
661
+ if (signRuleCount > 1) {
662
+ return null;
663
+ }
664
+ return {
665
+ schema: value.schema,
666
+ policyId: value.policyId,
667
+ defaultAction: value.defaultAction,
668
+ ruleCount: value.ruleCount,
669
+ rules: rules,
670
+ };
671
+ }
672
+ function sanitizePolicyProposeResultPolicy(value) {
673
+ if (value === undefined) {
674
+ return undefined;
675
+ }
676
+ if (!isRecord(value)) {
677
+ throw new ProtocolError("protocol_error", "policy_propose_result policy metadata is malformed.");
678
+ }
679
+ if (hasSecretPayloadKey(value)) {
680
+ throw new ProtocolError("protocol_error", "policy_propose_result policy metadata must not include secret material.");
681
+ }
682
+ if (!hasOnlyObjectKeys(value, ["policyHash", "ruleCount", "highestAction"])) {
683
+ throw new ProtocolError("protocol_error", "policy_propose_result policy metadata contains unsupported fields.");
684
+ }
685
+ if (typeof value.policyHash !== "string" ||
686
+ !POLICY_ID_PATTERN.test(value.policyHash) ||
687
+ typeof value.ruleCount !== "number" ||
688
+ !Number.isInteger(value.ruleCount) ||
689
+ value.ruleCount < 0 ||
690
+ value.ruleCount > MAX_POLICY_RULE_COUNT ||
691
+ !APPROVAL_HISTORY_HIGHEST_ACTIONS.includes(value.highestAction)) {
692
+ throw new ProtocolError("protocol_error", "policy_propose_result policy metadata is malformed.");
693
+ }
694
+ return {
695
+ policyHash: value.policyHash,
696
+ ruleCount: value.ruleCount,
697
+ highestAction: value.highestAction,
698
+ };
699
+ }
700
+ function sanitizeApprovalHistoryRecord(value) {
701
+ if (!isRecord(value)) {
702
+ throw new ProtocolError("protocol_error", "Approval history record is malformed.");
703
+ }
704
+ if (hasSecretPayloadKey(value)) {
705
+ throw new ProtocolError("protocol_error", "Approval history record must not include secret material.");
706
+ }
707
+ const allowedKeys = [
708
+ "seq",
709
+ "uptimeMs",
710
+ "timeSource",
711
+ "eventKind",
712
+ "confirmationKind",
713
+ "authorization",
714
+ "chain",
715
+ "method",
716
+ "reasonCode",
717
+ "payloadDigest",
718
+ "policyHash",
719
+ "ruleRef",
720
+ "result",
721
+ "ruleCount",
722
+ "highestAction",
723
+ "recordKind",
724
+ "terminalResult",
725
+ ];
726
+ if (!hasOnlyObjectKeys(value, allowedKeys)) {
727
+ throw new ProtocolError("protocol_error", "Approval history record contains unsupported fields.");
728
+ }
729
+ if (typeof value.seq !== "string" ||
730
+ !isUint64DecimalString(value.seq) ||
731
+ typeof value.uptimeMs !== "string" ||
732
+ !isUint64DecimalString(value.uptimeMs) ||
733
+ value.timeSource !== "uptime" ||
734
+ typeof value.reasonCode !== "string" ||
735
+ !APPROVAL_HISTORY_REASON_CODE_PATTERN.test(value.reasonCode)) {
736
+ throw new ProtocolError("protocol_error", "Approval history record is malformed.");
737
+ }
738
+ if (value.eventKind === "policy_update") {
739
+ if (value.confirmationKind !== undefined ||
740
+ value.chain !== undefined ||
741
+ value.method !== undefined ||
742
+ value.payloadDigest !== undefined ||
743
+ value.ruleRef !== undefined ||
744
+ value.recordKind !== undefined ||
745
+ value.terminalResult !== undefined ||
746
+ !APPROVAL_HISTORY_POLICY_UPDATE_RESULTS.includes(value.result) ||
747
+ typeof value.policyHash !== "string" ||
748
+ !POLICY_ID_PATTERN.test(value.policyHash) ||
749
+ typeof value.ruleCount !== "number" ||
750
+ !Number.isInteger(value.ruleCount) ||
751
+ value.ruleCount < 0 ||
752
+ value.ruleCount > MAX_POLICY_RULE_COUNT ||
753
+ !APPROVAL_HISTORY_HIGHEST_ACTIONS.includes(value.highestAction)) {
754
+ throw new ProtocolError("protocol_error", "Approval history policy update record is malformed.");
755
+ }
756
+ return {
757
+ seq: value.seq,
758
+ uptimeMs: value.uptimeMs,
759
+ timeSource: "uptime",
760
+ eventKind: "policy_update",
761
+ reasonCode: value.reasonCode,
762
+ result: value.result,
763
+ policyHash: value.policyHash,
764
+ ruleCount: value.ruleCount,
765
+ highestAction: value.highestAction,
766
+ };
767
+ }
768
+ if (value.eventKind === "signing") {
769
+ if (value.result !== undefined ||
770
+ value.ruleCount !== undefined ||
771
+ value.highestAction !== undefined ||
772
+ !SIGNING_HISTORY_RECORD_KINDS.includes(value.recordKind) ||
773
+ (value.authorization !== "user" && value.authorization !== "policy") ||
774
+ !isSignChain(value.chain) ||
775
+ !isSignMethod(value.method) ||
776
+ typeof value.payloadDigest !== "string" ||
777
+ !POLICY_ID_PATTERN.test(value.payloadDigest)) {
778
+ throw new ProtocolError("protocol_error", "Approval history signing record is malformed.");
779
+ }
780
+ if (value.recordKind === "confirmation") {
781
+ if (value.terminalResult !== undefined) {
782
+ throw new ProtocolError("protocol_error", "Approval history signing record is malformed.");
783
+ }
784
+ if (value.authorization === "user") {
785
+ if ((value.confirmationKind !== "local_pin" &&
786
+ value.confirmationKind !== "physical_confirm") ||
787
+ value.policyHash !== undefined ||
788
+ value.ruleRef !== undefined) {
789
+ throw new ProtocolError("protocol_error", "Approval history signing record is malformed.");
790
+ }
791
+ return {
792
+ seq: value.seq,
793
+ uptimeMs: value.uptimeMs,
794
+ timeSource: "uptime",
795
+ eventKind: "signing",
796
+ reasonCode: value.reasonCode,
797
+ recordKind: "confirmation",
798
+ authorization: "user",
799
+ confirmationKind: value.confirmationKind,
800
+ chain: value.chain,
801
+ method: value.method,
802
+ payloadDigest: value.payloadDigest,
803
+ };
804
+ }
805
+ if (value.confirmationKind !== "policy" ||
806
+ typeof value.policyHash !== "string" ||
807
+ !POLICY_ID_PATTERN.test(value.policyHash) ||
808
+ typeof value.ruleRef !== "string" ||
809
+ !APPROVAL_HISTORY_RULE_REF_PATTERN.test(value.ruleRef)) {
810
+ throw new ProtocolError("protocol_error", "Approval history signing policy metadata is malformed.");
811
+ }
812
+ return {
813
+ seq: value.seq,
814
+ uptimeMs: value.uptimeMs,
815
+ timeSource: "uptime",
816
+ eventKind: "signing",
817
+ reasonCode: value.reasonCode,
818
+ recordKind: "confirmation",
819
+ authorization: "policy",
820
+ confirmationKind: "policy",
821
+ chain: value.chain,
822
+ method: value.method,
823
+ payloadDigest: value.payloadDigest,
824
+ policyHash: value.policyHash,
825
+ ruleRef: value.ruleRef,
826
+ };
827
+ }
828
+ if (value.confirmationKind !== undefined ||
829
+ !SIGNING_HISTORY_TERMINAL_RESULTS.includes(value.terminalResult)) {
830
+ throw new ProtocolError("protocol_error", "Approval history signing record is malformed.");
831
+ }
832
+ const terminalResult = value.terminalResult;
833
+ if (value.authorization === "policy") {
834
+ if (!["signed", "policy_rejected", "signing_failed"].includes(terminalResult) ||
835
+ typeof value.policyHash !== "string" ||
836
+ !POLICY_ID_PATTERN.test(value.policyHash) ||
837
+ typeof value.ruleRef !== "string" ||
838
+ !APPROVAL_HISTORY_RULE_REF_PATTERN.test(value.ruleRef)) {
839
+ throw new ProtocolError("protocol_error", "Approval history signing policy metadata is malformed.");
840
+ }
841
+ }
842
+ else if (!["signed", "user_rejected", "user_timed_out", "signing_failed"].includes(terminalResult) ||
843
+ value.policyHash !== undefined ||
844
+ value.ruleRef !== undefined) {
845
+ throw new ProtocolError("protocol_error", "Approval history signing policy metadata is malformed.");
846
+ }
847
+ return {
848
+ seq: value.seq,
849
+ uptimeMs: value.uptimeMs,
850
+ timeSource: "uptime",
851
+ eventKind: "signing",
852
+ reasonCode: value.reasonCode,
853
+ recordKind: "terminal",
854
+ authorization: value.authorization,
855
+ terminalResult,
856
+ chain: value.chain,
857
+ method: value.method,
858
+ payloadDigest: value.payloadDigest,
859
+ ...(value.authorization === "policy" ? {
860
+ policyHash: value.policyHash,
861
+ ruleRef: value.ruleRef,
862
+ } : {}),
863
+ };
864
+ }
865
+ throw new ProtocolError("protocol_error", "Approval history record is malformed.");
866
+ }
867
+ export function sanitizeDeviceStatusSnapshot(value) {
868
+ if (!isRecord(value)) {
869
+ return null;
870
+ }
871
+ const device = sanitizeDeviceStatus(value.device);
872
+ const provisioning = sanitizeProvisioningStatus(value.provisioning);
873
+ if (device === null || provisioning === null) {
874
+ return null;
875
+ }
876
+ return { device, provisioning };
877
+ }
878
+ function isIdentificationCode(value) {
879
+ return typeof value === "string" && IDENTIFICATION_CODE_PATTERN.test(value);
880
+ }
881
+ function validateRequestId(id) {
882
+ if (!isSafeRequestId(id)) {
883
+ throw new ProtocolError("invalid_id", "Invalid request id.");
884
+ }
885
+ }
886
+ function requireWireDeviceStatusShape(value, label) {
887
+ if (!isRecord(value) ||
888
+ !hasOnlyObjectKeys(value, ["deviceId", "state", "firmwareName", "hardware", "firmwareVersion"])) {
889
+ throw new ProtocolError("protocol_error", `${label} contains unsupported fields.`);
890
+ }
891
+ }
892
+ function requireWireProvisioningStatusShape(value, label) {
893
+ if (!isRecord(value) || !hasOnlyObjectKeys(value, ["state"])) {
894
+ throw new ProtocolError("protocol_error", `${label} contains unsupported fields.`);
895
+ }
896
+ }
897
+ //# sourceMappingURL=protocol.js.map