@canonmsg/backend-contracts 2.2.0 → 3.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.
@@ -212,9 +212,68 @@
212
212
  ]
213
213
  },
214
214
  "sessionConfig": {
215
- "type": [
216
- "object",
217
- "null"
215
+ "oneOf": [
216
+ {
217
+ "type": "object",
218
+ "description": "Typed setup policy for an agent target. Values are stable runtime-advertised option ids and are validated against the target descriptor before session creation; labels, prompts, workspace paths, and arbitrary fields are not permitted.",
219
+ "additionalProperties": false,
220
+ "properties": {
221
+ "model": {
222
+ "type": "string",
223
+ "pattern": "^[A-Za-z0-9@_][A-Za-z0-9_.:/@+\\-]{0,255}$",
224
+ "maxLength": 256
225
+ },
226
+ "permissionMode": {
227
+ "type": "string",
228
+ "pattern": "^[A-Za-z0-9@_][A-Za-z0-9_.:/@+\\-]{0,255}$",
229
+ "maxLength": 256
230
+ },
231
+ "effort": {
232
+ "type": "string",
233
+ "pattern": "^[A-Za-z0-9@_][A-Za-z0-9_.:/@+\\-]{0,255}$",
234
+ "maxLength": 256
235
+ },
236
+ "workspaceId": {
237
+ "type": "string",
238
+ "pattern": "^[A-Za-z0-9@_][A-Za-z0-9_.:@+\\-]{0,255}$",
239
+ "maxLength": 256
240
+ },
241
+ "executionMode": {
242
+ "enum": [
243
+ "worktree",
244
+ "locked"
245
+ ]
246
+ },
247
+ "runtimeControlValues": {
248
+ "type": "object",
249
+ "maxProperties": 24,
250
+ "propertyNames": {
251
+ "pattern": "^[A-Za-z0-9_-]{1,80}$",
252
+ "not": {
253
+ "enum": [
254
+ "model",
255
+ "workspace",
256
+ "executionMode",
257
+ "permissionMode",
258
+ "effort",
259
+ "interrupt",
260
+ "runtimeControlValues",
261
+ "updatedAt",
262
+ "updatedBy"
263
+ ]
264
+ }
265
+ },
266
+ "additionalProperties": {
267
+ "type": "string",
268
+ "pattern": "^[A-Za-z0-9@_][A-Za-z0-9_.:/@+\\-]{0,255}$",
269
+ "maxLength": 256
270
+ }
271
+ }
272
+ }
273
+ },
274
+ {
275
+ "type": "null"
276
+ }
218
277
  ]
219
278
  },
220
279
  "idempotencyKey": {
@@ -253,7 +312,99 @@
253
312
  "$ref": "#/$defs/turn"
254
313
  },
255
314
  "native": {
256
- "type": "object"
315
+ "type": "object",
316
+ "description": "Content-free runtime correlation ids. Commands, paths, prompts, summaries, and model labels are not permitted in this public envelope.",
317
+ "maxProperties": 24,
318
+ "additionalProperties": false,
319
+ "properties": {
320
+ "runtime": {
321
+ "type": "string",
322
+ "pattern": "^[A-Za-z0-9@_][A-Za-z0-9_.:@+\\-]{0,255}$",
323
+ "maxLength": 256
324
+ },
325
+ "method": {
326
+ "type": "string",
327
+ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_./:\\-]{0,255}$",
328
+ "maxLength": 256
329
+ },
330
+ "requestId": {
331
+ "type": "string",
332
+ "pattern": "^[A-Za-z0-9@_][A-Za-z0-9_.:@+\\-]{0,255}$",
333
+ "maxLength": 256
334
+ },
335
+ "provider": {
336
+ "type": "string",
337
+ "pattern": "^[A-Za-z0-9@_][A-Za-z0-9_.:@+\\-]{0,255}$",
338
+ "maxLength": 256
339
+ },
340
+ "origin": {
341
+ "type": "string",
342
+ "pattern": "^[A-Za-z0-9@_][A-Za-z0-9_.:@+\\-]{0,255}$",
343
+ "maxLength": 256
344
+ },
345
+ "surface": {
346
+ "type": "string",
347
+ "pattern": "^[A-Za-z0-9@_][A-Za-z0-9_.:@+\\-]{0,255}$",
348
+ "maxLength": 256
349
+ },
350
+ "threadId": {
351
+ "type": "string",
352
+ "pattern": "^[A-Za-z0-9@_][A-Za-z0-9_.:@+\\-]{0,255}$",
353
+ "maxLength": 256
354
+ },
355
+ "turnId": {
356
+ "type": "string",
357
+ "pattern": "^[A-Za-z0-9@_][A-Za-z0-9_.:@+\\-]{0,255}$",
358
+ "maxLength": 256
359
+ },
360
+ "runId": {
361
+ "type": "string",
362
+ "pattern": "^[A-Za-z0-9@_][A-Za-z0-9_.:@+\\-]{0,255}$",
363
+ "maxLength": 256
364
+ },
365
+ "itemId": {
366
+ "type": "string",
367
+ "pattern": "^[A-Za-z0-9@_][A-Za-z0-9_.:@+\\-]{0,255}$",
368
+ "maxLength": 256
369
+ },
370
+ "toolCallId": {
371
+ "type": "string",
372
+ "pattern": "^[A-Za-z0-9@_][A-Za-z0-9_.:@+\\-]{0,255}$",
373
+ "maxLength": 256
374
+ },
375
+ "approvalId": {
376
+ "type": "string",
377
+ "pattern": "^[A-Za-z0-9@_][A-Za-z0-9_.:@+\\-]{0,255}$",
378
+ "maxLength": 256
379
+ },
380
+ "pluginId": {
381
+ "type": "string",
382
+ "pattern": "^[A-Za-z0-9@_][A-Za-z0-9_.:@+\\-]{0,255}$",
383
+ "maxLength": 256
384
+ },
385
+ "sessionKey": {
386
+ "type": "string",
387
+ "pattern": "^[A-Za-z0-9@_][A-Za-z0-9_.:@+\\-]{0,255}$",
388
+ "maxLength": 256
389
+ },
390
+ "nodeId": {
391
+ "type": "string",
392
+ "pattern": "^[A-Za-z0-9@_][A-Za-z0-9_.:@+\\-]{0,255}$",
393
+ "maxLength": 256
394
+ },
395
+ "handles": {
396
+ "type": "object",
397
+ "maxProperties": 16,
398
+ "propertyNames": {
399
+ "pattern": "^[A-Za-z0-9_.:-]{1,80}$"
400
+ },
401
+ "additionalProperties": {
402
+ "type": "string",
403
+ "pattern": "^[A-Za-z0-9@_][A-Za-z0-9_.:@+\\-]{0,255}$",
404
+ "maxLength": 256
405
+ }
406
+ }
407
+ }
257
408
  },
