@vess-id/ai-identity 0.19.0 → 0.20.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 (32) hide show
  1. package/dist/index.d.mts +2746 -863
  2. package/dist/index.d.ts +4 -1
  3. package/dist/index.d.ts.map +1 -1
  4. package/dist/index.js +447 -11
  5. package/dist/index.js.map +1 -1
  6. package/dist/index.mjs +440 -13
  7. package/dist/index.mjs.map +1 -1
  8. package/dist/registry/__tests__/action-registry-doc-review.spec.d.ts +14 -0
  9. package/dist/registry/__tests__/action-registry-doc-review.spec.d.ts.map +1 -0
  10. package/dist/registry/__tests__/action-risk.spec.d.ts +6 -1
  11. package/dist/registry/__tests__/action-risk.spec.d.ts.map +1 -1
  12. package/dist/registry/__tests__/salient-completeness.spec.d.ts +43 -0
  13. package/dist/registry/__tests__/salient-completeness.spec.d.ts.map +1 -0
  14. package/dist/registry/action-registry-json.d.ts +1801 -176
  15. package/dist/registry/action-registry-json.d.ts.map +1 -1
  16. package/dist/registry/action-registry.d.ts +13 -0
  17. package/dist/registry/action-registry.d.ts.map +1 -1
  18. package/dist/registry/action-risk.d.ts +27 -4
  19. package/dist/registry/action-risk.d.ts.map +1 -1
  20. package/dist/registry/action-summary.d.ts.map +1 -1
  21. package/dist/resolver/approval-time-resolution.d.ts +72 -0
  22. package/dist/resolver/approval-time-resolution.d.ts.map +1 -0
  23. package/dist/resolver/salient-digest.d.ts +48 -0
  24. package/dist/resolver/salient-digest.d.ts.map +1 -0
  25. package/dist/types/grant.d.ts +11 -0
  26. package/dist/types/grant.d.ts.map +1 -1
  27. package/dist/types/permission-vc.d.ts +81 -0
  28. package/dist/types/permission-vc.d.ts.map +1 -1
  29. package/dist/types/vc.d.ts +11 -1
  30. package/dist/types/vc.d.ts.map +1 -1
  31. package/dist/utils/sdjwt-disclosure.d.ts.map +1 -1
  32. package/package.json +3 -3
package/dist/index.mjs CHANGED
@@ -2576,6 +2576,8 @@ var CredentialType = /* @__PURE__ */ ((CredentialType2) => {
2576
2576
  CredentialType2["TEMPORARY"] = "TemporaryCredential";
2577
2577
  CredentialType2["ADMIN"] = "AdminCredential";
2578
2578
  CredentialType2["RECEIPT"] = "ReceiptCredential";
2579
+ CredentialType2["ACCOUNT"] = "AccountCredential";
2580
+ CredentialType2["AGENT_CARD"] = "AgentCardCredential";
2579
2581
  return CredentialType2;
2580
2582
  })(CredentialType || {});
