@suveren/gateway 0.29.0 → 0.29.1

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.
@@ -145,6 +145,8 @@
145
145
  },
146
146
  "list_messages": {
147
147
  "category": "read",
148
+ "boundField": "read_access",
149
+ "requiredValue": "unlimited",
148
150
  "read": {
149
151
  "ageField": "read_age_days",
150
152
  "queryArg": "q",
@@ -158,6 +160,8 @@
158
160
  },
159
161
  "get_message": {
160
162
  "category": "read",
163
+ "boundField": "read_access",
164
+ "requiredValue": "unlimited",
161
165
  "read": {
162
166
  "ageField": "read_age_days",
163
167
  "resultDatePath": "internalDate",
@@ -175,6 +179,8 @@
175
179
  },
176
180
  "list_drafts": {
177
181
  "category": "read",
182
+ "boundField": "read_access",
183
+ "requiredValue": "unlimited",
178
184
  "read": {
179
185
  "ageField": "read_age_days",
180
186
  "queryArg": "q",
@@ -187,6 +193,8 @@
187
193
  },
188
194
  "get_draft": {
189
195
  "category": "read",
196
+ "boundField": "read_access",
197
+ "requiredValue": "unlimited",
190
198
  "read": {
191
199
  "ageField": "read_age_days",
192
200
  "resultDatePath": "message.internalDate",
@@ -207,8 +215,11 @@
207
215
  },
208
216
  "get_attachment": {
209
217
  "category": "read",
210
- "readGovernance": "none",
211
- "readGovernanceReason": "Transitively age-gated: an attachmentId is only obtainable from get_message, which enforces the read-age window. No independently addressable content."
218
+ "boundField": "read_access",
219
+ "requiredValue": "unlimited",
220
+ "read": {
221
+ "ageField": "read_age_days"
222
+ }
212
223
  },
213
224
  "list_labels": {
214
225
  "category": "read",
@@ -7,6 +7,9 @@
7
7
  "profile": "email",
8
8
  "npmPackage": "@humanagencyp/email-mcp",
9
9
  "npmVersion": "0.3.0",
10
+ "readPolicy": {
11
+ "defaultAgeDays": 30
12
+ },
10
13
  "mcp": {
11
14
  "command": "email-mcp",
12
15
  "args": []
@@ -61,12 +64,19 @@
61
64
  "list_messages": {
62
65
  "category": "read",
63
66
  "boundField": "read_access",
64
- "requiredValue": "unlimited"
67
+ "requiredValue": "unlimited",
68
+ "read": {
69
+ "ageField": "read_age_days"
70
+ }
65
71
  },
66
72
  "get_message": {
67
73
  "category": "read",
68
74
  "boundField": "read_access",
69
- "requiredValue": "unlimited"
75
+ "requiredValue": "unlimited",
76
+ "read": {
77
+ "ageField": "read_age_days",
78
+ "resultDatePath": "received_at"
79
+ }
70
80
  },
71
81
  "send_message": {
72
82
  "executionMapping": {
@@ -129,6 +129,16 @@
129
129
  "staticExecution": {
130
130
  "action_type": "charge"
131
131
  }
132
+ },
133
+ "ListProfiles": {
134
+ "category": "read",
135
+ "readGovernance": "none",
136
+ "readGovernanceReason": "Returns the organisation's own payment profile configuration (names, websites, status) — no customer or payment data."
137
+ },
138
+ "GetOrganizationDetails": {
139
+ "category": "read",
140
+ "readGovernance": "none",
141
+ "readGovernanceReason": "Returns the connected Mollie organisation's own name/registration details — no customer or payment data."
132
142
  }
133
143
  }
134
144
  },
@@ -4625,7 +4625,7 @@ function createArchivedMandatesRouter(dataDir) {
4625
4625
  // src/routes/internal-events.ts
4626
4626
  import { Router as Router19 } from "express";
4627
4627
  import { timingSafeEqual as timingSafeEqual3 } from "crypto";
4628
- var ALLOWED = ["proposal-added", "action-approval-needed"];
4628
+ var ALLOWED = ["proposal-added", "action-approval-needed", "execution-failed"];
4629
4629
  var SESSION_EXPIRED = "session-expired";
4630
4630
  var AS_KEY_MISMATCH = "as-key-mismatch";
4631
4631
  var AS_TLS_MISMATCH = "as-tls-mismatch";
@@ -4887,9 +4887,13 @@ function createInternalMandateRouter(getSecret, deps) {
4887
4887
  }
4888
4888
 
4889
4889
  // src/lib/notification-dispatcher.ts
4890
- var TRIGGER_EVENTS = ["proposal-added", "action-approval-needed"];
4890
+ var REVIEW_EVENTS = ["proposal-added", "action-approval-needed"];
4891
+ var FAILURE_EVENTS = ["execution-failed"];
4892
+ var TRIGGER_EVENTS = [...REVIEW_EVENTS, ...FAILURE_EVENTS];
4891
4893
  var TITLE = "Suveren";
4892
4894
  var WAITING_MESSAGE = "Something is waiting for your review.";
4895
+ var FAILURE_MESSAGE = "An approved action failed.";
4896
+ var MIXED_MESSAGE = "Something needs your attention.";
4893
4897
  var FLOOD_THRESHOLD = 10;
4894
4898
  var floodMessage = (n) => `Unusual volume: ${n} requests in the last minute.`;
4895
4899
  var DEBOUNCE_MS = 5e3;
@@ -4900,6 +4904,9 @@ var NotificationDispatcher = class {
4900
4904
  pendingWhileCoolingDown = false;
4901
4905
  /** Events observed since the current window opened — the flood signal only. */
4902
4906
  observedInWindow = 0;
4907
+ /** Which kinds were observed since the current window opened — selects the message. */
4908
+ reviewSeenInWindow = false;
4909
+ failureSeenInWindow = false;
4903
4910
  unsubscribe = null;
4904
4911
  now;
4905
4912
  setTimeoutFn;
@@ -4917,7 +4924,8 @@ var NotificationDispatcher = class {
4917
4924
  start() {
4918
4925
  if (this.unsubscribe) return this.unsubscribe;
4919
4926
  const off = eventBus.subscribe((event) => {
4920
- if (TRIGGER_EVENTS.includes(event.type)) this.onTrigger();
4927
+ if (FAILURE_EVENTS.includes(event.type)) this.onTrigger("failure");
4928
+ else if (REVIEW_EVENTS.includes(event.type)) this.onTrigger("review");
4921
4929
  });
4922
4930
  this.unsubscribe = () => {
4923
4931
  off();
@@ -4929,9 +4937,15 @@ var NotificationDispatcher = class {
4929
4937
  };
4930
4938
  return this.unsubscribe;
4931
4939
  }
4932
- /** Exposed for tests; the bus subscription calls this. */
4933
- onTrigger() {
4940
+ /**
4941
+ * Exposed for tests; the bus subscription calls this. `kind` defaults to
4942
+ * 'review' so every existing call site/test (written before FAILURE_EVENTS
4943
+ * existed) keeps its exact behaviour.
4944
+ */
4945
+ onTrigger(kind = "review") {
4934
4946
  this.observedInWindow += 1;
4947
+ if (kind === "failure") this.failureSeenInWindow = true;
4948
+ else this.reviewSeenInWindow = true;
4935
4949
  if (this.cooldownTimer) {
4936
4950
  this.pendingWhileCoolingDown = true;
4937
4951
  return;
@@ -4944,7 +4958,11 @@ var NotificationDispatcher = class {
4944
4958
  }
4945
4959
  fire() {
4946
4960
  const count = this.observedInWindow;
4961
+ const sawReview = this.reviewSeenInWindow;
4962
+ const sawFailure = this.failureSeenInWindow;
4947
4963
  this.observedInWindow = 0;
4964
+ this.reviewSeenInWindow = false;
4965
+ this.failureSeenInWindow = false;
4948
4966
  let enabled = true;
4949
4967
  try {
4950
4968
  enabled = this.settingsFn().desktopNotifications;
@@ -4955,7 +4973,7 @@ var NotificationDispatcher = class {
4955
4973
  `[Control Plane] Review pending \u2014 desktop notification ${enabled ? "sent" : "suppressed (turned off)"}`
4956
4974
  );
4957
4975
  if (enabled) {
4958
- const message = count >= FLOOD_THRESHOLD ? floodMessage(count) : WAITING_MESSAGE;
4976
+ const message = count >= FLOOD_THRESHOLD ? floodMessage(count) : sawReview && sawFailure ? MIXED_MESSAGE : sawFailure ? FAILURE_MESSAGE : WAITING_MESSAGE;
4959
4977
  this.notifyFn(TITLE, message, process.platform, this.url);
4960
4978
  }
4961
4979
  this.cooldownTimer = this.setTimeoutFn(() => {