258
409
  "runtimeId": {
259
410
  "type": "string",
@@ -38,6 +38,8 @@
38
38
  "nativeKeys": 24,
39
39
  "nativeValueChars": 256,
40
40
  "nativeHandles": 16,
41
+ "sessionConfigValues": 24,
42
+ "sessionConfigValueChars": 256,
41
43
  "minTimeoutMs": 1000,
42
44
  "maxTimeoutMs": 1800000,
43
45
  "maxApprovalTimeoutMs": 259200000,
@@ -55,7 +57,13 @@
55
57
  "cardId": "^[A-Za-z0-9_.:-]{1,80}$",
56
58
  "actionId": "^[A-Za-z0-9_.:-]{1,80}$",
57
59
  "questionId": "^[A-Za-z0-9_.:-]{1,120}$",
58
- "sessionRuleToolPattern": "^[\\w.*:-]{1,128}$"
60
+ "sessionRuleToolPattern": "^[\\w.*:-]{1,128}$",
61
+ "runtimeCorrelationValue": "^[A-Za-z0-9@_][A-Za-z0-9_.:@+\\-]{0,255}$",
62
+ "runtimeMethod": "^[A-Za-z0-9_][A-Za-z0-9_./:\\-]{0,255}$",
63
+ "runtimeOptionValue": "^[A-Za-z0-9@_][A-Za-z0-9_.:/@+\\-]{0,255}$",
64
+ "workspaceOptionId": "^[A-Za-z0-9@_][A-Za-z0-9_.:@+\\-]{0,255}$",
65
+ "runtimeHandleKey": "^[A-Za-z0-9_.:-]{1,80}$",
66
+ "runtimeControlId": "^[A-Za-z0-9_-]{1,80}$"
59
67
  },
