@private.me/xbind 3.0.0 → 3.0.2

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 (217) hide show
  1. package/README.md +55 -7
  2. package/dist-standalone/_deps/mldsa-wasm/dist/mldsa.js +1920 -1
  3. package/dist-standalone/_deps/shared/cjs/errors.js +729 -1
  4. package/dist-standalone/_deps/shared/cjs/index.js +463 -1
  5. package/dist-standalone/_deps/shared/cjs/types.js +315 -1
  6. package/dist-standalone/_deps/shared/errors.js +244 -1
  7. package/dist-standalone/_deps/shared/index.js +72 -1
  8. package/dist-standalone/_deps/shared/types.js +86 -1
  9. package/dist-standalone/_deps/ux-helpers/cjs/errors.js +1 -1
  10. package/dist-standalone/_deps/ux-helpers/cjs/index.js +1 -1
  11. package/dist-standalone/_deps/ux-helpers/cjs/pagination.js +1 -1
  12. package/dist-standalone/_deps/ux-helpers/cjs/progress.js +1 -1
  13. package/dist-standalone/_deps/ux-helpers/cjs/search.js +1 -1
  14. package/dist-standalone/_deps/ux-helpers/cjs/types.js +1 -1
  15. package/dist-standalone/_deps/ux-helpers/errors.js +1 -1
  16. package/dist-standalone/_deps/ux-helpers/index.js +1 -1
  17. package/dist-standalone/_deps/ux-helpers/pagination.js +1 -1
  18. package/dist-standalone/_deps/ux-helpers/progress.js +1 -1
  19. package/dist-standalone/_deps/ux-helpers/search.js +1 -1
  20. package/dist-standalone/_deps/xchange/auto-accept.js +1 -1
  21. package/dist-standalone/_deps/xchange/cjs/auto-accept.js +1 -1
  22. package/dist-standalone/_deps/xchange/cjs/errors.js +1 -1
  23. package/dist-standalone/_deps/xchange/cjs/index.js +1 -1
  24. package/dist-standalone/_deps/xchange/cjs/invite-client.js +1 -1
  25. package/dist-standalone/_deps/xchange/cjs/lazy-init.js +1 -1
  26. package/dist-standalone/_deps/xchange/cjs/trust-integration.js +1 -1
  27. package/dist-standalone/_deps/xchange/cjs/xchange.js +1 -1
  28. package/dist-standalone/_deps/xchange/errors.js +1 -1
  29. package/dist-standalone/_deps/xchange/index.js +1 -1
  30. package/dist-standalone/_deps/xchange/invite-client.js +1 -1
  31. package/dist-standalone/_deps/xchange/lazy-init.js +1 -1
  32. package/dist-standalone/_deps/xchange/trust-integration.js +1 -1
  33. package/dist-standalone/_deps/xchange/xchange.js +1 -1
  34. package/dist-standalone/_deps/xregistry/cjs/discovery.js +1 -1
  35. package/dist-standalone/_deps/xregistry/cjs/errors.js +1 -1
  36. package/dist-standalone/_deps/xregistry/cjs/index.js +1 -1
  37. package/dist-standalone/_deps/xregistry/cjs/registry.js +1 -1
  38. package/dist-standalone/_deps/xregistry/cjs/schema.js +1 -1
  39. package/dist-standalone/_deps/xregistry/cjs/types.js +1 -1
  40. package/dist-standalone/_deps/xregistry/discovery.js +1 -1
  41. package/dist-standalone/_deps/xregistry/errors.js +1 -1
  42. package/dist-standalone/_deps/xregistry/index.js +1 -1
  43. package/dist-standalone/_deps/xregistry/registry.js +1 -1
  44. package/dist-standalone/_deps/xregistry/schema.js +1 -1
  45. package/dist-standalone/_deps/xregistry/types.js +1 -1
  46. package/dist-standalone/agent-call.js +659 -1
  47. package/dist-standalone/agent-sdk.js +328 -1
  48. package/dist-standalone/agent.js +1800 -1
  49. package/dist-standalone/approval.js +193 -1
  50. package/dist-standalone/async-iterators.js +382 -1
  51. package/dist-standalone/auth.js +219 -1
  52. package/dist-standalone/auto-accept.js +229 -1
  53. package/dist-standalone/backup-config.js +201 -1
  54. package/dist-standalone/backup.js +326 -1
  55. package/dist-standalone/batch-operations.js +388 -1
  56. package/dist-standalone/cancellation.js +477 -1
  57. package/dist-standalone/checkpoint.js +186 -1
  58. package/dist-standalone/circuit-breaker.js +468 -1
  59. package/dist-standalone/cjs/agent-call.js +701 -1
  60. package/dist-standalone/cjs/agent-sdk.js +332 -1
  61. package/dist-standalone/cjs/agent.js +1837 -1
  62. package/dist-standalone/cjs/approval.js +199 -1
  63. package/dist-standalone/cjs/async-iterators.js +392 -1
  64. package/dist-standalone/cjs/auth.js +225 -1
  65. package/dist-standalone/cjs/auto-accept.js +233 -1
  66. package/dist-standalone/cjs/backup-config.js +207 -1
  67. package/dist-standalone/cjs/backup.js +330 -1
  68. package/dist-standalone/cjs/batch-operations.js +397 -1
  69. package/dist-standalone/cjs/cancellation.js +490 -1
  70. package/dist-standalone/cjs/checkpoint.js +193 -1
  71. package/dist-standalone/cjs/circuit-breaker.js +476 -1
  72. package/dist-standalone/cjs/cli/init.js +492 -1
  73. package/dist-standalone/cjs/config-validation.js +522 -1
  74. package/dist-standalone/cjs/connect.js +312 -1
  75. package/dist-standalone/cjs/connection-pool.js +506 -1
  76. package/dist-standalone/cjs/correlation-id.js +339 -1
  77. package/dist-standalone/cjs/crypto-utils.js +176 -1
  78. package/dist-standalone/cjs/debug-mode.js +534 -1
  79. package/dist-standalone/cjs/did-document.js +101 -1
  80. package/dist-standalone/cjs/did-privateme.js +130 -1
  81. package/dist-standalone/cjs/did-web.js +201 -1
  82. package/dist-standalone/cjs/discovery.js +462 -1
  83. package/dist-standalone/cjs/dual-mode.js +251 -1
  84. package/dist-standalone/cjs/email-templates.js +313 -1
  85. package/dist-standalone/cjs/email-transport.js +239 -1
  86. package/dist-standalone/cjs/envelope.js +538 -1
  87. package/dist-standalone/cjs/errors.js +913 -1
  88. package/dist-standalone/cjs/event-emitter.js +461 -1
  89. package/dist-standalone/cjs/gateway-state.js +55 -1
  90. package/dist-standalone/cjs/gateway-transport.js +120 -1
  91. package/dist-standalone/cjs/graceful-degradation.js +403 -1
  92. package/dist-standalone/cjs/guardrails.js +223 -1
  93. package/dist-standalone/cjs/health-check.js +336 -1
  94. package/dist-standalone/cjs/http-compat.js +272 -1
  95. package/dist-standalone/cjs/http-status-map.js +571 -1
  96. package/dist-standalone/cjs/identity.js +645 -1
  97. package/dist-standalone/cjs/index.js +406 -1
  98. package/dist-standalone/cjs/invitation.js +421 -1
  99. package/dist-standalone/cjs/invite.js +328 -1
  100. package/dist-standalone/cjs/key-agreement.js +335 -1
  101. package/dist-standalone/cjs/lazy-init.js +300 -1
  102. package/dist-standalone/cjs/logger.js +291 -1
  103. package/dist-standalone/cjs/mdns-discovery.js +202 -1
  104. package/dist-standalone/cjs/nonce-store.js +80 -1
  105. package/dist-standalone/cjs/pairing-manager.js +223 -1
  106. package/dist-standalone/cjs/plugin-system.js +264 -1
  107. package/dist-standalone/cjs/plugins/logging.js +168 -1
  108. package/dist-standalone/cjs/plugins/metrics.js +181 -1
  109. package/dist-standalone/cjs/plugins/validation.js +302 -1
  110. package/dist-standalone/cjs/policy.js +320 -1
  111. package/dist-standalone/cjs/progress-callbacks.js +583 -1
  112. package/dist-standalone/cjs/redis-nonce-store.js +76 -1
  113. package/dist-standalone/cjs/registry-middleware.js +50 -1
  114. package/dist-standalone/cjs/retry-strategies.js +544 -1
  115. package/dist-standalone/cjs/retry-transport.js +102 -1
  116. package/dist-standalone/cjs/runtime/browser.js +533 -1
  117. package/dist-standalone/cjs/runtime/edge.js +526 -1
  118. package/dist-standalone/cjs/runtime/react-native.js +394 -1
  119. package/dist-standalone/cjs/security-policy.js +245 -1
  120. package/dist-standalone/cjs/serialization.js +1040 -1
  121. package/dist-standalone/cjs/split-channel.js +225 -1
  122. package/dist-standalone/cjs/subscription-proof.js +230 -1
  123. package/dist-standalone/cjs/succession.js +148 -1
  124. package/dist-standalone/cjs/timeouts.js +412 -1
  125. package/dist-standalone/cjs/trace-context.js +424 -1
  126. package/dist-standalone/cjs/trace-spans.js +495 -1
  127. package/dist-standalone/cjs/transport.js +63 -1
  128. package/dist-standalone/cjs/trust-registry.js +991 -1
  129. package/dist-standalone/cjs/types/error-response.js +56 -1
  130. package/dist-standalone/cjs/vault-auth.js +178 -1
  131. package/dist-standalone/cjs/vault-store-loader.js +194 -1
  132. package/dist-standalone/cjs/verify.js +25 -1
  133. package/dist-standalone/cjs/version-info.js +543 -1
  134. package/dist-standalone/cjs/xfetch.js +340 -1
  135. package/dist-standalone/cli/init.js +455 -1
  136. package/dist-standalone/cli/setup.js +514 -1
  137. package/dist-standalone/cli/types.js +27 -1
  138. package/dist-standalone/cli/xbind.js +148 -1
  139. package/dist-standalone/config-validation.js +513 -1
  140. package/dist-standalone/connect.js +274 -1
  141. package/dist-standalone/connection-pool.js +500 -1
  142. package/dist-standalone/correlation-id.js +326 -1
  143. package/dist-standalone/crypto-utils.js +157 -1
  144. package/dist-standalone/debug-mode.js +510 -1
  145. package/dist-standalone/did-document.js +96 -1
  146. package/dist-standalone/did-privateme.js +121 -1
  147. package/dist-standalone/did-web.js +196 -1
  148. package/dist-standalone/discovery.js +458 -1
  149. package/dist-standalone/dual-mode.js +247 -1
  150. package/dist-standalone/email-templates.js +309 -1
  151. package/dist-standalone/email-transport.js +232 -1
  152. package/dist-standalone/envelope.js +525 -1
  153. package/dist-standalone/errors.js +896 -1
  154. package/dist-standalone/event-emitter.js +456 -1
  155. package/dist-standalone/gateway-state.js +51 -1
  156. package/dist-standalone/gateway-transport.js +116 -1
  157. package/dist-standalone/graceful-degradation.js +396 -1
  158. package/dist-standalone/guardrails.js +216 -1
  159. package/dist-standalone/health-check.js +332 -1
  160. package/dist-standalone/http-compat.js +267 -1
  161. package/dist-standalone/http-status-map.js +561 -1
  162. package/dist-standalone/identity.js +619 -1
  163. package/dist-standalone/index.js +78 -1
  164. package/dist-standalone/invitation.js +415 -1
  165. package/dist-standalone/invite.js +324 -1
  166. package/dist-standalone/key-agreement.js +325 -1
  167. package/dist-standalone/lazy-init.js +295 -1
  168. package/dist-standalone/logger.js +285 -1
  169. package/dist-standalone/mdns-discovery.js +195 -1
  170. package/dist-standalone/nonce-store.js +76 -1
  171. package/dist-standalone/pairing-manager.js +219 -1
  172. package/dist-standalone/plugin-system.js +257 -1
  173. package/dist-standalone/plugins/logging.d.ts +84 -0
  174. package/dist-standalone/plugins/logging.js +163 -0
  175. package/dist-standalone/plugins/metrics.d.ts +111 -0
  176. package/dist-standalone/plugins/metrics.js +176 -0
  177. package/dist-standalone/plugins/validation.d.ts +104 -0
  178. package/dist-standalone/plugins/validation.js +297 -0
  179. package/dist-standalone/policy.js +315 -1
  180. package/dist-standalone/progress-callbacks.js +576 -1
  181. package/dist-standalone/redis-nonce-store.js +72 -1
  182. package/dist-standalone/registry-middleware.js +47 -1
  183. package/dist-standalone/retry-strategies.js +534 -1
  184. package/dist-standalone/retry-transport.js +98 -1
  185. package/dist-standalone/runtime/browser.d.ts +311 -0
  186. package/dist-standalone/runtime/browser.js +516 -0
  187. package/dist-standalone/runtime/edge.d.ts +282 -0
  188. package/dist-standalone/runtime/edge.js +511 -0
  189. package/dist-standalone/runtime/react-native.d.ts +157 -0
  190. package/dist-standalone/runtime/react-native.js +383 -0
  191. package/dist-standalone/security-policy.js +239 -1
  192. package/dist-standalone/serialization.js +1031 -1
  193. package/dist-standalone/split-channel.js +219 -1
  194. package/dist-standalone/subscription-proof.js +224 -1
  195. package/dist-standalone/succession.js +142 -1
  196. package/dist-standalone/timeouts.js +398 -1
  197. package/dist-standalone/trace-context.js +414 -1
  198. package/dist-standalone/trace-spans.js +488 -1
  199. package/dist-standalone/transport.js +59 -1
  200. package/dist-standalone/trust-registry.js +950 -1
  201. package/dist-standalone/types/error-response.d.ts +209 -0
  202. package/dist-standalone/types/error-response.js +52 -0
  203. package/dist-standalone/vault-auth.js +174 -1
  204. package/dist-standalone/vault-store-loader.js +187 -1
  205. package/dist-standalone/verify.js +16 -1
  206. package/dist-standalone/version-info.js +530 -1
  207. package/dist-standalone/xfetch.js +335 -1
  208. package/package.json +4 -10
  209. package/share1.dat +0 -0
  210. package/dist-standalone/_deps/mldsa-wasm/LICENSE +0 -24
  211. package/dist-standalone/_deps/mldsa-wasm/package.json +0 -46
  212. package/dist-standalone/_deps/shared/cjs/package.json +0 -1
  213. package/dist-standalone/_deps/ux-helpers/cjs/package.json +0 -1
  214. package/dist-standalone/_deps/xchange/cjs/package.json +0 -1
  215. package/dist-standalone/_deps/xregistry/cjs/package.json +0 -1
  216. package/dist-standalone/cjs/package.json +0 -3
  217. package/dist-standalone/package.json +0 -10