2581
2583
  var CredentialStatus = /* @__PURE__ */ ((CredentialStatus2) => {
@@ -3022,6 +3024,38 @@ var DEFAULT_DISCLOSURE_CONFIGS = {
3022
3024
  mandatoryFields: [],
3023
3025
  neverDisclose: DEFAULT_NEVER_DISCLOSE
3024
3026
  },
3027
+ // OEM ⑯ — account attributes an organisation asserts about the holder.
3028
+ //
3029
+ // Everything identifying is selective. An account credential is presented to
3030
+ // decide whether an action is permitted, and that rarely requires knowing
3031
+ // the account number or whose name is on it. Left always-disclosed, every
3032
+ // verifier the holder ever presents to would learn both — and the fallback
3033
+ // for an unregistered type is TEMPORARY, whose selectiveFields is empty,
3034
+ // which is exactly that outcome. Hence this entry.
3035
+ ["AccountCredential" /* ACCOUNT */]: {
3036
+ selectiveFields: ["accountNumber", "accountHolderName", "branchCode"],
3037
+ mandatoryFields: [],
3038
+ neverDisclose: DEFAULT_NEVER_DISCLOSE
3039
+ },
3040
+ // OEM ⑯ Model C — the instance card.
3041
+ //
3042
+ // service_card_url and service_card_hash are absent from selectiveFields,
3043
+ // which is what makes them always-disclosed: the hash proves the fetched
3044
+ // card is the one the issuer signed over, and a holder able to withhold it
3045
+ // could present a card body nobody attested to (AP2 binds checkout_hash for
3046
+ // the same reason).
3047
+ //
3048
+ // 🔴 They are listed in mandatoryFields as documentation, NOT as
3049
+ // enforcement. APIVCManager.issueSDJWTVC passes only `selectiveFields` to
3050
+ // the SD-JWT builder — `mandatoryFields` and `neverDisclose` are not
3051
+ // consumed on that path. Moving these three into selectiveFields would
3052
+ // silently make the hash withholdable and nothing would fail, so the
3053
+ // omission below is the actual guarantee.
3054
+ ["AgentCardCredential" /* AGENT_CARD */]: {
3055
+ selectiveFields: ["version"],
3056
+ mandatoryFields: ["service_card_url", "service_card_hash", "service_id"],
3057
+ neverDisclose: DEFAULT_NEVER_DISCLOSE
3058
+ },
3025
3059
  ["ReceiptCredential" /* RECEIPT */]: {
3026
3060
  selectiveFields: ["action", "resource", "outcome", "amount", "description", "date", "transactionId"],
3027
3061
  mandatoryFields: ["grantId", "auditEventId", "receiptType"],
@@ -3938,6 +3972,13 @@ var actionMetaSchema = {
3938
3972
  },
3939
3973
  risk: { type: "string", enum: ["low", "medium", "high", "critical"] },
3940
3974
  target_bindings: { type: "object" },
3975
+ // Approval-salient parameter names (P1 / #1205). Optional at the schema
3976
+ // level (legacy/low-risk actions may omit it); the completeness test makes
3977
+ // it mandatory for every high/critical action.
3978
+ approval_salient_fields: {
3979
+ type: "array",
3980
+ items: { type: "string", minLength: 1 }
3981
+ },
3941
3982
  version: { type: "string", minLength: 1 }
3942
3983
  }
3943
3984
  };
@@ -4372,6 +4413,16 @@ var ACTION_REGISTRY = {
4372
4413
  target_bindings: {
4373
4414
  resource_id: { source: "param", param: "channel" }
4374
4415
  },
4416
+ // #1205 P1: `channel` IS approval-critical (the human approved posting to
4417
+ // THAT channel) so it is declared salient. ⚠️ HOLE 2 (plan §0.5, resolved
4418
+ // in Step 4.6): the server normalizes `channel` (#name → native Cxxx id)
4419
+ // at execution via a LIVE Slack lookup, so the issued-time digest
4420
+ // (hash of "#general") and the PEP-time digest (hash of "C09…") diverge and
4421
+ // a legitimate approved post fail-closes. Step 4.6 must resolve the
4422
+ // name→id AT APPROVAL TIME and freeze it into actionParams so all
4423
+ // participants hash the same effective value. Do NOT drop `channel` from
4424
+ // this set to paper over HOLE 2 — that would unbind the approved target.
4425
+ approval_salient_fields: ["channel", "text", "blocks"],
4375
4426
  version: "1.0.0"
4376
4427
  },
4377
4428
  {
@@ -4513,6 +4564,7 @@ var ACTION_REGISTRY = {
4513
4564
  target_bindings: {
4514
4565
  resource_id: { source: "param", param: "channel" }
4515
4566
  },
4567
+ approval_salient_fields: ["channel", "ts", "text"],
4516
4568
  version: "1.0.0"
4517
4569
  },
4518
4570
  {
@@ -4536,6 +4588,7 @@ var ACTION_REGISTRY = {
4536
4588
  target_bindings: {
4537
4589
  resource_id: { source: "param", param: "channel" }
4538
4590
  },
4591
+ approval_salient_fields: ["channel", "ts"],
4539
4592
  version: "1.0.0"
4540
4593
  },
4541
4594
  // ─── GitHub Actions ───
@@ -4568,6 +4621,11 @@ var ACTION_REGISTRY = {
4568
4621
  target_bindings: {
4569
4622
  resource_id: { source: "param", param: "repo", fallback_param: "repository" }
4570
4623
  },
4624
+ // #1205 P1: assignees (who the issue is assigned to) is approval-critical —
4625
+ // an agent could otherwise reassign it post-approval. labels are part of
4626
+ // the approved issue definition. (milestone is an integer id; not
4627
+ // substance-of-approval so left out — see completeness-test allowlist.)
4628
+ approval_salient_fields: ["owner", "repo", "title", "body", "assignees", "labels"],
4571
4629
  version: "1.0.0"
4572
4630
  },
4573
4631
  {
@@ -4658,6 +4716,10 @@ var ACTION_REGISTRY = {
4658
4716
  target_bindings: {
4659
4717
  resource_id: { source: "param", param: "repo", fallback_param: "repository" }
4660
4718
  },
4719
+ // #1205 P1: assignees (who the issue is assigned to) is approval-critical —
4720
+ // an agent could otherwise reassign it post-approval. labels are part of
4721
+ // the approved change.
4722
+ approval_salient_fields: ["owner", "repo", "issueNumber", "title", "body", "state", "assignees", "labels"],
4661
4723
  version: "1.0.0"
4662
4724
  },
4663
4725
  // ─── GitHub Pull Request Actions ───
@@ -4687,6 +4749,9 @@ var ACTION_REGISTRY = {
4687
4749
  target_bindings: {
4688
4750
  resource_id: { source: "context", key: "resource_id" }
4689
4751
  },
4752
+ // #1205 P1: draft changes the real-world effect (a non-draft PR notifies
4753
+ // reviewers / can be merged immediately), so it is approval-critical.
4754
+ approval_salient_fields: ["owner", "repo", "title", "head", "base", "body", "draft"],
4690
4755
  version: "1.0.0"
4691
4756
  },
4692
4757
  {
@@ -4769,6 +4834,7 @@ var ACTION_REGISTRY = {
4769
4834
  target_bindings: {
4770
4835
  resource_id: { source: "context", key: "resource_id" }
4771
4836
  },
4837
+ approval_salient_fields: ["owner", "repo", "pullNumber", "title", "body", "state", "base"],
4772
4838
  version: "1.0.0"
4773
4839
  },
4774
4840
  {
@@ -4796,6 +4862,9 @@ var ACTION_REGISTRY = {
4796
4862
  target_bindings: {
4797
4863
  resource_id: { source: "context", key: "resource_id" }
4798
4864
  },
4865
+ // #1205 P1: commitTitle/commitMessage are persisted into the merge commit
4866
+ // (permanent git history), so they are approval-critical.
4867
+ approval_salient_fields: ["owner", "repo", "pullNumber", "mergeMethod", "commitTitle", "commitMessage"],
4799
4868
  version: "1.0.0"
4800
4869
  },
4801
4870
  {
@@ -4872,6 +4941,7 @@ var ACTION_REGISTRY = {
4872
4941
  target_bindings: {
4873
4942
  resource_id: { source: "context", key: "resource_id" }
4874
4943
  },
4944
+ approval_salient_fields: ["owner", "repo", "pullNumber", "event", "body"],
4875
4945
  version: "1.0.0"
4876
4946
  },
4877
4947
  {
@@ -4898,6 +4968,7 @@ var ACTION_REGISTRY = {
4898
4968
  target_bindings: {
4899
4969
  resource_id: { source: "context", key: "resource_id" }
4900
4970
  },
4971
+ approval_salient_fields: ["owner", "repo", "pullNumber", "commentId", "body"],
4901
4972
  version: "1.0.0"
4902
4973
  },
4903
4974
  {
@@ -4923,6 +4994,7 @@ var ACTION_REGISTRY = {
4923
4994
  target_bindings: {
4924
4995
  resource_id: { source: "context", key: "resource_id" }
4925
4996
  },
4997
+ approval_salient_fields: ["owner", "repo", "pullNumber"],
4926
4998
  version: "1.0.0"
4927
4999
  },
4928
5000
  {
@@ -5301,6 +5373,7 @@ var ACTION_REGISTRY = {
5301
5373
  target_bindings: {
5302
5374
  resource_id: { source: "context", key: "resource_id" }
5303
5375
  },
5376
+ approval_salient_fields: ["name", "org", "private"],
5304
5377
  version: "1.0.0"
5305
5378
  },
5306
5379
  {
@@ -5325,6 +5398,7 @@ var ACTION_REGISTRY = {
5325
5398
  target_bindings: {
5326
5399
  resource_id: { source: "context", key: "resource_id" }
5327
5400
  },
5401
+ approval_salient_fields: ["owner", "repo", "organization"],
5328
5402
  version: "1.0.0"
5329
5403
  },
5330
5404
  {
@@ -5353,6 +5427,14 @@ var ACTION_REGISTRY = {
5353
5427
  target_bindings: {
5354
5428
  resource_id: { source: "context", key: "resource_id" }
5355
5429
  },
5430
+ // #1205 P1: sha is a version pin (the blob revision being overwritten). The
5431
+ // executor forwards params.sha verbatim — never recomputed/refetched between
5432
+ // approval and execution (github.service.ts:1194) — so binding it causes NO
5433
+ // false-fail-closed; an unbound sha would let an agent overwrite a different
5434
+ // file revision than the human reviewed. On create sha is omitted → the
5435
+ // serializer represents the missing key as JSON null (salient-digest rule 2),
5436
+ // stable on both issued and PEP sides.
5437
+ approval_salient_fields: ["owner", "repo", "path", "content", "message", "branch", "sha"],
5356
5438
  version: "1.0.0"
5357
5439
  },
5358
5440
  {
@@ -5380,6 +5462,11 @@ var ACTION_REGISTRY = {
5380
5462
  target_bindings: {
5381
5463
  resource_id: { source: "context", key: "resource_id" }
5382
5464
  },
5465
+ // #1205 P1: sha (required here) pins the exact blob being deleted; forwarded
5466
+ // verbatim to the API, so binding it prevents an agent from swapping the
5467
+ // target revision post-approval. Same no-false-fail-closed reasoning as
5468
+ // github.content.createOrUpdate.
5469
+ approval_salient_fields: ["owner", "repo", "path", "message", "branch", "sha"],
5383
5470
  version: "1.0.0"
5384
5471
  },
5385
5472
  {
@@ -5405,6 +5492,7 @@ var ACTION_REGISTRY = {
5405
5492
  target_bindings: {
5406
5493
  resource_id: { source: "context", key: "resource_id" }
5407
5494
  },
5495
+ approval_salient_fields: ["owner", "repo", "branch", "fromBranch"],
5408
5496
  version: "1.0.0"
5409
5497
  },
5410
5498
  // ─── GitHub Issues 拡張 & Labels Actions ───
@@ -5431,6 +5519,7 @@ var ACTION_REGISTRY = {
5431
5519
  target_bindings: {
5432
5520
  resource_id: { source: "context", key: "resource_id" }
5433
5521
  },
5522
+ approval_salient_fields: ["owner", "repo", "issueNumber", "body"],
5434
5523
  version: "1.0.0"
5435
5524
  },
5436
5525
  {
@@ -5456,6 +5545,7 @@ var ACTION_REGISTRY = {
5456
5545
  target_bindings: {
5457
5546
  resource_id: { source: "context", key: "resource_id" }
5458
5547
  },
5548
+ approval_salient_fields: ["owner", "repo", "issueNumber", "subIssueId"],
5459
5549
  version: "1.0.0"
5460
5550
  },
5461
5551
  {
@@ -5580,6 +5670,7 @@ var ACTION_REGISTRY = {
5580
5670
  target_bindings: {
5581
5671
  resource_id: { source: "context", key: "resource_id" }
5582
5672
  },
5673
+ approval_salient_fields: ["owner", "repo", "name", "newName"],
5583
5674
  version: "1.0.0"
5584
5675
  },
5585
5676
  // ─── GitHub Actions(CI) / Stargazers / Context / Users Actions ───
@@ -5666,6 +5757,7 @@ var ACTION_REGISTRY = {
5666
5757
  target_bindings: {
5667
5758
  resource_id: { source: "context", key: "resource_id" }
5668
5759
  },
5760
+ approval_salient_fields: ["owner", "repo", "workflowId", "ref"],
5669
5761
  version: "1.0.0"
5670
5762
  },
5671
5763
  {
@@ -5738,6 +5830,7 @@ var ACTION_REGISTRY = {
5738
5830
  target_bindings: {
5739
5831
  resource_id: { source: "context", key: "resource_id" }
5740
5832
  },
5833
+ approval_salient_fields: ["owner", "repo"],
5741
5834
  version: "1.0.0"
5742
5835
  },
5743
5836
  {
@@ -5761,6 +5854,7 @@ var ACTION_REGISTRY = {
5761
5854
  target_bindings: {
5762
5855
  resource_id: { source: "context", key: "resource_id" }
5763
5856
  },
5857
+ approval_salient_fields: ["owner", "repo"],
5764
5858
  version: "1.0.0"
5765
5859
  },
5766
5860
  {
@@ -5928,6 +6022,7 @@ var ACTION_REGISTRY = {
5928
6022
  target_bindings: {
5929
6023
  resource_id: { source: "context", key: "resource_id" }
5930
6024
  },
6025
+ approval_salient_fields: ["threadId"],
5931
6026
  version: "1.0.0"
5932
6027
  },
5933
6028
  {
@@ -5950,6 +6045,7 @@ var ACTION_REGISTRY = {
5950
6045
  target_bindings: {
5951
6046
  resource_id: { source: "context", key: "resource_id" }
5952
6047
  },
6048
+ approval_salient_fields: ["lastReadAt"],
5953
6049
  version: "1.0.0"
5954
6050
  },
5955
6051
  {
@@ -5973,6 +6069,7 @@ var ACTION_REGISTRY = {
5973
6069
  target_bindings: {
5974
6070
  resource_id: { source: "context", key: "resource_id" }
5975
6071
  },
6072
+ approval_salient_fields: ["threadId", "ignored"],
5976
6073
  version: "1.0.0"
5977
6074
  },
5978
6075
  {
@@ -5998,6 +6095,7 @@ var ACTION_REGISTRY = {
5998
6095
  target_bindings: {
5999
6096
  resource_id: { source: "context", key: "resource_id" }
6000
6097
  },
6098
+ approval_salient_fields: ["owner", "repo", "subscribed", "ignored"],
6001
6099
  version: "1.0.0"
6002
6100
  },
6003
6101
  {
@@ -6026,6 +6124,7 @@ var ACTION_REGISTRY = {
6026
6124
  target_bindings: {
6027
6125
  resource_id: { source: "context", key: "resource_id" }
6028
6126
  },
6127
+ approval_salient_fields: ["files", "description", "public"],
6029
6128
  version: "1.0.0"
6030
6129
  },
6031
6130
  {
@@ -6101,6 +6200,7 @@ var ACTION_REGISTRY = {
6101
6200
  target_bindings: {
6102
6201
  resource_id: { source: "context", key: "resource_id" }
6103
6202
  },
6203
+ approval_salient_fields: ["gistId", "files", "description"],
6104
6204
  version: "1.0.0"
6105
6205
  },
6106
6206
  // ─── Gmail Actions ───
@@ -6180,6 +6280,7 @@ var ACTION_REGISTRY = {
6180
6280
  { name: "bcc", source: { source: "param", param: "bcc" }, type: "recipient" }
6181
6281
  ]
6182
6282
  },
6283
+ approval_salient_fields: ["to", "cc", "bcc", "subject", "body"],
6183
6284
  version: "1.0.0"
6184
6285
  },
6185
6286
  {
@@ -6213,6 +6314,7 @@ var ACTION_REGISTRY = {
6213
6314
  { name: "bcc", source: { source: "param", param: "bcc" }, type: "recipient" }
6214
6315
  ]
6215
6316
  },
6317
+ approval_salient_fields: ["to", "cc", "bcc", "subject", "body"],
6216
6318
  version: "1.0.0"
6217
6319
  },
6218
6320
  {
@@ -6281,6 +6383,7 @@ var ACTION_REGISTRY = {
6281
6383
  target_bindings: {
6282
6384
  resource_id: { source: "param", param: "messageId" }
6283
6385
  },
6386
+ approval_salient_fields: ["messageId"],
6284
6387
  version: "1.0.0"
6285
6388
  },
6286
6389
  {
@@ -6303,6 +6406,7 @@ var ACTION_REGISTRY = {
6303
6406
  target_bindings: {
6304
6407
  resource_id: { source: "param", param: "messageId" }
6305
6408
  },
6409
+ approval_salient_fields: ["messageId"],
6306
6410
  version: "1.0.0"
6307
6411
  },
6308
6412
  // ─── Calendar Actions ───
@@ -6427,6 +6531,9 @@ var ACTION_REGISTRY = {
6427
6531
  { name: "attendees", source: { source: "param", param: "attendees" }, type: "participant" }
6428
6532
  ]
6429
6533
  },
6534
+ // #1205 P1: addMeet changes the real-world effect (attaches a Google Meet
6535
+ // link / video conference), so it is approval-critical.
6536
+ approval_salient_fields: ["calendarId", "summary", "description", "start", "end", "attendees", "location", "addMeet"],
6430
6537
  version: "1.0.0"
6431
6538
  },
6432
6539
  {
@@ -6477,6 +6584,9 @@ var ACTION_REGISTRY = {
6477
6584
  { name: "attendees", source: { source: "param", param: "attendees" }, type: "participant" }
6478
6585
  ]
6479
6586
  },
6587
+ // #1205 P1: addMeet changes the real-world effect (attaches a Google Meet
6588
+ // link), so it is approval-critical.
6589
+ approval_salient_fields: ["calendarId", "eventId", "summary", "description", "start", "end", "attendees", "location", "addMeet"],
6480
6590
  version: "1.0.0"
6481
6591
  },
6482
6592
  {
@@ -6500,6 +6610,7 @@ var ACTION_REGISTRY = {
6500
6610
  target_bindings: {
6501
6611
  resource_id: { source: "param", param: "calendarId", default: "primary" }
6502
6612
  },
6613
+ approval_salient_fields: ["calendarId", "eventId"],
6503
6614
  version: "1.0.0"
6504
6615
  },
6505
6616
  // ─── Jira Actions ───
@@ -6692,6 +6803,10 @@ var ACTION_REGISTRY = {
6692
6803
  target_bindings: {
6693
6804
  resource_id: { source: "param", param: "projectKey" }
6694
6805
  },
6806
+ // #1205 P1: assigneeAccountId (who the issue is assigned to) is
6807
+ // approval-critical — an agent could otherwise reassign it post-approval.
6808
+ // labels are part of the approved issue definition.
6809
+ approval_salient_fields: ["projectKey", "summary", "description", "issueTypeName", "assigneeAccountId", "labels"],
6695
6810
  version: "1.0.0"
6696
6811
  },
6697
6812
  {
@@ -6703,6 +6818,15 @@ var ACTION_REGISTRY = {
6703
6818
  input_schema: {
6704
6819
  type: "object",
6705
6820
  properties: {
6821
+ // #1205 P1 / HOLE 2 audit (Step 4.7): issueIdOrKey accepts EITHER a
6822
+ // project key form ("PROJ-123") OR a numeric internal id. VERIFIED:
6823
+ // it is passed VERBATIM into the Jira REST URL path
6824
+ // `/api/3/issue/{issueIdOrKey}` (jira.service.ts) — the API accepts
6825
+ // both forms and nothing canonicalizes one to the other, and no pre-PEP
6826
+ // mutation touches jira params (only normalizeSlackChannelParam mutates,
6827
+ // slack-only). So issuance digest == PEP digest for whichever form the
6828
+ // approver approved — NO resolve-at-approval-time needed (unlike slack
6829
+ // channel). Regression guard: salient-digest.test.ts Step 4.7 block.
6706
6830
  issueIdOrKey: { type: "string", minLength: 1 },
6707
6831
  summary: { type: "string" },
6708
6832
  description: {
@@ -6725,6 +6849,9 @@ var ACTION_REGISTRY = {
6725
6849
  target_bindings: {
6726
6850
  resource_id: { source: "param", param: "issueIdOrKey", fallback_param: "issueKey", derive: "project_key" }
6727
6851
  },
6852
+ // #1205 P1: assigneeAccountId (reassignment) is approval-critical; labels
6853
+ // are part of the approved change. Same rationale as jira.issue.create.
6854
+ approval_salient_fields: ["issueIdOrKey", "summary", "description", "assigneeAccountId", "labels"],
6728
6855
  version: "1.0.0"
6729
6856
  },
6730
6857
  {
@@ -6747,6 +6874,7 @@ var ACTION_REGISTRY = {
6747
6874
  target_bindings: {
6748
6875
  resource_id: { source: "param", param: "issueIdOrKey", fallback_param: "issueKey", derive: "project_key" }
6749
6876
  },
6877
+ approval_salient_fields: ["issueIdOrKey"],
6750
6878
  version: "1.0.0"
6751
6879
  },
6752
6880
  {
@@ -6777,6 +6905,7 @@ var ACTION_REGISTRY = {
6777
6905
  target_bindings: {
6778
6906
  resource_id: { source: "param", param: "issueIdOrKey", fallback_param: "issueKey", derive: "project_key" }
6779
6907
  },
6908
+ approval_salient_fields: ["issueIdOrKey", "body"],
6780
6909
  version: "1.0.0"
6781
6910
  },
6782
6911
  {
@@ -6805,6 +6934,7 @@ var ACTION_REGISTRY = {
6805
6934
  target_bindings: {
6806
6935
  resource_id: { source: "param", param: "issueIdOrKey", fallback_param: "issueKey", derive: "project_key" }
6807
6936
  },
6937
+ approval_salient_fields: ["issueIdOrKey", "commentId"],
6808
6938
  version: "1.0.0"
6809
6939
  },
6810
6940
  {
@@ -6850,6 +6980,7 @@ var ACTION_REGISTRY = {
6850
6980
  target_bindings: {
6851
6981
  resource_id: { source: "param", param: "issueIdOrKey", fallback_param: "issueKey", derive: "project_key" }
6852
6982
  },
6983
+ approval_salient_fields: ["issueIdOrKey", "transitionId"],
6853
6984
  version: "1.0.0"
6854
6985
  },
6855
6986
  {
@@ -6924,6 +7055,7 @@ var ACTION_REGISTRY = {
6924
7055
  // 完全な制御をしたい場合は別途 inwardIssueKey 側も grant チェックする運用を要する。
6925
7056
  resource_id: { source: "param", param: "outwardIssueKey", derive: "project_key" }
6926
7057
  },
7058
+ approval_salient_fields: ["typeName", "inwardIssueKey", "outwardIssueKey", "commentBody"],
6927
7059
  version: "1.0.0"
6928
7060
  },
6929
7061
  {
@@ -6946,6 +7078,7 @@ var ACTION_REGISTRY = {
6946
7078
  target_bindings: {
6947
7079
  resource_id: { source: "param", param: "linkId" }
6948
7080
  },
7081
+ approval_salient_fields: ["linkId"],
6949
7082
  version: "1.0.0"
6950
7083
  },
6951
7084
  {
@@ -7027,6 +7160,7 @@ var ACTION_REGISTRY = {
7027
7160
  target_bindings: {
7028
7161
  resource_id: { source: "param", param: "file_path" }
7029
7162
  },
7163
+ approval_salient_fields: ["file_path"],
7030
7164
  version: "1.0.0"
7031
7165
  },
7032
7166
  {
@@ -7048,6 +7182,7 @@ var ACTION_REGISTRY = {
7048
7182
  target_bindings: {
7049
7183
  resource_id: { source: "param", param: "file_path" }
7050
7184
  },
7185
+ approval_salient_fields: ["file_path", "content"],
7051
7186
  version: "1.0.0"
7052
7187
  },
7053
7188
  {
@@ -7084,6 +7219,7 @@ var ACTION_REGISTRY = {
7084
7219
  target_bindings: {
7085
7220
  resource_id: { source: "param", param: "command" }
7086
7221
  },
7222
+ approval_salient_fields: ["command", "working_directory", "env_profile"],
7087
7223
  version: "1.0.0"
7088
7224
  },
7089
7225
  // ─── HubSpot CRM Objects (objectType 汎用) ───
@@ -7204,6 +7340,7 @@ var ACTION_REGISTRY = {
7204
7340
  target_bindings: {
7205
7341
  resource_id: { source: "param", param: "objectType" }
7206
7342
  },
7343
+ approval_salient_fields: ["objectType", "inputs"],
7207
7344
  version: "1.0.0"
7208
7345
  },
7209
7346
  {
@@ -7236,6 +7373,7 @@ var ACTION_REGISTRY = {
7236
7373
  target_bindings: {
7237
7374
  resource_id: { source: "param", param: "objectType" }
7238
7375
  },
7376
+ approval_salient_fields: ["objectType", "inputs"],
7239
7377
  version: "1.0.0"
7240
7378
  },
7241
7379
  {
@@ -7325,6 +7463,7 @@ var ACTION_REGISTRY = {
7325
7463
  effects: ["Create:CrmProperty"],
7326
7464
  risk: "high",
7327
7465
  target_bindings: { resource_id: { source: "param", param: "objectType" } },
7466
+ approval_salient_fields: ["objectType", "name", "label", "type", "fieldType"],
7328
7467
  version: "1.0.0"
7329
7468
  },
7330
7469
  {
@@ -7352,6 +7491,7 @@ var ACTION_REGISTRY = {
7352
7491
  effects: ["Update:CrmProperty"],
7353
7492
  risk: "high",
7354
7493
  target_bindings: { resource_id: { source: "param", param: "objectType" } },
7494
+ approval_salient_fields: ["objectType", "propertyName", "label"],
7355
7495
  version: "1.0.0"
7356
7496
  },
7357
7497
  // ─── HubSpot Associations (v4) ───
@@ -7420,6 +7560,7 @@ var ACTION_REGISTRY = {
7420
7560
  effects: ["Create:CrmAssociation"],
7421
7561
  risk: "high",
7422
7562
  target_bindings: { resource_id: { source: "param", param: "fromObjectType" } },
7563
+ approval_salient_fields: ["fromObjectType", "toObjectType", "inputs"],
7423
7564
  version: "1.0.0"
7424
7565
  },
7425
7566
  // ─── HubSpot Engagements (legacy v1: NOTE / TASK) ───
@@ -7451,8 +7592,20 @@ var ACTION_REGISTRY = {
7451
7592
  type: "object",
7452
7593
  properties: {
7453
7594
  type: { type: "string", enum: ["NOTE", "TASK"] },
7454
- ownerId: { type: "number" },
7455
- timestamp: { type: "number" },
7595
+ // #1205 P1: HubSpot owner ids are integers; retyped from `number` to
7596
+ // `integer` so this ownership field is bindable (float-ban rule 5).
7597
+ // DRIFT (follow-up, out of Step 2 scope): the API adapter still types
7598
+ // ownerId/timestamp as `number` (hubspot.adapter.ts:294-295,317-318).
7599
+ // No test enforces registry↔adapter type identity, so nothing breaks;
7600
+ // a later step should align the adapter schemas to `integer`.
7601
+ ownerId: { type: "integer" },
7602
+ // #1205 P1: caller-supplied epoch-ms activity time when present, else
7603
+ // the service defaults to Date.now() (hubspot.service.ts:555
7604
+ // `params.timestamp ?? Date.now()`). Retyped to `integer` (epoch ms)
7605
+ // so it is bindable; when the caller omits it both the issued and PEP
7606
+ // digests see the same JSON null (the default is applied server-side
7607
+ // AFTER the salient params are read), so binding it is safe.
7608
+ timestamp: { type: "integer" },
7456
7609
  associations: { type: "object" },
7457
7610
  metadata: { type: "object" }
7458
7611
  },
@@ -7465,6 +7618,10 @@ var ACTION_REGISTRY = {
7465
7618
  // Bind to the engagement type (NOTE/TASK) as the container that exists at
7466
7619
  // call time, consistent with other create actions (see CREATE-binding note above).
7467
7620
  target_bindings: { resource_id: { source: "param", param: "type" } },
7621
+ // #1205 P1: ownerId (who owns the engagement) is approval-critical — an
7622
+ // agent could otherwise reassign ownership post-approval. timestamp binds
7623
+ // the activity time. associations/metadata carry the note/task substance.
7624
+ approval_salient_fields: ["type", "ownerId", "timestamp", "associations", "metadata"],
7468
7625
  version: "1.0.0"
7469
7626
  },
7470
7627
  {
@@ -7477,8 +7634,13 @@ var ACTION_REGISTRY = {
7477
7634
  type: "object",
7478
7635
  properties: {
7479
7636
  engagementId: { type: "string" },
7480
- ownerId: { type: "number" },
7481
- timestamp: { type: "number" },
7637
+ // #1205 P1: retyped `number` → `integer` (HubSpot owner id) so this
7638
+ // ownership field is bindable (float-ban rule 5).
7639
+ ownerId: { type: "integer" },
7640
+ // #1205 P1: caller-supplied epoch-ms activity time (update sets it
7641
+ // directly, no default here — hubspot.service.ts:577 only assigns when
7642
+ // provided). Retyped to `integer` so it is bindable.
7643
+ timestamp: { type: "integer" },
7482
7644
  associations: { type: "object" },
7483
7645
  metadata: { type: "object" }
7484
7646
  },
@@ -7489,6 +7651,9 @@ var ACTION_REGISTRY = {
7489
7651
  effects: ["Update:CrmEngagement"],
7490
7652
  risk: "high",
7491
7653
  target_bindings: { resource_id: { source: "param", param: "engagementId" } },
7654
+ // #1205 P1: ownerId (reassignment) + timestamp are approval-critical on
7655
+ // update, same as create.
7656
+ approval_salient_fields: ["engagementId", "ownerId", "timestamp", "metadata"],
7492
7657
  version: "1.0.0"
7493
7658
  },
7494
7659
  // ─── HubSpot Workflows (automation v4 flows, read-only) ───
@@ -7659,7 +7824,11 @@ var ACTION_REGISTRY = {
7659
7824
  description: { type: "string", description: "Issue description (Markdown)" },
7660
7825
  assigneeId: { type: "string", description: "Assignee user ID" },
7661
7826
  stateId: { type: "string", description: "Workflow state ID" },
7662
- priority: { type: "number", minimum: 0, maximum: 4, description: "0=No priority, 1=Urgent, 2=High, 3=Medium, 4=Low" },
7827
+ // #1205 P1: priority is always an integer 0-4; retyped `number` →
7828
+ // `integer` so it is bindable (float-ban rule 5). DRIFT (follow-up): the
7829
+ // API adapter still types priority as `number` (linear.adapter.ts:93,112);
7830
+ // align in a later step (no test enforces the mirror, nothing breaks).
7831
+ priority: { type: "integer", minimum: 0, maximum: 4, description: "0=No priority, 1=Urgent, 2=High, 3=Medium, 4=Low" },
7663
7832
  labelIds: { type: "array", items: { type: "string" }, description: "Label IDs to attach" }
7664
7833
  },
7665
7834
  required: ["teamId", "title"],
@@ -7671,6 +7840,10 @@ var ACTION_REGISTRY = {
7671
7840
  target_bindings: {
7672
7841
  resource_id: { source: "param", param: "teamId" }
7673
7842
  },
7843
+ // #1205 P1: assigneeId (who the issue is assigned to) is approval-critical
7844
+ // — an agent could otherwise reassign the issue post-approval. priority and
7845
+ // labelIds are part of the approved issue definition.
7846
+ approval_salient_fields: ["teamId", "title", "description", "assigneeId", "priority", "labelIds"],
7674
7847
  version: "1.0.0"
7675
7848
  },
7676
7849
  {
@@ -7682,12 +7855,23 @@ var ACTION_REGISTRY = {
7682
7855
  input_schema: {
7683
7856
  type: "object",
7684
7857
  properties: {
7858
+ // #1205 P1 / HOLE 2 audit (Step 4.7): issueId accepts EITHER a uuid OR
7859
+ // a human identifier like "ENG-123". VERIFIED: it is passed VERBATIM
7860
+ // into the Linear GraphQL `issue(id: $issueId)` / `issueUpdate(id:)`
7861
+ // arg (linear.service.ts) — the API accepts both forms and nothing
7862
+ // canonicalizes identifier→uuid, and no pre-PEP mutation touches linear
7863
+ // params (only normalizeSlackChannelParam mutates, slack-only). So
7864
+ // issuance digest == PEP digest for whichever form the approver
7865
+ // approved — NO resolve-at-approval-time needed (unlike slack channel).
7866
+ // Regression guard: salient-digest.test.ts Step 4.7 block. Do NOT drop
7867
+ // issueId (it IS the target).
7685
7868
  issueId: { type: "string", minLength: 1, description: "Issue ID or identifier (e.g. ENG-123)" },
7686
7869
  title: { type: "string", description: "New title" },
7687
7870
  description: { type: "string", description: "New description (Markdown)" },
7688
7871
  assigneeId: { type: "string", description: "Assignee user ID (null to unassign)" },
7689
7872
  stateId: { type: "string", description: "Workflow state ID" },
7690
- priority: { type: "number", minimum: 0, maximum: 4, description: "0=No priority, 1=Urgent, 2=High, 3=Medium, 4=Low" },
7873
+ // #1205 P1: retyped `number` → `integer` (priority is always int 0-4).
7874
+ priority: { type: "integer", minimum: 0, maximum: 4, description: "0=No priority, 1=Urgent, 2=High, 3=Medium, 4=Low" },
7691
7875
  labelIds: { type: "array", items: { type: "string" }, description: "New set of label IDs" }
7692
7876
  },
7693
7877
  required: ["issueId"],
@@ -7699,6 +7883,9 @@ var ACTION_REGISTRY = {
7699
7883
  target_bindings: {
7700
7884
  resource_id: { source: "param", param: "issueId" }
7701
7885
  },
7886
+ // #1205 P1: assigneeId (reassignment) is approval-critical; priority and
7887
+ // labelIds are part of the approved change.
7888
+ approval_salient_fields: ["issueId", "title", "description", "assigneeId", "stateId", "priority", "labelIds"],
7702
7889
  version: "1.0.0"
7703
7890
  },
7704
7891
  {
@@ -7722,6 +7909,7 @@ var ACTION_REGISTRY = {
7722
7909
  target_bindings: {
7723
7910
  resource_id: { source: "param", param: "issueId" }
7724
7911
  },
7912
+ approval_salient_fields: ["issueId", "body"],
7725
7913
  version: "1.0.0"
7726
7914
  },
7727
7915
  {
@@ -7923,6 +8111,7 @@ var ACTION_REGISTRY = {
7923
8111
  target_bindings: {
7924
8112
  resource_id: { source: "param", param: "issueId" }
7925
8113
  },
8114
+ approval_salient_fields: ["issueId"],
7926
8115
  version: "1.0.0"
7927
8116
  },
7928
8117
  {
@@ -7958,6 +8147,7 @@ var ACTION_REGISTRY = {
7958
8147
  // resource binding 必須契約を明示 (teamIds は minItems:1 なので常に解決する)。
7959
8148
  resource_id: { source: "param", param: "teamId", fallback_param: "teamIds", required: true }
7960
8149
  },
8150
+ approval_salient_fields: ["teamId", "teamIds", "name", "description"],
7961
8151
  version: "1.0.0"
7962
8152
  },
7963
8153
  {
@@ -7985,6 +8175,7 @@ var ACTION_REGISTRY = {
7985
8175
  target_bindings: {
7986
8176
  resource_id: { source: "param", param: "projectId" }
7987
8177
  },
8178
+ approval_salient_fields: ["projectId", "name", "description"],
7988
8179
  version: "1.0.0"
7989
8180
  },
7990
8181
  // ─── Linear Comment ops ───
@@ -8009,6 +8200,7 @@ var ACTION_REGISTRY = {
8009
8200
  target_bindings: {
8010
8201
  resource_id: { source: "param", param: "commentId" }
8011
8202
  },
8203
+ approval_salient_fields: ["commentId", "body"],
8012
8204
  version: "1.0.0"
8013
8205
  },
8014
8206
  {
@@ -8031,6 +8223,7 @@ var ACTION_REGISTRY = {
8031
8223
  target_bindings: {
8032
8224
  resource_id: { source: "param", param: "commentId" }
8033
8225
  },
8226
+ approval_salient_fields: ["commentId"],
8034
8227
  version: "1.0.0"
8035
8228
  },
8036
8229
  {
@@ -8053,6 +8246,7 @@ var ACTION_REGISTRY = {
8053
8246
  target_bindings: {
8054
8247
  resource_id: { source: "param", param: "commentId" }
8055
8248
  },
8249
+ approval_salient_fields: ["commentId"],
8056
8250
  version: "1.0.0"
8057
8251
  },
8058
8252
  {
@@ -8075,6 +8269,7 @@ var ACTION_REGISTRY = {
8075
8269
  target_bindings: {
8076
8270
  resource_id: { source: "param", param: "commentId" }
8077
8271
  },
8272
+ approval_salient_fields: ["commentId"],
8078
8273
  version: "1.0.0"
8079
8274
  },
8080
8275
  // ─── Linear Issue ops ───
@@ -8098,6 +8293,7 @@ var ACTION_REGISTRY = {
8098
8293
  target_bindings: {
8099
8294
  resource_id: { source: "param", param: "issueId" }
8100
8295
  },
8296
+ approval_salient_fields: ["issueId"],
8101
8297
  version: "1.0.0"
8102
8298
  },
8103
8299
  {
@@ -8121,6 +8317,7 @@ var ACTION_REGISTRY = {
8121
8317
  target_bindings: {
8122
8318
  resource_id: { source: "param", param: "issueId" }
8123
8319
  },
8320
+ approval_salient_fields: ["issueId", "labelId"],
8124
8321
  version: "1.0.0"
8125
8322
  },
8126
8323
  {
@@ -8144,6 +8341,7 @@ var ACTION_REGISTRY = {
8144
8341
  target_bindings: {
8145
8342
  resource_id: { source: "param", param: "issueId" }
8146
8343
  },
8344
+ approval_salient_fields: ["issueId", "labelId"],
8147
8345
  version: "1.0.0"
8148
8346
  },
8149
8347
  {
@@ -8166,6 +8364,7 @@ var ACTION_REGISTRY = {
8166
8364
  target_bindings: {
8167
8365
  resource_id: { source: "param", param: "issueId" }
8168
8366
  },
8367
+ approval_salient_fields: ["issueId"],
8169
8368
  version: "1.0.0"
8170
8369
  },
8171
8370
  {
@@ -8188,6 +8387,7 @@ var ACTION_REGISTRY = {
8188
8387
  target_bindings: {
8189
8388
  resource_id: { source: "param", param: "issueId" }
8190
8389
  },
8390
+ approval_salient_fields: ["issueId"],
8191
8391
  version: "1.0.0"
8192
8392
  },
8193
8393
  // ─── Linear Issue Relations ───
@@ -8213,6 +8413,7 @@ var ACTION_REGISTRY = {
8213
8413
  target_bindings: {
8214
8414
  resource_id: { source: "param", param: "issueId" }
8215
8415
  },
8416
+ approval_salient_fields: ["issueId", "relatedIssueId", "type"],
8216
8417
  version: "1.0.0"
8217
8418
  },
8218
8419
  {
@@ -8257,6 +8458,7 @@ var ACTION_REGISTRY = {
8257
8458
  target_bindings: {
8258
8459
  resource_id: { source: "param", param: "relationId" }
8259
8460
  },
8461
+ approval_salient_fields: ["relationId"],
8260
8462
  version: "1.0.0"
8261
8463
  },
8262
8464
  // ─── Linear Cycles ───
@@ -8328,6 +8530,7 @@ var ACTION_REGISTRY = {
8328
8530
  target_bindings: {
8329
8531
  resource_id: { source: "param", param: "teamId" }
8330
8532
  },
8533
+ approval_salient_fields: ["teamId", "name", "startsAt", "endsAt"],
8331
8534
  version: "1.0.0"
8332
8535
  },
8333
8536
  {
@@ -8355,6 +8558,7 @@ var ACTION_REGISTRY = {
8355
8558
  target_bindings: {
8356
8559
  resource_id: { source: "param", param: "cycleId" }
8357
8560
  },
8561
+ approval_salient_fields: ["cycleId", "name", "startsAt", "endsAt"],
8358
8562
  version: "1.0.0"
8359
8563
  },
8360
8564
  // ─── Linear Labels ───
@@ -8381,6 +8585,7 @@ var ACTION_REGISTRY = {
8381
8585
  target_bindings: {
8382
8586
  resource_id: { source: "param", param: "teamId", required: false }
8383
8587
  },
8588
+ approval_salient_fields: ["teamId", "name"],
8384
8589
  version: "1.0.0"
8385
8590
  },
8386
8591
  {
@@ -8406,6 +8611,7 @@ var ACTION_REGISTRY = {
8406
8611
  target_bindings: {
8407
8612
  resource_id: { source: "param", param: "labelId" }
8408
8613
  },
8614
+ approval_salient_fields: ["labelId", "name"],
8409
8615
  version: "1.0.0"
8410
8616
  },
8411
8617
  // ─── Linear Attachments ───
@@ -8432,6 +8638,7 @@ var ACTION_REGISTRY = {
8432
8638
  target_bindings: {
8433
8639
  resource_id: { source: "param", param: "issueId" }
8434
8640
  },
8641
+ approval_salient_fields: ["issueId", "title", "url"],
8435
8642
  version: "1.0.0"
8436
8643
  },
8437
8644
  {
@@ -8476,6 +8683,7 @@ var ACTION_REGISTRY = {
8476
8683
  target_bindings: {
8477
8684
  resource_id: { source: "param", param: "attachmentId" }
8478
8685
  },
8686
+ approval_salient_fields: ["attachmentId"],
8479
8687
  version: "1.0.0"
8480
8688
  },
8481
8689
  // ─── Agent Inbox Actions (internal) ───
@@ -8506,6 +8714,7 @@ var ACTION_REGISTRY = {
8506
8714
  target_bindings: {
8507
8715
  resource_id: { source: "param", param: "to" }
8508
8716
  },
8717
+ approval_salient_fields: ["to", "taskType", "payload", "message"],
8509
8718
  version: "1.0.0"
8510
8719
  },
8511
8720
  {
@@ -8536,6 +8745,7 @@ var ACTION_REGISTRY = {
8536
8745
  target_bindings: {
8537
8746
  resource_id: { source: "param", param: "id" }
8538
8747
  },
8748
+ approval_salient_fields: ["id", "action", "data"],
8539
8749
  version: "1.0.0"
8540
8750
  },
8541
8751
  {
@@ -8708,6 +8918,10 @@ var ACTION_REGISTRY = {
8708
8918
  target_bindings: {
8709
8919
  resource_id: { source: "param", param: "to" }
8710
8920
  },
8921
+ // #1205 P1: hold changes the real-world effect — when true it tentatively
8922
+ // books [仮] events on the sender's calendar for every offered slot, so it
8923
+ // is approval-critical.
8924
+ approval_salient_fields: ["to", "topic", "durationMinutes", "candidates", "candidateWindow", "hold"],
8711
8925
  version: "1.0.0"
8712
8926
  },
8713
8927
  // ─── PR-R2 (design §3.2): the raw booking executor's ONE new action ───
@@ -8768,6 +8982,46 @@ var ACTION_REGISTRY = {
8768
8982
  target_bindings: {
8769
8983
  resource_id: { source: "param", param: "itemId" }
8770
8984
  },
8985
+ approval_salient_fields: ["itemId", "decision", "slots"],
8986
+ version: "1.0.0"
8987
+ },
8988
+ {
8989
+ action: "doc_review.submit_response",
8990
+ resource_type: "inbox:item",
8991
+ required_relations: ["editor", "act_as"],
8992
+ required_scopes: ["inbox"],
8993
+ capability: "inbox.write",
8994
+ input_schema: {
8995
+ type: "object",
8996
+ properties: {
8997
+ itemId: { type: "string", minLength: 1, description: "The inbox item the review answers" },
8998
+ decision: { type: "string", enum: ["reviewed", "declined"], description: "Whether the agent returns review findings or declines" },
8999
+ findings: {
9000
+ type: "array",
9001
+ maxItems: 50,
9002
+ description: "Review findings (empty for a decline). Each cites a verbatim quote from the document.",
9003
+ items: {
9004
+ type: "object",
9005
+ properties: {
9006
+ quote: { type: "string", description: "Verbatim excerpt from the reviewed document that the comment refers to" },
9007
+ comment: { type: "string", description: "The reviewer comment on that excerpt" }
9008
+ },
9009
+ required: ["quote", "comment"],
9010
+ additionalProperties: false
9011
+ }
9012
+ },
9013
+ summary: { type: "string", description: "Optional overall summary of the review" }
9014
+ },
9015
+ required: ["itemId", "decision"],
9016
+ additionalProperties: false
9017
+ },
9018
+ constraints: { rate_bucket: "inbox.write" },
9019
+ effects: ["Update:InboxItem"],
9020
+ risk: "high",
9021
+ target_bindings: {
9022
+ resource_id: { source: "param", param: "itemId" }
9023
+ },
9024
+ approval_salient_fields: ["itemId", "decision", "findings", "summary"],
8771
9025
  version: "1.0.0"
8772
9026
  }
8773
9027
  ],
@@ -8865,7 +9119,7 @@ var ACTION_REGISTRY = {
8865
9119
  {
8866
9120
  capability: "inbox.write",
8867
9121
  description: "Send and respond to agent inbox tasks",
8868
- includes: ["inbox.send", "inbox.respond", "inbox.complete", "scheduling.request", "scheduling.submit_response"],
9122
+ includes: ["inbox.send", "inbox.respond", "inbox.complete", "scheduling.request", "scheduling.submit_response", "doc_review.submit_response"],
8869
9123
  version: "1.0.0"
8870
9124
  },
8871
9125
  {
@@ -9254,6 +9508,27 @@ var GATEWAY_ERROR_CODE = {
9254
9508
  APPROVAL_REQUIRED: "APPROVAL_REQUIRED"
9255
9509
  };
9256
9510
 
9511
+ // src/resolver/approval-time-resolution.ts
9512
+ var RESOLVED_PARAMS_KEY = "__resolvedParams";
9513
+ function stripResolvedParams(params) {
9514
+ if (!params || typeof params !== "object") return params;
9515
+ if (!Object.prototype.hasOwnProperty.call(params, RESOLVED_PARAMS_KEY)) return params;
9516
+ const { [RESOLVED_PARAMS_KEY]: _dropped, ...rest } = params;
9517
+ return rest;
9518
+ }
9519
+ var RESOLVER_BACKED_FIELDS = {
9520
+ "slack.message.post": [{ field: "channel", kind: "slack-channel" }],
9521
+ "slack.message.update": [{ field: "channel", kind: "slack-channel" }],
9522
+ "slack.message.delete": [{ field: "channel", kind: "slack-channel" }]
9523
+ // jira `issueIdOrKey` / linear `issueId` are intentionally NOT here — verified
9524
+ // verbatim passthrough, no pre-PEP server canonicalization (see the block
9525
+ // above). Do not add them without a real, demonstrated pre-PEP rewrite.
9526
+ };
9527
+ function resolverBackedFieldsFor(action) {
9528
+ const fields = RESOLVER_BACKED_FIELDS[action];
9529
+ return fields && fields.length > 0 ? fields : void 0;
9530
+ }
9531
+
9257
9532
  // src/registry/action-summary.ts
9258
9533
  function extractJiraAssigneeDisplay(params) {
9259
9534
  const pickFromShape = (value) => {
@@ -9308,6 +9583,37 @@ var ACTION_DISPLAY_CONFIGS = {
9308
9583
  { key: "text", label: "Message" }
9309
9584
  ]
9310
9585
  },
9586
+ // #1205 P1 Step 4.6 — update / delete need explicit display configs so the
9587
+ // approval UI renders a labeled `Channel` field (which the API-side enricher
9588
+ // upserts the frozen id→name mapping into) instead of falling through to the
9589
+ // all-keys fallback branch, which would (a) render a raw lowercase `channel`
9590
+ // id row that the enricher then DUPLICATES with a `Channel` name row, and
9591
+ // (b) risk leaking the reserved __resolvedParams key.
9592
+ "slack.message.update": {
9593
+ summaryTemplate: (p) => {
9594
+ const parts = [];
9595
+ if (p.channel) parts.push(p.channel + ":");
9596
+ if (p.text) parts.push(String(p.text));
9597
+ return parts.join(" ");
9598
+ },
9599
+ displayFields: [
9600
+ { key: "channel", label: "Channel" },
9601
+ { key: "ts", label: "Timestamp" },
9602
+ { key: "text", label: "Message" }
9603
+ ]
9604
+ },
9605
+ "slack.message.delete": {
9606
+ summaryTemplate: (p) => {
9607
+ const parts = [];
9608
+ if (p.channel) parts.push(p.channel + ":");
9609
+ if (p.ts) parts.push(String(p.ts));
9610
+ return parts.join(" ");
9611
+ },
9612
+ displayFields: [
9613
+ { key: "channel", label: "Channel" },
9614
+ { key: "ts", label: "Timestamp" }
9615
+ ]
9616
+ },
9311
9617
  "gmail.message.send": {
9312
9618
  summaryTemplate: (p) => {
9313
9619
  const parts = [];
@@ -10152,6 +10458,20 @@ var ACTION_DISPLAY_CONFIGS = {
10152
10458
  { key: "recommendedSlotIndex", label: "Recommended index" },
10153
10459
  { key: "recommendationReason", label: "Reason" }
10154
10460
  ]
10461
+ },
10462
+ // Phase D D0 (doc-review design §3.1), registry checklist B. Like
10463
+ // scheduling.submit_response this is risk:'high', so its summary/displayFields
10464
+ // are what a human actually reads in the approval and audit surfaces — the
10465
+ // string is user-facing, not decoration. Names the item being answered the way
10466
+ // scheduling.submit_response does.
10467
+ "doc_review.submit_response": {
10468
+ summaryTemplate: (p) => p.decision === "declined" ? `Decline the document review for item ${p.itemId ?? "(unknown)"}` : `Submit a document review for item ${p.itemId ?? "(unknown)"}`,
10469
+ displayFields: [
10470
+ { key: "itemId", label: "Inbox item" },
10471
+ { key: "decision", label: "Decision" },
10472
+ { key: "findings", label: "Findings" },
10473
+ { key: "summary", label: "Summary" }
10474
+ ]
10155
10475
  }
10156
10476
  };
10157
10477
  function truncate(text, maxLength) {
@@ -10314,6 +10634,7 @@ function generateActionSummary(action, params, maxLength = 50) {
10314
10634
  return raw ? truncate(raw, maxLength) : "";
10315
10635
  }
10316
10636
  for (const [key, val] of Object.entries(params)) {
10637
+ if (key === RESOLVED_PARAMS_KEY) continue;
10317
10638
  if (val !== null && val !== void 0 && typeof val !== "object") {
10318
10639
  return truncate(`${key}: ${String(val)}`, maxLength);
10319
10640
  }
@@ -10329,7 +10650,7 @@ function generateActionParamsDisplay(action, params) {
10329
10650
  return { label: f.label, raw };
10330
10651
  }).filter((item) => item.raw !== void 0).map((item) => ({ label: item.label, value: formatValueFull(item.raw) }));
10331
10652
  }
10332
- return Object.entries(params).filter(([, v]) => v !== void 0 && v !== null).map(([key, val]) => ({
10653
+ return Object.entries(params).filter(([key, v]) => key !== RESOLVED_PARAMS_KEY && v !== void 0 && v !== null).map(([key, val]) => ({
10333
10654
  label: key,
10334
10655
  value: formatValueFull(val)
10335
10656
  }));
@@ -10433,6 +10754,102 @@ function canonicalResourceId(action, params, ctx) {
10433
10754
  return id;
10434
10755
  }
10435
10756
 
10757
+ // src/resolver/salient-digest.ts
10758
+ import { createHash } from "crypto";
10759
+ function serializeValue(value) {
10760
+ if (value === null || value === void 0) return "null";
10761
+ const t = typeof value;
10762
+ if (t === "boolean") return value ? "true" : "false";
10763
+ if (t === "number") {
10764
+ const n = value;
10765
+ if (!Number.isFinite(n)) {
10766
+ throw new Error(`salient-digest: non-finite number is not allowed in salient params: ${String(n)}`);
10767
+ }
10768
+ if (!Number.isInteger(n)) {
10769
+ throw new Error(
10770
+ `salient-digest: float value is not allowed in salient params (phase 1 forbids non-integer numbers): ${String(n)}`
10771
+ );
10772
+ }
10773
+ if (!Number.isSafeInteger(n)) {
10774
+ throw new Error(
10775
+ `salient-digest: unsafe integer is not allowed in salient params (exceeds 2^53-1, cannot round-trip losslessly): ${String(n)}`
10776
+ );
10777
+ }
10778
+ return String(n);
10779
+ }
10780
+ if (t === "string") return JSON.stringify(value.normalize("NFC"));
10781
+ if (Array.isArray(value)) {
10782
+ return "[" + value.map(serializeValue).join(",") + "]";
10783
+ }
10784
+ if (t === "object") {
10785
+ const obj = value;
10786
+ const seen = /* @__PURE__ */ new Map();
10787
+ for (const rawKey of Object.keys(obj)) {
10788
+ const normKey = rawKey.normalize("NFC");
10789
+ const prior = seen.get(normKey);
10790
+ if (prior !== void 0 && prior !== rawKey) {
10791
+ throw new Error(
10792
+ `salient-digest: duplicate object key after NFC normalization ("${prior}" and "${rawKey}" both normalize to "${normKey}")`
10793
+ );
10794
+ }
10795
+ seen.set(normKey, rawKey);
10796
+ }
10797
+ const normKeys = [...seen.keys()].sort();
10798
+ const parts = normKeys.map((normKey) => {
10799
+ const rawKey = seen.get(normKey);
10800
+ return JSON.stringify(normKey) + ":" + serializeValue(obj[rawKey]);
10801
+ });
10802
+ return "{" + parts.join(",") + "}";
10803
+ }
10804
+ throw new Error(`salient-digest: unsupported value type in salient params: ${t}`);
10805
+ }
10806
+ function canonicalizeWithFields(fields, params) {
10807
+ const seen = /* @__PURE__ */ new Map();
10808
+ for (const field of fields) {
10809
+ const normField = field.normalize("NFC");
10810
+ const prior = seen.get(normField);
10811
+ if (prior !== void 0 && prior !== field) {
10812
+ throw new Error(
10813
+ `salient-digest: duplicate salient field after NFC normalization ("${prior}" and "${field}" both normalize to "${normField}")`
10814
+ );
10815
+ }
10816
+ seen.set(normField, field);
10817
+ }
10818
+ const normFields = [...seen.keys()].sort();
10819
+ const parts = normFields.map((normField) => {
10820
+ const field = seen.get(normField);
10821
+ const present = Object.prototype.hasOwnProperty.call(params, field);
10822
+ const value = present ? params[field] : null;
10823
+ return JSON.stringify(normField) + ":" + serializeValue(value);
10824
+ });
10825
+ return "{" + parts.join(",") + "}";
10826
+ }
10827
+ function digestWithFields(fields, params) {
10828
+ const canonical = canonicalizeWithFields(fields, params);
10829
+ const hex = createHash("sha256").update(canonical, "utf8").digest("hex");
10830
+ return `sha256:${hex}`;
10831
+ }
10832
+ function salientFieldsFor(action) {
10833
+ const def = ACTION_REGISTRY.actions.find((a) => a.action === action);
10834
+ if (!def) return void 0;
10835
+ const fields = def.approval_salient_fields;
10836
+ if (!Array.isArray(fields)) return void 0;
10837
+ if (!fields.every((f) => typeof f === "string")) return void 0;
10838
+ return fields;
10839
+ }
10840
+ function canonicalizeSalient(action, params) {
10841
+ const fields = salientFieldsFor(action);
10842
+ if (!fields) {
10843
+ throw new Error(`salient-digest: action "${action}" has no declared approval-salient fields`);
10844
+ }
10845
+ return canonicalizeWithFields(fields, params);
10846
+ }
10847
+ function salientDigest(action, params) {
10848
+ const fields = salientFieldsFor(action);
10849
+ if (!fields) return void 0;
10850
+ return digestWithFields(fields, params);
10851
+ }
10852
+
10436
10853
  // src/resolver/compare-resource-id.ts
10437
10854
  function looksLikeNativeId(value) {
10438
10855
  if (/^[CDG][A-Z0-9]{8,}$/.test(value)) return true;
@@ -10517,11 +10934,11 @@ function isFreemailDomain(domain) {
10517
10934
  }
10518
10935
 
10519
10936
  // src/internal-signature/canonical.ts
10520
- import { createHash } from "crypto";
10937
+ import { createHash as createHash2 } from "crypto";
10521
10938
  var SIGNATURE_HEADER = "x-internal-signature";
10522
10939
  var SIGNATURE_VERSION_PREFIX = "v1=";
10523
10940
  function sha256Hex(input) {
10524
- return createHash("sha256").update(input).digest("hex");
10941
+ return createHash2("sha256").update(input).digest("hex");
10525
10942
  }
10526
10943
  function buildCanonicalString(args) {
10527
10944
  const { method, path: path4, unixSeconds, rawBody } = args;
@@ -10768,6 +11185,7 @@ function parseEntityUid(uid) {
10768
11185
  }
10769
11186
 
10770
11187
  // src/registry/action-risk.ts
11188
+ var UNCLASSIFIED_ACTION_RISK = "high";
10771
11189
  var HIGH_RISK_SUFFIXES = /* @__PURE__ */ new Set([
10772
11190
  "send",
10773
11191
  "post",
@@ -10811,13 +11229,13 @@ var REGISTRY_RISK_BY_ACTION = (() => {
10811
11229
  function resolveActionRiskBySuffix(lowerAction) {
10812
11230
  const segments = lowerAction.split(".");
10813
11231
  const last = segments[segments.length - 1] ?? "";
10814
- if (!last) return "medium";
11232
+ if (!last) return UNCLASSIFIED_ACTION_RISK;
10815
11233
  if (HIGH_RISK_SUFFIXES.has(last)) return "high";
10816
11234
  if (LOW_RISK_SUFFIXES.has(last)) return "low";
10817
- return "medium";
11235
+ return UNCLASSIFIED_ACTION_RISK;
10818
11236
  }
10819
11237
  function resolveActionRisk(action) {
10820
- if (!action || typeof action !== "string") return "medium";
11238
+ if (!action || typeof action !== "string") return UNCLASSIFIED_ACTION_RISK;
10821
11239
  const lower = action.toLowerCase();
10822
11240
  const registryRisk = REGISTRY_RISK_BY_ACTION.get(lower);
10823
11241
  if (registryRisk) return registryRisk;
@@ -10908,6 +11326,7 @@ export {
10908
11326
  PHASE_1_VC_LAYER,
10909
11327
  PROVIDER_ALIASES,
10910
11328
  REAUTH_REQUIRED_ACTION,
11329
+ RESOLVED_PARAMS_KEY,
10911
11330
  RESOURCE_TYPES,
10912
11331
  RESOURCE_TYPE_LABELS,
10913
11332
  ReceiptStatus,
@@ -10920,6 +11339,7 @@ export {
10920
11339
  TIER_LIMITS,
10921
11340
  TargetResolver,
10922
11341
  ToolManager,
11342
+ UNCLASSIFIED_ACTION_RISK,
10923
11343
  UserIdentityManager,
10924
11344
  UserKeyPairManager,
10925
11345
  VALID_MCP_ACTIONS,
@@ -10939,6 +11359,8 @@ export {
10939
11359
  buildValidationErrors,
10940
11360
  canonicalResourceId,
10941
11361
  canonicalizeAction,
11362
+ canonicalizeSalient,
11363
+ canonicalizeWithFields,
10942
11364
  checkPermissionWithVP,
10943
11365
  classifyCedarErrorMessage,
10944
11366
  compareResourceIds,
@@ -10948,6 +11370,7 @@ export {
10948
11370
  createDidJwk,
10949
11371
  credentialStatusToVCStatus,
10950
11372
  defaultConstraintEvaluator,
11373
+ digestWithFields,
10951
11374
  evaluateConstraints,
10952
11375
  extractProjectKey,
10953
11376
  extractPublicKey,
@@ -10997,9 +11420,13 @@ export {
10997
11420
  resolveProvider,
10998
11421
  resolveResourceType,
10999
11422
  resolveUserTier,
11423
+ resolverBackedFieldsFor,
11424
+ salientDigest,
11425
+ salientFieldsFor,
11000
11426
  sha256Hex,
11001
11427
  signJWT,
11002
11428
  signRequest,
11429
+ stripResolvedParams,
11003
11430
  validateActionInput,
11004
11431
  validateRegistryObject,
11005
11432
  vcStatusToCredentialStatus,