60
68
  "byteSemantics": {
61
69
  "send_to.text": "utf8_bytes<=messageTextBytes",
@@ -424,8 +424,156 @@
424
424
  },
425
425
  "native": {
426
426
  "type": "object",
427
- "description": "Runtime correlation handles (<= 24 keys, string values <= 256 chars or a nested handles map <= 16; functions/src/api/interactionKinds.ts normalizeNative).",
428
- "additionalProperties": true
427
+ "description": "Content-free runtime correlation ids. Commands, paths, prompts, summaries, and model labels are not permitted in this public envelope.",
428
+ "maxProperties": 24,
429
+ "additionalProperties": false,
430
+ "properties": {
431
+ "runtime": {
432
+ "type": "string",
433
+ "pattern": "^[A-Za-z0-9@_][A-Za-z0-9_.:@+\\-]{0,255}$",
434
+ "maxLength": 256
435
+ },
436
+ "method": {
437
+ "type": "string",
438
+ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_./:\\-]{0,255}$",
439
+ "maxLength": 256
440
+ },
441
+ "requestId": {
442
+ "type": "string",
443
+ "pattern": "^[A-Za-z0-9@_][A-Za-z0-9_.:@+\\-]{0,255}$",
444
+ "maxLength": 256
445
+ },
446
+ "provider": {
447
+ "type": "string",
448
+ "pattern": "^[A-Za-z0-9@_][A-Za-z0-9_.:@+\\-]{0,255}$",
449
+ "maxLength": 256
450
+ },
451
+ "origin": {
452
+ "type": "string",
453
+ "pattern": "^[A-Za-z0-9@_][A-Za-z0-9_.:@+\\-]{0,255}$",
454
+ "maxLength": 256
455
+ },
456
+ "surface": {
457
+ "type": "string",
458
+ "pattern": "^[A-Za-z0-9@_][A-Za-z0-9_.:@+\\-]{0,255}$",
459
+ "maxLength": 256
460
+ },
461
+ "threadId": {
462
+ "type": "string",
463
+ "pattern": "^[A-Za-z0-9@_][A-Za-z0-9_.:@+\\-]{0,255}$",
464
+ "maxLength": 256
465
+ },
466
+ "turnId": {
467
+ "type": "string",
468
+ "pattern": "^[A-Za-z0-9@_][A-Za-z0-9_.:@+\\-]{0,255}$",
469
+ "maxLength": 256
470
+ },
471
+ "runId": {
472
+ "type": "string",
473
+ "pattern": "^[A-Za-z0-9@_][A-Za-z0-9_.:@+\\-]{0,255}$",
474
+ "maxLength": 256
475
+ },
476
+ "itemId": {
477
+ "type": "string",
478
+ "pattern": "^[A-Za-z0-9@_][A-Za-z0-9_.:@+\\-]{0,255}$",
479
+ "maxLength": 256
480
+ },
481
+ "toolCallId": {
482
+ "type": "string",
483
+ "pattern": "^[A-Za-z0-9@_][A-Za-z0-9_.:@+\\-]{0,255}$",
484
+ "maxLength": 256
485
+ },
486
+ "approvalId": {
487
+ "type": "string",
488
+ "pattern": "^[A-Za-z0-9@_][A-Za-z0-9_.:@+\\-]{0,255}$",
489
+ "maxLength": 256
490
+ },
491
+ "pluginId": {
492
+ "type": "string",
493
+ "pattern": "^[A-Za-z0-9@_][A-Za-z0-9_.:@+\\-]{0,255}$",
494
+ "maxLength": 256
495
+ },
496
+ "sessionKey": {
497
+ "type": "string",
498
+ "pattern": "^[A-Za-z0-9@_][A-Za-z0-9_.:@+\\-]{0,255}$",
499
+ "maxLength": 256
500
+ },
501
+ "nodeId": {
502
+ "type": "string",
503
+ "pattern": "^[A-Za-z0-9@_][A-Za-z0-9_.:@+\\-]{0,255}$",
504
+ "maxLength": 256
505
+ },
506
+ "handles": {
507
+ "type": "object",
508
+ "maxProperties": 16,
509
+ "propertyNames": {
510
+ "pattern": "^[A-Za-z0-9_.:-]{1,80}$"
511
+ },
512
+ "additionalProperties": {
513
+ "type": "string",
514
+ "pattern": "^[A-Za-z0-9@_][A-Za-z0-9_.:@+\\-]{0,255}$",
515
+ "maxLength": 256
516
+ }
517
+ }
518
+ }
519
+ },
520
+ "sessionConfig": {
521
+ "type": "object",
522
+ "description": "Typed setup policy for an agent target. Values are stable runtime-advertised option ids and are validated against the target descriptor before session creation; labels, prompts, workspace paths, and arbitrary fields are not permitted.",
523
+ "additionalProperties": false,
524
+ "properties": {
525
+ "model": {
526
+ "type": "string",
527
+ "pattern": "^[A-Za-z0-9@_][A-Za-z0-9_.:/@+\\-]{0,255}$",
528
+ "maxLength": 256
529
+ },
530
+ "permissionMode": {
531
+ "type": "string",
532
+ "pattern": "^[A-Za-z0-9@_][A-Za-z0-9_.:/@+\\-]{0,255}$",
533
+ "maxLength": 256
534
+ },
535
+ "effort": {
536
+ "type": "string",
537
+ "pattern": "^[A-Za-z0-9@_][A-Za-z0-9_.:/@+\\-]{0,255}$",
538
+ "maxLength": 256
539
+ },
540
+ "workspaceId": {
541
+ "type": "string",
542
+ "pattern": "^[A-Za-z0-9@_][A-Za-z0-9_.:@+\\-]{0,255}$",
543
+ "maxLength": 256
544
+ },
545
+ "executionMode": {
546
+ "enum": [
547
+ "worktree",
548
+ "locked"
549
+ ]
550
+ },
551
+ "runtimeControlValues": {
552
+ "type": "object",
553
+ "maxProperties": 24,
554
+ "propertyNames": {
555
+ "pattern": "^[A-Za-z0-9_-]{1,80}$",
556
+ "not": {
557
+ "enum": [
558
+ "model",
559
+ "workspace",
560
+ "executionMode",
561
+ "permissionMode",
562
+ "effort",
563
+ "interrupt",
564
+ "runtimeControlValues",
565
+ "updatedAt",
566
+ "updatedBy"
567
+ ]
568
+ }
569
+ },
570
+ "additionalProperties": {
571
+ "type": "string",
572
+ "pattern": "^[A-Za-z0-9@_][A-Za-z0-9_.:/@+\\-]{0,255}$",
573
+ "maxLength": 256
574
+ }
575
+ }
576
+ }
429
577
  },
