@vess-id/ai-identity 0.18.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 +4342 -1541
  2. package/dist/index.d.ts +4 -1
  3. package/dist/index.d.ts.map +1 -1
  4. package/dist/index.js +522 -23
  5. package/dist/index.js.map +1 -1
  6. package/dist/index.mjs +515 -25
  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 +3868 -1325
  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.js CHANGED
@@ -76,6 +76,7 @@ __export(index_exports, {
76
76
  PHASE_1_VC_LAYER: () => PHASE_1_VC_LAYER,
77
77
  PROVIDER_ALIASES: () => PROVIDER_ALIASES,
78
78
  REAUTH_REQUIRED_ACTION: () => REAUTH_REQUIRED_ACTION,
79
+ RESOLVED_PARAMS_KEY: () => RESOLVED_PARAMS_KEY,
79
80
  RESOURCE_TYPES: () => RESOURCE_TYPES,
80
81
  RESOURCE_TYPE_LABELS: () => RESOURCE_TYPE_LABELS,
81
82
  ReceiptStatus: () => ReceiptStatus,
@@ -88,6 +89,7 @@ __export(index_exports, {
88
89
  TIER_LIMITS: () => TIER_LIMITS,
89
90
  TargetResolver: () => TargetResolver,
90
91
  ToolManager: () => ToolManager,
92
+ UNCLASSIFIED_ACTION_RISK: () => UNCLASSIFIED_ACTION_RISK,
91
93
  UserIdentityManager: () => UserIdentityManager,
92
94
  UserKeyPairManager: () => UserKeyPairManager,
93
95
  VALID_MCP_ACTIONS: () => VALID_MCP_ACTIONS,
@@ -107,6 +109,8 @@ __export(index_exports, {
107
109
  buildValidationErrors: () => buildValidationErrors,
108
110
  canonicalResourceId: () => canonicalResourceId,
109
111
  canonicalizeAction: () => canonicalizeAction,
112
+ canonicalizeSalient: () => canonicalizeSalient,
113
+ canonicalizeWithFields: () => canonicalizeWithFields,
110
114
  checkPermissionWithVP: () => checkPermissionWithVP,
111
115
  classifyCedarErrorMessage: () => classifyCedarErrorMessage,
112
116
  compareResourceIds: () => compareResourceIds,
@@ -116,6 +120,7 @@ __export(index_exports, {
116
120
  createDidJwk: () => createDidJwk,
117
121
  credentialStatusToVCStatus: () => credentialStatusToVCStatus,
118
122
  defaultConstraintEvaluator: () => defaultConstraintEvaluator,
123
+ digestWithFields: () => digestWithFields,
119
124
  evaluateConstraints: () => evaluateConstraints,
120
125
  extractProjectKey: () => extractProjectKey,
121
126
  extractPublicKey: () => extractPublicKey,
@@ -165,9 +170,13 @@ __export(index_exports, {
165
170
  resolveProvider: () => resolveProvider,
166
171
  resolveResourceType: () => resolveResourceType,
167
172
  resolveUserTier: () => resolveUserTier,
173
+ resolverBackedFieldsFor: () => resolverBackedFieldsFor,
174
+ salientDigest: () => salientDigest,
175
+ salientFieldsFor: () => salientFieldsFor,
168
176
  sha256Hex: () => sha256Hex,
169
177
  signJWT: () => signJWT,
170
178
  signRequest: () => signRequest,
179
+ stripResolvedParams: () => stripResolvedParams,
171
180
  validateActionInput: () => validateActionInput,
172
181
  validateRegistryObject: () => validateRegistryObject,
173
182
  vcStatusToCredentialStatus: () => vcStatusToCredentialStatus,
@@ -2754,6 +2763,8 @@ var CredentialType = /* @__PURE__ */ ((CredentialType2) => {
2754
2763
  CredentialType2["TEMPORARY"] = "TemporaryCredential";
2755
2764
  CredentialType2["ADMIN"] = "AdminCredential";
2756
2765
  CredentialType2["RECEIPT"] = "ReceiptCredential";
2766
+ CredentialType2["ACCOUNT"] = "AccountCredential";
2767
+ CredentialType2["AGENT_CARD"] = "AgentCardCredential";
2757
2768
  return CredentialType2;
2758
2769
  })(CredentialType || {});
2759
2770
  var CredentialStatus = /* @__PURE__ */ ((CredentialStatus2) => {
@@ -3200,6 +3211,38 @@ var DEFAULT_DISCLOSURE_CONFIGS = {
3200
3211
  mandatoryFields: [],
3201
3212
  neverDisclose: DEFAULT_NEVER_DISCLOSE
3202
3213
  },
3214
+ // OEM ⑯ — account attributes an organisation asserts about the holder.
3215
+ //
3216
+ // Everything identifying is selective. An account credential is presented to
3217
+ // decide whether an action is permitted, and that rarely requires knowing
3218
+ // the account number or whose name is on it. Left always-disclosed, every
3219
+ // verifier the holder ever presents to would learn both — and the fallback
3220
+ // for an unregistered type is TEMPORARY, whose selectiveFields is empty,
3221
+ // which is exactly that outcome. Hence this entry.
3222
+ ["AccountCredential" /* ACCOUNT */]: {
3223
+ selectiveFields: ["accountNumber", "accountHolderName", "branchCode"],
3224
+ mandatoryFields: [],
3225
+ neverDisclose: DEFAULT_NEVER_DISCLOSE
3226
+ },
3227
+ // OEM ⑯ Model C — the instance card.
3228
+ //
3229
+ // service_card_url and service_card_hash are absent from selectiveFields,
3230
+ // which is what makes them always-disclosed: the hash proves the fetched
3231
+ // card is the one the issuer signed over, and a holder able to withhold it
3232
+ // could present a card body nobody attested to (AP2 binds checkout_hash for
3233
+ // the same reason).
3234
+ //
3235
+ // 🔴 They are listed in mandatoryFields as documentation, NOT as
3236
+ // enforcement. APIVCManager.issueSDJWTVC passes only `selectiveFields` to
3237
+ // the SD-JWT builder — `mandatoryFields` and `neverDisclose` are not
3238
+ // consumed on that path. Moving these three into selectiveFields would
3239
+ // silently make the hash withholdable and nothing would fail, so the
3240
+ // omission below is the actual guarantee.
3241
+ ["AgentCardCredential" /* AGENT_CARD */]: {
3242
+ selectiveFields: ["version"],
3243
+ mandatoryFields: ["service_card_url", "service_card_hash", "service_id"],
3244
+ neverDisclose: DEFAULT_NEVER_DISCLOSE
3245
+ },
3203
3246
  ["ReceiptCredential" /* RECEIPT */]: {
3204
3247
  selectiveFields: ["action", "resource", "outcome", "amount", "description", "date", "transactionId"],
3205
3248
  mandatoryFields: ["grantId", "auditEventId", "receiptType"],
@@ -4116,6 +4159,13 @@ var actionMetaSchema = {
4116
4159
  },
4117
4160
  risk: { type: "string", enum: ["low", "medium", "high", "critical"] },
4118
4161
  target_bindings: { type: "object" },
4162
+ // Approval-salient parameter names (P1 / #1205). Optional at the schema
4163
+ // level (legacy/low-risk actions may omit it); the completeness test makes
4164
+ // it mandatory for every high/critical action.
4165
+ approval_salient_fields: {
4166
+ type: "array",
4167
+ items: { type: "string", minLength: 1 }
4168
+ },
4119
4169
  version: { type: "string", minLength: 1 }
4120
4170
  }
4121
4171
  };
@@ -4550,6 +4600,16 @@ var ACTION_REGISTRY = {
4550
4600
  target_bindings: {
4551
4601
  resource_id: { source: "param", param: "channel" }
4552
4602
  },
4603
+ // #1205 P1: `channel` IS approval-critical (the human approved posting to
4604
+ // THAT channel) so it is declared salient. ⚠️ HOLE 2 (plan §0.5, resolved
4605
+ // in Step 4.6): the server normalizes `channel` (#name → native Cxxx id)
4606
+ // at execution via a LIVE Slack lookup, so the issued-time digest
4607
+ // (hash of "#general") and the PEP-time digest (hash of "C09…") diverge and
4608
+ // a legitimate approved post fail-closes. Step 4.6 must resolve the
4609
+ // name→id AT APPROVAL TIME and freeze it into actionParams so all
4610
+ // participants hash the same effective value. Do NOT drop `channel` from
4611
+ // this set to paper over HOLE 2 — that would unbind the approved target.
4612
+ approval_salient_fields: ["channel", "text", "blocks"],
4553
4613
  version: "1.0.0"
4554
4614
  },
4555
4615
  {
@@ -4691,6 +4751,7 @@ var ACTION_REGISTRY = {
4691
4751
  target_bindings: {
4692
4752
  resource_id: { source: "param", param: "channel" }
4693
4753
  },
4754
+ approval_salient_fields: ["channel", "ts", "text"],
4694
4755
  version: "1.0.0"
4695
4756
  },
4696
4757
  {
@@ -4714,6 +4775,7 @@ var ACTION_REGISTRY = {
4714
4775
  target_bindings: {
4715
4776
  resource_id: { source: "param", param: "channel" }
4716
4777
  },
4778
+ approval_salient_fields: ["channel", "ts"],
4717
4779
  version: "1.0.0"
4718
4780
  },
4719
4781
  // ─── GitHub Actions ───
@@ -4746,6 +4808,11 @@ var ACTION_REGISTRY = {
4746
4808
  target_bindings: {
4747
4809
  resource_id: { source: "param", param: "repo", fallback_param: "repository" }
4748
4810
  },
4811
+ // #1205 P1: assignees (who the issue is assigned to) is approval-critical —
4812
+ // an agent could otherwise reassign it post-approval. labels are part of
4813
+ // the approved issue definition. (milestone is an integer id; not
4814
+ // substance-of-approval so left out — see completeness-test allowlist.)
4815
+ approval_salient_fields: ["owner", "repo", "title", "body", "assignees", "labels"],
4749
4816
  version: "1.0.0"
4750
4817
  },
4751
4818
  {
@@ -4836,6 +4903,10 @@ var ACTION_REGISTRY = {
4836
4903
  target_bindings: {
4837
4904
  resource_id: { source: "param", param: "repo", fallback_param: "repository" }
4838
4905
  },
4906
+ // #1205 P1: assignees (who the issue is assigned to) is approval-critical —
4907
+ // an agent could otherwise reassign it post-approval. labels are part of
4908
+ // the approved change.
4909
+ approval_salient_fields: ["owner", "repo", "issueNumber", "title", "body", "state", "assignees", "labels"],
4839
4910
  version: "1.0.0"
4840
4911
  },
4841
4912
  // ─── GitHub Pull Request Actions ───
@@ -4865,6 +4936,9 @@ var ACTION_REGISTRY = {
4865
4936
  target_bindings: {
4866
4937
  resource_id: { source: "context", key: "resource_id" }
4867
4938
  },
4939
+ // #1205 P1: draft changes the real-world effect (a non-draft PR notifies
4940
+ // reviewers / can be merged immediately), so it is approval-critical.
4941
+ approval_salient_fields: ["owner", "repo", "title", "head", "base", "body", "draft"],
4868
4942
  version: "1.0.0"
4869
4943
  },
4870
4944
  {
@@ -4947,6 +5021,7 @@ var ACTION_REGISTRY = {
4947
5021
  target_bindings: {
4948
5022
  resource_id: { source: "context", key: "resource_id" }
4949
5023
  },
5024
+ approval_salient_fields: ["owner", "repo", "pullNumber", "title", "body", "state", "base"],
4950
5025
  version: "1.0.0"
4951
5026
  },
4952
5027
  {
@@ -4974,6 +5049,9 @@ var ACTION_REGISTRY = {
4974
5049
  target_bindings: {
4975
5050
  resource_id: { source: "context", key: "resource_id" }
4976
5051
  },
5052
+ // #1205 P1: commitTitle/commitMessage are persisted into the merge commit
5053
+ // (permanent git history), so they are approval-critical.
5054
+ approval_salient_fields: ["owner", "repo", "pullNumber", "mergeMethod", "commitTitle", "commitMessage"],
4977
5055
  version: "1.0.0"
4978
5056
  },
4979
5057
  {
@@ -5050,6 +5128,7 @@ var ACTION_REGISTRY = {
5050
5128
  target_bindings: {
5051
5129
  resource_id: { source: "context", key: "resource_id" }
5052
5130
  },
5131
+ approval_salient_fields: ["owner", "repo", "pullNumber", "event", "body"],
5053
5132
  version: "1.0.0"
5054
5133
  },
5055
5134
  {
@@ -5076,6 +5155,7 @@ var ACTION_REGISTRY = {
5076
5155
  target_bindings: {
5077
5156
  resource_id: { source: "context", key: "resource_id" }
5078
5157
  },
5158
+ approval_salient_fields: ["owner", "repo", "pullNumber", "commentId", "body"],
5079
5159
  version: "1.0.0"
5080
5160
  },
5081
5161
  {
@@ -5101,6 +5181,7 @@ var ACTION_REGISTRY = {
5101
5181
  target_bindings: {
5102
5182
  resource_id: { source: "context", key: "resource_id" }
5103
5183
  },
5184
+ approval_salient_fields: ["owner", "repo", "pullNumber"],
5104
5185
  version: "1.0.0"
5105
5186
  },
5106
5187
  {
@@ -5479,6 +5560,7 @@ var ACTION_REGISTRY = {
5479
5560
  target_bindings: {
5480
5561
  resource_id: { source: "context", key: "resource_id" }
5481
5562
  },
5563
+ approval_salient_fields: ["name", "org", "private"],
5482
5564
  version: "1.0.0"
5483
5565
  },
5484
5566
  {
@@ -5503,6 +5585,7 @@ var ACTION_REGISTRY = {
5503
5585
  target_bindings: {
5504
5586
  resource_id: { source: "context", key: "resource_id" }
5505
5587
  },
5588
+ approval_salient_fields: ["owner", "repo", "organization"],
5506
5589
  version: "1.0.0"
5507
5590
  },
5508
5591
  {
@@ -5531,6 +5614,14 @@ var ACTION_REGISTRY = {
5531
5614
  target_bindings: {
5532
5615
  resource_id: { source: "context", key: "resource_id" }
5533
5616
  },
5617
+ // #1205 P1: sha is a version pin (the blob revision being overwritten). The
5618
+ // executor forwards params.sha verbatim — never recomputed/refetched between
5619
+ // approval and execution (github.service.ts:1194) — so binding it causes NO
5620
+ // false-fail-closed; an unbound sha would let an agent overwrite a different
5621
+ // file revision than the human reviewed. On create sha is omitted → the
5622
+ // serializer represents the missing key as JSON null (salient-digest rule 2),
5623
+ // stable on both issued and PEP sides.
5624
+ approval_salient_fields: ["owner", "repo", "path", "content", "message", "branch", "sha"],
5534
5625
  version: "1.0.0"
5535
5626
  },
5536
5627
  {
@@ -5558,6 +5649,11 @@ var ACTION_REGISTRY = {
5558
5649
  target_bindings: {
5559
5650
  resource_id: { source: "context", key: "resource_id" }
5560
5651
  },
5652
+ // #1205 P1: sha (required here) pins the exact blob being deleted; forwarded
5653
+ // verbatim to the API, so binding it prevents an agent from swapping the
5654
+ // target revision post-approval. Same no-false-fail-closed reasoning as
5655
+ // github.content.createOrUpdate.
5656
+ approval_salient_fields: ["owner", "repo", "path", "message", "branch", "sha"],
5561
5657
  version: "1.0.0"
5562
5658
  },
5563
5659
  {
@@ -5583,6 +5679,7 @@ var ACTION_REGISTRY = {
5583
5679
  target_bindings: {
5584
5680
  resource_id: { source: "context", key: "resource_id" }
5585
5681
  },
5682
+ approval_salient_fields: ["owner", "repo", "branch", "fromBranch"],
5586
5683
  version: "1.0.0"
5587
5684
  },
5588
5685
  // ─── GitHub Issues 拡張 & Labels Actions ───
@@ -5609,6 +5706,7 @@ var ACTION_REGISTRY = {
5609
5706
  target_bindings: {
5610
5707
  resource_id: { source: "context", key: "resource_id" }
5611
5708
  },
5709
+ approval_salient_fields: ["owner", "repo", "issueNumber", "body"],
5612
5710
  version: "1.0.0"
5613
5711
  },
5614
5712
  {
@@ -5634,6 +5732,7 @@ var ACTION_REGISTRY = {
5634
5732
  target_bindings: {
5635
5733
  resource_id: { source: "context", key: "resource_id" }
5636
5734
  },
5735
+ approval_salient_fields: ["owner", "repo", "issueNumber", "subIssueId"],
5637
5736
  version: "1.0.0"
5638
5737
  },
5639
5738
  {
@@ -5758,6 +5857,7 @@ var ACTION_REGISTRY = {
5758
5857
  target_bindings: {
5759
5858
  resource_id: { source: "context", key: "resource_id" }
5760
5859
  },
5860
+ approval_salient_fields: ["owner", "repo", "name", "newName"],
5761
5861
  version: "1.0.0"
5762
5862
  },
5763
5863
  // ─── GitHub Actions(CI) / Stargazers / Context / Users Actions ───
@@ -5844,6 +5944,7 @@ var ACTION_REGISTRY = {
5844
5944
  target_bindings: {
5845
5945
  resource_id: { source: "context", key: "resource_id" }
5846
5946
  },
5947
+ approval_salient_fields: ["owner", "repo", "workflowId", "ref"],
5847
5948
  version: "1.0.0"
5848
5949
  },
5849
5950
  {
@@ -5916,6 +6017,7 @@ var ACTION_REGISTRY = {
5916
6017
  target_bindings: {
5917
6018
  resource_id: { source: "context", key: "resource_id" }
5918
6019
  },
6020
+ approval_salient_fields: ["owner", "repo"],
5919
6021
  version: "1.0.0"
5920
6022
  },
5921
6023
  {
@@ -5939,6 +6041,7 @@ var ACTION_REGISTRY = {
5939
6041
  target_bindings: {
5940
6042
  resource_id: { source: "context", key: "resource_id" }
5941
6043
  },
6044
+ approval_salient_fields: ["owner", "repo"],
5942
6045
  version: "1.0.0"
5943
6046
  },
5944
6047
  {
@@ -6106,6 +6209,7 @@ var ACTION_REGISTRY = {
6106
6209
  target_bindings: {
6107
6210
  resource_id: { source: "context", key: "resource_id" }
6108
6211
  },
6212
+ approval_salient_fields: ["threadId"],
6109
6213
  version: "1.0.0"
6110
6214
  },
6111
6215
  {
@@ -6128,6 +6232,7 @@ var ACTION_REGISTRY = {
6128
6232
  target_bindings: {
6129
6233
  resource_id: { source: "context", key: "resource_id" }
6130
6234
  },
6235
+ approval_salient_fields: ["lastReadAt"],
6131
6236
  version: "1.0.0"
6132
6237
  },
6133
6238
  {
@@ -6151,6 +6256,7 @@ var ACTION_REGISTRY = {
6151
6256
  target_bindings: {
6152
6257
  resource_id: { source: "context", key: "resource_id" }
6153
6258
  },
6259
+ approval_salient_fields: ["threadId", "ignored"],
6154
6260
  version: "1.0.0"
6155
6261
  },
6156
6262
  {
@@ -6176,6 +6282,7 @@ var ACTION_REGISTRY = {
6176
6282
  target_bindings: {
6177
6283
  resource_id: { source: "context", key: "resource_id" }
6178
6284
  },
6285
+ approval_salient_fields: ["owner", "repo", "subscribed", "ignored"],
6179
6286
  version: "1.0.0"
6180
6287
  },
6181
6288
  {
@@ -6204,6 +6311,7 @@ var ACTION_REGISTRY = {
6204
6311
  target_bindings: {
6205
6312
  resource_id: { source: "context", key: "resource_id" }
6206
6313
  },
6314
+ approval_salient_fields: ["files", "description", "public"],
6207
6315
  version: "1.0.0"
6208
6316
  },
6209
6317
  {
@@ -6279,6 +6387,7 @@ var ACTION_REGISTRY = {
6279
6387
  target_bindings: {
6280
6388
  resource_id: { source: "context", key: "resource_id" }
6281
6389
  },
6390
+ approval_salient_fields: ["gistId", "files", "description"],
6282
6391
  version: "1.0.0"
6283
6392
  },
6284
6393
  // ─── Gmail Actions ───
@@ -6358,6 +6467,7 @@ var ACTION_REGISTRY = {
6358
6467
  { name: "bcc", source: { source: "param", param: "bcc" }, type: "recipient" }
6359
6468
  ]
6360
6469
  },
6470
+ approval_salient_fields: ["to", "cc", "bcc", "subject", "body"],
6361
6471
  version: "1.0.0"
6362
6472
  },
6363
6473
  {
@@ -6391,6 +6501,7 @@ var ACTION_REGISTRY = {
6391
6501
  { name: "bcc", source: { source: "param", param: "bcc" }, type: "recipient" }
6392
6502
  ]
6393
6503
  },
6504
+ approval_salient_fields: ["to", "cc", "bcc", "subject", "body"],
6394
6505
  version: "1.0.0"
6395
6506
  },
6396
6507
  {
@@ -6459,6 +6570,7 @@ var ACTION_REGISTRY = {
6459
6570
  target_bindings: {
6460
6571
  resource_id: { source: "param", param: "messageId" }
6461
6572
  },
6573
+ approval_salient_fields: ["messageId"],
6462
6574
  version: "1.0.0"
6463
6575
  },
6464
6576
  {
@@ -6481,6 +6593,7 @@ var ACTION_REGISTRY = {
6481
6593
  target_bindings: {
6482
6594
  resource_id: { source: "param", param: "messageId" }
6483
6595
  },
6596
+ approval_salient_fields: ["messageId"],
6484
6597
  version: "1.0.0"
6485
6598
  },
6486
6599
  // ─── Calendar Actions ───
@@ -6605,6 +6718,9 @@ var ACTION_REGISTRY = {
6605
6718
  { name: "attendees", source: { source: "param", param: "attendees" }, type: "participant" }
6606
6719
  ]
6607
6720
  },
6721
+ // #1205 P1: addMeet changes the real-world effect (attaches a Google Meet
6722
+ // link / video conference), so it is approval-critical.
6723
+ approval_salient_fields: ["calendarId", "summary", "description", "start", "end", "attendees", "location", "addMeet"],
6608
6724
  version: "1.0.0"
6609
6725
  },
6610
6726
  {
@@ -6655,6 +6771,9 @@ var ACTION_REGISTRY = {
6655
6771
  { name: "attendees", source: { source: "param", param: "attendees" }, type: "participant" }
6656
6772
  ]
6657
6773
  },
6774
+ // #1205 P1: addMeet changes the real-world effect (attaches a Google Meet
6775
+ // link), so it is approval-critical.
6776
+ approval_salient_fields: ["calendarId", "eventId", "summary", "description", "start", "end", "attendees", "location", "addMeet"],
6658
6777
  version: "1.0.0"
6659
6778
  },
6660
6779
  {
@@ -6678,6 +6797,7 @@ var ACTION_REGISTRY = {
6678
6797
  target_bindings: {
6679
6798
  resource_id: { source: "param", param: "calendarId", default: "primary" }
6680
6799
  },
6800
+ approval_salient_fields: ["calendarId", "eventId"],
6681
6801
  version: "1.0.0"
6682
6802
  },
6683
6803
  // ─── Jira Actions ───
@@ -6870,6 +6990,10 @@ var ACTION_REGISTRY = {
6870
6990
  target_bindings: {
6871
6991
  resource_id: { source: "param", param: "projectKey" }
6872
6992
  },
6993
+ // #1205 P1: assigneeAccountId (who the issue is assigned to) is
6994
+ // approval-critical — an agent could otherwise reassign it post-approval.
6995
+ // labels are part of the approved issue definition.
6996
+ approval_salient_fields: ["projectKey", "summary", "description", "issueTypeName", "assigneeAccountId", "labels"],
6873
6997
  version: "1.0.0"
6874
6998
  },
6875
6999
  {
@@ -6881,6 +7005,15 @@ var ACTION_REGISTRY = {
6881
7005
  input_schema: {
6882
7006
  type: "object",
6883
7007
  properties: {
7008
+ // #1205 P1 / HOLE 2 audit (Step 4.7): issueIdOrKey accepts EITHER a
7009
+ // project key form ("PROJ-123") OR a numeric internal id. VERIFIED:
7010
+ // it is passed VERBATIM into the Jira REST URL path
7011
+ // `/api/3/issue/{issueIdOrKey}` (jira.service.ts) — the API accepts
7012
+ // both forms and nothing canonicalizes one to the other, and no pre-PEP
7013
+ // mutation touches jira params (only normalizeSlackChannelParam mutates,
7014
+ // slack-only). So issuance digest == PEP digest for whichever form the
7015
+ // approver approved — NO resolve-at-approval-time needed (unlike slack
7016
+ // channel). Regression guard: salient-digest.test.ts Step 4.7 block.
6884
7017
  issueIdOrKey: { type: "string", minLength: 1 },
6885
7018
  summary: { type: "string" },
6886
7019
  description: {
@@ -6903,6 +7036,9 @@ var ACTION_REGISTRY = {
6903
7036
  target_bindings: {
6904
7037
  resource_id: { source: "param", param: "issueIdOrKey", fallback_param: "issueKey", derive: "project_key" }
6905
7038
  },
7039
+ // #1205 P1: assigneeAccountId (reassignment) is approval-critical; labels
7040
+ // are part of the approved change. Same rationale as jira.issue.create.
7041
+ approval_salient_fields: ["issueIdOrKey", "summary", "description", "assigneeAccountId", "labels"],
6906
7042
  version: "1.0.0"
6907
7043
  },
6908
7044
  {
@@ -6925,6 +7061,7 @@ var ACTION_REGISTRY = {
6925
7061
  target_bindings: {
6926
7062
  resource_id: { source: "param", param: "issueIdOrKey", fallback_param: "issueKey", derive: "project_key" }
6927
7063
  },
7064
+ approval_salient_fields: ["issueIdOrKey"],
6928
7065
  version: "1.0.0"
6929
7066
  },
6930
7067
  {
@@ -6955,6 +7092,7 @@ var ACTION_REGISTRY = {
6955
7092
  target_bindings: {
6956
7093
  resource_id: { source: "param", param: "issueIdOrKey", fallback_param: "issueKey", derive: "project_key" }
6957
7094
  },
7095
+ approval_salient_fields: ["issueIdOrKey", "body"],
6958
7096
  version: "1.0.0"
6959
7097
  },
6960
7098
  {
@@ -6983,6 +7121,7 @@ var ACTION_REGISTRY = {
6983
7121
  target_bindings: {
6984
7122
  resource_id: { source: "param", param: "issueIdOrKey", fallback_param: "issueKey", derive: "project_key" }
6985
7123
  },
7124
+ approval_salient_fields: ["issueIdOrKey", "commentId"],
6986
7125
  version: "1.0.0"
6987
7126
  },
6988
7127
  {
@@ -7028,6 +7167,7 @@ var ACTION_REGISTRY = {
7028
7167
  target_bindings: {
7029
7168
  resource_id: { source: "param", param: "issueIdOrKey", fallback_param: "issueKey", derive: "project_key" }
7030
7169
  },
7170
+ approval_salient_fields: ["issueIdOrKey", "transitionId"],
7031
7171
  version: "1.0.0"
7032
7172
  },
7033
7173
  {
@@ -7102,6 +7242,7 @@ var ACTION_REGISTRY = {
7102
7242
  // 完全な制御をしたい場合は別途 inwardIssueKey 側も grant チェックする運用を要する。
7103
7243
  resource_id: { source: "param", param: "outwardIssueKey", derive: "project_key" }
7104
7244
  },
7245
+ approval_salient_fields: ["typeName", "inwardIssueKey", "outwardIssueKey", "commentBody"],
7105
7246
  version: "1.0.0"
7106
7247
  },
7107
7248
  {
@@ -7124,6 +7265,7 @@ var ACTION_REGISTRY = {
7124
7265
  target_bindings: {
7125
7266
  resource_id: { source: "param", param: "linkId" }
7126
7267
  },
7268
+ approval_salient_fields: ["linkId"],
7127
7269
  version: "1.0.0"
7128
7270
  },
7129
7271
  {
@@ -7205,6 +7347,7 @@ var ACTION_REGISTRY = {
7205
7347
  target_bindings: {
7206
7348
  resource_id: { source: "param", param: "file_path" }
7207
7349
  },
7350
+ approval_salient_fields: ["file_path"],
7208
7351
  version: "1.0.0"
7209
7352
  },
7210
7353
  {
@@ -7226,6 +7369,7 @@ var ACTION_REGISTRY = {
7226
7369
  target_bindings: {
7227
7370
  resource_id: { source: "param", param: "file_path" }
7228
7371
  },
7372
+ approval_salient_fields: ["file_path", "content"],
7229
7373
  version: "1.0.0"
7230
7374
  },
7231
7375
  {
@@ -7262,6 +7406,7 @@ var ACTION_REGISTRY = {
7262
7406
  target_bindings: {
7263
7407
  resource_id: { source: "param", param: "command" }
7264
7408
  },
7409
+ approval_salient_fields: ["command", "working_directory", "env_profile"],
7265
7410
  version: "1.0.0"
7266
7411
  },
7267
7412
  // ─── HubSpot CRM Objects (objectType 汎用) ───
@@ -7382,6 +7527,7 @@ var ACTION_REGISTRY = {
7382
7527
  target_bindings: {
7383
7528
  resource_id: { source: "param", param: "objectType" }
7384
7529
  },
7530
+ approval_salient_fields: ["objectType", "inputs"],
7385
7531
  version: "1.0.0"
7386
7532
  },
7387
7533
  {
@@ -7414,6 +7560,7 @@ var ACTION_REGISTRY = {
7414
7560
  target_bindings: {
7415
7561
  resource_id: { source: "param", param: "objectType" }
7416
7562
  },
7563
+ approval_salient_fields: ["objectType", "inputs"],
7417
7564
  version: "1.0.0"
7418
7565
  },
7419
7566
  {
@@ -7503,6 +7650,7 @@ var ACTION_REGISTRY = {
7503
7650
  effects: ["Create:CrmProperty"],
7504
7651
  risk: "high",
7505
7652
  target_bindings: { resource_id: { source: "param", param: "objectType" } },
7653
+ approval_salient_fields: ["objectType", "name", "label", "type", "fieldType"],
7506
7654
  version: "1.0.0"
7507
7655
  },
7508
7656
  {
@@ -7530,6 +7678,7 @@ var ACTION_REGISTRY = {
7530
7678
  effects: ["Update:CrmProperty"],
7531
7679
  risk: "high",
7532
7680
  target_bindings: { resource_id: { source: "param", param: "objectType" } },
7681
+ approval_salient_fields: ["objectType", "propertyName", "label"],
7533
7682
  version: "1.0.0"
7534
7683
  },
7535
7684
  // ─── HubSpot Associations (v4) ───
@@ -7598,6 +7747,7 @@ var ACTION_REGISTRY = {
7598
7747
  effects: ["Create:CrmAssociation"],
7599
7748
  risk: "high",
7600
7749
  target_bindings: { resource_id: { source: "param", param: "fromObjectType" } },
7750
+ approval_salient_fields: ["fromObjectType", "toObjectType", "inputs"],
7601
7751
  version: "1.0.0"
7602
7752
  },
7603
7753
  // ─── HubSpot Engagements (legacy v1: NOTE / TASK) ───
@@ -7629,8 +7779,20 @@ var ACTION_REGISTRY = {
7629
7779
  type: "object",
7630
7780
  properties: {
7631
7781
  type: { type: "string", enum: ["NOTE", "TASK"] },
7632
- ownerId: { type: "number" },
7633
- timestamp: { type: "number" },
7782
+ // #1205 P1: HubSpot owner ids are integers; retyped from `number` to
7783
+ // `integer` so this ownership field is bindable (float-ban rule 5).
7784
+ // DRIFT (follow-up, out of Step 2 scope): the API adapter still types
7785
+ // ownerId/timestamp as `number` (hubspot.adapter.ts:294-295,317-318).
7786
+ // No test enforces registry↔adapter type identity, so nothing breaks;
7787
+ // a later step should align the adapter schemas to `integer`.
7788
+ ownerId: { type: "integer" },
7789
+ // #1205 P1: caller-supplied epoch-ms activity time when present, else
7790
+ // the service defaults to Date.now() (hubspot.service.ts:555
7791
+ // `params.timestamp ?? Date.now()`). Retyped to `integer` (epoch ms)
7792
+ // so it is bindable; when the caller omits it both the issued and PEP
7793
+ // digests see the same JSON null (the default is applied server-side
7794
+ // AFTER the salient params are read), so binding it is safe.
7795
+ timestamp: { type: "integer" },
7634
7796
  associations: { type: "object" },
7635
7797
  metadata: { type: "object" }
7636
7798
  },
@@ -7643,6 +7805,10 @@ var ACTION_REGISTRY = {
7643
7805
  // Bind to the engagement type (NOTE/TASK) as the container that exists at
7644
7806
  // call time, consistent with other create actions (see CREATE-binding note above).
7645
7807
  target_bindings: { resource_id: { source: "param", param: "type" } },
7808
+ // #1205 P1: ownerId (who owns the engagement) is approval-critical — an
7809
+ // agent could otherwise reassign ownership post-approval. timestamp binds
7810
+ // the activity time. associations/metadata carry the note/task substance.
7811
+ approval_salient_fields: ["type", "ownerId", "timestamp", "associations", "metadata"],
7646
7812
  version: "1.0.0"
7647
7813
  },
7648
7814
  {
@@ -7655,8 +7821,13 @@ var ACTION_REGISTRY = {
7655
7821
  type: "object",
7656
7822
  properties: {
7657
7823
  engagementId: { type: "string" },
7658
- ownerId: { type: "number" },
7659
- timestamp: { type: "number" },
7824
+ // #1205 P1: retyped `number` → `integer` (HubSpot owner id) so this
7825
+ // ownership field is bindable (float-ban rule 5).
7826
+ ownerId: { type: "integer" },
7827
+ // #1205 P1: caller-supplied epoch-ms activity time (update sets it
7828
+ // directly, no default here — hubspot.service.ts:577 only assigns when
7829
+ // provided). Retyped to `integer` so it is bindable.
7830
+ timestamp: { type: "integer" },
7660
7831
  associations: { type: "object" },
7661
7832
  metadata: { type: "object" }
7662
7833
  },
@@ -7667,6 +7838,9 @@ var ACTION_REGISTRY = {
7667
7838
  effects: ["Update:CrmEngagement"],
7668
7839
  risk: "high",
7669
7840
  target_bindings: { resource_id: { source: "param", param: "engagementId" } },
7841
+ // #1205 P1: ownerId (reassignment) + timestamp are approval-critical on
7842
+ // update, same as create.
7843
+ approval_salient_fields: ["engagementId", "ownerId", "timestamp", "metadata"],
7670
7844
  version: "1.0.0"
7671
7845
  },
7672
7846
  // ─── HubSpot Workflows (automation v4 flows, read-only) ───
@@ -7837,7 +8011,11 @@ var ACTION_REGISTRY = {
7837
8011
  description: { type: "string", description: "Issue description (Markdown)" },
7838
8012
  assigneeId: { type: "string", description: "Assignee user ID" },
7839
8013
  stateId: { type: "string", description: "Workflow state ID" },
7840
- priority: { type: "number", minimum: 0, maximum: 4, description: "0=No priority, 1=Urgent, 2=High, 3=Medium, 4=Low" },
8014
+ // #1205 P1: priority is always an integer 0-4; retyped `number` →
8015
+ // `integer` so it is bindable (float-ban rule 5). DRIFT (follow-up): the
8016
+ // API adapter still types priority as `number` (linear.adapter.ts:93,112);
8017
+ // align in a later step (no test enforces the mirror, nothing breaks).
8018
+ priority: { type: "integer", minimum: 0, maximum: 4, description: "0=No priority, 1=Urgent, 2=High, 3=Medium, 4=Low" },
7841
8019
  labelIds: { type: "array", items: { type: "string" }, description: "Label IDs to attach" }
7842
8020
  },
7843
8021
  required: ["teamId", "title"],
@@ -7849,6 +8027,10 @@ var ACTION_REGISTRY = {
7849
8027
  target_bindings: {
7850
8028
  resource_id: { source: "param", param: "teamId" }
7851
8029
  },
8030
+ // #1205 P1: assigneeId (who the issue is assigned to) is approval-critical
8031
+ // — an agent could otherwise reassign the issue post-approval. priority and
8032
+ // labelIds are part of the approved issue definition.
8033
+ approval_salient_fields: ["teamId", "title", "description", "assigneeId", "priority", "labelIds"],
7852
8034
  version: "1.0.0"
7853
8035
  },
7854
8036
  {
@@ -7860,12 +8042,23 @@ var ACTION_REGISTRY = {
7860
8042
  input_schema: {
7861
8043
  type: "object",
7862
8044
  properties: {
8045
+ // #1205 P1 / HOLE 2 audit (Step 4.7): issueId accepts EITHER a uuid OR
8046
+ // a human identifier like "ENG-123". VERIFIED: it is passed VERBATIM
8047
+ // into the Linear GraphQL `issue(id: $issueId)` / `issueUpdate(id:)`
8048
+ // arg (linear.service.ts) — the API accepts both forms and nothing
8049
+ // canonicalizes identifier→uuid, and no pre-PEP mutation touches linear
8050
+ // params (only normalizeSlackChannelParam mutates, slack-only). So
8051
+ // issuance digest == PEP digest for whichever form the approver
8052
+ // approved — NO resolve-at-approval-time needed (unlike slack channel).
8053
+ // Regression guard: salient-digest.test.ts Step 4.7 block. Do NOT drop
8054
+ // issueId (it IS the target).
7863
8055
  issueId: { type: "string", minLength: 1, description: "Issue ID or identifier (e.g. ENG-123)" },
7864
8056
  title: { type: "string", description: "New title" },
7865
8057
  description: { type: "string", description: "New description (Markdown)" },
7866
8058
  assigneeId: { type: "string", description: "Assignee user ID (null to unassign)" },
7867
8059
  stateId: { type: "string", description: "Workflow state ID" },
7868
- priority: { type: "number", minimum: 0, maximum: 4, description: "0=No priority, 1=Urgent, 2=High, 3=Medium, 4=Low" },
8060
+ // #1205 P1: retyped `number` → `integer` (priority is always int 0-4).
8061
+ priority: { type: "integer", minimum: 0, maximum: 4, description: "0=No priority, 1=Urgent, 2=High, 3=Medium, 4=Low" },
7869
8062
  labelIds: { type: "array", items: { type: "string" }, description: "New set of label IDs" }
7870
8063
  },
7871
8064
  required: ["issueId"],
@@ -7877,6 +8070,9 @@ var ACTION_REGISTRY = {
7877
8070
  target_bindings: {
7878
8071
  resource_id: { source: "param", param: "issueId" }
7879
8072
  },
8073
+ // #1205 P1: assigneeId (reassignment) is approval-critical; priority and
8074
+ // labelIds are part of the approved change.
8075
+ approval_salient_fields: ["issueId", "title", "description", "assigneeId", "stateId", "priority", "labelIds"],
7880
8076
  version: "1.0.0"
7881
8077
  },
7882
8078
  {
@@ -7900,6 +8096,7 @@ var ACTION_REGISTRY = {
7900
8096
  target_bindings: {
7901
8097
  resource_id: { source: "param", param: "issueId" }
7902
8098
  },
8099
+ approval_salient_fields: ["issueId", "body"],
7903
8100
  version: "1.0.0"
7904
8101
  },
7905
8102
  {
@@ -8101,6 +8298,7 @@ var ACTION_REGISTRY = {
8101
8298
  target_bindings: {
8102
8299
  resource_id: { source: "param", param: "issueId" }
8103
8300
  },
8301
+ approval_salient_fields: ["issueId"],
8104
8302
  version: "1.0.0"
8105
8303
  },
8106
8304
  {
@@ -8136,6 +8334,7 @@ var ACTION_REGISTRY = {
8136
8334
  // resource binding 必須契約を明示 (teamIds は minItems:1 なので常に解決する)。
8137
8335
  resource_id: { source: "param", param: "teamId", fallback_param: "teamIds", required: true }
8138
8336
  },
8337
+ approval_salient_fields: ["teamId", "teamIds", "name", "description"],
8139
8338
  version: "1.0.0"
8140
8339
  },
8141
8340
  {
@@ -8163,6 +8362,7 @@ var ACTION_REGISTRY = {
8163
8362
  target_bindings: {
8164
8363
  resource_id: { source: "param", param: "projectId" }
8165
8364
  },
8365
+ approval_salient_fields: ["projectId", "name", "description"],
8166
8366
  version: "1.0.0"
8167
8367
  },
8168
8368
  // ─── Linear Comment ops ───
@@ -8187,6 +8387,7 @@ var ACTION_REGISTRY = {
8187
8387
  target_bindings: {
8188
8388
  resource_id: { source: "param", param: "commentId" }
8189
8389
  },
8390
+ approval_salient_fields: ["commentId", "body"],
8190
8391
  version: "1.0.0"
8191
8392
  },
8192
8393
  {
@@ -8209,6 +8410,7 @@ var ACTION_REGISTRY = {
8209
8410
  target_bindings: {
8210
8411
  resource_id: { source: "param", param: "commentId" }
8211
8412
  },
8413
+ approval_salient_fields: ["commentId"],
8212
8414
  version: "1.0.0"
8213
8415
  },
8214
8416
  {
@@ -8231,6 +8433,7 @@ var ACTION_REGISTRY = {
8231
8433
  target_bindings: {
8232
8434
  resource_id: { source: "param", param: "commentId" }
8233
8435
  },
8436
+ approval_salient_fields: ["commentId"],
8234
8437
  version: "1.0.0"
8235
8438
  },
8236
8439
  {
@@ -8253,6 +8456,7 @@ var ACTION_REGISTRY = {
8253
8456
  target_bindings: {
8254
8457
  resource_id: { source: "param", param: "commentId" }
8255
8458
  },
8459
+ approval_salient_fields: ["commentId"],
8256
8460
  version: "1.0.0"
8257
8461
  },
8258
8462
  // ─── Linear Issue ops ───
@@ -8276,6 +8480,7 @@ var ACTION_REGISTRY = {
8276
8480
  target_bindings: {
8277
8481
  resource_id: { source: "param", param: "issueId" }
8278
8482
  },
8483
+ approval_salient_fields: ["issueId"],
8279
8484
  version: "1.0.0"
8280
8485
  },
8281
8486
  {
@@ -8299,6 +8504,7 @@ var ACTION_REGISTRY = {
8299
8504
  target_bindings: {
8300
8505
  resource_id: { source: "param", param: "issueId" }
8301
8506
  },
8507
+ approval_salient_fields: ["issueId", "labelId"],
8302
8508
  version: "1.0.0"
8303
8509
  },
8304
8510
  {
@@ -8322,6 +8528,7 @@ var ACTION_REGISTRY = {
8322
8528
  target_bindings: {
8323
8529
  resource_id: { source: "param", param: "issueId" }
8324
8530
  },
8531
+ approval_salient_fields: ["issueId", "labelId"],
8325
8532
  version: "1.0.0"
8326
8533
  },
8327
8534
  {
@@ -8344,6 +8551,7 @@ var ACTION_REGISTRY = {
8344
8551
  target_bindings: {
8345
8552
  resource_id: { source: "param", param: "issueId" }
8346
8553
  },
8554
+ approval_salient_fields: ["issueId"],
8347
8555
  version: "1.0.0"
8348
8556
  },
8349
8557
  {
@@ -8366,6 +8574,7 @@ var ACTION_REGISTRY = {
8366
8574
  target_bindings: {
8367
8575
  resource_id: { source: "param", param: "issueId" }
8368
8576
  },
8577
+ approval_salient_fields: ["issueId"],
8369
8578
  version: "1.0.0"
8370
8579
  },
8371
8580
  // ─── Linear Issue Relations ───
@@ -8391,6 +8600,7 @@ var ACTION_REGISTRY = {
8391
8600
  target_bindings: {
8392
8601
  resource_id: { source: "param", param: "issueId" }
8393
8602
  },
8603
+ approval_salient_fields: ["issueId", "relatedIssueId", "type"],
8394
8604
  version: "1.0.0"
8395
8605
  },
8396
8606
  {
@@ -8435,6 +8645,7 @@ var ACTION_REGISTRY = {
8435
8645
  target_bindings: {
8436
8646
  resource_id: { source: "param", param: "relationId" }
8437
8647
  },
8648
+ approval_salient_fields: ["relationId"],
8438
8649
  version: "1.0.0"
8439
8650
  },
8440
8651
  // ─── Linear Cycles ───
@@ -8506,6 +8717,7 @@ var ACTION_REGISTRY = {
8506
8717
  target_bindings: {
8507
8718
  resource_id: { source: "param", param: "teamId" }
8508
8719
  },
8720
+ approval_salient_fields: ["teamId", "name", "startsAt", "endsAt"],
8509
8721
  version: "1.0.0"
8510
8722
  },
8511
8723
  {
@@ -8533,6 +8745,7 @@ var ACTION_REGISTRY = {
8533
8745
  target_bindings: {
8534
8746
  resource_id: { source: "param", param: "cycleId" }
8535
8747
  },
8748
+ approval_salient_fields: ["cycleId", "name", "startsAt", "endsAt"],
8536
8749
  version: "1.0.0"
8537
8750
  },
8538
8751
  // ─── Linear Labels ───
@@ -8559,6 +8772,7 @@ var ACTION_REGISTRY = {
8559
8772
  target_bindings: {
8560
8773
  resource_id: { source: "param", param: "teamId", required: false }
8561
8774
  },
8775
+ approval_salient_fields: ["teamId", "name"],
8562
8776
  version: "1.0.0"
8563
8777
  },
8564
8778
  {
@@ -8584,6 +8798,7 @@ var ACTION_REGISTRY = {
8584
8798
  target_bindings: {
8585
8799
  resource_id: { source: "param", param: "labelId" }
8586
8800
  },
8801
+ approval_salient_fields: ["labelId", "name"],
8587
8802
  version: "1.0.0"
8588
8803
  },
8589
8804
  // ─── Linear Attachments ───
@@ -8610,6 +8825,7 @@ var ACTION_REGISTRY = {
8610
8825
  target_bindings: {
8611
8826
  resource_id: { source: "param", param: "issueId" }
8612
8827
  },
8828
+ approval_salient_fields: ["issueId", "title", "url"],
8613
8829
  version: "1.0.0"
8614
8830
  },
8615
8831
  {
@@ -8654,6 +8870,7 @@ var ACTION_REGISTRY = {
8654
8870
  target_bindings: {
8655
8871
  resource_id: { source: "param", param: "attachmentId" }
8656
8872
  },
8873
+ approval_salient_fields: ["attachmentId"],
8657
8874
  version: "1.0.0"
8658
8875
  },
8659
8876
  // ─── Agent Inbox Actions (internal) ───
@@ -8684,6 +8901,7 @@ var ACTION_REGISTRY = {
8684
8901
  target_bindings: {
8685
8902
  resource_id: { source: "param", param: "to" }
8686
8903
  },
8904
+ approval_salient_fields: ["to", "taskType", "payload", "message"],
8687
8905
  version: "1.0.0"
8688
8906
  },
8689
8907
  {
@@ -8714,6 +8932,7 @@ var ACTION_REGISTRY = {
8714
8932
  target_bindings: {
8715
8933
  resource_id: { source: "param", param: "id" }
8716
8934
  },
8935
+ approval_salient_fields: ["id", "action", "data"],
8717
8936
  version: "1.0.0"
8718
8937
  },
8719
8938
  {
@@ -8886,6 +9105,110 @@ var ACTION_REGISTRY = {
8886
9105
  target_bindings: {
8887
9106
  resource_id: { source: "param", param: "to" }
8888
9107
  },
9108
+ // #1205 P1: hold changes the real-world effect — when true it tentatively
9109
+ // books [仮] events on the sender's calendar for every offered slot, so it
9110
+ // is approval-critical.
9111
+ approval_salient_fields: ["to", "topic", "durationMinutes", "candidates", "candidateWindow", "hold"],
9112
+ version: "1.0.0"
9113
+ },
9114
+ // ─── PR-R2 (design §3.2): the raw booking executor's ONE new action ───
9115
+ // The booking agent submits its scheduling proposal through a REAL PEP
9116
+ // crossing instead of a managed custom_tool. Registered here in R2; the
9117
+ // adapter dispatch case lands in R3.
9118
+ //
9119
+ // 🔴 risk: 'high' is deliberate and must NOT be downgraded. Every writing
9120
+ // sibling (inbox.send / inbox.respond / scheduling.request) is high, and
9121
+ // inbox.respond's own comment states that agent-mediated responses must
9122
+ // always go through human approval. The approval basis here is the
9123
+ // visitor's explicit "AIで日程調整する" click (or the flag-gated OIDC
9124
+ // auto-consent), captured at mint time as a first-class ApprovalToken
9125
+ // ledger row — NOT an absence of approval.
9126
+ //
9127
+ // 🪤 target_bindings.resource_id binds the BARE `itemId`. It must equal
9128
+ // canonicalResourceId('scheduling.submit_response', { itemId }) exactly.
9129
+ // A prefixed value (e.g. 'inbox:item/<id>') does not match the approval
9130
+ // assertion at invoke time and the call SILENTLY falls through to
9131
+ // auth_required (202) — no error surfaces, which is why the api side
9132
+ // carries a preflight-parity spec asserting three-way equality
9133
+ // (migration grant resource == assertion resource_id == invoke-time
9134
+ // canonicalResourceId).
9135
+ {
9136
+ action: "scheduling.submit_response",
9137
+ resource_type: "inbox:item",
9138
+ required_relations: ["editor", "act_as"],
9139
+ required_scopes: ["inbox"],
9140
+ capability: "inbox.write",
9141
+ input_schema: {
9142
+ type: "object",
9143
+ properties: {
9144
+ itemId: { type: "string", minLength: 1, description: "The inbox item the proposal answers" },
9145
+ decision: { type: "string", enum: ["propose", "decline"], description: "Whether the agent proposes slots or declines" },
9146
+ slots: {
9147
+ type: "array",
9148
+ maxItems: 10,
9149
+ description: "Proposed candidate slots (empty for a decline)",
9150
+ items: {
9151
+ type: "object",
9152
+ properties: {
9153
+ start: { type: "string", description: "ISO 8601 start" },
9154
+ end: { type: "string", description: "ISO 8601 end" }
9155
+ },
9156
+ required: ["start", "end"],
9157
+ additionalProperties: false
9158
+ }
9159
+ },
9160
+ recommendedSlotIndex: { type: ["integer", "null"], description: "Index into slots of the recommended option, or null" },
9161
+ recommendationReason: { type: "string", description: "Short rationale shown to the visitor" }
9162
+ },
9163
+ required: ["itemId", "decision"],
9164
+ additionalProperties: false
9165
+ },
9166
+ constraints: { rate_bucket: "inbox.write" },
9167
+ effects: ["Update:InboxItem"],
9168
+ risk: "high",
9169
+ target_bindings: {
9170
+ resource_id: { source: "param", param: "itemId" }
9171
+ },
9172
+ approval_salient_fields: ["itemId", "decision", "slots"],
9173
+ version: "1.0.0"
9174
+ },
9175
+ {
9176
+ action: "doc_review.submit_response",
9177
+ resource_type: "inbox:item",
9178
+ required_relations: ["editor", "act_as"],
9179
+ required_scopes: ["inbox"],
9180
+ capability: "inbox.write",
9181
+ input_schema: {
9182
+ type: "object",
9183
+ properties: {
9184
+ itemId: { type: "string", minLength: 1, description: "The inbox item the review answers" },
9185
+ decision: { type: "string", enum: ["reviewed", "declined"], description: "Whether the agent returns review findings or declines" },
9186
+ findings: {
9187
+ type: "array",
9188
+ maxItems: 50,
9189
+ description: "Review findings (empty for a decline). Each cites a verbatim quote from the document.",
9190
+ items: {
9191
+ type: "object",
9192
+ properties: {
9193
+ quote: { type: "string", description: "Verbatim excerpt from the reviewed document that the comment refers to" },
9194
+ comment: { type: "string", description: "The reviewer comment on that excerpt" }
9195
+ },
9196
+ required: ["quote", "comment"],
9197
+ additionalProperties: false
9198
+ }
9199
+ },
9200
+ summary: { type: "string", description: "Optional overall summary of the review" }
9201
+ },
9202
+ required: ["itemId", "decision"],
9203
+ additionalProperties: false
9204
+ },
9205
+ constraints: { rate_bucket: "inbox.write" },
9206
+ effects: ["Update:InboxItem"],
9207
+ risk: "high",
9208
+ target_bindings: {
9209
+ resource_id: { source: "param", param: "itemId" }
9210
+ },
9211
+ approval_salient_fields: ["itemId", "decision", "findings", "summary"],
8889
9212
  version: "1.0.0"
8890
9213
  }
8891
9214
  ],
@@ -8983,7 +9306,7 @@ var ACTION_REGISTRY = {
8983
9306
  {
8984
9307
  capability: "inbox.write",
8985
9308
  description: "Send and respond to agent inbox tasks",
8986
- includes: ["inbox.send", "inbox.respond", "inbox.complete", "scheduling.request"],
9309
+ includes: ["inbox.send", "inbox.respond", "inbox.complete", "scheduling.request", "scheduling.submit_response", "doc_review.submit_response"],
8987
9310
  version: "1.0.0"
8988
9311
  },
8989
9312
  {
@@ -9043,18 +9366,6 @@ var ACTION_REGISTRY = {
9043
9366
  "linear.attachment.delete"
9044
9367
  ],
9045
9368
  version: "1.0.0"
9046
- },
9047
- {
9048
- capability: "inbox.write",
9049
- description: "Send and respond to agent inbox tasks",
9050
- includes: ["inbox.send", "inbox.respond", "inbox.complete", "scheduling.request"],
9051
- version: "1.0.0"
9052
- },
9053
- {
9054
- capability: "inbox.read",
9055
- description: "Read agent inbox tasks",
9056
- includes: ["inbox.check", "inbox.checkByToken"],
9057
- version: "1.0.0"
9058
9369
  }
9059
9370
  ]
9060
9371
  };
@@ -9384,6 +9695,27 @@ var GATEWAY_ERROR_CODE = {
9384
9695
  APPROVAL_REQUIRED: "APPROVAL_REQUIRED"
9385
9696
  };
9386
9697
 
9698
+ // src/resolver/approval-time-resolution.ts
9699
+ var RESOLVED_PARAMS_KEY = "__resolvedParams";
9700
+ function stripResolvedParams(params) {
9701
+ if (!params || typeof params !== "object") return params;
9702
+ if (!Object.prototype.hasOwnProperty.call(params, RESOLVED_PARAMS_KEY)) return params;
9703
+ const { [RESOLVED_PARAMS_KEY]: _dropped, ...rest } = params;
9704
+ return rest;
9705
+ }
9706
+ var RESOLVER_BACKED_FIELDS = {
9707
+ "slack.message.post": [{ field: "channel", kind: "slack-channel" }],
9708
+ "slack.message.update": [{ field: "channel", kind: "slack-channel" }],
9709
+ "slack.message.delete": [{ field: "channel", kind: "slack-channel" }]
9710
+ // jira `issueIdOrKey` / linear `issueId` are intentionally NOT here — verified
9711
+ // verbatim passthrough, no pre-PEP server canonicalization (see the block
9712
+ // above). Do not add them without a real, demonstrated pre-PEP rewrite.
9713
+ };
9714
+ function resolverBackedFieldsFor(action) {
9715
+ const fields = RESOLVER_BACKED_FIELDS[action];
9716
+ return fields && fields.length > 0 ? fields : void 0;
9717
+ }
9718
+
9387
9719
  // src/registry/action-summary.ts
9388
9720
  function extractJiraAssigneeDisplay(params) {
9389
9721
  const pickFromShape = (value) => {
@@ -9438,6 +9770,37 @@ var ACTION_DISPLAY_CONFIGS = {
9438
9770
  { key: "text", label: "Message" }
9439
9771
  ]
9440
9772
  },
9773
+ // #1205 P1 Step 4.6 — update / delete need explicit display configs so the
9774
+ // approval UI renders a labeled `Channel` field (which the API-side enricher
9775
+ // upserts the frozen id→name mapping into) instead of falling through to the
9776
+ // all-keys fallback branch, which would (a) render a raw lowercase `channel`
9777
+ // id row that the enricher then DUPLICATES with a `Channel` name row, and
9778
+ // (b) risk leaking the reserved __resolvedParams key.
9779
+ "slack.message.update": {
9780
+ summaryTemplate: (p) => {
9781
+ const parts = [];
9782
+ if (p.channel) parts.push(p.channel + ":");
9783
+ if (p.text) parts.push(String(p.text));
9784
+ return parts.join(" ");
9785
+ },
9786
+ displayFields: [
9787
+ { key: "channel", label: "Channel" },
9788
+ { key: "ts", label: "Timestamp" },
9789
+ { key: "text", label: "Message" }
9790
+ ]
9791
+ },
9792
+ "slack.message.delete": {
9793
+ summaryTemplate: (p) => {
9794
+ const parts = [];
9795
+ if (p.channel) parts.push(p.channel + ":");
9796
+ if (p.ts) parts.push(String(p.ts));
9797
+ return parts.join(" ");
9798
+ },
9799
+ displayFields: [
9800
+ { key: "channel", label: "Channel" },
9801
+ { key: "ts", label: "Timestamp" }
9802
+ ]
9803
+ },
9441
9804
  "gmail.message.send": {
9442
9805
  summaryTemplate: (p) => {
9443
9806
  const parts = [];
@@ -10267,6 +10630,35 @@ var ACTION_DISPLAY_CONFIGS = {
10267
10630
  { key: "candidates", label: "Candidates" },
10268
10631
  { key: "message", label: "Message" }
10269
10632
  ]
10633
+ },
10634
+ // PR-R2 (design §3.2), registry checklist B. This action is risk:'high', so
10635
+ // its summary/displayFields are what a human actually reads in the approval
10636
+ // and audit surfaces — the string is user-facing, not decoration. Names the
10637
+ // item being answered (the counterpart anchor available on this action's
10638
+ // params) the way scheduling.request names its topic.
10639
+ "scheduling.submit_response": {
10640
+ summaryTemplate: (p) => p.decision === "decline" ? `Decline the scheduling request for item ${p.itemId ?? "(unknown)"}` : `Submit a scheduling proposal for item ${p.itemId ?? "(unknown)"}`,
10641
+ displayFields: [
10642
+ { key: "itemId", label: "Inbox item" },
10643
+ { key: "decision", label: "Decision" },
10644
+ { key: "slots", label: "Proposed slots" },
10645
+ { key: "recommendedSlotIndex", label: "Recommended index" },
10646
+ { key: "recommendationReason", label: "Reason" }
10647
+ ]
10648
+ },
10649
+ // Phase D D0 (doc-review design §3.1), registry checklist B. Like
10650
+ // scheduling.submit_response this is risk:'high', so its summary/displayFields
10651
+ // are what a human actually reads in the approval and audit surfaces — the
10652
+ // string is user-facing, not decoration. Names the item being answered the way
10653
+ // scheduling.submit_response does.
10654
+ "doc_review.submit_response": {
10655
+ summaryTemplate: (p) => p.decision === "declined" ? `Decline the document review for item ${p.itemId ?? "(unknown)"}` : `Submit a document review for item ${p.itemId ?? "(unknown)"}`,
10656
+ displayFields: [
10657
+ { key: "itemId", label: "Inbox item" },
10658
+ { key: "decision", label: "Decision" },
10659
+ { key: "findings", label: "Findings" },
10660
+ { key: "summary", label: "Summary" }
10661
+ ]
10270
10662
  }
10271
10663
  };
10272
10664
  function truncate(text, maxLength) {
@@ -10429,6 +10821,7 @@ function generateActionSummary(action, params, maxLength = 50) {
10429
10821
  return raw ? truncate(raw, maxLength) : "";
10430
10822
  }
10431
10823
  for (const [key, val] of Object.entries(params)) {
10824
+ if (key === RESOLVED_PARAMS_KEY) continue;
10432
10825
  if (val !== null && val !== void 0 && typeof val !== "object") {
10433
10826
  return truncate(`${key}: ${String(val)}`, maxLength);
10434
10827
  }
@@ -10444,7 +10837,7 @@ function generateActionParamsDisplay(action, params) {
10444
10837
  return { label: f.label, raw };
10445
10838
  }).filter((item) => item.raw !== void 0).map((item) => ({ label: item.label, value: formatValueFull(item.raw) }));
10446
10839
  }
10447
- return Object.entries(params).filter(([, v]) => v !== void 0 && v !== null).map(([key, val]) => ({
10840
+ return Object.entries(params).filter(([key, v]) => key !== RESOLVED_PARAMS_KEY && v !== void 0 && v !== null).map(([key, val]) => ({
10448
10841
  label: key,
10449
10842
  value: formatValueFull(val)
10450
10843
  }));
@@ -10548,6 +10941,102 @@ function canonicalResourceId(action, params, ctx) {
10548
10941
  return id;
10549
10942
  }
10550
10943
 
10944
+ // src/resolver/salient-digest.ts
10945
+ var import_node_crypto3 = require("crypto");
10946
+ function serializeValue(value) {
10947
+ if (value === null || value === void 0) return "null";
10948
+ const t = typeof value;
10949
+ if (t === "boolean") return value ? "true" : "false";
10950
+ if (t === "number") {
10951
+ const n = value;
10952
+ if (!Number.isFinite(n)) {
10953
+ throw new Error(`salient-digest: non-finite number is not allowed in salient params: ${String(n)}`);
10954
+ }
10955
+ if (!Number.isInteger(n)) {
10956
+ throw new Error(
10957
+ `salient-digest: float value is not allowed in salient params (phase 1 forbids non-integer numbers): ${String(n)}`
10958
+ );
10959
+ }
10960
+ if (!Number.isSafeInteger(n)) {
10961
+ throw new Error(
10962
+ `salient-digest: unsafe integer is not allowed in salient params (exceeds 2^53-1, cannot round-trip losslessly): ${String(n)}`
10963
+ );
10964
+ }
10965
+ return String(n);
10966
+ }
10967
+ if (t === "string") return JSON.stringify(value.normalize("NFC"));
10968
+ if (Array.isArray(value)) {
10969
+ return "[" + value.map(serializeValue).join(",") + "]";
10970
+ }
10971
+ if (t === "object") {
10972
+ const obj = value;
10973
+ const seen = /* @__PURE__ */ new Map();
10974
+ for (const rawKey of Object.keys(obj)) {
10975
+ const normKey = rawKey.normalize("NFC");
10976
+ const prior = seen.get(normKey);
10977
+ if (prior !== void 0 && prior !== rawKey) {
10978
+ throw new Error(
10979
+ `salient-digest: duplicate object key after NFC normalization ("${prior}" and "${rawKey}" both normalize to "${normKey}")`
10980
+ );
10981
+ }
10982
+ seen.set(normKey, rawKey);
10983
+ }
10984
+ const normKeys = [...seen.keys()].sort();
10985
+ const parts = normKeys.map((normKey) => {
10986
+ const rawKey = seen.get(normKey);
10987
+ return JSON.stringify(normKey) + ":" + serializeValue(obj[rawKey]);
10988
+ });
10989
+ return "{" + parts.join(",") + "}";
10990
+ }
10991
+ throw new Error(`salient-digest: unsupported value type in salient params: ${t}`);
10992
+ }
10993
+ function canonicalizeWithFields(fields, params) {
10994
+ const seen = /* @__PURE__ */ new Map();
10995
+ for (const field of fields) {
10996
+ const normField = field.normalize("NFC");
10997
+ const prior = seen.get(normField);
10998
+ if (prior !== void 0 && prior !== field) {
10999
+ throw new Error(
11000
+ `salient-digest: duplicate salient field after NFC normalization ("${prior}" and "${field}" both normalize to "${normField}")`
11001
+ );
11002
+ }
11003
+ seen.set(normField, field);
11004
+ }
11005
+ const normFields = [...seen.keys()].sort();
11006
+ const parts = normFields.map((normField) => {
11007
+ const field = seen.get(normField);
11008
+ const present = Object.prototype.hasOwnProperty.call(params, field);
11009
+ const value = present ? params[field] : null;
11010
+ return JSON.stringify(normField) + ":" + serializeValue(value);
11011
+ });
11012
+ return "{" + parts.join(",") + "}";
11013
+ }
11014
+ function digestWithFields(fields, params) {
11015
+ const canonical = canonicalizeWithFields(fields, params);
11016
+ const hex = (0, import_node_crypto3.createHash)("sha256").update(canonical, "utf8").digest("hex");
11017
+ return `sha256:${hex}`;
11018
+ }
11019
+ function salientFieldsFor(action) {
11020
+ const def = ACTION_REGISTRY.actions.find((a) => a.action === action);
11021
+ if (!def) return void 0;
11022
+ const fields = def.approval_salient_fields;
11023
+ if (!Array.isArray(fields)) return void 0;
11024
+ if (!fields.every((f) => typeof f === "string")) return void 0;
11025
+ return fields;
11026
+ }
11027
+ function canonicalizeSalient(action, params) {
11028
+ const fields = salientFieldsFor(action);
11029
+ if (!fields) {
11030
+ throw new Error(`salient-digest: action "${action}" has no declared approval-salient fields`);
11031
+ }
11032
+ return canonicalizeWithFields(fields, params);
11033
+ }
11034
+ function salientDigest(action, params) {
11035
+ const fields = salientFieldsFor(action);
11036
+ if (!fields) return void 0;
11037
+ return digestWithFields(fields, params);
11038
+ }
11039
+
10551
11040
  // src/resolver/compare-resource-id.ts
10552
11041
  function looksLikeNativeId(value) {
10553
11042
  if (/^[CDG][A-Z0-9]{8,}$/.test(value)) return true;
@@ -10883,6 +11372,7 @@ function parseEntityUid(uid) {
10883
11372
  }
10884
11373
 
10885
11374
  // src/registry/action-risk.ts
11375
+ var UNCLASSIFIED_ACTION_RISK = "high";
10886
11376
  var HIGH_RISK_SUFFIXES = /* @__PURE__ */ new Set([
10887
11377
  "send",
10888
11378
  "post",
@@ -10926,13 +11416,13 @@ var REGISTRY_RISK_BY_ACTION = (() => {
10926
11416
  function resolveActionRiskBySuffix(lowerAction) {
10927
11417
  const segments = lowerAction.split(".");
10928
11418
  const last = segments[segments.length - 1] ?? "";
10929
- if (!last) return "medium";
11419
+ if (!last) return UNCLASSIFIED_ACTION_RISK;
10930
11420
  if (HIGH_RISK_SUFFIXES.has(last)) return "high";
10931
11421
  if (LOW_RISK_SUFFIXES.has(last)) return "low";
10932
- return "medium";
11422
+ return UNCLASSIFIED_ACTION_RISK;
10933
11423
  }
10934
11424
  function resolveActionRisk(action) {
10935
- if (!action || typeof action !== "string") return "medium";
11425
+ if (!action || typeof action !== "string") return UNCLASSIFIED_ACTION_RISK;
10936
11426
  const lower = action.toLowerCase();
10937
11427
  const registryRisk = REGISTRY_RISK_BY_ACTION.get(lower);
10938
11428
  if (registryRisk) return registryRisk;
@@ -11024,6 +11514,7 @@ var version = "0.0.1";
11024
11514
  PHASE_1_VC_LAYER,
11025
11515
  PROVIDER_ALIASES,
11026
11516
  REAUTH_REQUIRED_ACTION,
11517
+ RESOLVED_PARAMS_KEY,
11027
11518
  RESOURCE_TYPES,
11028
11519
  RESOURCE_TYPE_LABELS,
11029
11520
  ReceiptStatus,
@@ -11036,6 +11527,7 @@ var version = "0.0.1";
11036
11527
  TIER_LIMITS,
11037
11528
  TargetResolver,
11038
11529
  ToolManager,
11530
+ UNCLASSIFIED_ACTION_RISK,
11039
11531
  UserIdentityManager,
11040
11532
  UserKeyPairManager,
11041
11533
  VALID_MCP_ACTIONS,
@@ -11055,6 +11547,8 @@ var version = "0.0.1";
11055
11547
  buildValidationErrors,
11056
11548
  canonicalResourceId,
11057
11549
  canonicalizeAction,
11550
+ canonicalizeSalient,
11551
+ canonicalizeWithFields,
11058
11552
  checkPermissionWithVP,
11059
11553
  classifyCedarErrorMessage,
11060
11554
  compareResourceIds,
@@ -11064,6 +11558,7 @@ var version = "0.0.1";
11064
11558
  createDidJwk,
11065
11559
  credentialStatusToVCStatus,
11066
11560
  defaultConstraintEvaluator,
11561
+ digestWithFields,
11067
11562
  evaluateConstraints,
11068
11563
  extractProjectKey,
11069
11564
  extractPublicKey,
@@ -11113,9 +11608,13 @@ var version = "0.0.1";
11113
11608
  resolveProvider,
11114
11609
  resolveResourceType,
11115
11610
  resolveUserTier,
11611
+ resolverBackedFieldsFor,
11612
+ salientDigest,
11613
+ salientFieldsFor,
11116
11614
  sha256Hex,
11117
11615
  signJWT,
11118
11616
  signRequest,
11617
+ stripResolvedParams,
11119
11618
  validateActionInput,
11120
11619
  validateRegistryObject,
11121
11620
  vcStatusToCredentialStatus,