@@ -1 +1,403 @@
1
- "use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.GracefulDegradationManager=void 0,exports.registryLookupWithFallback=registryLookupWithFallback,exports.sendWithTransportFallback=sendWithTransportFallback,exports.enhanceError=enhanceError;const shared_1=require("../_deps/shared/index.js"),ux_helpers_1=require("../_deps/ux-helpers/index.js"),DEFAULT_CACHE_CONFIG={defaultTTL:3e5,maxSize:1e3,allowStale:!0,maxStaleMs:36e5},DEFAULT_RETRY_CONFIG={maxAttempts:3,initialDelayMs:1e3,backoffMultiplier:2,maxDelayMs:3e4,jitter:!0};class GracefulDegradationManager{cacheConfig;retryConfig;cache=new Map;serviceStatus=new Map;constructor(e,t){this.cacheConfig={...DEFAULT_CACHE_CONFIG,...e},this.retryConfig={...DEFAULT_RETRY_CONFIG,...t}}getCached(e,t){const r=this.cache.get(e);if(!r)return;const a=Date.now()-r.cachedAt,s=a<=r.ttl,i=a>r.ttl&&a<=this.cacheConfig.maxStaleMs;return"CRITICAL"===t||"HIGH"===t?s?r.value:void 0:this.cacheConfig.allowStale&&(s||i)?(r.usageCount++,r.value):s?r.value:void 0}setCached(e,t,r){if(this.cache.size>=this.cacheConfig.maxSize){const e=this.cache.keys().next().value;e&&this.cache.delete(e)}this.cache.set(e,{value:t,cachedAt:Date.now(),ttl:r??this.cacheConfig.defaultTTL,usageCount:0})}invalidate(e){this.cache.delete(e)}clearCache(){this.cache.clear()}getCacheStats(){const e=Date.now(),t=Array.from(this.cache.entries()).map(([t,r])=>({key:t,age:e-r.cachedAt,usageCount:r.usageCount}));return{size:this.cache.size,maxSize:this.cacheConfig.maxSize,entries:t}}recordSuccess(e){const t=this.serviceStatus.get(e)||{health:"HEALTHY",lastSuccess:0,failureCount:0};t.health="HEALTHY",t.lastSuccess=Date.now(),t.failureCount=0,t.lastError=void 0,this.serviceStatus.set(e,t)}recordFailure(e,t){const r=this.serviceStatus.get(e)||{health:"HEALTHY",lastSuccess:0,failureCount:0};r.failureCount++,r.lastError=t,r.failureCount>=5?r.health="UNAVAILABLE":r.failureCount>=2&&(r.health="DEGRADED"),this.serviceStatus.set(e,r)}getServiceHealth(e){return this.serviceStatus.get(e)?.health??"HEALTHY"}getServiceStatus(e){return this.serviceStatus.get(e)}resetServiceHealth(e){e?this.serviceStatus.delete(e):this.serviceStatus.clear()}async withRetry(e,t){const r=this.getMaxAttempts(t.qos);let a;for(let t=1;t<=r;t++){const s=await e();if(s.ok)return s;if(a=s.error,t<r){const e=this.calculateBackoff(t);await this.sleep(e)}}return(0,shared_1.err)(a)}getMaxAttempts(e){switch(e){case"CRITICAL":case"LOW":return 1;case"HIGH":return this.retryConfig.maxAttempts;case"NORMAL":return Math.max(1,this.retryConfig.maxAttempts-1)}}calculateBackoff(e){const t=this.retryConfig.initialDelayMs*Math.pow(this.retryConfig.backoffMultiplier,e-1),r=Math.min(t,this.retryConfig.maxDelayMs);if(!this.retryConfig.jitter)return r;const a=new Uint8Array(4);crypto.getRandomValues(a);const s=.75+.5*(new DataView(a.buffer).getUint32(0)/4294967295);return Math.floor(r*s)}sleep(e){return new Promise(t=>setTimeout(t,e))}}async function registryLookupWithFallback(e,t,r,a){const s=`registry:${t}`;if("LOW"===r.qos){const e=a.getCached(s,r.qos);if(e)return(0,shared_1.ok)(e)}const i=await a.withRetry(()=>e.getEntry(t),r);if(i.ok){a.recordSuccess("registry");const e={publicKey:Buffer.from(i.value.publicKey).toString("base64")};return a.setCached(s,e),(0,shared_1.ok)(e)}if(a.recordFailure("registry",String(i.error)),"HIGH"===r.qos||"NORMAL"===r.qos){const e=a.getCached(s,r.qos);if(e)return(0,shared_1.ok)(e)}return(0,shared_1.err)((0,ux_helpers_1.createDetailedError)("REGISTRY_UNAVAILABLE",`Failed to resolve DID: ${t}`,{hint:"UNAVAILABLE"===a.getServiceHealth("registry")?"Registry service is currently unavailable. Using cached data may help.":"Registry lookup failed after retries. Check network connectivity.",suggested_action:"CRITICAL"===r.qos?"Wait for registry service to recover before retrying critical operations.":"Lower QoS level to NORMAL or LOW to use cached data.",docs:"https://private.me/docs/xbind/registry-fallback",severity:"CRITICAL"===r.qos?"critical":"error"}))}async function sendWithTransportFallback(e,t,r,a,s){if(0===e.length)return(0,shared_1.err)((0,ux_helpers_1.createDetailedError)("NO_TRANSPORT","No transport adapters configured",{hint:"Configure at least one transport adapter for envelope delivery.",suggested_action:"Add HttpsTransportAdapter or custom transport to agent configuration.",docs:"https://private.me/docs/xbind/transport",severity:"critical"}));const i=[];for(let o=0;o<e.length;o++){const c=e[o];if(!c)continue;const n=await s.withRetry(()=>c.send(t,r),a);if(n.ok)return s.recordSuccess(`transport:${o}`),(0,shared_1.ok)(void 0);i.push({transport:o,error:n.error}),s.recordFailure(`transport:${o}`,n.error)}const o=i[0]?.error??"SEND_FAILED";return(0,shared_1.err)((0,ux_helpers_1.createDetailedError)("TRANSPORT_EXHAUSTED",`All transport adapters failed: ${i.map(e=>e.error).join(", ")}`,{hint:"NETWORK_ERROR"===o?"Network connectivity issue detected. Check internet connection.":"TIMEOUT"===o?"Transport timeout occurred. Recipient may be unreachable or overloaded.":"RECIPIENT_UNREACHABLE"===o?"Recipient DID not found or offline.":"Transport layer failure. Check transport configuration.",suggested_action:"Verify recipient endpoint and network connectivity. Consider increasing timeout or adding fallback transports.",docs:"https://private.me/docs/xbind/transport-fallback",severity:"CRITICAL"===a.qos?"critical":"error"}))}function enhanceError(e,t,r){const a=r.getServiceHealth(t.type.split(":")[0]??"unknown"),s="DEGRADED"===a?" Service is experiencing degraded performance.":"UNAVAILABLE"===a?" Service is currently unavailable.":"",i="CRITICAL"===t.qos?" Consider retrying when service recovers.":"HIGH"===t.qos?" Cached data may be used if available.":" Using cached data regardless of staleness.";return{...e,hint:(e.hint??"")+s+i}}exports.GracefulDegradationManager=GracefulDegradationManager;
1
+ "use strict";
2
+ /**
3
+ * @module graceful-degradation
4
+ * Enhanced Reliability Capabilities for xBind
5
+ *
6
+ * Provides graceful degradation mechanisms to maintain service continuity
7
+ * when external services (registry, gateway, transport) experience issues.
8
+ *
9
+ * Features:
10
+ * - Quality-of-Service (QoS) tiers for operation prioritization
11
+ * - Fallback mechanisms with exponential backoff
12
+ * - Intelligent caching with TTL
13
+ * - User-friendly error messages with actionable recovery hints
14
+ */
15
+ Object.defineProperty(exports, "__esModule", { value: true });
16
+ exports.GracefulDegradationManager = void 0;
17
+ exports.registryLookupWithFallback = registryLookupWithFallback;
18
+ exports.sendWithTransportFallback = sendWithTransportFallback;
19
+ exports.enhanceError = enhanceError;
20
+ const shared_1 = require("../_deps/shared/index.js");
21
+ const ux_helpers_1 = require("../_deps/ux-helpers/index.js");
22
+ const DEFAULT_CACHE_CONFIG = {
23
+ defaultTTL: 300_000, // 5 minutes
24
+ maxSize: 1000,
25
+ allowStale: true,
26
+ maxStaleMs: 3600_000, // 1 hour
27
+ };
28
+ const DEFAULT_RETRY_CONFIG = {
29
+ maxAttempts: 3,
30
+ initialDelayMs: 1000,
31
+ backoffMultiplier: 2,
32
+ maxDelayMs: 30_000,
33
+ jitter: true,
34
+ };
35
+ /**
36
+ * Graceful Degradation Manager
37
+ *
38
+ * Coordinates fallback mechanisms, caching, and retry logic across
39
+ * registry, gateway, and transport layers.
40
+ */
41
+ class GracefulDegradationManager {
42
+ cacheConfig;
43
+ retryConfig;
44
+ cache = new Map();
45
+ serviceStatus = new Map();
46
+ constructor(cacheConfig, retryConfig) {
47
+ this.cacheConfig = { ...DEFAULT_CACHE_CONFIG, ...cacheConfig };
48
+ this.retryConfig = { ...DEFAULT_RETRY_CONFIG, ...retryConfig };
49
+ }
50
+ /* ── Cache Operations ── */
51
+ /**
52
+ * Get cached value if available and valid for the given QoS level.
53
+ *
54
+ * @param key Cache key
55
+ * @param qos Quality of service level
56
+ * @returns Cached value or undefined
57
+ */
58
+ getCached(key, qos) {
59
+ const entry = this.cache.get(key);
60
+ if (!entry)
61
+ return undefined;
62
+ const now = Date.now();
63
+ const age = now - entry.cachedAt;
64
+ const isFresh = age <= entry.ttl;
65
+ const isStale = age > entry.ttl && age <= this.cacheConfig.maxStaleMs;
66
+ // CRITICAL/HIGH: Only use fresh cache
67
+ if (qos === 'CRITICAL' || qos === 'HIGH') {
68
+ return isFresh ? entry.value : undefined;
69
+ }
70
+ // NORMAL/LOW: Use stale cache if allowed
71
+ if (this.cacheConfig.allowStale && (isFresh || isStale)) {
72
+ entry.usageCount++;
73
+ return entry.value;
74
+ }
75
+ return isFresh ? entry.value : undefined;
76
+ }
77
+ /**
78
+ * Store value in cache with TTL.
79
+ *
80
+ * @param key Cache key
81
+ * @param value Value to cache
82
+ * @param ttl Time-to-live in milliseconds (uses default if omitted)
83
+ */
84
+ setCached(key, value, ttl) {
85
+ // Evict oldest entry if cache is full
86
+ if (this.cache.size >= this.cacheConfig.maxSize) {
87
+ const oldestKey = this.cache.keys().next().value;
88
+ if (oldestKey)
89
+ this.cache.delete(oldestKey);
90
+ }
91
+ this.cache.set(key, {
92
+ value,
93
+ cachedAt: Date.now(),
94
+ ttl: ttl ?? this.cacheConfig.defaultTTL,
95
+ usageCount: 0,
96
+ });
97
+ }
98
+ /**
99
+ * Invalidate cached entry.
100
+ *
101
+ * @param key Cache key
102
+ */
103
+ invalidate(key) {
104
+ this.cache.delete(key);
105
+ }
106
+ /**
107
+ * Clear all cached entries.
108
+ */
109
+ clearCache() {
110
+ this.cache.clear();
111
+ }
112
+ /**
113
+ * Get cache statistics.
114
+ */
115
+ getCacheStats() {
116
+ const now = Date.now();
117
+ const entries = Array.from(this.cache.entries()).map(([key, entry]) => ({
118
+ key,
119
+ age: now - entry.cachedAt,
120
+ usageCount: entry.usageCount,
121
+ }));
122
+ return {
123
+ size: this.cache.size,
124
+ maxSize: this.cacheConfig.maxSize,
125
+ entries,
126
+ };
127
+ }
128
+ /* ── Service Health Tracking ── */
129
+ /**
130
+ * Record successful operation for a service.
131
+ *
132
+ * @param serviceName Service identifier (e.g., 'registry', 'gateway', 'transport')
133
+ */
134
+ recordSuccess(serviceName) {
135
+ const existing = this.serviceStatus.get(serviceName);
136
+ const status = existing || {
137
+ health: 'HEALTHY',
138
+ lastSuccess: 0,
139
+ failureCount: 0,
140
+ };
141
+ status.health = 'HEALTHY';
142
+ status.lastSuccess = Date.now();
143
+ status.failureCount = 0;
144
+ status.lastError = undefined;
145
+ this.serviceStatus.set(serviceName, status);
146
+ }
147
+ /**
148
+ * Record failed operation for a service.
149
+ *
150
+ * @param serviceName Service identifier
151
+ * @param error Error message
152
+ */
153
+ recordFailure(serviceName, error) {
154
+ const existing = this.serviceStatus.get(serviceName);
155
+ const status = existing || {
156
+ health: 'HEALTHY',
157
+ lastSuccess: 0,
158
+ failureCount: 0,
159
+ };
160
+ status.failureCount++;
161
+ status.lastError = error;
162
+ // Update health based on consecutive failures
163
+ if (status.failureCount >= 5) {
164
+ status.health = 'UNAVAILABLE';
165
+ }
166
+ else if (status.failureCount >= 2) {
167
+ status.health = 'DEGRADED';
168
+ }
169
+ this.serviceStatus.set(serviceName, status);
170
+ }
171
+ /**
172
+ * Get service health status.
173
+ *
174
+ * @param serviceName Service identifier
175
+ * @returns Service health status
176
+ */
177
+ getServiceHealth(serviceName) {
178
+ return this.serviceStatus.get(serviceName)?.health ?? 'HEALTHY';
179
+ }
180
+ /**
181
+ * Get detailed service status.
182
+ *
183
+ * @param serviceName Service identifier
184
+ * @returns Service status or undefined if not tracked
185
+ */
186
+ getServiceStatus(serviceName) {
187
+ return this.serviceStatus.get(serviceName);
188
+ }
189
+ /**
190
+ * Reset service health tracking.
191
+ *
192
+ * @param serviceName Service identifier (all services if omitted)
193
+ */
194
+ resetServiceHealth(serviceName) {
195
+ if (serviceName) {
196
+ this.serviceStatus.delete(serviceName);
197
+ }
198
+ else {
199
+ this.serviceStatus.clear();
200
+ }
201
+ }
202
+ /* ── Retry Logic ── */
203
+ /**
204
+ * Execute operation with retry logic based on QoS level.
205
+ *
206
+ * @param operation Async operation to execute
207
+ * @param context Operation context with QoS level
208
+ * @returns Result of operation
209
+ */
210
+ async withRetry(operation, context) {
211
+ const maxAttempts = this.getMaxAttempts(context.qos);
212
+ let lastError;
213
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) {
214
+ const result = await operation();
215
+ if (result.ok) {
216
+ return result;
217
+ }
218
+ lastError = result.error;
219
+ // Don't retry on last attempt
220
+ if (attempt < maxAttempts) {
221
+ const delay = this.calculateBackoff(attempt);
222
+ await this.sleep(delay);
223
+ }
224
+ }
225
+ // All retries exhausted
226
+ return (0, shared_1.err)(lastError);
227
+ }
228
+ /**
229
+ * Get maximum retry attempts for QoS level.
230
+ */
231
+ getMaxAttempts(qos) {
232
+ switch (qos) {
233
+ case 'CRITICAL':
234
+ return 1; // Fail fast
235
+ case 'HIGH':
236
+ return this.retryConfig.maxAttempts;
237
+ case 'NORMAL':
238
+ return Math.max(1, this.retryConfig.maxAttempts - 1);
239
+ case 'LOW':
240
+ return 1; // No retries
241
+ }
242
+ }
243
+ /**
244
+ * Calculate exponential backoff delay with optional jitter.
245
+ */
246
+ calculateBackoff(attempt) {
247
+ const base = this.retryConfig.initialDelayMs * Math.pow(this.retryConfig.backoffMultiplier, attempt - 1);
248
+ const capped = Math.min(base, this.retryConfig.maxDelayMs);
249
+ if (!this.retryConfig.jitter) {
250
+ return capped;
251
+ }
252
+ // Add ±25% jitter using cryptographically secure random
253
+ const randomBytes = new Uint8Array(4);
254
+ crypto.getRandomValues(randomBytes);
255
+ const randomValue = new DataView(randomBytes.buffer).getUint32(0) / 0xffffffff;
256
+ const jitterFactor = 0.75 + randomValue * 0.5;
257
+ return Math.floor(capped * jitterFactor);
258
+ }
259
+ /**
260
+ * Sleep for specified milliseconds.
261
+ */
262
+ sleep(ms) {
263
+ return new Promise((resolve) => setTimeout(resolve, ms));
264
+ }
265
+ }
266
+ exports.GracefulDegradationManager = GracefulDegradationManager;
267
+ /* ── Registry Fallback ── */
268
+ /**
269
+ * Registry lookup with graceful degradation.
270
+ *
271
+ * Provides intelligent fallback for DID resolution:
272
+ * 1. Try primary registry lookup
273
+ * 2. On failure, check cache based on QoS level
274
+ * 3. Return user-friendly error if all fallbacks exhausted
275
+ *
276
+ * @param registry Trust registry instance
277
+ * @param did DID to resolve
278
+ * @param context Operation context
279
+ * @param manager Degradation manager
280
+ * @returns Registry lookup result
281
+ */
282
+ async function registryLookupWithFallback(registry, did, context, manager) {
283
+ const cacheKey = `registry:${did}`;
284
+ // Check cache first for LOW QoS
285
+ if (context.qos === 'LOW') {
286
+ const cached = manager.getCached(cacheKey, context.qos);
287
+ if (cached) {
288
+ return (0, shared_1.ok)(cached);
289
+ }
290
+ }
291
+ // Attempt registry lookup with retry
292
+ const lookupResult = await manager.withRetry(() => registry.getEntry(did), context);
293
+ if (lookupResult.ok) {
294
+ manager.recordSuccess('registry');
295
+ // Convert Uint8Array publicKey to base64 string for caching
296
+ const publicKeyStr = Buffer.from(lookupResult.value.publicKey).toString('base64');
297
+ const entryValue = { publicKey: publicKeyStr };
298
+ manager.setCached(cacheKey, entryValue);
299
+ return (0, shared_1.ok)(entryValue);
300
+ }
301
+ // Lookup failed - record failure
302
+ manager.recordFailure('registry', String(lookupResult.error));
303
+ // Try cache fallback for HIGH/NORMAL QoS
304
+ if (context.qos === 'HIGH' || context.qos === 'NORMAL') {
305
+ const cached = manager.getCached(cacheKey, context.qos);
306
+ if (cached) {
307
+ return (0, shared_1.ok)(cached);
308
+ }
309
+ }
310
+ // All fallbacks exhausted - return detailed error
311
+ return (0, shared_1.err)((0, ux_helpers_1.createDetailedError)('REGISTRY_UNAVAILABLE', `Failed to resolve DID: ${did}`, {
312
+ hint: manager.getServiceHealth('registry') === 'UNAVAILABLE'
313
+ ? 'Registry service is currently unavailable. Using cached data may help.'
314
+ : 'Registry lookup failed after retries. Check network connectivity.',
315
+ suggested_action: context.qos === 'CRITICAL'
316
+ ? 'Wait for registry service to recover before retrying critical operations.'
317
+ : 'Lower QoS level to NORMAL or LOW to use cached data.',
318
+ docs: 'https://private.me/docs/xbind/registry-fallback',
319
+ severity: context.qos === 'CRITICAL' ? 'critical' : 'error',
320
+ }));
321
+ }
322
+ /* ── Transport Fallback ── */
323
+ /**
324
+ * Send envelope with transport fallback.
325
+ *
326
+ * Tries multiple transports in order until one succeeds:
327
+ * 1. Primary transport with retry
328
+ * 2. Fallback transports (if configured)
329
+ * 3. Return detailed error if all transports fail
330
+ *
331
+ * @param transports Array of transport adapters
332
+ * @param envelope Envelope to send
333
+ * @param recipientDid Recipient DID
334
+ * @param context Operation context
335
+ * @param manager Degradation manager
336
+ * @returns Send result
337
+ */
338
+ async function sendWithTransportFallback(transports, envelope, recipientDid, context, manager) {
339
+ if (transports.length === 0) {
340
+ return (0, shared_1.err)((0, ux_helpers_1.createDetailedError)('NO_TRANSPORT', 'No transport adapters configured', {
341
+ hint: 'Configure at least one transport adapter for envelope delivery.',
342
+ suggested_action: 'Add HttpsTransportAdapter or custom transport to agent configuration.',
343
+ docs: 'https://private.me/docs/xbind/transport',
344
+ severity: 'critical',
345
+ }));
346
+ }
347
+ const errors = [];
348
+ for (let i = 0; i < transports.length; i++) {
349
+ const transport = transports[i];
350
+ if (!transport)
351
+ continue;
352
+ const sendResult = await manager.withRetry(() => transport.send(envelope, recipientDid), context);
353
+ if (sendResult.ok) {
354
+ manager.recordSuccess(`transport:${i}`);
355
+ return (0, shared_1.ok)(undefined);
356
+ }
357
+ // Record failure and try next transport
358
+ errors.push({ transport: i, error: sendResult.error });
359
+ manager.recordFailure(`transport:${i}`, sendResult.error);
360
+ }
361
+ // All transports failed
362
+ const primaryError = errors[0]?.error ?? 'SEND_FAILED';
363
+ return (0, shared_1.err)((0, ux_helpers_1.createDetailedError)('TRANSPORT_EXHAUSTED', `All transport adapters failed: ${errors.map(e => e.error).join(', ')}`, {
364
+ hint: primaryError === 'NETWORK_ERROR'
365
+ ? 'Network connectivity issue detected. Check internet connection.'
366
+ : primaryError === 'TIMEOUT'
367
+ ? 'Transport timeout occurred. Recipient may be unreachable or overloaded.'
368
+ : primaryError === 'RECIPIENT_UNREACHABLE'
369
+ ? 'Recipient DID not found or offline.'
370
+ : 'Transport layer failure. Check transport configuration.',
371
+ suggested_action: 'Verify recipient endpoint and network connectivity. Consider increasing timeout or adding fallback transports.',
372
+ docs: 'https://private.me/docs/xbind/transport-fallback',
373
+ severity: context.qos === 'CRITICAL' ? 'critical' : 'error',
374
+ }));
375
+ }
376
+ /* ── User-Friendly Error Messages ── */
377
+ /**
378
+ * Enhance error with QoS-appropriate messaging.
379
+ *
380
+ * @param error Original error
381
+ * @param context Operation context
382
+ * @param manager Degradation manager
383
+ * @returns Enhanced error with actionable hints
384
+ */
385
+ function enhanceError(error, context, manager) {
386
+ const serviceHealth = manager.getServiceHealth(context.type.split(':')[0] ?? 'unknown');
387
+ // Add service health context to hint
388
+ const healthHint = serviceHealth === 'DEGRADED'
389
+ ? ' Service is experiencing degraded performance.'
390
+ : serviceHealth === 'UNAVAILABLE'
391
+ ? ' Service is currently unavailable.'
392
+ : '';
393
+ // Add QoS-specific suggestions
394
+ const qosHint = context.qos === 'CRITICAL'
395
+ ? ' Consider retrying when service recovers.'
396
+ : context.qos === 'HIGH'
397
+ ? ' Cached data may be used if available.'
398
+ : ' Using cached data regardless of staleness.';
399
+ return {
400
+ ...error,
401
+ hint: (error.hint ?? '') + healthHint + qosHint,
402
+ };
403
+ }
@@ -1 +1,223 @@
1
- "use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.Guardrails=exports.PolicyDenied=void 0,exports.extractSuggestion=extractSuggestion,exports.toPolicyDenied=toPolicyDenied;class PolicyDenied extends Error{rule;allowed;requested;suggestion;details;constructor(e,t,o,i,l){super(`Policy violation: ${e}`),this.rule=e,this.allowed=t,this.requested=o,this.suggestion=i,this.details=l,this.name="PolicyDenied"}toAgentFormat(){return[`POLICY_VIOLATION: ${this.rule}`,`REQUESTED: ${JSON.stringify(this.requested)}`,`ALLOWED: ${JSON.stringify(this.allowed)}`,`SUGGESTION: ${this.suggestion}`].join("\n")}toUserFormat(){return[`❌ Policy Violation: ${this.rule}`,"",`You requested: ${this.formatValue(this.requested)}`,`Policy allows: ${this.formatValue(this.allowed)}`,"",`💡 Suggestion: ${this.suggestion}`].join("\n")}toLogFormat(){return{error:"PolicyDenied",rule:this.rule,allowed:this.allowed,requested:this.requested,suggestion:this.suggestion,details:this.details,timestamp:(new Date).toISOString()}}formatValue(e){return"number"==typeof e?this.rule.includes("amount")||this.rule.includes("Amount")?`$${e.toLocaleString()}`:e.toLocaleString():Array.isArray(e)?e.map(e=>`"${String(e)}"`).join(", "):JSON.stringify(e)}}exports.PolicyDenied=PolicyDenied;class Guardrails{static amountExceeded(e){let t,o;switch(e.type){case"per-transaction":t="maxAmount",o=`Reduce amount to $${e.limit.toLocaleString()} or request policy update from admin`;break;case"daily":t="dailyAmount",o=`Reduce amount to stay within daily limit of $${e.limit.toLocaleString()}, or wait until tomorrow`;break;case"monthly":t="monthlyAmount",o=`Reduce amount to stay within monthly limit of $${e.limit.toLocaleString()}, or wait until next month`}return new PolicyDenied(t,e.limit,e.requested,o)}static rateLimitExceeded(e){const t=e.windowMs/1e3,o=t>=60?`Wait ${Math.ceil(t/60)} minutes before making additional calls`:`Wait ${t} seconds before making additional calls`;return new PolicyDenied("callsPerMinute",e.limit,e.current+1,o)}static toolDenied(e){const[t]=e.tool.split(":"),o=0===e.allowed.length?`No tools are currently allowed. Request approval from admin to enable "${e.tool}"`:`Request approval from admin to add "${e.tool}" or "${t}:*" to allowed tools`;return new PolicyDenied("allowedTools",e.allowed,e.tool,o)}static scopeDenied(e){const t=0===e.allowed.length?`No scopes are currently allowed. Request approval from admin to enable "${e.scope}"`:`Request approval from admin to add "${e.scope}" to allowed scopes`;return new PolicyDenied("allowedScopes",e.allowed,e.scope,t)}static fieldDenied(e){const t=`Field "${e.field}" is restricted. Contact admin to update policy.fieldFilters for "${e.tool}"`;return new PolicyDenied("fieldFilters",e.allowed,e.field,t)}static timeWindowDenied(e){const t=`Actions are only allowed between ${e.allowedStart} and ${e.allowedEnd}. Current time is ${e.current.toTimeString()}`;return new PolicyDenied("timeWindow",`${e.allowedStart} - ${e.allowedEnd}`,e.current.toTimeString(),t)}static custom(e){return new PolicyDenied(e.rule,e.allowed,e.requested,e.suggestion)}}function extractSuggestion(e){return e.fix??"Contact administrator to update policy"}function toPolicyDenied(e){let t;switch(e.constraint){case"amountPerTxn":t="maxAmount";break;case"dailyAmount":t="dailyAmount";break;case"callsPerMinute":t="callsPerMinute";break;case"scope":t="allowedScopes";break;case"tool":t="allowedTools";break;default:t=String(e.constraint)}return new PolicyDenied(t,e.allowed,e.requested,e.fix,e)}exports.Guardrails=Guardrails;
1
+ "use strict";
2
+ /**
3
+ * @module guardrails
4
+ * Enhanced error messages with actionable suggestions for policy violations
5
+ *
6
+ * When policies deny requests, guardrails provide specific, actionable
7
+ * guidance on how to fix the issue or what to request from admins.
8
+ */
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.Guardrails = exports.PolicyDenied = void 0;
11
+ exports.extractSuggestion = extractSuggestion;
12
+ exports.toPolicyDenied = toPolicyDenied;
13
+ /**
14
+ * Policy violation error with actionable suggestions
15
+ */
16
+ class PolicyDenied extends Error {
17
+ rule;
18
+ allowed;
19
+ requested;
20
+ suggestion;
21
+ details;
22
+ constructor(
23
+ /** Which policy rule was violated */
24
+ rule,
25
+ /** What the policy allows */
26
+ allowed,
27
+ /** What was requested */
28
+ requested,
29
+ /** Actionable suggestion for how to fix */
30
+ suggestion,
31
+ /** Full policy violation details */
32
+ details) {
33
+ super(`Policy violation: ${rule}`);
34
+ this.rule = rule;
35
+ this.allowed = allowed;
36
+ this.requested = requested;
37
+ this.suggestion = suggestion;
38
+ this.details = details;
39
+ this.name = 'PolicyDenied';
40
+ }
41
+ /**
42
+ * Format error for AI agent consumption
43
+ *
44
+ * Structured format optimized for LLM parsing
45
+ */
46
+ toAgentFormat() {
47
+ return [
48
+ `POLICY_VIOLATION: ${this.rule}`,
49
+ `REQUESTED: ${JSON.stringify(this.requested)}`,
50
+ `ALLOWED: ${JSON.stringify(this.allowed)}`,
51
+ `SUGGESTION: ${this.suggestion}`,
52
+ ].join('\n');
53
+ }
54
+ /**
55
+ * Format error for human consumption
56
+ *
57
+ * User-friendly format with clear next steps
58
+ */
59
+ toUserFormat() {
60
+ return [
61
+ `❌ Policy Violation: ${this.rule}`,
62
+ '',
63
+ `You requested: ${this.formatValue(this.requested)}`,
64
+ `Policy allows: ${this.formatValue(this.allowed)}`,
65
+ '',
66
+ `💡 Suggestion: ${this.suggestion}`,
67
+ ].join('\n');
68
+ }
69
+ /**
70
+ * Format error for logs (structured JSON)
71
+ */
72
+ toLogFormat() {
73
+ return {
74
+ error: 'PolicyDenied',
75
+ rule: this.rule,
76
+ allowed: this.allowed,
77
+ requested: this.requested,
78
+ suggestion: this.suggestion,
79
+ details: this.details,
80
+ timestamp: new Date().toISOString(),
81
+ };
82
+ }
83
+ formatValue(value) {
84
+ if (typeof value === 'number') {
85
+ // Format currency if it looks like money
86
+ if (this.rule.includes('amount') || this.rule.includes('Amount')) {
87
+ return `$${value.toLocaleString()}`;
88
+ }
89
+ return value.toLocaleString();
90
+ }
91
+ if (Array.isArray(value)) {
92
+ return value.map((v) => `"${String(v)}"`).join(', ');
93
+ }
94
+ return JSON.stringify(value);
95
+ }
96
+ }
97
+ exports.PolicyDenied = PolicyDenied;
98
+ /**
99
+ * Guardrail builder - fluent API for creating policy errors with suggestions
100
+ *
101
+ * @example
102
+ * ```typescript
103
+ * const error = Guardrails.amountExceeded({
104
+ * requested: 5000,
105
+ * limit: 1000,
106
+ * type: 'per-transaction'
107
+ * });
108
+ *
109
+ * throw error;
110
+ * // ❌ Policy Violation: maxAmount
111
+ * // You requested: $5,000
112
+ * // Policy allows: $1,000
113
+ * // 💡 Suggestion: Reduce amount to $1,000 or request policy update from admin
114
+ * ```
115
+ */
116
+ class Guardrails {
117
+ /**
118
+ * Amount exceeded (per-transaction limit)
119
+ */
120
+ static amountExceeded(options) {
121
+ let rule;
122
+ let suggestion;
123
+ switch (options.type) {
124
+ case 'per-transaction':
125
+ rule = 'maxAmount';
126
+ suggestion = `Reduce amount to $${options.limit.toLocaleString()} or request policy update from admin`;
127
+ break;
128
+ case 'daily':
129
+ rule = 'dailyAmount';
130
+ suggestion = `Reduce amount to stay within daily limit of $${options.limit.toLocaleString()}, or wait until tomorrow`;
131
+ break;
132
+ case 'monthly':
133
+ rule = 'monthlyAmount';
134
+ suggestion = `Reduce amount to stay within monthly limit of $${options.limit.toLocaleString()}, or wait until next month`;
135
+ break;
136
+ }
137
+ return new PolicyDenied(rule, options.limit, options.requested, suggestion);
138
+ }
139
+ /**
140
+ * Rate limit exceeded
141
+ */
142
+ static rateLimitExceeded(options) {
143
+ const windowSec = options.windowMs / 1000;
144
+ const suggestion = windowSec >= 60
145
+ ? `Wait ${Math.ceil(windowSec / 60)} minutes before making additional calls`
146
+ : `Wait ${windowSec} seconds before making additional calls`;
147
+ return new PolicyDenied('callsPerMinute', options.limit, options.current + 1, suggestion);
148
+ }
149
+ /**
150
+ * Tool not allowed
151
+ */
152
+ static toolDenied(options) {
153
+ const [service] = options.tool.split(':');
154
+ const suggestion = options.allowed.length === 0
155
+ ? `No tools are currently allowed. Request approval from admin to enable "${options.tool}"`
156
+ : `Request approval from admin to add "${options.tool}" or "${service}:*" to allowed tools`;
157
+ return new PolicyDenied('allowedTools', options.allowed, options.tool, suggestion);
158
+ }
159
+ /**
160
+ * Scope not allowed
161
+ */
162
+ static scopeDenied(options) {
163
+ const suggestion = options.allowed.length === 0
164
+ ? `No scopes are currently allowed. Request approval from admin to enable "${options.scope}"`
165
+ : `Request approval from admin to add "${options.scope}" to allowed scopes`;
166
+ return new PolicyDenied('allowedScopes', options.allowed, options.scope, suggestion);
167
+ }
168
+ /**
169
+ * Field filter denied (data access restriction)
170
+ */
171
+ static fieldDenied(options) {
172
+ const suggestion = `Field "${options.field}" is restricted. Contact admin to update policy.fieldFilters for "${options.tool}"`;
173
+ return new PolicyDenied('fieldFilters', options.allowed, options.field, suggestion);
174
+ }
175
+ /**
176
+ * Time window restriction
177
+ */
178
+ static timeWindowDenied(options) {
179
+ const suggestion = `Actions are only allowed between ${options.allowedStart} and ${options.allowedEnd}. Current time is ${options.current.toTimeString()}`;
180
+ return new PolicyDenied('timeWindow', `${options.allowedStart} - ${options.allowedEnd}`, options.current.toTimeString(), suggestion);
181
+ }
182
+ /**
183
+ * Generic policy violation
184
+ */
185
+ static custom(options) {
186
+ return new PolicyDenied(options.rule, options.allowed, options.requested, options.suggestion);
187
+ }
188
+ }
189
+ exports.Guardrails = Guardrails;
190
+ /**
191
+ * Helper: Extract actionable suggestion from AgentError details
192
+ *
193
+ * Used by agent.call() to convert PolicyViolationDetails into PolicyDenied
194
+ */
195
+ function extractSuggestion(details) {
196
+ return details.fix ?? 'Contact administrator to update policy';
197
+ }
198
+ /**
199
+ * Helper: Convert PolicyViolationDetails to PolicyDenied
200
+ */
201
+ function toPolicyDenied(details) {
202
+ let rule;
203
+ switch (details.constraint) {
204
+ case 'amountPerTxn':
205
+ rule = 'maxAmount';
206
+ break;
207
+ case 'dailyAmount':
208
+ rule = 'dailyAmount';
209
+ break;
210
+ case 'callsPerMinute':
211
+ rule = 'callsPerMinute';
212
+ break;
213
+ case 'scope':
214
+ rule = 'allowedScopes';
215
+ break;
216
+ case 'tool':
217
+ rule = 'allowedTools';
218
+ break;
219
+ default:
220
+ rule = String(details.constraint);
221
+ }
222
+ return new PolicyDenied(rule, details.allowed, details.requested, details.fix, details);
223
+ }