430
578
  "send_to_input": {
431
579
  "type": "object",
@@ -466,9 +614,13 @@
466
614
  "$ref": "#/$defs/sessionSelection"
467
615
  },
468
616
  "sessionConfig": {
469
- "type": [
470
- "object",
471
- "null"
617
+ "oneOf": [
618
+ {
619
+ "$ref": "#/$defs/sessionConfig"
620
+ },
621
+ {
622
+ "type": "null"
623
+ }
472
624
  ],
473
625
  "description": "Coding-agent session setup (model/permissionMode/effort/workspaceId/executionMode); only applied to agent targets, stripped for humans."
474
626
  },
@@ -667,7 +819,7 @@
667
819
  "expiresAt": {
668
820
  "type": "integer",
669
821
  "minimum": 0,
670
- "description": "Absolute epoch-ms deadline. The REST interaction creators REQUIRE a deadline (and /runtime-input/request also requires kind): bindings must fill defaults and convert timeoutMs into expiresAt before calling the endpoint."
822
+ "description": "Absolute epoch-ms deadline. The REST interaction creators REQUIRE a deadline (input interactions also require input subtype kind): bindings must fill defaults and convert timeoutMs into expiresAt before calling the endpoint."
671
823
  }
672
824
  }
673
825
  },
@@ -836,7 +988,7 @@
836
988
  "expiresAt": {
837
989
  "type": "integer",
838
990
  "minimum": 0,
839
- "description": "Absolute epoch-ms deadline. The REST interaction creators REQUIRE a deadline (and /runtime-input/request also requires kind): bindings must fill defaults and convert timeoutMs into expiresAt before calling the endpoint."
991
+ "description": "Absolute epoch-ms deadline. The REST interaction creators REQUIRE a deadline (input interactions also require input subtype kind): bindings must fill defaults and convert timeoutMs into expiresAt before calling the endpoint."
840
992
  }
841
993
  }
842
994
  },
@@ -951,7 +1103,7 @@
951
1103
  }
952
1104
  },
953
1105
  "check_approval_result": {
954
- "description": "'unknown' is NOT a denial — only 'resolved' carries a decision. Canonical statuses map from the /runtime-approval/consume wire as: allow/deny -> resolved.decision, timeout -> expired, pending -> pending, HTTP 404 RUNTIME_APPROVAL_NOT_FOUND -> unknown.",
1106
+ "description": "'unknown' is NOT a denial — only 'resolved' carries a decision. Canonical statuses map from the approval interaction consume result as: allow/deny -> resolved.decision, timeout -> expired, pending -> pending, HTTP 404 RUNTIME_APPROVAL_NOT_FOUND -> unknown.",
955
1107
  "oneOf": [
956
1108
  {
957
1109
  "type": "object",
@@ -1108,7 +1260,7 @@
1108
1260
  "expiresAt": {
1109
1261
  "type": "integer",
1110
1262
  "minimum": 0,
1111
- "description": "Absolute epoch-ms deadline. The REST interaction creators REQUIRE a deadline (and /runtime-input/request also requires kind): bindings must fill defaults and convert timeoutMs into expiresAt before calling the endpoint."
1263
+ "description": "Absolute epoch-ms deadline. The REST interaction creators REQUIRE a deadline (input interactions also require input subtype kind): bindings must fill defaults and convert timeoutMs into expiresAt before calling the endpoint."
1112
1264
  }
1113
1265
  }
1114
1266
  },
@@ -74,7 +74,10 @@ function serializeContactRequest(requestId, data) {
74
74
  targetAvatarUrl: typeof data.targetAvatarUrl === 'string' ? data.targetAvatarUrl : null,
75
75
  targetUserType: data.targetUserType,
76
76
  approverId: data.approverId,
77
- message: typeof data.message === 'string' ? data.message : null,
77
+ // Free-form request content is owner-review material. This shared DTO is
78
+ // delivered to the target agent over REST/SSE, so it must never cross the
79
+ // admission boundary before the human approver accepts the reach-out.
80
+ message: null,
78
81
  status: normalizeStatus(data.status),
79
82
  kind: kindValue,
80
83
  createdAt: normalizeTimestamp(data.createdAt),
@@ -0,0 +1,29 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CANON_ENVIRONMENT_CONTRACTS = void 0;
4
+ exports.getCanonEnvironmentContract = getCanonEnvironmentContract;
5
+ exports.CANON_ENVIRONMENT_CONTRACTS = Object.freeze({
6
+ 'canon-dev-v1': Object.freeze({
7
+ name: 'dev',
8
+ projectId: 'canonmail-dev',
9
+ region: 'europe-west1',
10
+ }),
11
+ 'canon-prod-v1': Object.freeze({
12
+ name: 'prod',
13
+ projectId: 'canonmail-prod',
14
+ region: 'europe-west1',
15
+ }),
16
+ 'canon-legacy-v1': Object.freeze({
17
+ name: 'legacy',
18
+ projectId: 'project-007a8e71-ba01-49e6-8aa',
19
+ region: 'us-central1',
20
+ }),
21
+ });
22
+ function getCanonEnvironmentContract(value) {
23
+ const environmentId = value.trim();
24
+ const contract = exports.CANON_ENVIRONMENT_CONTRACTS[environmentId];
25
+ if (!contract) {
26
+ throw new Error(`Unsupported Canon environment ID: ${value}`);
27
+ }
28
+ return Object.freeze({ environmentId, ...contract });
29
+ }
package/dist/cjs/index.js CHANGED
@@ -15,6 +15,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
15
15
  };
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
17
  __exportStar(require("./diffRedaction.js"), exports);
18
+ __exportStar(require("./environment.js"), exports);
18
19
  __exportStar(require("./media.js"), exports);
19
20
  __exportStar(require("./message.js"), exports);
20
21
  __exportStar(require("./runtimeCardFields.js"), exports);
@@ -41,7 +41,8 @@
41
41
  * canon-verbs.limits.json) after schema validation.
42
42
  */
43
43
  Object.defineProperty(exports, "__esModule", { value: true });
44
- exports.CANON_VERB_LIMITS_ARTIFACT = exports.CREATE_GROUP_CREATOR_SETUP_REQUIRED_CODE = exports.CREATE_GROUP_NO_ADDABLE_MEMBERS_CODE = exports.CANON_VERB_NAMES = exports.SELF_CONTEXT_TYPE = exports.VERB_RATE_LIMITS = exports.VERB_LIMITS = exports.VERB_ID_PATTERNS = exports.CANON_CARD_SCHEMA_ID = exports.CANON_VERB_NAMESPACE = exports.CANON_VERBS_SCHEMA_ID = exports.CANON_VERBS_SCHEMA_VERSION = void 0;
44
+ exports.CANON_VERB_LIMITS_ARTIFACT = exports.CREATE_GROUP_CREATOR_SETUP_REQUIRED_CODE = exports.CREATE_GROUP_NO_ADDABLE_MEMBERS_CODE = exports.VERB_NATIVE_METADATA_KEYS = exports.CANON_VERB_NAMES = exports.SELF_CONTEXT_TYPE = exports.VERB_RATE_LIMITS = exports.VERB_LIMITS = exports.VERB_ID_PATTERNS = exports.CANON_CARD_SCHEMA_ID = exports.CANON_VERB_NAMESPACE = exports.CANON_VERBS_SCHEMA_ID = exports.CANON_VERBS_SCHEMA_VERSION = void 0;
45
+ exports.normalizeVerbNativeMetadata = normalizeVerbNativeMetadata;
45
46
  exports.canonVerbToolName = canonVerbToolName;
46
47
  exports.findVerbByteLimitViolations = findVerbByteLimitViolations;
47
48
  /** Identifier for this contract document. */
@@ -72,6 +73,12 @@ exports.VERB_ID_PATTERNS = {
72
73
  actionId: '^[A-Za-z0-9_.:-]{1,80}$',
73
74
  questionId: '^[A-Za-z0-9_.:-]{1,120}$',
74
75
  sessionRuleToolPattern: '^[\\w.*:-]{1,128}$',
76
+ runtimeCorrelationValue: '^[A-Za-z0-9@_][A-Za-z0-9_.:@+\\-]{0,255}$',
77
+ runtimeMethod: '^[A-Za-z0-9_][A-Za-z0-9_./:\\-]{0,255}$',
78
+ runtimeOptionValue: '^[A-Za-z0-9@_][A-Za-z0-9_.:/@+\\-]{0,255}$',
79
+ workspaceOptionId: '^[A-Za-z0-9@_][A-Za-z0-9_.:@+\\-]{0,255}$',
80
+ runtimeHandleKey: '^[A-Za-z0-9_.:-]{1,80}$',
81
+ runtimeControlId: '^[A-Za-z0-9_-]{1,80}$',
75
82
  };
76
83
  /**
77
84
  * Server-enforced limits, single-sourced. Each value cites its enforcement
@@ -139,6 +146,9 @@ exports.VERB_LIMITS = {
139
146
  nativeKeys: 24,
140
147
  nativeValueChars: 256,
141
148
  nativeHandles: 16,
149
+ /** Initial agent-session setup carried by send_to. */
150
+ sessionConfigValues: 24,
151
+ sessionConfigValueChars: 256,
142
152
  /** Deadlines (functions/src/utils/runtimeRequestHelpers.ts). */
143
153
  minTimeoutMs: 1000,
144
154
  /** input/card/plan ceiling (30 minutes). */
@@ -186,6 +196,65 @@ exports.CANON_VERB_NAMES = [
186
196
  'list_contact_requests',
187
197
  'list_conversations',
188
198
  ];
199
+ exports.VERB_NATIVE_METADATA_KEYS = [
200
+ 'runtime',
201
+ 'method',
202
+ 'requestId',
203
+ 'provider',
204
+ 'origin',
205
+ 'surface',
206
+ 'threadId',
207
+ 'turnId',
208
+ 'runId',
209
+ 'itemId',
210
+ 'toolCallId',
211
+ 'approvalId',
212
+ 'pluginId',
213
+ 'sessionKey',
214
+ 'nodeId',
215
+ 'handles',
216
+ ];
217
+ const VERB_NATIVE_STRING_KEYS = exports.VERB_NATIVE_METADATA_KEYS.filter((key) => key !== 'handles');
218
+ const RUNTIME_CORRELATION_VALUE_PATTERN = new RegExp(exports.VERB_ID_PATTERNS.runtimeCorrelationValue);
219
+ const RUNTIME_METHOD_PATTERN = new RegExp(exports.VERB_ID_PATTERNS.runtimeMethod);
220
+ const RUNTIME_HANDLE_KEY_PATTERN = new RegExp(exports.VERB_ID_PATTERNS.runtimeHandleKey);
221
+ function isPlainRecord(value) {
222
+ return Boolean(value && typeof value === 'object' && !Array.isArray(value));
223
+ }
224
+ /**
225
+ * Keep only the typed, content-free runtime correlation contract. Invalid or
226
+ * unknown fields are omitted on compatibility REST paths; canon.verb-wire.v1
227
+ * rejects the same fields through its strict JSON Schema.
228
+ */
229
+ function normalizeVerbNativeMetadata(value) {
230
+ if (!isPlainRecord(value))
231
+ return undefined;
232
+ const native = {};
233
+ for (const key of VERB_NATIVE_STRING_KEYS) {
234
+ const raw = value[key];
235
+ if (typeof raw !== 'string')
236
+ continue;
237
+ const normalized = raw.trim();
238
+ const pattern = key === 'method'
239
+ ? RUNTIME_METHOD_PATTERN
240
+ : RUNTIME_CORRELATION_VALUE_PATTERN;
241
+ if (pattern.test(normalized))
242
+ native[key] = normalized;
243
+ }
244
+ if (isPlainRecord(value.handles)) {
245
+ const handles = {};
246
+ for (const [key, raw] of Object.entries(value.handles).slice(0, exports.VERB_LIMITS.nativeHandles)) {
247
+ if (!RUNTIME_HANDLE_KEY_PATTERN.test(key) || typeof raw !== 'string')
248
+ continue;
249
+ const normalized = raw.trim();
250
+ if (RUNTIME_CORRELATION_VALUE_PATTERN.test(normalized))
251
+ handles[key] = normalized;
252
+ }
253
+ if (Object.keys(handles).length > 0)
254
+ native.handles = handles;
255
+ }
256
+ return Object.keys(native).length > 0 ? native : undefined;
257
+ }
189
258
  /**
190
259
  * Error code on the 400 a create_group receives when NO member is directly
191
260
  * addable (approval-required members can only be invited to an existing